Rev 9011 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
9011 | leency | 1 | #include |
2 | #include |
||
3 | #include "images.hpp" |
||
4 | |||
5 | //Global const strings |
||
6 | const char HEADER_STRING[] = "Flappy bird"; |
||
7 | const char CONTROL_STRING[] = "SPACEBAR TO JUMP"; |
||
8 | const char GAMEOVER_STRING[] = "GAMEOVER"; |
||
9 | const char ANY_KEY_STRING[] = "Press any key for restart"; |
||
10 | const char SELECT_SPEED_STRING[] = "select the speed of the game"; |
||
11 | const char FAST_STRING[] = "1 FAST"; |
||
12 | const char SLOW_STRING[] = "2 SLOW"; |
||
13 | |||
14 | //Global const variables |
||
15 | const int WINDOW_WIDTH = 400; |
||
16 | const int WINDOW_HEIGHT = 400; |
||
17 | const int BORDER_TOP = 24; |
||
18 | const int BORDER_LEFT = 5; |
||
19 | const int BORDER_RIGHT = 5; |
||
20 | const int BORDER_DOWN = 5; |
||
21 | |||
22 | enum GameState |
||
23 | { |
||
24 | GAMESTATE_MENU, |
||
25 | GAMESTATE_STARTED, |
||
26 | GAMESTATE_GAMEOVER |
||
27 | }; |
||
28 | |||
29 | struct ScreenSize |
||
30 | { |
||
31 | int width; |
||
32 | int height; |
||
33 | }; |
||
34 | |||
35 | class Bird |
||
36 | { |
||
37 | public: |
||
9046 | leency | 38 | static const int sizeX = 19; |
39 | static const int sizeY = 20; |
||
9011 | leency | 40 | static const int x = 100; |
41 | int prev_y; |
||
42 | int y; |
||
43 | int acceleration; |
||
44 | |||
45 | inline void initialize() |
||
46 | { |
||
47 | y = WINDOW_HEIGHT / 2; |
||
48 | acceleration = 0; |
||
49 | } |
||
50 | |||
51 | inline void move() |
||
52 | { |
||
53 | if (acceleration <= 30) |
||
54 | acceleration += 2; |
||
55 | prev_y = y; |
||
56 | y += acceleration / 10; |
||
57 | } |
||
58 | |||
59 | inline void jump() |
||
60 | { |
||
61 | acceleration = -50; |
||
62 | } |
||
63 | |||
64 | inline void draw() |
||
65 | { |
||
66 | kos_PutImage(birdImage, sizeX, sizeY, x, y); |
||
67 | } |
||
68 | }; |
||
69 | |||
70 | class Tube |
||
71 | { |
||
72 | public: |
||
73 | static const int width = 50; |
||
74 | static const int gapHeight = 100; |
||
75 | static const int headHeight = 18; |
||
76 | int x; |
||
77 | int gapY; |
||
78 | |||
79 | inline void randomize() |
||
80 | { |
||
81 | x = WINDOW_WIDTH + 1; |
||
82 | gapY = rtlRand() % 200 + 50; |
||
83 | } |
||
84 | |||
85 | inline void move() |
||
86 | { |
||
87 | x -= 2; |
||
88 | if (x < -width - 2) |
||
89 | randomize(); |
||
90 | } |
||
91 | |||
92 | void draw() |
||
93 | { |
||
9046 | leency | 94 | //cleanup |
95 | int pixels = (WINDOW_WIDTH - (BORDER_LEFT + BORDER_RIGHT - 1)) - (x + width + 2); |
||
96 | if (pixels >= -1) |
||
97 | { |
||
98 | pixels = (pixels == -1) ? 1 : 2; |
||
99 | kos_DrawBar(x + width, gapY - headHeight, pixels, headHeight, 0x00FFFF); |
||
100 | kos_DrawBar(x + width, gapY + gapHeight, pixels, headHeight, 0x00FFFF); |
||
101 | } |
||
102 | |||
9011 | leency | 103 | int offset = x >= 0 ? 0 : -x; |
104 | int trim = x + width >= WINDOW_WIDTH - (BORDER_LEFT + BORDER_RIGHT - 1) ? WINDOW_WIDTH - x - width - (BORDER_LEFT + BORDER_RIGHT - 1) : 0; |
||
9046 | leency | 105 | int trimHead = x + width >= WINDOW_WIDTH - (BORDER_LEFT + BORDER_RIGHT - 1) ? WINDOW_WIDTH - x - width - (BORDER_LEFT + BORDER_RIGHT - 1) : 0; |
9011 | leency | 106 | |
107 | //top |
||
108 | for (int y = 0; y < gapY - headHeight; ++y) |
||
109 | kos_PutImage(tubeBodyImage + offset, width - offset + trim, 1, x + offset, y); |
||
110 | //head top |
||
111 | for (int y = gapY - headHeight; y < gapY; ++y) |
||
9046 | leency | 112 | kos_PutImage(tubeHeadImage + width * (y - (gapY - headHeight)) + offset, width - offset + trimHead, 1, x + offset, y); |
9011 | leency | 113 | //head down |
114 | for (int y = gapY + gapHeight; y < gapY + gapHeight + headHeight; ++y) |
||
9046 | leency | 115 | kos_PutImage(tubeHeadImage + width * (y - (gapY + gapHeight)) + offset, width - offset + trimHead, 1, x + offset, y); |
9011 | leency | 116 | //down |
117 | for (int y = gapY + gapHeight + headHeight; y < WINDOW_HEIGHT - (BORDER_TOP + BORDER_DOWN - 1); ++y) |
||
118 | kos_PutImage(tubeBodyImage + offset, width - offset + trim, 1, x + offset, y); |
||
119 | |||
120 | } |
||
121 | }; |
||
122 | |||
123 | //Global variables |
||
124 | int loopDelay; |
||
125 | GameState gameState; |
||
126 | char scoreString[] = "Score: "; |
||
127 | bool scoreChanged; |
||
128 | int score; |
||
129 | Bird bird; |
||
130 | int tubeNumber; |
||
131 | Tube tubes[3]; |
||
132 | int windowX; |
||
133 | int windowY; |
||
134 | |||
135 | //Function prototypes |
||
136 | void kos_Main(); |
||
137 | void startGame(); |
||
138 | ScreenSize getScreenSize(); |
||
139 | void updateScoreString(); |
||
140 | void WriteBorderedText(Word x, Word y, Byte fontType, Dword textColor, const char* textPtr, Dword textLen, Dword borderColor, int borderSize); |
||
141 | inline bool checkAddScore(Tube tube); |
||
142 | inline bool checkCollision(Tube tube); |
||
143 | |||
144 | void drawMenuWindow(); |
||
145 | void drawGameWindow(); |
||
146 | void redrawGameWindow(); |
||
147 | void drawGameoverWindow(); |
||
148 | |||
149 | //Functions |
||
150 | |||
151 | void startGame() |
||
152 | { |
||
153 | kos_SetMaskForEvents(0x7); /// 111 in binary |
||
154 | |||
155 | bird.initialize(); |
||
156 | |||
157 | score = 0; |
||
158 | memset((Byte*)scoreString + 6, ' ', 3); |
||
159 | updateScoreString(); |
||
160 | |||
161 | tubeNumber = 1; |
||
162 | tubes[0].randomize(); |
||
163 | |||
164 | gameState = GAMESTATE_STARTED; |
||
165 | drawGameWindow(); |
||
166 | } |
||
167 | |||
168 | ScreenSize getScreenSize() |
||
169 | { |
||
170 | Dword result; |
||
171 | __asm { |
||
172 | push 14 //System function 14 |
||
173 | pop eax |
||
174 | int 0x40 |
||
175 | mov result, eax |
||
176 | } |
||
177 | ScreenSize screenSize; |
||
178 | screenSize.height = (result & 0xFFFF) + 1; //last two bytes |
||
179 | screenSize.width = (result >> 16) + 1; //first two bytes |
||
180 | return screenSize; |
||
181 | } |
||
182 | |||
183 | void kos_Main() |
||
184 | { |
||
185 | rtlSrand( kos_GetSystemClock() ); |
||
186 | |||
187 | //Centring window |
||
188 | ScreenSize screenSize = getScreenSize(); |
||
189 | windowX = (screenSize.width - WINDOW_WIDTH) / 2; |
||
190 | windowY = (screenSize.height - WINDOW_HEIGHT) / 2; |
||
191 | |||
192 | gameState = GAMESTATE_MENU; |
||
193 | |||
194 | kos_SetMaskForEvents(0x27); // 100111 in binary |
||
195 | |||
196 | while( true ) |
||
197 | { |
||
198 | switch (gameState) |
||
199 | { |
||
200 | case GAMESTATE_STARTED: |
||
201 | kos_Pause(loopDelay); |
||
202 | |||
203 | bird.move(); |
||
204 | |||
205 | //Adding new tubes |
||
206 | if ((tubeNumber == 1 || tubeNumber == 2) && (tubes[tubeNumber - 1].x < (WINDOW_WIDTH - WINDOW_WIDTH / 3))) |
||
207 | tubes[tubeNumber++].randomize(); |
||
208 | |||
209 | //Processing all tubes |
||
210 | scoreChanged = false; |
||
211 | for (int i = 0; i < tubeNumber; ++i) |
||
212 | { |
||
213 | //Adding score |
||
214 | if (checkAddScore(tubes[i])) |
||
215 | { |
||
216 | ++score; |
||
217 | scoreChanged = true; |
||
218 | } |
||
219 | |||
220 | //Check collision with bird |
||
221 | if (checkCollision(tubes[i])) |
||
222 | { |
||
223 | gameState = GAMESTATE_GAMEOVER; |
||
224 | continue; |
||
225 | } |
||
226 | |||
227 | //Move tube |
||
228 | tubes[i].move(); |
||
229 | } |
||
230 | |||
231 | if (scoreChanged) |
||
232 | updateScoreString(); |
||
233 | |||
234 | //Cheking the bird is too high or low |
||
235 | if (bird.y + bird.sizeY > WINDOW_HEIGHT - (BORDER_TOP + BORDER_DOWN - 1) || bird.y < 0) |
||
236 | { |
||
237 | gameState = GAMESTATE_GAMEOVER; |
||
238 | continue; |
||
239 | } |
||
240 | |||
241 | redrawGameWindow(); |
||
242 | |||
243 | switch (kos_CheckForEvent()) |
||
244 | { |
||
245 | case 1: |
||
246 | drawGameWindow(); |
||
247 | break; |
||
248 | |||
249 | case 2: // key pressed |
||
250 | Byte keyCode; |
||
251 | kos_GetKey(keyCode); |
||
252 | if (keyCode == 32) //if pressed key is spacebar |
||
253 | bird.jump(); |
||
254 | break; |
||
255 | |||
256 | case 3: // button pressed; we have only one button, close |
||
257 | kos_ExitApp(); |
||
258 | } |
||
259 | break; |
||
260 | |||
261 | case GAMESTATE_GAMEOVER: |
||
262 | drawGameoverWindow(); |
||
263 | |||
264 | switch (kos_WaitForEvent()) |
||
265 | { |
||
266 | case 1: |
||
267 | drawGameoverWindow(); |
||
268 | break; |
||
269 | |||
270 | case 2: |
||
271 | startGame(); |
||
272 | break; |
||
273 | |||
274 | case 3: |
||
275 | kos_ExitApp(); |
||
276 | } |
||
277 | break; |
||
278 | |||
279 | case GAMESTATE_MENU: |
||
280 | switch (kos_WaitForEvent()) |
||
281 | { |
||
282 | case 1: |
||
283 | drawMenuWindow(); |
||
284 | break; |
||
285 | |||
286 | case 2: |
||
287 | Byte keyCode; |
||
288 | kos_GetKey(keyCode); |
||
289 | if (keyCode == 0x31 || keyCode == 0x61) //1 or NumPad1 |
||
290 | { |
||
291 | loopDelay = 1; |
||
292 | startGame(); |
||
293 | } |
||
294 | else if (keyCode == 0x32 || keyCode == 0x62) //2 or NumPad2 |
||
295 | { |
||
296 | loopDelay = 2; |
||
297 | startGame(); |
||
298 | } |
||
299 | break; |
||
300 | |||
301 | case 3: |
||
302 | kos_ExitApp(); |
||
303 | |||
304 | case 6: |
||
305 | Dword result; |
||
306 | __asm { |
||
307 | push 37 //Function 37 - work with mouse |
||
308 | pop eax |
||
309 | mov ebx, 3 //Subfunction 3 - states and events of the mouse buttons |
||
310 | int 0x40 |
||
311 | mov result, eax |
||
312 | } |
||
313 | result &= 0x100; //bit 8 is set = left button is pressed |
||
314 | if ( result ) |
||
315 | { |
||
316 | Dword coordinates; |
||
317 | __asm { |
||
318 | push 37 //Function 37 - work with mouse |
||
319 | pop eax |
||
320 | mov ebx, 1 //Subfunction 1 - coordinates of the mouse relative to the window |
||
321 | int 0x40 |
||
322 | mov coordinates, eax |
||
323 | } |
||
324 | int clickX = coordinates >> 16; |
||
325 | int clickY = coordinates & 0xFFFF; |
||
326 | if (clickX >= 100 && clickX < 390 && clickY >= 170 && clickY < 230) |
||
327 | { |
||
328 | loopDelay = 1; |
||
329 | startGame(); |
||
330 | } |
||
331 | else if (clickX >= 100 && clickX < 390 && clickY >= 270 && clickY < 330) |
||
332 | { |
||
333 | loopDelay = 2; |
||
334 | startGame(); |
||
335 | } |
||
336 | } |
||
337 | break; |
||
338 | } |
||
339 | break; |
||
340 | } |
||
341 | } |
||
342 | } |
||
343 | |||
344 | void drawGameWindow() |
||
345 | { |
||
346 | kos_DefineAndDrawWindow(windowX, windowY, WINDOW_WIDTH, WINDOW_HEIGHT, 0x33, 0x00FFFF, 0, 0, (Dword)HEADER_STRING); |
||
347 | bird.draw(); |
||
348 | for (int i = 0; i < tubeNumber; ++i) |
||
349 | tubes[i].draw(); |
||
350 | kos_WriteTextToWindow(10, 10, 0x81, 0x000000, scoreString, 0); |
||
351 | kos_WriteTextToWindow(10, 30, 0x81, 0x000000, CONTROL_STRING, 0); |
||
352 | } |
||
353 | void redrawGameWindow() |
||
354 | { |
||
355 | //cleaning the screen |
||
356 | if (scoreChanged) |
||
357 | kos_DrawBar(80, 10, 50, 15, 0x00FFFF); |
||
358 | if (bird.y > bird.prev_y) |
||
359 | kos_DrawBar(bird.x, bird.prev_y, bird.sizeX, bird.y - bird.prev_y, 0x00FFFF); |
||
360 | else |
||
361 | kos_DrawBar(bird.x, bird.y + bird.sizeY, bird.sizeX, bird.prev_y - bird.y, 0x00FFFF); |
||
362 | |||
363 | bird.draw(); |
||
364 | for (int i = 0; i < tubeNumber; ++i) |
||
365 | tubes[i].draw(); |
||
366 | |||
367 | kos_WriteTextToWindow(10, 10, 0x81, 0x000000, scoreString, 0); |
||
368 | kos_WriteTextToWindow(10, 30, 0x81, 0x000000, CONTROL_STRING, 0); |
||
369 | } |
||
370 | |||
371 | void drawGameoverWindow() |
||
372 | { |
||
373 | kos_DefineAndDrawWindow(windowX, windowY, WINDOW_WIDTH, WINDOW_HEIGHT, 0x33, 0x000000, 0, 0, (Dword)HEADER_STRING); |
||
374 | kos_WriteTextToWindow(125, 50, 0x82, 0xFFFFFF, GAMEOVER_STRING, 0); |
||
375 | kos_WriteTextToWindow(135, 100, 0x81, 0xFFFFFF, scoreString, 0); |
||
376 | kos_WriteTextToWindow(50, 150, 0x81, 0xFFFFFF, ANY_KEY_STRING, 0); |
||
377 | } |
||
378 | |||
379 | void WriteBorderedText(Word x, Word y, Byte fontType, Dword textColor, const char *textPtr, Dword textLen, Dword borderColor, int borderSize) |
||
380 | { |
||
381 | kos_WriteTextToWindow(x - borderSize, y - borderSize, fontType, borderColor, textPtr, textLen); |
||
382 | kos_WriteTextToWindow(x - borderSize, y + borderSize, fontType, borderColor, textPtr, textLen); |
||
383 | kos_WriteTextToWindow(x + borderSize, y - borderSize, fontType, borderColor, textPtr, textLen); |
||
384 | kos_WriteTextToWindow(x + borderSize, y + borderSize, fontType, borderColor, textPtr, textLen); |
||
385 | kos_WriteTextToWindow(x, y, fontType, textColor, textPtr, textLen); |
||
386 | } |
||
387 | |||
388 | void drawMenuWindow() |
||
389 | { |
||
390 | kos_DefineAndDrawWindow(windowX, windowY, WINDOW_WIDTH, WINDOW_HEIGHT, 0x33, 0x00FFFF, 0, 0, (Dword)HEADER_STRING); |
||
391 | |||
392 | WriteBorderedText(85, 40, 0x4, 0xFFFFFF, HEADER_STRING, 6, 0x000000, 2); |
||
393 | WriteBorderedText(185, 80, 0x84, 0xFFFFFF, HEADER_STRING + 7, 0, 0x000000, 2); |
||
394 | |||
395 | RGB* pos = &tubeHeadImage[0]; |
||
396 | for (int x = 100 - 1; x >= 100 - Tube::headHeight; --x) |
||
9046 | leency | 397 | for (int y = 170; y < 170 + Tube::width; ++y) |
9011 | leency | 398 | { |
399 | kos_PutPixel(x, y, (pos->r << 16) + (pos->g << 8) + (pos->b)); //first tube |
||
400 | kos_PutPixel(x, y+100, (pos->r << 16) + (pos->g << 8) + (pos->b)); //second tube |
||
401 | ++pos; |
||
402 | } |
||
403 | |||
404 | //First button |
||
405 | for(int x = 100; x < WINDOW_WIDTH - (BORDER_LEFT + BORDER_RIGHT - 1); ++x) |
||
406 | kos_PutImage(tubeBodyImage, 1, Tube::width, x, 170); |
||
407 | WriteBorderedText(140, 185, 0x82, 0x000000, FAST_STRING, 0, 0xFFFFFF, 1); |
||
408 | |||
409 | //Second button |
||
410 | for (int x = 100; x < WINDOW_WIDTH - (BORDER_LEFT + BORDER_RIGHT - 1); ++x) |
||
411 | kos_PutImage(tubeBodyImage, 1, Tube::width, x, 270); |
||
412 | WriteBorderedText(140, 285, 0x82, 0x000000, SLOW_STRING, 0, 0xFFFFFF, 1); |
||
413 | } |
||
414 | |||
415 | inline bool checkCollision(Tube tube) |
||
416 | { |
||
9046 | leency | 417 | return ((tube.x <= (bird.x + bird.sizeX) && tube.x + tube.width >= bird.x) |
418 | && (bird.y <= tube.gapY || bird.y + bird.sizeY >= tube.gapY + tube.gapHeight)); |
||
9011 | leency | 419 | } |
420 | |||
421 | inline bool checkAddScore(Tube tube) |
||
422 | { |
||
423 | //int diff = bird.x - (tube.x + tube.width); |
||
424 | //return diff == 0 || diff == 1; |
||
425 | return ((bird.x - (tube.x + tube.width)) >> 1) == 0; |
||
426 | } |
||
427 | |||
428 | void updateScoreString() |
||
429 | { |
||
430 | int temp = score; |
||
431 | int index = 9; |
||
432 | do { |
||
433 | scoreString[index--] = temp % 10 + '0'; |
||
434 | temp /= 10; |
||
435 | } while (temp > 0); |
||
436 | }=>=>>>><>><>><>><>>>>>>>>>>>>>>>>=> |