1 /*
2     Copyright © 2020, Luna Nielsen
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module engine.vn.script;
8 import engine.vn;
9 public import engine.vn.script.instr;
10 
11 /**
12     A bookmark to where in the script to continue from (for saving)
13 */
14 struct Bookmark {
15     /**
16         The scene current being shown
17     */
18     string scene;
19 
20     /**
21         The last blocking line + 1
22     */
23     int line;
24 }
25 
26 /**
27     A visual novel script
28 */
29 class Script {
30 private:
31     /**
32         Manuscript holder
33     */
34     Scene[string] manuscripts;
35 
36     /**
37         Execution offset.
38     */
39     int next;
40 
41     /**
42         Current instruction
43     */
44     int current() { return next-1; }
45 
46     /**
47         The current scene
48     */
49     string currentScene;
50 
51     /**
52         Returns the current instruction list
53     */
54     IScriptInstr[] instructions() { return manuscripts[currentScene].instructions; }
55 
56 public:
57 
58     /**
59         The state of the visual novel
60     */
61     VNState state;
62 
63     /**
64         Destructor
65     */
66     ~this() {
67         next = -1;
68         manuscripts.clear();
69     }
70 
71     /**
72         Constructs a new script
73         Always runs the "main" scene
74     */
75     this(VNState state, Scene[string] manuscript) {
76         this.state = state;
77         manuscripts = manuscript;
78         this.changeScene("main");
79     }
80 
81     /**
82         Changes the scene
83     */
84     void changeScene(string scene) {
85         currentScene = scene;
86         next = 0;
87         state.loadCharacters(manuscripts[currentScene].characters);
88     }
89 
90     /**
91         Run the next instruction(s)
92         Execution continues until the next non-blocking instruction
93     */
94     void runNext() {
95         while(!instructions[next].execute(this)) {
96             next++;
97             
98             // Loop if we're at the end of the instructions
99             if (next >= instructions.length) {
100                 next = 0;
101                 runNext();
102                 return;
103             }
104         }
105         next++;
106 
107         // Loop if we're at the end of the instructions
108         if (next >= instructions.length) {
109             next = 0;
110             return;
111         }
112     }
113 
114     /**
115         Gets a bookmark
116     */
117     Bookmark bookmark() {
118 
119         // Count back instructions to the last blocking instruction
120         int c = current();
121         if (c >= 1) {
122             while (!instructions[c].isBlocking) {
123 
124                 // Make sure we don't go *too* far back
125                 if (c >= 1) c--;
126                 else break;
127             }
128         }
129         
130         // Cap to first instruction if we went under instruction 0
131         if (c < 0) c = 0;
132 
133         return Bookmark(
134             currentScene,
135             c+1 // We want to return the instruction *after* the last blocking one
136         );
137     }
138 }
139 
140 /**
141     A scene in the VN
142 */
143 class Scene {
144 public:
145 
146     /**
147         Creates a new scene
148     */
149     this(string[] characters, IScriptInstr[] instructions) {
150         this.characters = characters;
151         this.instructions = instructions;
152     }
153 
154     /**
155         The characters to be loaded in this scene
156     */
157     string[] characters;
158 
159     /**
160         The instructions associated with a scene
161     */
162     IScriptInstr[] instructions;
163 }
164 
165 /**
166     A script instruction
167 */
168 interface IScriptInstr {
169 
170     /**
171         Execution function that runs the instruction
172     */
173     bool execute(Script script);
174 
175     /**
176         Gets whether the instruction is blocking
177     */
178     bool isBlocking();
179 }