1 module game; 2 3 import cloud; 4 import birdo; 5 import player; 6 import cactus; 7 import ground; 8 import counter; 9 import helpers; 10 import resourcemanager; 11 12 import dsfml.graphics; 13 import dsfml.window; 14 import dsfml.system; 15 import dsfml.audio; 16 17 import std.conv; 18 import std.random; 19 import std.algorithm; 20 import std.datetime; 21 import std.datetime.stopwatch : StopWatch; 22 23 static this() { 24 assert(resource_manager.register!Texture("assets/gameover.png", "gameover")); 25 assert(resource_manager.register!SoundBuffer("assets/sound_button.mp3", "button")); 26 assert(resource_manager.register!SoundBuffer("assets/sound_hit.mp3", "hit")); 27 assert(resource_manager.register!SoundBuffer("assets/sound_score100.mp3", "score")); 28 } 29 30 /++ 31 + Dino game, 32 + 33 + Creates a properly sized window and runs the whole simulation 34 +/ 35 class Game { 36 private RenderWindow window; 37 private Player player; 38 private Ground ground; 39 private Birdo[] birdos; 40 private Cloud[] clouds; 41 private Cactus[] cactuses; 42 private float seconds_to_next_birdo; 43 private float seconds_to_next_cloud; 44 private float seconds_to_next_cactus; 45 private StopWatch birdo_stopwatch; 46 private StopWatch cloud_stopwatch; 47 private StopWatch cactus_stopwatch; 48 private Counter counter; 49 50 private Sound button_sound; 51 private Sound hit_sound; 52 private Sound score_sound; 53 54 /++ Width/height ratio +/ 55 static immutable float width_to_height_ratio = 4; 56 57 private void birdo_generation() { 58 if(birdo_stopwatch.peek.asSeconds > seconds_to_next_birdo) { 59 birdos ~= new Birdo(window.getSize.x, Birdo.random_level); 60 birdos[$-1].window_height = window.getSize.y; 61 seconds_to_next_birdo = uniform01!float * 8 + 1; 62 birdo_stopwatch.reset(); 63 64 if(seconds_to_next_cactus - cactus_stopwatch.peek.asSeconds < .6) { 65 seconds_to_next_cactus += .6; 66 } 67 } 68 } 69 70 private void cactus_generation() { 71 if(cactus_stopwatch.peek.asSeconds > seconds_to_next_cactus) { 72 cactuses ~= random_cactus_at(600, window.getSize.y); 73 seconds_to_next_cactus = uniform01!float * 1 + .6; 74 cactus_stopwatch.reset(); 75 76 if(seconds_to_next_birdo - birdo_stopwatch.peek.asSeconds < .6) { 77 seconds_to_next_birdo += .6; 78 } 79 } 80 } 81 82 private void cloud_generation() { 83 if(cloud_stopwatch.peek.asSeconds > seconds_to_next_cloud) { 84 clouds ~= new Cloud(window.getSize.x + 100, uniform01!float * 50 + 20); 85 seconds_to_next_cloud = uniform01!float * 8 + 1; 86 cloud_stopwatch.reset(); 87 } 88 } 89 90 private void on_key_pressed(Keyboard.Key code) { 91 if(code == Keyboard.Key.Space || code == Keyboard.Key.Up) { 92 const jumped = player.jump(); 93 if(jumped && button_sound.status != Sound.Status.Playing) { 94 button_sound.play(); 95 } 96 } 97 if(code == Keyboard.Key.Down && player.vert_velocity > 0) { 98 player.vert_velocity = 0; 99 } 100 } 101 102 private bool update_simulation() { 103 bool close; 104 105 birdo_generation(); 106 cloud_generation(); 107 cactus_generation(); 108 109 foreach(cactus; cactuses) { 110 if(player.collider.intersects(cactus.collider)) { 111 close = true; 112 player.dead = true; 113 } 114 } 115 foreach(birdo; birdos) { 116 if(player.collider.intersects(birdo.collider)) { 117 close = true; 118 player.dead = true; 119 } 120 } 121 122 player.update(); 123 foreach(cactus; cactuses) { 124 cactus.move(player.displacement); 125 } 126 foreach(birdo; birdos) { 127 birdo.move(player.displacement); 128 } 129 foreach(cloud; clouds) { 130 cloud.move(player.displacement / 12); 131 } 132 ground.move(player.displacement); 133 134 // Remove cactuses that moved outside of the screen 135 cactuses = remove!"a.horizontal_offset < -100"(cactuses); 136 137 counter.num = player.displacement_tot.to!uint / 45; 138 if(counter.num % 100 == 0 && counter.num != 0 && score_sound.status != Sound.Status.Playing) { 139 score_sound.play(); 140 } 141 142 return close; 143 } 144 145 private void draw_objects() { 146 window.clear(Color(247, 247, 247)); 147 148 window.draw(ground); 149 150 foreach(cactus; cactuses) { 151 window.draw(cactus); 152 } 153 foreach(cloud; clouds) { 154 window.draw(cloud); 155 } 156 foreach(birdo; birdos) { 157 window.draw(birdo); 158 } 159 160 window.draw(player); 161 window.draw(counter); 162 window.display(); 163 } 164 165 private void session() { 166 bool close; 167 while(!close) { 168 foreach(ev; window.events) 169 switch(ev.type) { 170 case Event.EventType.Closed: window.close(); close = true; break; 171 case Event.EventType.KeyPressed: on_key_pressed(ev.key.code); break; 172 default: break; 173 } 174 175 close = update_simulation(); 176 draw_objects(); 177 } 178 } 179 180 private void endscreen() { 181 const tex = window.screenshot; 182 auto screenshot = new Sprite(tex); 183 auto gameover_sprite = new Sprite(resource_manager.get!Texture("gameover")); 184 gameover_sprite.position((window.middle - resource_manager.get!Texture("gameover").middle).changeType!float); 185 186 bool open = true; 187 bool key_released = true, key_pressed; 188 189 import std.traits : EnumMembers; 190 foreach(key; EnumMembers!(Keyboard.Key)) 191 if(Keyboard.isKeyPressed(key)) { 192 key_released = false; 193 } 194 195 while(open) { 196 foreach(ev; window.events) { 197 switch(ev.type) { 198 case Event.EventType.Closed: window.close(); open = false; break; 199 case Event.EventType.KeyReleased: key_released = true; break; 200 case Event.EventType.KeyPressed: key_pressed = key_released; break; 201 default: break; 202 } 203 } 204 if(key_pressed && key_released) { 205 open = false; 206 } 207 208 window.draw(screenshot); 209 window.draw(gameover_sprite); 210 window.display(); 211 } 212 } 213 214 private void initialize(uint size) { 215 player = new Player(); 216 player.window_height = size; 217 218 cactus_stopwatch.start(); 219 cloud_stopwatch.start(); 220 birdo_stopwatch.start(); 221 222 ground = new Ground(); 223 224 button_sound = new Sound; 225 button_sound.setBuffer(resource_manager.get!SoundBuffer("button")); 226 227 hit_sound = new Sound; 228 hit_sound.setBuffer(resource_manager.get!SoundBuffer("hit")); 229 230 score_sound = new Sound; 231 score_sound.setBuffer(resource_manager.get!SoundBuffer("score")); 232 233 cactuses.length = 0; 234 birdos.length = 0; 235 236 counter = new Counter; 237 238 seconds_to_next_birdo = 30; 239 seconds_to_next_cloud = 0; 240 seconds_to_next_cactus = 3; 241 } 242 243 /++ Creates a Dino game instance, size refers to height of the window +/ 244 this(uint size) { 245 window = new RenderWindow(VideoMode(cast(uint) width_to_height_ratio * size, size), "dino"); 246 window.setVerticalSyncEnabled(true); 247 248 initialize(size); 249 } 250 251 /++ Runs the game +/ 252 void run() { 253 while(window.isOpen) { 254 session(); 255 window.display(); 256 hit_sound.play(); 257 258 if(!window.isOpen) { 259 break; 260 } 261 262 endscreen(); 263 button_sound.play(); 264 265 initialize(window.size.y); 266 } 267 } 268 }