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.character; 8 import engine.vn.render; 9 import engine; 10 import std.format; 11 12 /** 13 Character declaration 14 */ 15 struct CharDecl { 16 /** 17 The symbolic name for the character 18 */ 19 string name; 20 21 /** 22 Name of character 23 */ 24 dstring[string] names; 25 26 /** 27 Textures for character 28 */ 29 string[string] textures; 30 } 31 32 /** 33 Character registry 34 */ 35 class CharacterRegistry { 36 private static: 37 CharDecl[string] registry; 38 39 public static: 40 /** 41 Register a character 42 */ 43 public void register(string name, CharDecl declaration) { 44 registry[name] = declaration; 45 } 46 47 /** 48 Create a character from its name 49 */ 50 public Character create(string name) { 51 return new Character(registry[name]); 52 } 53 } 54 55 /** 56 A character in the visual novel 57 */ 58 class Character { 59 private: 60 61 /** 62 The system name of the character 63 */ 64 string name; 65 66 /** 67 All the names the character can have 68 */ 69 dstring[string] dnames; 70 71 /** 72 Named textures for the character 73 */ 74 AtlasIndex[string] textures; 75 76 /** 77 The texture of the character 78 */ 79 string texture; 80 81 /** 82 Cached texture size 83 */ 84 vec2 textureSize; 85 86 /** 87 How visible the character is 88 */ 89 float visibility = 0; 90 91 public: 92 93 dstring displayName() { 94 return dnames[CurrentLanguage]; 95 } 96 97 /** 98 Whether to show the character 99 */ 100 bool shown; 101 102 /** 103 Position of the character on the screen 104 */ 105 vec2 position; 106 107 /** 108 Whether the character's texture is flipped vertically 109 */ 110 bool isFlipped; 111 112 /** 113 The current texture 114 */ 115 AtlasIndex* currentTexture() { 116 return texture in textures; 117 } 118 119 /** 120 Sets the character's texture 121 */ 122 void setTexture(string texture) { 123 this.texture = texture; 124 this.textureSize = currentTexture.area.zw; 125 } 126 127 /** 128 Creates a character from a definition 129 */ 130 this(CharDecl decl) { 131 this.name = decl.name; 132 this.dnames = decl.names; 133 foreach (texName, texture; decl.textures) { 134 AppLog.info("VN Engine", "Added character texture %s", texName); 135 GameAtlas.add(genTexName(name, texName), texture); 136 textures[texName] = GameAtlas[genTexName(name, texName)]; 137 } 138 139 // Set texture to neutral if any was given for the character 140 if (decl.textures.length > 0) this.setTexture("neutral"); 141 } 142 143 /** 144 Clean up textures between scene switches. 145 */ 146 ~this() { 147 148 // Remove unused textures from the atlas 149 foreach(texName, _; textures) { 150 GameAtlas.remove(genTexName(name, texName)); 151 } 152 } 153 154 /** 155 Activates the character causing them to jump if visible 156 */ 157 private float activationTimer = 0.0; 158 private float jump = 0; 159 void activate() { 160 activationTimer = 1; 161 } 162 163 void update() { 164 165 // Handle visibility fade 166 if (shown) { 167 if (visibility < 1) visibility += 10*deltaTime(); 168 if (visibility > 1) visibility = 1; 169 } else { 170 if (visibility > 0) visibility -= 10*deltaTime(); 171 if (visibility < 0) visibility = 0; 172 } 173 174 // TODO: use the animation system for this 175 176 // Handle activation timer countdown 177 if (activationTimer > 0) activationTimer -= 5*deltaTime(); 178 if (activationTimer < 0) activationTimer = 0; 179 180 if (activationTimer > 0.5) { 181 float t = (0.5-activationTimer)*2; 182 jump = lerp!float(-12, 0, t); 183 } else { 184 float t = (0.5-activationTimer)*2; 185 jump = lerp!float(0, -12, t); 186 } 187 } 188 189 void draw() { 190 if (!shown && visibility == 0) return; 191 if (currentTexture is null) return; 192 GameBatch.draw( 193 *currentTexture, 194 vec4(position.x, position.y-jump, textureSize.x, textureSize.y), 195 vec4.init, 196 vec2(textureSize.x/2, textureSize.y), 197 0, 198 isFlipped ? SpriteFlip.Horizontal : SpriteFlip.None, 199 vec4(1, 1, 1, visibility) 200 ); 201 } 202 } 203 204 private string genTexName(string charName, string texName) { 205 return "%s/%s".format(charName, texName); 206 }