1 /* 2 Visual Novel Subsystem 3 4 Copyright © 2020, Luna Nielsen 5 Distributed under the 2-Clause BSD License, see LICENSE file. 6 7 Authors: Luna Nielsen 8 */ 9 module engine.vn; 10 import engine.vn.render; 11 import engine.vn.script; 12 public import engine.vn.character; 13 public import engine.vn.dialg; 14 public import engine.vn.script; 15 import engine; 16 17 /** 18 Holds the current state of the Visual Novel system 19 */ 20 class VNState { 21 public: 22 this() { 23 dialogue = new DialogueManager(this); 24 } 25 26 /** 27 Music being played 28 */ 29 Music music; 30 31 /** 32 Ambience loops being played 33 */ 34 Music[] ambience; 35 36 /** 37 The background/cg texture 38 */ 39 Texture cg; 40 41 /** 42 The list of the characters currently in the scene 43 */ 44 Character[string] characters; 45 46 /** 47 The dialogue manager 48 */ 49 DialogueManager dialogue; 50 51 /** 52 The current script being executed by the engine 53 */ 54 Script script; 55 56 /** 57 Changes the currently active scene 58 */ 59 void changeScene(string scene=null) { 60 61 // Clear all the old characters out 62 this.characters.clear(); 63 64 // Change the scene 65 script.changeScene(scene); 66 } 67 68 /** 69 Cleanup procedure before closing VN state 70 */ 71 void cleanup() { 72 import core.memory : GC; 73 74 // Clear all the old characters out 75 foreach(name, character; characters) { 76 debug AppLog.info("VN Memory Managment", "Destroying %s...", name); 77 destroy(character); 78 } 79 80 // Clear all the old characters out 81 this.characters.clear(); 82 83 destroy(cg); 84 destroy(dialogue); 85 destroy(script); 86 87 // Force GC to collect everything 88 GC.collect(); 89 } 90 91 /** 92 Sets dialogue auto next flag 93 */ 94 void markAutoNext() { 95 dialogue.autoNext = true; 96 } 97 98 /** 99 Loads the characters defined in a list 100 */ 101 void loadCharacters(string[] characters) { 102 103 // Load characters from the character registry 104 foreach(character; characters) { 105 this.characters[character] = CharacterRegistry.create(character); 106 } 107 } 108 109 /** 110 Loads a script 111 */ 112 void loadScript(Scene[string] manuscript) { 113 this.script = new Script(this, manuscript); 114 script.runNext(); 115 } 116 117 /** 118 Updates the visual novel 119 */ 120 void update() { 121 122 // No need to update when there's no manuscript loaded 123 if (script is null) return; 124 125 // If the "Confirm" binding or left mouse button was pressed, advance dialogue 126 if (Input.wasPressed("Confirm") || Mouse.isButtonClicked(MouseButton.Left)) { 127 128 // AutoNext dialogue cannot be skipped 129 // Note: AutoNext dialogue should be brief 130 if (!dialogue.autoNext) { 131 if (!dialogue.done) { 132 dialogue.skip(); 133 } else { 134 script.runNext(); 135 } 136 } 137 } 138 139 // Handle AutoNext dialogue 140 if (dialogue.autoNext) { 141 if (dialogue.done()) { 142 dialogue.autoNext = false; 143 script.runNext(); 144 } 145 } 146 147 // Update the characters 148 foreach(character; characters) { 149 character.update(); 150 } 151 152 // Update the dialogue manager 153 dialogue.update(); 154 155 // Cleanup 156 foreach(amb; ambience) { 157 if (!amb.isPlaying) { 158 destroy(amb); 159 } 160 } 161 } 162 163 /** 164 Draw's the visual novel 165 */ 166 void draw() { 167 168 // If there's no manuscript then we don't need to try to draw stuff 169 if (script is null) { 170 GameFont.changeSize(48); 171 GameFont.draw("No manuscripts are loaded!"d, vec2(16, 16)); 172 GameFont.flush(); 173 return; 174 } 175 176 // First draw background/cg 177 if (cg !is null) { 178 GameBatch.draw(cg, vec4(0, 0, kmCameraViewWidth, kmCameraViewHeight)); 179 GameBatch.flush(); 180 } 181 182 // Draw the characters 183 foreach(character; characters) { 184 character.draw(); 185 } 186 187 // Draw dialogue 188 dialogue.draw(); 189 } 190 } 191 192 shared static this() { 193 Input.registerKey("Confirm", Key.KeySpace); 194 }