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 }