diff --git a/Assets/GameFlow/P_GameFlowDirector.prefab b/Assets/GameFlow/P_GameFlowDirector.prefab
index 9683b07711b0f22a7bdc5314e677e2b5c642f278..eacf86a5844e3972120f2fe987175680befc0292 100644
--- a/Assets/GameFlow/P_GameFlowDirector.prefab
+++ b/Assets/GameFlow/P_GameFlowDirector.prefab
@@ -9,7 +9,6 @@ GameObject:
   serializedVersion: 6
   m_Component:
   - component: {fileID: 2588948569428291066}
-  - component: {fileID: 9064289196084631770}
   - component: {fileID: 3125219115457853314}
   m_Layer: 0
   m_Name: P_GameFlowDirector
@@ -33,19 +32,6 @@ Transform:
   m_Children: []
   m_Father: {fileID: 0}
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!114 &9064289196084631770
-MonoBehaviour:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 5757977688348045174}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 17c7e1f216297f1479ff6723af0f1ef9, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  lineDelaySeconds: 3
 --- !u!114 &3125219115457853314
 MonoBehaviour:
   m_ObjectHideFlags: 0
@@ -59,15 +45,4 @@ MonoBehaviour:
   m_Name: 
   m_EditorClassIdentifier: 
   gameModeLibrary: {fileID: 11400000, guid: 94af40f0f7d742d4a8899f1c73be4e49, type: 2}
-  characterPrefabs:
-  - {fileID: 7709689548724039816, guid: 1caedb3ce263f0644899455bca4abc8c, type: 3}
-  - {fileID: 7709689548724039816, guid: fe5876d5925daf74e9f72e1b03c31cb5, type: 3}
-  visitorArrivalDelay: 5
-  monologPlayer: {fileID: 9064289196084631770}
   visitorSpawnSlot: {fileID: 0}
-  onCharacterMonologRequested:
-    m_PersistentCalls:
-      m_Calls: []
-  onCharacterMonologSectionRequested:
-    m_PersistentCalls:
-      m_Calls: []
diff --git a/Assets/Scenes/SCENE_Game.unity b/Assets/Scenes/SCENE_Game.unity
index 2cf77465522ee1a95d3121cf04ba9021c71c89ac..f1b3896ad98fceb37c0e5c09d7b52b48c07d83fe 100644
--- a/Assets/Scenes/SCENE_Game.unity
+++ b/Assets/Scenes/SCENE_Game.unity
@@ -1532,7 +1532,8 @@ PrefabInstance:
       propertyPath: m_Name
       value: P_GameFlowDirector
       objectReference: {fileID: 0}
-    m_RemovedComponents: []
+    m_RemovedComponents:
+    - {fileID: 9064289196084631770, guid: 3b8067e793844d944beeb1f515d63235, type: 3}
     m_RemovedGameObjects: []
     m_AddedGameObjects: []
     m_AddedComponents: []
diff --git a/Assets/Scripts/Runtime/Character/CharacterDefinition.cs b/Assets/Scripts/Runtime/Character/CharacterDefinition.cs
index bba5e934ccc4d15d33e73dc6af88526e4d154a01..cf00d6ed4ac21da4adcf35bcc6f14c5b71e68aa6 100644
--- a/Assets/Scripts/Runtime/Character/CharacterDefinition.cs
+++ b/Assets/Scripts/Runtime/Character/CharacterDefinition.cs
@@ -1,10 +1,11 @@
 using Ink.Runtime;
 using Retropair.Devices;
+using Retropair.Ink;
 using Slothsoft.UnityExtensions;
 using UnityEngine;
 
 namespace Retropair.Character {
-    sealed class CharacterDefinition : MonoBehaviour {
+    sealed class CharacterDefinition : MonoBehaviour, IInkStoryProvider {
 
         [Header("Story")]
 
diff --git a/Assets/Scripts/Runtime/Character/CharacterMonologLineDisplayer.cs b/Assets/Scripts/Runtime/Character/CharacterMonologLineDisplayer.cs
index a5b224ba57b93e6c80949e9dd7349518a874553b..27e83cf7ba17bb9fa5c32c22a27693eb2005a98e 100644
--- a/Assets/Scripts/Runtime/Character/CharacterMonologLineDisplayer.cs
+++ b/Assets/Scripts/Runtime/Character/CharacterMonologLineDisplayer.cs
@@ -1,5 +1,7 @@
 using System.Collections;
 using MyBox;
+using Retropair.Ink;
+using Retropair.Services;
 using UnityEngine;
 using UnityEngine.UI;
 
@@ -29,13 +31,16 @@ sealed class CharacterMonologLineDisplayer : MonoBehaviour {
 
         void Start() {
             DisplayLine("", true);
-            CharacterMonologPlayer.onLineChanged += DisplayLine;
-            CharacterMonologPlayer.onTryCompleteLine += TryCompleteLine;
+            var inkPlayer = ServiceLocator.Get<IInkPlayerService>();
+            inkPlayer.onLineChanged += DisplayLine;
+            inkPlayer.onTryCompleteLine += TryCompleteLine;
         }
 
         void OnDestroy() {
-            CharacterMonologPlayer.onLineChanged -= DisplayLine;
-            CharacterMonologPlayer.onTryCompleteLine -= TryCompleteLine;
+            if (ServiceLocator.TryGet<IInkPlayerService>(out var inkPlayer)) {
+                inkPlayer.onLineChanged -= DisplayLine;
+                inkPlayer.onTryCompleteLine -= TryCompleteLine;
+            }
         }
 
         void DisplayLine(string line, bool shouldHideHint) {
diff --git a/Assets/Scripts/Runtime/Character/CharacterMonologPlayer.cs.meta b/Assets/Scripts/Runtime/Character/CharacterMonologPlayer.cs.meta
deleted file mode 100644
index c1f5ce84ea13327e8db5273eeea84ab89d0980b2..0000000000000000000000000000000000000000
--- a/Assets/Scripts/Runtime/Character/CharacterMonologPlayer.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 17c7e1f216297f1479ff6723af0f1ef9
\ No newline at end of file
diff --git a/Assets/Scripts/Runtime/Character/ECharacterMonologSection.cs b/Assets/Scripts/Runtime/Character/ECharacterMonologSection.cs
rename from Assets/Scripts/Runtime/Character/CharacterMonologSection.cs
rename to Assets/Scripts/Runtime/Character/ECharacterMonologSection.cs
index 034804a5676bc1feb96c241de61a5cecdd7357fd..f9b8b9d74f440385f3fec9bb1c9fed1d8aa940e6
--- a/Assets/Scripts/Runtime/Character/CharacterMonologSection.cs
+++ b/Assets/Scripts/Runtime/Character/ECharacterMonologSection.cs
@@ -1,6 +1,6 @@
 namespace Retropair.Character {
 
-    public enum CharacterMonologSection {
+    public enum ECharacterMonologSection {
         Arrival,
         Main,
         Success,
diff --git a/Assets/Scripts/Runtime/GameFlow/GameFlowDirector.cs b/Assets/Scripts/Runtime/GameFlow/GameFlowDirector.cs
index af8d7e20693f6d25851f9bc175bccb20eb4142bd..83b4b2d16cbcf0574c05ab7292bc17fcc7cb4f6b 100644
--- a/Assets/Scripts/Runtime/GameFlow/GameFlowDirector.cs
+++ b/Assets/Scripts/Runtime/GameFlow/GameFlowDirector.cs
@@ -10,10 +10,6 @@ sealed class GameFlowDirector : MonoBehaviour {
         [SerializeField]
         GameModeLibrary gameModeLibrary = default;
 
-        // TODO: refactor to service, then access like that in game modes
-        [SerializeField]
-        public CharacterMonologPlayer monologPlayer = default;
-
         [Header("World References")]
         // TODO: move into dedicated service
         // TOOD: create dictionary of slots, e.g. SerializableKeyValuePairs<EScenePivot, Transform>
diff --git a/Assets/Scripts/Runtime/GameFlow/GameModeStory.cs b/Assets/Scripts/Runtime/GameFlow/GameModeStory.cs
index 6527ad7e4973ac598b71542c17547f2682a77981..f0796951067a7d8b677d9289f2afefa5f17df0f7 100644
--- a/Assets/Scripts/Runtime/GameFlow/GameModeStory.cs
+++ b/Assets/Scripts/Runtime/GameFlow/GameModeStory.cs
@@ -1,6 +1,8 @@
 using System;
 using System.Collections;
 using Retropair.Character;
+using Retropair.Ink;
+using Retropair.Services;
 using UnityEngine;
 
 namespace Retropair.GameFlow {
@@ -23,7 +25,7 @@ public GameObject ChooseNextVisitor() {
 
         public override IEnumerator StartFlow(GameFlowDirector director) {
             var visitorSpawnSlot = director.visitorSpawnSlot;
-            var monologPlayer = director.monologPlayer;
+            var inkPlayer = ServiceLocator.Get<IInkPlayerService>();
 
             while (true) {
                 yield return new WaitForSeconds(visitorArrivalDelay);
@@ -33,16 +35,16 @@ public override IEnumerator StartFlow(GameFlowDirector director) {
                 director.visitorSpawnSlot.SpawnVisitor(visitorPrefab);
 
                 // prepare monolog playing
-                monologPlayer.SetMonolog(visitorSpawnSlot.currentVisitorDefinition);
+                inkPlayer.SetInkStory(visitorSpawnSlot.currentVisitorDefinition);
 
                 // arrival monolog
                 onMoodChanged?.Invoke(CharacterMood.Initial);
-                yield return monologPlayer.PlayMonologHighPrioBlocking(CharacterMonologSection.Arrival);
+                yield return inkPlayer.PlayHighPrioBlocking(ECharacterMonologSection.Arrival.ToString());
 
                 // spawn console and trigger main monolog
                 visitorSpawnSlot.SpawnVisitorConsole();
                 yield return new WaitWhile(() => visitorSpawnSlot.isHoldingDevice);
-                monologPlayer.PlayMonologLowPrioParallel(CharacterMonologSection.Main);
+                inkPlayer.PlayLowPrioParallel(ECharacterMonologSection.Main.ToString());
 
                 // visitor waits while device not successfully returned
                 while (true) {
@@ -58,7 +60,7 @@ public override IEnumerator StartFlow(GameFlowDirector director) {
 
                     // fail monolog if device was returned, but requirements not met
                     onMoodChanged?.Invoke(CharacterMood.Deny);
-                    yield return monologPlayer.PlayMonologHighPrioBlocking(CharacterMonologSection.Failure);
+                    yield return inkPlayer.PlayHighPrioBlocking(ECharacterMonologSection.Failure.ToString());
 
                     onMoodChanged?.Invoke(CharacterMood.Initial);
                     visitorSpawnSlot.SetInteractionWithVisitorConsoleAllowed(true);
@@ -67,10 +69,10 @@ public override IEnumerator StartFlow(GameFlowDirector director) {
 
                 // device was returned successfully
                 onMoodChanged?.Invoke(CharacterMood.Success);
-                yield return monologPlayer.PlayMonologHighPrioBlocking(CharacterMonologSection.Success);
+                yield return inkPlayer.PlayHighPrioBlocking(ECharacterMonologSection.Success.ToString());
 
                 // success monolog done, cleanup scene
-                monologPlayer.Clear();
+                inkPlayer.Stop();
                 visitorSpawnSlot.DespawnVisitorConsole();
                 visitorSpawnSlot.DespawnVisitor();
             }
diff --git a/Assets/Scripts/Runtime/Ink.meta b/Assets/Scripts/Runtime/Ink.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6dbe4cea197e3ce0b1bb9ee258b54ab046ebb26c
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 769bda9f6bef6624e937c4bb8a17b214
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/Runtime/Ink/IInkPlayerService.cs b/Assets/Scripts/Runtime/Ink/IInkPlayerService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2c4cdd05a094e61bc891c9813040f32d76f9de46
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink/IInkPlayerService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections;
+
+namespace Retropair.Ink {
+    interface IInkPlayerService {
+        public void Stop();
+
+        public void SetInkStory(IInkStoryProvider inkStoryProvider);
+
+        public void PlayLowPrioParallel(string inkStorySection);
+        public IEnumerator PlayHighPrioBlocking(string inkStorySection);
+
+        public event Action<string, bool> onLineChanged;
+        public event Func<bool> onTryCompleteLine;
+        public event Action onMonologFinished;
+    }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Runtime/Ink/IInkPlayerService.cs.meta b/Assets/Scripts/Runtime/Ink/IInkPlayerService.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..90bc0348a12f0e253b724db41e50cddc260430b1
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink/IInkPlayerService.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 5e77893dc1ab357429220e4f1413dd8c
\ No newline at end of file
diff --git a/Assets/Scripts/Runtime/Ink/IInkStoryProvider.cs b/Assets/Scripts/Runtime/Ink/IInkStoryProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..857d2ea559de5a181711cd66c4d6e04600c82e93
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink/IInkStoryProvider.cs
@@ -0,0 +1,8 @@
+using Ink.Runtime;
+
+namespace Retropair.Ink {
+    interface IInkStoryProvider {
+        public Story GetStory();
+        public Story GetHighPrioStory();
+    }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Runtime/Ink/IInkStoryProvider.cs.meta b/Assets/Scripts/Runtime/Ink/IInkStoryProvider.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cf32bfa404bd0ace8355dc558485854929f241f7
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink/IInkStoryProvider.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: caf1591436eb2324f8bcd03071679f65
\ No newline at end of file
diff --git a/Assets/Scripts/Runtime/Ink/InkPlayerServiceAsset.cs b/Assets/Scripts/Runtime/Ink/InkPlayerServiceAsset.cs
new file mode 100644
index 0000000000000000000000000000000000000000..36e476a5db6470d916f35f64a59c80bf65d6c9a8
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink/InkPlayerServiceAsset.cs
@@ -0,0 +1,9 @@
+using Retropair.Services;
+using UnityEngine;
+
+namespace Retropair.Ink {
+    [CreateAssetMenu]
+    sealed class InkPlayerServiceAsset : ScriptableObject, IServiceFactory<IInkPlayerService> {
+        public IInkPlayerService InstantiateService(GameObject parent) => parent.AddComponent<InkPlayerServiceImpl>();
+    }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Runtime/Ink/InkPlayerServiceAsset.cs.meta b/Assets/Scripts/Runtime/Ink/InkPlayerServiceAsset.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8b2348fd5db306895d9187d66e5934d9b0e9efec
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink/InkPlayerServiceAsset.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 2b2d9bb998598964db00b225a6c154d7
\ No newline at end of file
diff --git a/Assets/Scripts/Runtime/Ink/InkPlayerServiceImpl.cs b/Assets/Scripts/Runtime/Ink/InkPlayerServiceImpl.cs
rename from Assets/Scripts/Runtime/Character/CharacterMonologPlayer.cs
rename to Assets/Scripts/Runtime/Ink/InkPlayerServiceImpl.cs
index 77cbbc5ee798f52887f2a972c3c8df3617f911de..15137df2f17575c99502d3409713d98633fe9ef8
--- a/Assets/Scripts/Runtime/Character/CharacterMonologPlayer.cs
+++ b/Assets/Scripts/Runtime/Ink/InkPlayerServiceImpl.cs
@@ -4,13 +4,12 @@
 using Retropair.Player;
 using UnityEngine;
 
-namespace Retropair.Character {
-    sealed class CharacterMonologPlayer : MonoBehaviour {
-        public float lineDelaySeconds = 3.0f;
+namespace Retropair.Ink {
+    sealed class InkPlayerServiceImpl : MonoBehaviour, IInkPlayerService {
 
-        public static event Action<string, bool> onLineChanged;
-        public static event Func<bool> onTryCompleteLine;
-        public static event Action onMonologFinished;
+        public event Action<string, bool> onLineChanged;
+        public event Func<bool> onTryCompleteLine;
+        public event Action onMonologFinished;
 
         Story currentStory = default;
         Story currentHighPrioStory = default;
@@ -31,28 +30,24 @@ void OnDestroy() {
             PlayerComponent.onDialogueLineAdvanceIntent -= HandleLineAdvanceIntent;
         }
 
-        public void Clear() {
+        public void Stop() {
             currentStory = currentHighPrioStory = null;
             StopCoroutine(currentMonologCoroutine);
             lineAdvanceRequested = false;
             onLineChanged?.Invoke("", true);
         }
 
-        public void SetMonolog(CharacterDefinition monologProvider) {
-            if (currentStory == monologProvider.GetStory()) {
+        public void SetInkStory(IInkStoryProvider inkStoryProvider) {
+            if (currentStory == inkStoryProvider.GetStory()) {
                 return;
             }
 
-            currentStory = monologProvider.GetStory();
-            currentHighPrioStory = monologProvider.GetHighPrioStory();
+            currentStory = inkStoryProvider.GetStory();
+            currentHighPrioStory = inkStoryProvider.GetHighPrioStory();
         }
 
-        public void SetHideHint(bool newShouldHideHint) {
-            shouldHideHint = newShouldHideHint;
-        }
-
-        public void PlayMonologLowPrioParallel(CharacterMonologSection section) {
-            currentStory?.ChoosePathString(section.ToString());
+        public void PlayLowPrioParallel(string inkStorySection) {
+            currentStory?.ChoosePathString(inkStorySection);
 
             if (currentMonologCoroutine != null) {
                 StopCoroutine(currentMonologCoroutine);
@@ -61,8 +56,8 @@ public void PlayMonologLowPrioParallel(CharacterMonologSection section) {
             currentMonologCoroutine = StartCoroutine(PlayLines(false));
         }
 
-        public IEnumerator PlayMonologHighPrioBlocking(CharacterMonologSection section) {
-            currentHighPrioStory?.ChoosePathString(section.ToString());
+        public IEnumerator PlayHighPrioBlocking(string inkStorySection) {
+            currentHighPrioStory?.ChoosePathString(inkStorySection);
             isHighPrioMonologPlaying = true;
             yield return PlayLines(true);
             isHighPrioMonologPlaying = false;
diff --git a/Assets/Scripts/Runtime/Ink/InkPlayerServiceImpl.cs.meta b/Assets/Scripts/Runtime/Ink/InkPlayerServiceImpl.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..732be16c3b95ad1488f69de04331c95bdaa90a9e
--- /dev/null
+++ b/Assets/Scripts/Runtime/Ink/InkPlayerServiceImpl.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 742952beb1a7c9346b5a53b1baafaeab
\ No newline at end of file
diff --git a/Assets/Services/InkPlayerService.asset b/Assets/Services/InkPlayerService.asset
new file mode 100644
index 0000000000000000000000000000000000000000..582893cc1d89f47c51a65bbcf40ab377c6e0487b
--- /dev/null
+++ b/Assets/Services/InkPlayerService.asset
@@ -0,0 +1,15 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2b2d9bb998598964db00b225a6c154d7, type: 3}
+  m_Name: InkPlayerService
+  m_EditorClassIdentifier: 
+  prefab: {fileID: 0}
diff --git a/Assets/Services/InkPlayerService.asset.meta b/Assets/Services/InkPlayerService.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..729b1e9a95fd08315a8a4aa639acc5445230666b
--- /dev/null
+++ b/Assets/Services/InkPlayerService.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 83fcfcdd82c484944a6eb9b7c7933727
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 11400000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Services/ServiceRegistry.asset b/Assets/Services/ServiceRegistry.asset
index 35897a77e8cc0975380cc97f9396e2fd08eb3577..9730ae935786ed3b1793fcb8c01cda4594e2d5af 100644
--- a/Assets/Services/ServiceRegistry.asset
+++ b/Assets/Services/ServiceRegistry.asset
@@ -17,7 +17,10 @@ MonoBehaviour:
   - Retropair.Audio.IAudioService
   - Retropair.IDebugService
   - Retropair.Cursor.ICursorService
+  - Retropair.Ink.IInkPlayerService
   factories:
   - {fileID: 11400000, guid: f22a73e488a372a48bf338048fdbb7eb, type: 2}
   - {fileID: 11400000, guid: 6b5129fbec85a6e4f96e3dce749b3007, type: 2}
   - {fileID: 11400000, guid: a9a4f179d4594104ab343aa13e840607, type: 2}
+  - {fileID: 11400000, guid: 83fcfcdd82c484944a6eb9b7c7933727, type: 2}
+  - {fileID: 0}
