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 }