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 }