1 #include <nm_core.h>
2 #include <nm_string.h>
3 #include <nm_vector.h>
4 #include <nm_window.h>
5 #include <nm_ncurses.h>
6
7 #include <time.h>
8
9 #define NM_SI_ENEMIE_COUNT 60
10 #define NM_SI_ENEMIE_INROW 10
11 #define NM_SI_EMEMIE_ROTATE 10
12 #define NM_SI_DELAY 20000
13 #define NM_SI_BULLETS 200
14 #define NM_SI_BONIS_DELAY 1000
15 #define NM_SI_GAIN_COUNT 500
16
nm_si_game(void)17 static void nm_si_game(void)
18 {
19 typedef enum {
20 NM_SI_SHIP_1,
21 NM_SI_SHIP_2,
22 NM_SI_SHIP_3,
23 NM_SI_BULLET,
24 NM_SI_PLAYER,
25 NM_SI_BONUS
26 } nm_si_type_t;
27
28 typedef struct {
29 int pos_x;
30 int pos_y;
31 int health;
32 bool hit;
33 nm_si_type_t type;
34 } nm_si_t;
35
36 static bool si_player_init = false;
37 static size_t bullets_cnt;
38 static nm_si_t player;
39 static size_t score;
40 static size_t level;
41 static size_t speed;
42 static size_t gain;
43
44 bool play = true, change_dir = false, next = false;
45 int max_x, max_y, start_x, start_y;
46 nm_vect_t pl_bullets = NM_INIT_VECT;
47 nm_vect_t en_bullets = NM_INIT_VECT;
48 nm_vect_t enemies = NM_INIT_VECT;
49 nm_str_t info = NM_INIT_STR;
50 bool bonus_active = false;
51 bool gain_active = false;
52 size_t gain_counter = 0;
53 int direction = 1, mch;
54 int b_direction = 1;
55 uint64_t iter = 0;
56 nm_si_t bonus;
57
58 const char *shape_player = "<^>";
59 const char *shape_ship0 = "#v#";
60 const char *shape_ship1 = ")v(";
61 const char *shape_ship2 = "]v[";
62 const char *shape_dead = "x-x";
63 const char *shape_hit = ">-<";
64 const char *shape_win = "^_^";
65 const char *shape_bonus = "(?)";
66
67 nodelay(action_window, TRUE);
68 getmaxyx(action_window, max_y, max_x);
69
70 bonus = (nm_si_t) {
71 .pos_x = 1,
72 .pos_y = 3,
73 .health = 1,
74 .hit = false,
75 .type = NM_SI_BONUS
76 };
77
78 if (!si_player_init) {
79 player = (nm_si_t) {
80 .pos_x = max_x / 2,
81 .pos_y = max_y - 2,
82 .health = 5,
83 .hit = false,
84 .type = NM_SI_PLAYER
85 };
86 score = 0, bullets_cnt = NM_SI_BULLETS;
87 si_player_init = true;
88 speed = NM_SI_EMEMIE_ROTATE;
89 level = 0;
90 gain = 0;
91 } else {
92 level++;
93 bullets_cnt += (NM_SI_BULLETS - 50);
94 if (speed > 1) {
95 speed--;
96 }
97 }
98 start_x = (max_x / 5);
99 start_y = 5;
100
101 for (size_t n = 0; n < NM_SI_ENEMIE_COUNT; n++) {
102 nm_si_type_t type = (n < 10) ? NM_SI_SHIP_3 :
103 (n >= 10 && n < 30) ? NM_SI_SHIP_2 : NM_SI_SHIP_1;
104 size_t health = (n < 10) ? 3 :
105 (n >= 10 && n < 30) ? 2 : 1;
106 nm_si_t ship = (nm_si_t) {
107 .pos_x = start_x,
108 .pos_y = start_y,
109 .type = type,
110 .hit = false,
111 .health = health
112 };
113
114 nm_vect_insert(&enemies, &ship, sizeof(nm_si_t), NULL);
115 start_x += 5;
116 if (!((n + 1) % NM_SI_ENEMIE_INROW)) {
117 start_y++;
118 start_x = (max_x / 5);
119 }
120 }
121
122 while (play) {
123 int ch = wgetch(action_window);
124
125 werase(action_window);
126 nm_str_format(&info, "Level %zu [score: %zu hp: %d ammo: %zu ap: %zu]",
127 level, score, player.health, bullets_cnt, gain);
128 nm_init_action(info.data);
129
130 switch (ch) {
131 case KEY_LEFT:
132 case NM_KEY_A:
133 if (player.pos_x > 1) {
134 player.pos_x -= 1;
135 }
136 break;
137 case KEY_RIGHT:
138 case NM_KEY_D:
139 if (player.pos_x < max_x - 4) {
140 player.pos_x += 1;
141 }
142 break;
143 case NM_KEY_S:
144 if (gain && !gain_active) {
145 gain_counter = NM_SI_GAIN_COUNT;
146 gain_active = true;
147 gain--;
148 }
149 break;
150 case ' ':
151 {
152 nm_si_t bullet = (nm_si_t) {
153 .pos_x = player.pos_x + 1,
154 .pos_y = max_y - 3,
155 .health = (gain_active) ? 3 : 1,
156 .hit = false,
157 .type = NM_SI_BULLET,
158 };
159
160 if (bullets_cnt) {
161 nm_vect_insert(&pl_bullets, &bullet, sizeof(nm_si_t), NULL);
162 bullets_cnt--;
163 }
164 }
165 break;
166 case NM_KEY_Q:
167 play = false;
168 break;
169 case ERR:
170 default:
171 break;
172 }
173
174 if (!enemies.n_memb) {
175 play = false;
176 next = true;
177 mvwaddstr(action_window, player.pos_y, player.pos_x, shape_win);
178 break;
179 }
180
181 for (size_t n = 0; n < enemies.n_memb; n++) {
182 nm_si_t *e = nm_vect_at(&enemies, n);
183
184 for (size_t nb = 0; nb < pl_bullets.n_memb; nb++) {
185 nm_si_t *b = nm_vect_at(&pl_bullets, nb);
186
187 if (((b->pos_x >= e->pos_x && b->pos_x <= e->pos_x + 2)) &&
188 (e->pos_y == b->pos_y)) {
189 size_t hp_hit = e->health;
190 e->health -= b->health;
191 b->health -= hp_hit;
192 if (e->health <= 0) {
193 switch (e->type) {
194 case NM_SI_SHIP_1:
195 score += 10;
196 break;
197 case NM_SI_SHIP_2:
198 score += 20;
199 break;
200 case NM_SI_SHIP_3:
201 score += 30;
202 break;
203 default:
204 break;
205 }
206 } else {
207 e->hit = true;
208 switch (e->type) {
209 case NM_SI_SHIP_1:
210 score += 1;
211 break;
212 case NM_SI_SHIP_2:
213 score += 2;
214 break;
215 case NM_SI_SHIP_3:
216 score += 3;
217 break;
218 default:
219 break;
220 }
221 }
222 if (b->health <= 0) {
223 nm_vect_delete(&pl_bullets, nb, NULL);
224 nb--;
225 }
226 break;
227 }
228 }
229
230 if (e->health > 0) {
231 bool can_shoot = true;
232 const char *shape;
233 switch (e->type) {
234 case NM_SI_SHIP_1:
235 shape = shape_ship0;
236 break;
237 case NM_SI_SHIP_2:
238 shape = shape_ship1;
239 break;
240 case NM_SI_SHIP_3:
241 shape = shape_ship2;
242 break;
243 default:
244 break;
245 }
246
247 if (e->hit) {
248 shape = shape_hit;
249 e->hit = false;
250 }
251
252 mvwaddstr(action_window, e->pos_y, e->pos_x, shape);
253 if (iter % (speed + 1) == speed) {
254 direction ? e->pos_x++ : e->pos_x--;
255 if (!direction && e->pos_x == 1) {
256 change_dir = true;
257 } else if (direction && (e->pos_x == max_x - 4)) {
258 change_dir = true;
259 }
260 }
261
262 for (size_t no = n; no < enemies.n_memb; no++) {
263 nm_si_t *eo = nm_vect_at(&enemies, no);
264 if (((eo->pos_y - 1 >= e->pos_y &&
265 eo->pos_y - 1 <= e->pos_y + 5)) &&
266 (e->pos_x == eo->pos_x)) {
267 can_shoot = false;
268 break;
269 }
270 }
271
272 if (can_shoot) {
273 struct timespec ts;
274 int r;
275
276 clock_gettime(CLOCK_MONOTONIC, &ts);
277 srand((time_t) ts.tv_nsec);
278 r = rand() % 1000;
279 if (r > 498 && r < 500) { /* fire! */
280 nm_si_t bullet = (nm_si_t) {
281 .pos_x = e->pos_x + 1,
282 .pos_y = e->pos_y + 1,
283 .health = 0,
284 .hit = false,
285 .type = NM_SI_BULLET
286 };
287 nm_vect_insert(&en_bullets, &bullet,
288 sizeof(nm_si_t), NULL);
289 }
290 }
291
292 } else {
293 mvwaddstr(action_window, e->pos_y, e->pos_x, shape_dead);
294 nm_vect_delete(&enemies, n, NULL);
295 n--;
296 }
297 }
298
299 if (enemies.n_memb && change_dir) {
300 for (size_t n = 0; n < enemies.n_memb; n++) {
301 nm_si_t *e = nm_vect_at(&enemies, n);
302 e->pos_y += 1;
303 if (e->pos_y == max_y - 2) {
304 play = false;
305 }
306 }
307
308 if (!direction) {
309 direction = 1;
310 } else {
311 direction = 0;
312 }
313 change_dir = false;
314 }
315
316 for (size_t n = 0; n < pl_bullets.n_memb; n++) {
317 nm_si_t *b = nm_vect_at(&pl_bullets, n);
318 mvwaddch(action_window, b->pos_y, b->pos_x, gain_active ? '^' : '*');
319 b->pos_y--;
320
321 if (b->pos_y == 1) {
322 nm_vect_delete(&pl_bullets, n, NULL);
323 n--;
324 }
325 }
326
327 if (iter % (NM_SI_BONIS_DELAY + 1) == NM_SI_BONIS_DELAY &&
328 !bonus_active) {
329 bonus_active = true;
330 }
331
332 if (bonus_active) {
333 for (size_t n = 0; n < pl_bullets.n_memb; n++) {
334 nm_si_t *b = nm_vect_at(&pl_bullets, n);
335
336 if (((b->pos_x >= bonus.pos_x && b->pos_x <= bonus.pos_x + 2)) &&
337 (bonus.pos_y == b->pos_y)) {
338 bonus.hit = true;
339 b->health--;
340 if (!b->health) {
341 nm_vect_delete(&pl_bullets, n, NULL);
342 }
343 break;
344 }
345 }
346
347 if (bonus.hit) {
348 mvwaddstr(action_window, bonus.pos_y, bonus.pos_x, shape_dead);
349 bonus.hit = false;
350 bonus_active = false;
351 if (b_direction) {
352 b_direction = 0;
353 bonus.pos_x = max_x - 4;
354 } else {
355 b_direction = 1;
356 bonus.pos_x = 1;
357 }
358 score += 100;
359 switch (iter % 3) {
360 case 0:
361 bullets_cnt += 50;
362 break;
363 case 1:
364 player.health++;
365 break;
366 case 2:
367 gain++;
368 break;
369 }
370 } else {
371 mvwaddstr(action_window, bonus.pos_y, bonus.pos_x, shape_bonus);
372 if (iter % (speed + 1) == speed) {
373 (b_direction) ? bonus.pos_x++ : bonus.pos_x--;
374 if (!b_direction && bonus.pos_x == 1) {
375 bonus_active = false;
376 b_direction = 1;
377 } else if (b_direction && (bonus.pos_x == max_x - 4)) {
378 bonus_active = false;
379 b_direction = 0;
380 }
381 }
382 }
383 }
384
385 for (size_t n = 0; n < en_bullets.n_memb; n++) {
386 nm_si_t *b = nm_vect_at(&en_bullets, n);
387 mvwaddch(action_window, b->pos_y, b->pos_x, '|');
388
389 if (((b->pos_x >= player.pos_x && b->pos_x <= player.pos_x + 2)) &&
390 (player.pos_y == b->pos_y)) {
391 player.health--;
392 player.hit = true;
393 }
394
395 b->pos_y++;
396
397 if (b->pos_y == max_y - 1) {
398 nm_vect_delete(&en_bullets, n, NULL);
399 n--;
400 }
401 }
402
403 if (player.health) {
404 if (player.hit) {
405 mvwaddstr(action_window, player.pos_y, player.pos_x, shape_hit);
406 player.hit = false;
407 } else {
408 mvwaddstr(action_window, player.pos_y, player.pos_x, shape_player);
409 }
410 } else {
411 mvwaddstr(action_window, player.pos_y, player.pos_x, shape_dead);
412 play = false;
413 }
414
415 wrefresh(action_window);
416
417 usleep(NM_SI_DELAY);
418 iter++;
419 if (gain_active) {
420 gain_counter--;
421 if (!gain_counter) {
422 gain_active = false;
423 }
424 }
425 }
426
427 nodelay(action_window, FALSE);
428 NM_ERASE_TITLE(action, getmaxx(action_window));
429 nm_str_format(&info, "%s [score: %zu hp: %d ammo: %zu ap: %zu]",
430 next ? "(n)ext level, (q)uit" : "Game over, (q)uit, (r)eplay",
431 score, player.health, bullets_cnt, gain);
432 nm_init_action(info.data);
433
434 nm_str_free(&info);
435 nm_vect_free(&pl_bullets, NULL);
436 nm_vect_free(&en_bullets, NULL);
437 nm_vect_free(&enemies, NULL);
438
439 do {
440 mch = wgetch(action_window);
441 } while ((mch != 'n') && (mch != 'q') && (mch != 'r'));
442
443 if (mch == 'n' && next) {
444 nm_si_game();
445 } else if (mch == 'r') {
446 si_player_init = false;
447 nm_si_game();
448 } else {
449 si_player_init = false;
450 }
451 }
452
nm_print_nemu(void)453 void nm_print_nemu(void)
454 {
455 int ch;
456 size_t max_y = getmaxy(action_window);
457 const char *nemu[] = {
458 " .x@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@o. ",
459 " .x@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@x. ",
460 " o@@@@@@@@@@@@@x@@@@@@@@@@@@@@@@@@@@@@@@@o ",
461 " .x@@@@@@@@x@@@@xx@@@@@@@@@@@@@@@@@@@@@@@@@@o ",
462 " .x@@@@@@@x. .x@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
463 " x@@@@@ox@x..o@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
464 " o@@@@@@xx@@@@@@@@@@@@@@@@@@@@@@@@x@@@@@@@@@@@@@o ",
465 " .x@@@@@@@@@@x@@@@@@@@@@@@@@@@@@@@@o@@@@@@@@@@@@@@. ",
466 " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@x.@@@@@@@@@@@@@@o ",
467 " o@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@xx..o@@@@@@@@@@@@@x ",
468 " x@@@@@@@@@@@@@@@@@@xxoooooooo........oxxx@@@@@@@@x. ",
469 " x@@@@@@@@@@x@@@@@@x. .oxxx@@xo. .oxo.o@@@@@x ",
470 " o@@@@@@@@x..o@@@@@o .xxx@@@o .. .x@ooox@@@@@o ",
471 " .x@@@@@@@oo..x@@@@. ...oo.. .oo. o@@@@@o. ",
472 " .x@@@@@@xoooo@@@@. x@@@x.. ",
473 " o@@@@@@ooxx@@@@. . .@@@x.. ",
474 " .o@@@@@@o.x@@@. .. o@@x. ",
475 " .x@@@@@@xx@@@. .. .x@o ",
476 " o@@@@@@@o.x@@. .x@x. ",
477 " o@@@@@@@o o@@@xo. ..... .x@x. ",
478 " .@@@@@x. .x@o.x@x.. .o@@@. ",
479 " .x@@@@@o.. o@o .o@@xoo.. .o..@@o ",
480 " o@@@@oo@@xx@x. .x@@@ooooooo. o@x. ",
481 " .o@@o..xx@@@@@xo.. o@x. xx. ",
482 " .oo. ....ox@@@@@@xx@o xo. ",
483 " .... .o@@@@@@@@o .@.. ",
484 " ... ox.ox@@@. .x.. ",
485 " ... .x. .@@x. .o ",
486 " . .o .@@o. o ",
487 " o. x@@o . ",
488 " .o o@@.. . ",
489 " ...@o.. . ",
490 " .x@xo. ",
491 " .x.. ",
492 " .x.... ",
493 " o@..xo ",
494 " o@o oo ",
495 " .. "
496 };
497
498 for (size_t l = 3, n = 0; n < (max_y - 4) && n < nm_arr_len(nemu); n++, l++)
499 mvwprintw(action_window, l, 1, "%s", nemu[n]);
500
501 ch = wgetch(action_window);
502 if (ch == NM_KEY_S) {
503 nm_si_game();
504 }
505 }
506
507 /* vim:set ts=4 sw=4: */
508