australia spawnmap: fix off by one causing boss to get lost
[rofl0r-openDOW.git] / spriteview.c
blobff70501a648134cfc81a505a908304bcdd7b8271
1 #include "../lib/include/timelib.h"
2 #include "../lib/include/macros.h"
3 #include "../lib/include/sblist.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <stdbool.h>
8 #include <assert.h>
9 #include "vec2f.h"
10 #include "anim.h"
11 #include "gameobj.h"
12 #include "video.h"
13 #include "direction.h"
14 #include "weapon.h"
15 #include "palpic.h"
16 #include "sdl_rgb.h"
17 #include "audio.h"
18 #include "muzzle_tab.h"
19 #include "spritemaps.h"
20 #include "enemy.h"
21 #include "font.h"
22 #include "maps.h"
23 #include "mapsprites.h"
24 #include "walls.h"
25 #include "music.h"
27 #include <SDL/SDL.h>
29 #ifndef IN_KDEVELOP_PARSER
30 #include "../lib/include/bitarray.h"
31 #include "weapon_sprites.c"
33 #endif
35 enum mousebutton {
36 MB_LEFT = 0,
37 MB_RIGHT,
40 // 1 if button down, 0 if not, >1 to count ms pressed
41 unsigned long mousebutton_down[] = {
42 [MB_LEFT] = 0,
43 [MB_RIGHT] = 0,
46 //RcB: LINK "-lSDL"
47 #if 0
48 static void get_last_move_event(SDL_Event* e) {
49 #define numpeek 32
50 SDL_Event peek[numpeek];
51 SDL_Event* last_event = NULL;
52 int i, results;
53 results = SDL_PeepEvents(peek, numpeek, SDL_PEEKEVENT, (uint32_t) ~0);
54 if(results == -1) return;
55 for(i = 0; i < results; i++) {
56 if(peek[i].type == SDL_MOUSEMOTION)
57 last_event = &peek[i];
58 else
59 break;
61 if(last_event) {
62 *e = *last_event;
63 SDL_PeepEvents(peek, i + 1, SDL_GETEVENT, (uint32_t) ~0);
65 #undef numpeek
67 #endif
69 static vec2f get_sprite_center(const struct palpic *p) {
70 assert(p->spritecount);
71 vec2f res;
72 res.x = palpic_getspritewidth(p) * SCALE / 2;
73 res.y = palpic_getspriteheight(p) * SCALE / 2;
74 return res;
77 static int player_ids[2];
78 static int crosshair_id;
79 static enum weapon_id player_weapons[2][WP_MAX];
80 static int weapon_count[2];
81 static enum weapon_id weapon_active[2]; // index into player_weapons[playerno]
82 static int player_ammo[2][AMMO_MAX];
83 static enum weapon_id get_active_weapon_id(int player_no);
84 static void switch_anim(int obj_id, int aid);
85 static vec2f get_vel_from_direction(enum direction dir, float speed);
86 static vec2f get_vel_from_direction16(enum direction16 dir, float speed);
87 // used by game_tick
88 static sblist go_player_bullets;
89 static sblist go_enemy_bullets;
90 static sblist go_explosions;
91 static sblist go_enemy_explosions;
92 static sblist go_walls;
93 static sblist go_enemies;
94 static sblist go_players;
95 static sblist go_flames;
96 static sblist go_rockets;
97 static sblist go_grenades;
98 static sblist go_enemy_grenades;
99 static sblist go_vehicles;
100 static sblist go_mines;
101 static void add_pbullet(uint8_t bullet_id) {
102 sblist_add(&go_player_bullets, &bullet_id);
104 static void add_ebullet(uint8_t bullet_id) {
105 sblist_add(&go_enemy_bullets, &bullet_id);
107 static void add_player(uint8_t player_id) {
108 sblist_add(&go_players, &player_id);
110 static void add_enemy(uint8_t enem_id) {
111 sblist_add(&go_enemies, &enem_id);
113 static void add_explosion(uint8_t expl_id) {
114 sblist_add(&go_explosions, &expl_id);
116 static void add_enemy_explosion(uint8_t expl_id) {
117 sblist_add(&go_enemy_explosions, &expl_id);
119 static void add_wall(uint8_t wall_id) {
120 sblist_add(&go_walls, &wall_id);
122 static void add_flame(uint8_t id) {
123 sblist_add(&go_flames, &id);
125 static void add_grenade(uint8_t id) {
126 sblist_add(&go_grenades, &id);
128 static void add_enemy_grenade(uint8_t id) {
129 sblist_add(&go_enemy_grenades, &id);
131 static void add_rocket(uint8_t id) {
132 sblist_add(&go_rockets, &id);
134 static void add_vehicle(uint8_t id) {
135 sblist_add(&go_vehicles, &id);
137 static void add_mine(uint8_t id) {
138 sblist_add(&go_mines, &id);
140 static void golist_remove(sblist *l, uint8_t objid) {
141 size_t i;
142 uint8_t *itemid;
143 sblist_iter_counter2(l, i, itemid) {
144 if(*itemid == objid) {
145 sblist_delete(l, i);
146 return;
151 static int get_next_anim_frame(enum animation_id aid, anim_step curr) {
152 if(curr == ANIM_STEP_INIT) return animations[aid].first;
153 curr++;
154 if(curr > animations[aid].last) return animations[aid].first;
155 return curr;
158 #define SCREEN_MIN_X 64*SCALE
159 #define SCREEN_MAX_X VMODE_W - 64*SCALE
160 #define SCREEN_MIN_Y 0
161 #define SCREEN_MAX_Y 200*SCALE
163 static void draw_status_bar(void) {
164 enum weapon_id wid = get_active_weapon_id(0);
165 int x, y;
166 sdl_rgb_t *ptr = (sdl_rgb_t *) video.mem;
167 unsigned pitch = video.pitch/4;
168 for(y = SCREEN_MAX_Y; y < VMODE_H; y++)
169 for (x = SCREEN_MIN_X; x < SCREEN_MAX_X; x++)
170 ptr[y*pitch + x] = SRGB_BLACK;
172 blit_sprite(((320 / 2) - (59 / 2)) * SCALE, (200 + (40/2) - (16/2)) * SCALE,
173 &video, SCALE, &weapon_sprites.header, wid, 0);
175 char buf[16];
176 snprintf(buf, 16, "%.6u", objs[player_ids[0]].objspecific.playerdata.score);
177 font_print(SCREEN_MIN_X + 8, SCREEN_MAX_Y + 8, buf, 6, 1 * SCALE, PRGB(255,255,255));
180 enum map_index current_map = MI_VIETNAM;
181 const struct map *map;
182 const struct map_screen* map_scr;
183 const struct palpic *map_bg;
184 const struct palpic *map_fg;
185 const mapscreen_index *map_bonus_indices;
186 const struct map_fglayer *map_bonus_scr;
188 int mapscreen_yoff, mapscreen_xoff;
189 struct { int x,y; } mapsquare;
190 enum map_scrolldir mapscrolldir;
191 unsigned map_spawn_screen_index;
192 unsigned map_spawn_line;
193 unsigned map_spawn_current;
194 #include "maps/spawn_australia.c"
195 const struct enemy_spawn_screen *spawn_map = spawn_screens_australia;
197 static void init_map(enum map_index mapindex) {
198 map = maps[mapindex];
199 map_scr = map_screens[mapindex];
200 map_bg = map_bg_sprites[map->maptype];
201 map_fg = map_fg_sprites[map->maptype];
202 map_bonus_scr = map_bonus_screens[mapindex];
203 map_bonus_indices = map_bonus_layer_indices[mapindex];
204 mapscreen_yoff = 0;
205 mapscreen_xoff = 0;
206 mapsquare.x = 5;
207 mapsquare.y = 26;
208 mapscrolldir = MS_UP;
209 map_spawn_screen_index = 0;
210 map_spawn_line = 0;
211 map_spawn_current = 0;
214 static mapscreen_index get_bonus_layer_index(mapscreen_index screen) {
215 unsigned i;
216 for (i = 0; i < map->bonuslayer_count; i++)
217 if(map_bonus_indices[i] == screen) return i;
218 return MAPSCREEN_BLOCKED;
221 static mapscreen_index screen_to_mapscreen(int *x, int *y) {
222 *x = ((int) *x - SCREEN_MIN_X) / SCALE;
223 *y = ((int) *y - SCREEN_MIN_Y) / SCALE;
224 int yscr = (*y + mapscreen_yoff) / 192;
225 *y = (*y + mapscreen_yoff) - yscr*192;
226 int xscr = (*x + mapscreen_xoff) / 192;
227 *x = (*x + mapscreen_xoff) - xscr*192;
228 return map->screen_map[mapsquare.y + yscr][mapsquare.x + xscr];
231 static enum walltype is_wall(vec2f *pos) {
232 int x = pos->x;
233 int y = pos->y;
234 mapscreen_index scr_idx = screen_to_mapscreen(&x, &y);
235 /* can happen when a bullet goes partially off-screen */
236 if(scr_idx == MAPSCREEN_BLOCKED) return WT_NONE;
237 uint8_t spriteno = map_scr[scr_idx].fg.fg[y/16][x/16];
238 if(spriteno && walls[map->maptype][spriteno]) return walls[map->maptype][spriteno];
239 scr_idx = get_bonus_layer_index(scr_idx);
240 if(scr_idx == MAPSCREEN_BLOCKED) return WT_NONE;
241 spriteno = map_bonus_scr[scr_idx].fg[y/16][x/16];
242 if(spriteno && walls[map->maptype][spriteno]) return walls[map->maptype][spriteno];
243 return WT_NONE;
246 static void draw_map() {
247 int y, x, my, mx;
248 unsigned map_off = 192-mapscreen_yoff;
249 unsigned vis_x = 192-mapscreen_xoff;
250 int x_iter_max16 = 192/16;
251 int x_iter_max64 = 192/64;
252 unsigned x_iter_start64 = x_iter_max64 - (vis_x / 64 + !!(vis_x % 64));
253 unsigned x_iter_start16 = x_iter_max16 - (vis_x / 16 + !!(vis_x % 16));
255 int x_screen_start64 = -(!!(vis_x%64)*64-(vis_x%64));
256 int x_screen_start16 = -(!!(vis_x%16)*16-(vis_x%16));
258 unsigned x_screen_iter;
259 for(x_screen_iter = 0; x_screen_iter <= !!mapscreen_xoff; x_screen_iter++) {
260 mapscreen_index bonus_layer;
261 if(x_screen_iter) {
262 x_screen_start16 = x_screen_start16+(x_iter_max16-x_iter_start16)*16;
263 x_screen_start64 = x_screen_start64+(x_iter_max64-x_iter_start64)*64;
264 x_iter_max16 = mapscreen_xoff / 16 + !!(mapscreen_xoff % 16);
265 x_iter_max64 = mapscreen_xoff / 64 + !!(mapscreen_xoff % 64);
266 x_iter_start16 = 0;
267 x_iter_start64 = 0;
268 assert(map->screen_map[mapsquare.y][mapsquare.x+x_screen_iter] != MAPSCREEN_BLOCKED);
270 uint8_t spriteno;
272 for(my = 6-map_off/32-!!(map_off%32), y = SCREEN_MIN_Y + (!!(map_off%32)*32-(map_off%32))*-SCALE; my < 6; my++, y+=32*SCALE)
273 for(mx = x_iter_start64, x = SCREEN_MIN_X + x_screen_start64*SCALE; mx < x_iter_max64; mx++, x += 64*SCALE) {
274 spriteno = map_scr[map->screen_map[mapsquare.y][mapsquare.x+x_screen_iter]].bg.bg[my][mx];
275 blit_sprite(x, y, &video,
276 SCALE, map_bg, spriteno, 0);
278 for(my = 12-map_off/16-!!(map_off%16), y = SCREEN_MIN_Y + (!!(map_off%16)*16-(map_off%16))*-SCALE; my < 12; my++, y+=16*SCALE)
279 for(mx = x_iter_start16, x = SCREEN_MIN_X + x_screen_start16*SCALE; mx < x_iter_max16; mx++, x += 16*SCALE) {
280 spriteno = map_scr[map->screen_map[mapsquare.y][mapsquare.x+x_screen_iter]].fg.fg[my][mx];
281 if(spriteno) blit_sprite(x, y, &video, SCALE, map_fg, spriteno, 0);
283 bonus_layer = get_bonus_layer_index(map->screen_map[mapsquare.y][mapsquare.x+x_screen_iter]);
284 if(bonus_layer != MAPSCREEN_BLOCKED) {
285 for(my = 12-map_off/16-!!(map_off%16), y = SCREEN_MIN_Y + (!!(map_off%16)*16-(map_off%16))*-SCALE; my < 12; my++, y+=16*SCALE)
286 for(mx = x_iter_start16, x = SCREEN_MIN_X + x_screen_start16*SCALE; mx < x_iter_max16; mx++, x += 16*SCALE) {
287 spriteno = map_bonus_scr[bonus_layer].fg[my][mx];
288 if(spriteno) blit_sprite(x, y, &video, SCALE, map_fg, spriteno, 0);
293 int yleft = 200-map_off;
294 if(yleft > 192) yleft = 192;
295 for(my = 0, y = SCREEN_MIN_Y + (map_off * SCALE); my < yleft/32+!!(yleft%32); my++, y+=32*SCALE)
296 for(mx = x_iter_start64, x = SCREEN_MIN_X + x_screen_start64*SCALE; mx < x_iter_max64; mx++, x += 64*SCALE) {
297 spriteno = map_scr[map->screen_map[mapsquare.y+1][mapsquare.x+x_screen_iter]].bg.bg[my][mx];
298 blit_sprite(x, y, &video, SCALE, map_bg, spriteno, 0);
300 for(my = 0, y = SCREEN_MIN_Y + (map_off * SCALE); my < yleft/16+!!(yleft%16); my++, y+=16*SCALE)
301 for(mx = x_iter_start16, x = SCREEN_MIN_X + x_screen_start16*SCALE; mx < x_iter_max16; mx++, x += 16*SCALE) {
302 spriteno = map_scr[map->screen_map[mapsquare.y+1][mapsquare.x+x_screen_iter]].fg.fg[my][mx];
303 if(spriteno) blit_sprite(x, y, &video, SCALE, map_fg, spriteno, 0);
306 bonus_layer = get_bonus_layer_index(map->screen_map[mapsquare.y+1][mapsquare.x+x_screen_iter]);
307 if(bonus_layer != MAPSCREEN_BLOCKED) {
308 for(my = 0, y = SCREEN_MIN_Y + (map_off * SCALE); my < yleft/16+!!(yleft%16); my++, y+=16*SCALE)
309 for(mx = x_iter_start16, x = SCREEN_MIN_X + x_screen_start16*SCALE; mx < x_iter_max16; mx++, x += 16*SCALE) {
310 spriteno = map_bonus_scr[bonus_layer].fg[my][mx];
311 if(spriteno) blit_sprite(x, y, &video, SCALE, map_fg, spriteno, 0);
315 /* this is never triggered when mapscreen_xoff != 0 */
316 if(mapscreen_yoff > 192 - 8) {
317 for(mx = 0, x = SCREEN_MIN_X + x_screen_start64*SCALE; mx < 3; mx++, x += 64*SCALE)
318 blit_sprite(x, SCALE*(192*2-mapscreen_yoff), &video,
319 SCALE, map_bg, map_scr[map->screen_map[mapsquare.y+2][mapsquare.x]].bg.bg[0][mx], 0);
320 for(mx = 0, x = SCREEN_MIN_X + x_screen_start16*SCALE; mx < 12; mx++, x += 16*SCALE) {
321 spriteno = map_scr[map->screen_map[mapsquare.y+2][mapsquare.x]].fg.fg[0][mx];
322 if(spriteno) blit_sprite(x, SCALE*(192*2-mapscreen_yoff), &video, SCALE, map_fg, spriteno, 0);
324 bonus_layer = get_bonus_layer_index(map->screen_map[mapsquare.y+2][mapsquare.x]);
325 if(bonus_layer != MAPSCREEN_BLOCKED) {
326 for(mx = 0, x = SCREEN_MIN_X + x_screen_start16*SCALE; mx < 12; mx++, x += 16*SCALE) {
327 spriteno = map_bonus_scr[bonus_layer].fg[0][mx];
328 if(spriteno) blit_sprite(x, SCALE*(192*2-mapscreen_yoff), &video, SCALE, map_fg, spriteno, 0);
335 #define VSCROLL_TRESHOLD (200-74)
336 #define HSCROLLR_TRESHOLD (54-6)
337 #define HSCROLLL_TRESHOLD (192-(78+3))
338 static int scroll_needed() {
339 struct gameobj *player = &objs[player_ids[0]];
340 if((mapscrolldir == MS_UP && player->pos.y - SCREEN_MIN_Y < VSCROLL_TRESHOLD*SCALE) ||
341 (mapscrolldir == MS_RIGHT && player->pos.x - SCREEN_MIN_X > HSCROLLR_TRESHOLD*SCALE) ||
342 (mapscrolldir == MS_LEFT && player->pos.x - SCREEN_MIN_X < HSCROLLL_TRESHOLD*SCALE))
343 return 1;
344 return 0;
347 static void scroll_gameobjs(int scroll_step) {
348 if(!scroll_step) return;
349 unsigned i, avail = obj_count;
350 for(i = 0; i < OBJ_MAX && avail; i++) {
351 if(!obj_slot_used[i]) continue;
352 avail--;
353 if(objs[i].objtype == OBJ_CROSSHAIR) continue;
355 if(mapscrolldir == MS_UP)
356 objs[i].pos.y += scroll_step*SCALE;
357 else if(mapscrolldir == MS_LEFT)
358 objs[i].pos.x += scroll_step*SCALE;
359 else if(mapscrolldir == MS_RIGHT)
360 objs[i].pos.x -= scroll_step*SCALE;
364 static void next_screen() {
365 map_spawn_screen_index++;
366 map_spawn_line = 0;
367 map_spawn_current = 0;
370 static int init_enemy(const struct enemy_spawn *spawn);
371 static void handle_spawns(unsigned scrollstep) {
372 assert(scrollstep <= 192);
373 unsigned i;
374 if(!spawn_map[map_spawn_screen_index].spawns) goto done;
375 for(i = 0; i < scrollstep; i++) {
376 while(map_spawn_current < spawn_map[map_spawn_screen_index].num_spawns &&
377 map_spawn_line+i >= spawn_map[map_spawn_screen_index].spawns[map_spawn_current].scroll_line) {
378 init_enemy(&spawn_map[map_spawn_screen_index].spawns[map_spawn_current]);
379 map_spawn_current++;
382 done:
383 map_spawn_line += scrollstep;
386 static int scroll_map() {
387 int ret = 0;
388 int scroll_step = 1;
389 if(scroll_needed()) {
390 if(mapscrolldir == MS_UP) {
391 mapscreen_yoff -= scroll_step;
392 if(mapscreen_yoff < 0) {
393 mapsquare.y--;
394 if(map->screen_map[mapsquare.y][mapsquare.x] == MAPSCREEN_BLOCKED) {
395 scroll_step = -mapscreen_yoff;
396 mapscreen_yoff = 0;
397 mapsquare.y++;
398 scroll_gameobjs(scroll_step);
399 if(map->screen_map[mapsquare.y][mapsquare.x+1] == MAPSCREEN_BLOCKED) {
400 mapscrolldir = MS_LEFT;
401 } else {
402 mapscrolldir = MS_RIGHT;
403 next_screen();
405 scroll_step = 0;
406 } else {
407 next_screen();
408 mapscreen_yoff += 192;
411 handle_objs:;
412 handle_spawns(scroll_step);
413 scroll_gameobjs(scroll_step);
414 ret = 1;
415 } else if(mapscrolldir == MS_LEFT) {
416 mapscreen_xoff -= scroll_step;
417 if(mapscreen_xoff < 0) {
418 mapsquare.x--;
419 if(map->screen_map[mapsquare.y][mapsquare.x] == MAPSCREEN_BLOCKED) {
420 scroll_step = -mapscreen_xoff;
421 mapscreen_xoff = 0;
422 mapscreen_yoff = 0;
423 mapsquare.x++;
424 scroll_gameobjs(scroll_step);
425 mapscrolldir = MS_UP;
426 scroll_step = 0;
427 } else {
428 mapscreen_xoff += 192;
429 next_screen();
432 goto handle_objs;
433 } else if(mapscrolldir == MS_RIGHT) {
434 mapscreen_xoff += scroll_step;
435 if(mapscreen_xoff >= 192) {
436 mapsquare.x++;
437 if(map->screen_map[mapsquare.y][mapsquare.x+1] == MAPSCREEN_BLOCKED) {
438 scroll_step = mapscreen_xoff - 192;
439 mapscreen_xoff = 0;
440 mapscreen_yoff = 0;
441 scroll_gameobjs(scroll_step);
442 mapscrolldir = MS_UP;
443 scroll_step = 0;
444 } else {
445 next_screen();
446 mapscreen_xoff -= 192;
449 goto handle_objs;
452 return ret;
455 static int init_player(int player_no) {
456 assert(player_no == 0 || player_no == 1);
457 int pid = gameobj_alloc();
458 gameobj_init(pid, &VEC( SCREEN_MIN_X, SCREEN_MAX_Y - (25 * SCALE) ), &VEC( 0, 0 ),
459 SI_PLAYERS, player_no == 0 ? ANIM_P1_MOVE_N : ANIM_P2_MOVE_N, player_no == 0 ? OBJ_P1 : OBJ_P2);
460 if(pid == -1) return -1;
461 player_ids[player_no] = pid;
462 objs[pid].objspecific.playerdata.score = 0;
463 player_weapons[player_no][0] = WP_COLT45;
464 weapon_count[player_no] = 1;
465 weapon_active[player_no] = 0;
466 size_t i = 0;
467 for(; i < AMMO_MAX; i++)
468 player_ammo[player_no][i] = 50000;
469 add_player(pid);
470 return pid;
473 static vec2f *mousepos;
474 static int init_crosshair() {
475 int id = gameobj_alloc();
476 gameobj_init(id, &VEC(VMODE_W/2, VMODE_H/2), &VEC(0,0), SI_CROSSHAIR, ANIM_CROSSHAIR, OBJ_CROSSHAIR);
477 if(id == -1) return -1;
478 crosshair_id = id;
479 mousepos = &objs[id].pos;
480 return id;
483 static int init_bullet(vec2f *pos, vec2f *vel, int steps) {
484 int id = gameobj_alloc();
485 gameobj_init(id, pos, vel, SI_BULLET, ANIM_BULLET, OBJ_BULLET);
486 gameobj_init_bulletdata(id, steps);
487 return id;
490 static int init_grenade(vec2f *pos, vec2f *vel, int steps) {
491 int id = gameobj_alloc();
492 gameobj_init(id, pos, vel, SI_GRENADE, ANIM_GRENADE_SMALL, OBJ_GRENADE);
493 gameobj_init_bulletdata(id, steps);
494 return id;
497 static int init_grenade_explosion(vec2f *pos, int from_enemy) {
498 const int ticks_per_anim_frame = 4;
499 const int expl_anim_frames = 11;
500 vec2f grenade_center = get_sprite_center(spritemaps[SI_GRENADE_EXPLOSION]);
501 vec2f mypos = vecsub(pos, &grenade_center);
502 int id = gameobj_alloc();
503 if(id == -1) return -1;
504 gameobj_init(id, &mypos, &VEC(0,0), SI_GRENADE_EXPLOSION, ANIM_GRENADE_EXPLOSION, OBJ_GRENADE_EXPLOSION);
505 gameobj_init_bulletdata(id, expl_anim_frames*ticks_per_anim_frame -1);
506 audio_play_wave_resource(wavesounds[WS_GRENADE_EXPLOSION]);
507 if(!from_enemy) add_explosion(id);
508 else add_enemy_explosion(id);
509 return id;
512 static int init_big_explosion(vec2f *pos) {
513 const int ticks_per_anim_frame = 8;
514 const int expl_anim_frames = 5;
515 vec2f rocket_center = get_sprite_center(spritemaps[SI_BIG_EXPLOSION]);
516 vec2f mypos = vecsub(pos, &rocket_center);
517 int id = gameobj_alloc();
518 if(id == -1) return -1;
519 gameobj_init(id, &mypos, &VEC(0,0), SI_BIG_EXPLOSION, ANIM_BIG_EXPLOSION, OBJ_BIG_EXPLOSION);
520 gameobj_init_bulletdata(id, expl_anim_frames*ticks_per_anim_frame -1);
521 audio_play_wave_resource(wavesounds[WS_GRENADE_EXPLOSION]);
522 add_explosion(id);
523 return id;
526 static int init_rocket_explosion(vec2f *pos) {
527 vec2f ax = vecadd(pos, &VEC(-15*SCALE, 9*SCALE));
528 vec2f bx = vecadd(pos, &VEC(1*SCALE, -6*SCALE));
529 vec2f cx = vecadd(pos, &VEC(-8*SCALE, -8*SCALE));
530 vec2f dx = vecadd(pos, &VEC(8*SCALE, 8*SCALE));
531 int ret = 0;
532 ret += init_grenade_explosion(&ax, 0) != -1;
533 ret += init_grenade_explosion(&bx, 0) != -1;
534 ret += init_big_explosion(&cx) != -1;
535 ret += init_big_explosion(&dx) != -1;
536 return ret;
539 static int init_flame(enum direction dir, vec2f *pos, vec2f *vel, int steps) {
540 static const vec2f flame_origin[] = {
541 [DIR_O] = { 4.0, 8.0 },
542 [DIR_NO] = { 5.0, 11.0 },
543 [DIR_N] = { 7.5, 12.0 },
544 [DIR_NW] = { 10.0, 11.0 },
545 [DIR_W] = { 11.0, 8.0 },
546 [DIR_SW] = { 10.0, 5.0 },
547 [DIR_S] = { 7.5, 3.0 },
548 [DIR_SO] = { 4.0, 4.0 },
550 vec2f mypos = *pos;
551 mypos.x -= flame_origin[dir].x * SCALE;
552 mypos.y -= flame_origin[dir].y * SCALE;
553 int id = gameobj_alloc();
554 if(id == -1) return -1;
555 gameobj_init(id, &mypos, vel, SI_FLAME, ANIM_FLAME, OBJ_FLAME);
556 gameobj_init_bulletdata(id, steps);
557 add_flame(id);
558 return id;
561 static int init_rocket(enum direction dir, vec2f *pos, vec2f *vel, int steps) {
562 static const vec2f rocket_origin[] = {
563 [DIR_N] = { 1.0, 10.0 },
564 [DIR_S] = { 1.0, 0.0 },
565 [DIR_O] = { 0.0, 1.0 },
566 [DIR_W] = { 10.0, 1.0 },
567 [DIR_NO] = { 0.0, 7.0 },
568 [DIR_SO] = { 0.0, 0.0 },
569 [DIR_SW] = { 7.0, 0.0 },
570 [DIR_NW] = { 7.0, 7.0 },
572 static const enum animation_id rocket_anim[] = {
573 [DIR_N] = ANIM_ROCKET_N,
574 [DIR_S] = ANIM_ROCKET_S,
575 [DIR_O] = ANIM_ROCKET_O,
576 [DIR_W] = ANIM_ROCKET_W,
577 [DIR_NO] = ANIM_ROCKET_NO,
578 [DIR_SO] = ANIM_ROCKET_SO,
579 [DIR_SW] = ANIM_ROCKET_SW,
580 [DIR_NW] = ANIM_ROCKET_NW,
582 vec2f mypos = *pos;
583 mypos.x -= rocket_origin[dir].x * SCALE;
584 mypos.y -= rocket_origin[dir].y * SCALE;
585 int id = gameobj_alloc();
586 if(id == -1) return -1;
587 gameobj_init(id, &mypos, vel, SI_ROCKET, rocket_anim[dir], OBJ_ROCKET);
588 gameobj_init_bulletdata(id, steps);
589 add_rocket(id);
590 return id;
594 static const struct enemy_route* get_enemy_current_route(int curr_step, const struct enemy_spawn *spawn) {
595 int i = ENEMY_MAX_ROUTE -1;
596 for(; i >= 0; i--)
597 if(spawn->route[i].shape != ES_INVALID &&
598 curr_step >= spawn->route[i].start_step)
599 return &spawn->route[i];
600 return 0;
603 static vec2f get_enemy_vel(int curr_step, const struct enemy_spawn *spawn) {
604 const struct enemy_route *route = get_enemy_current_route(curr_step, spawn);
605 return get_vel_from_direction16(route->dir, (float)route->vel/8.f);
608 static const enum animation_id enemy_animation_lut[] = {
609 [ES_SOLDIER1_DOWN] = ANIM_ENEMY_GUNNER_DOWN,
610 [ES_SOLDIER1_RIGHT] = ANIM_ENEMY_GUNNER_RIGHT,
611 [ES_SOLDIER1_LEFT] = ANIM_ENEMY_GUNNER_LEFT,
612 [ES_SOLDIER2_DOWN] = ANIM_ENEMY_BOMBER_DOWN,
613 [ES_SOLDIER2_RIGHT] = ANIM_ENEMY_BOMBER_RIGHT,
614 [ES_SOLDIER2_LEFT] = ANIM_ENEMY_BOMBER_LEFT,
615 [ES_JEEP] = ANIM_JEEP,
616 [ES_TANK_SMALL] = ANIM_TANK_SMALL,
617 [ES_TANK_BIG] = ANIM_TANK_BIG,
618 [ES_TRANSPORTER] = ANIM_TRANSPORTER,
619 [ES_GUNTURRET_MOVABLE_MACHINE] = ANIM_GUNTURRET_MOVABLE_MACHINE_S,
620 [ES_GUNTURRET_MOVABLE_MAN] = ANIM_GUNTURRET_MOVABLE_MAN_S,
621 [ES_MINE_FLAT] = ANIM_MINE_FLAT,
622 [ES_MINE_CROSS] = ANIM_MINE_CROSSED,
623 [ES_FLAMETURRET] = ANIM_FLAMETURRET,
624 [ES_GUNTURRET_FIXED_SOUTH] = ANIM_GUNTURRET_FIXED_SOUTH,
625 [ES_GUNTURRET_FIXED_NORTH] = ANIM_GUNTURRET_FIXED_NORTH,
626 [ES_BUNKER_1] = ANIM_BUNKER1,
627 [ES_BUNKER_2] = ANIM_BUNKER2,
628 [ES_BUNKER_3] = ANIM_BUNKER3,
629 [ES_BUNKER_4] = ANIM_BUNKER4,
630 [ES_BUNKER_5] = ANIM_BUNKER5,
633 static int init_enemy(const struct enemy_spawn *spawn) {
634 const enum objtype enemy_soldier_objtype_lut[] = {
635 [0] = OBJ_ENEMY_SHOOTER,
636 [1] = OBJ_ENEMY_BOMBER
638 const enum objtype enemy_objtype_lut[] = {
639 [ES_JEEP] = OBJ_JEEP,
640 [ES_TANK_SMALL] = OBJ_TANK_SMALL,
641 [ES_TANK_BIG] = OBJ_TANK_BIG,
642 [ES_TRANSPORTER] = OBJ_TRANSPORTER,
643 [ES_GUNTURRET_MOVABLE_MACHINE] = OBJ_GUNTURRET_MOVABLE_MACHINE,
644 [ES_GUNTURRET_MOVABLE_MAN] = OBJ_GUNTURRET_MOVABLE_MAN,
645 [ES_MINE_FLAT] = OBJ_MINE_FLAT,
646 [ES_MINE_CROSS] = OBJ_MINE_CROSSED,
647 [ES_FLAMETURRET] = OBJ_FLAMETURRET,
648 [ES_GUNTURRET_FIXED_SOUTH] = OBJ_GUNTURRET_FIXED_SOUTH,
649 [ES_GUNTURRET_FIXED_NORTH] = OBJ_GUNTURRET_FIXED_NORTH,
650 [ES_BUNKER_1] = OBJ_BUNKER1,
651 [ES_BUNKER_2] = OBJ_BUNKER2,
652 [ES_BUNKER_3] = OBJ_BUNKER3,
653 [ES_BUNKER_4] = OBJ_BUNKER4,
654 [ES_BUNKER_5] = OBJ_BUNKER5,
655 [ES_BOSS] = OBJ_BOSS,
657 const enum animation_id boss_animation_lut[] = {
658 [0] = ANIM_BOSS1,
659 [1] = ANIM_BOSS2,
660 [2] = ANIM_BOSS3,
661 [3] = ANIM_BOSS4,
662 [4] = ANIM_BOSS5,
663 [5] = ANIM_BOSS6,
664 [6] = ANIM_BOSS7,
665 [7] = ANIM_BOSS8,
666 [8] = ANIM_BOSS9,
667 [9] = ANIM_BOSS10,
668 [10] = ANIM_BOSS11,
669 [11] = ANIM_BOSS12,
671 const enum sprite_index enemy_soldier_sprite_lut[] = {
672 [ET_ASIAN] = SI_ENEMY_ASIAN,
673 [ET_WESTERN] = SI_ENEMY_WESTERN,
675 const enum sprite_index enemy_sprite_lut[] = {
676 [ES_JEEP] = SI_VEHICLES_SMALL,
677 [ES_TANK_SMALL] = SI_VEHICLES_MEDIUM,
678 [ES_TANK_BIG] = SI_VEHICLES_BIG,
679 [ES_TRANSPORTER] = SI_VEHICLES_BIG,
680 [ES_BUNKER_1] = SI_BUNKERS,
681 [ES_BUNKER_2] = SI_BUNKERS,
682 [ES_BUNKER_3] = SI_BUNKERS,
683 [ES_BUNKER_4] = SI_BUNKERS,
684 [ES_BUNKER_5] = SI_BUNKERS,
685 [ES_GUNTURRET_MOVABLE_MACHINE] = SI_GUNTURRET,
686 [ES_GUNTURRET_MOVABLE_MAN] = SI_GUNTURRET,
687 [ES_MINE_FLAT] = SI_MINES,
688 [ES_MINE_CROSS] = SI_MINES,
689 [ES_FLAMETURRET] = SI_MINES,
690 [ES_GUNTURRET_FIXED_SOUTH] = SI_MINES,
691 [ES_GUNTURRET_FIXED_NORTH] = SI_MINES,
692 [ES_BOSS] = SI_BOSSES,
695 vec2f spawnpos = VEC(SCREEN_MIN_X + spawn->x*SCALE, SCREEN_MIN_Y + spawn->y*SCALE);
696 int id = gameobj_alloc();
697 if(id == -1) return -1;
698 const struct enemy_route* route_curr = get_enemy_current_route(0, spawn);
699 vec2f vel = get_enemy_vel(0, spawn);
701 int is_soldier = route_curr->shape <= ES_SOLDIER2_RIGHT;
702 int is_boss = route_curr->shape == ES_BOSS;
703 enum sprite_index spriteid;
704 enum objtype objid;
705 enum animation_id animid;
706 if(is_soldier) {
707 spriteid = enemy_soldier_sprite_lut[map->enemy_type];
708 objid = enemy_soldier_objtype_lut[spawn->weapon];
709 } else {
710 spriteid = enemy_sprite_lut[route_curr->shape];
711 objid = enemy_objtype_lut[route_curr->shape];
713 if(is_boss) animid = boss_animation_lut[map->boss_id];
714 else animid = enemy_animation_lut[route_curr->shape];
716 gameobj_init(id, &spawnpos, &vel, spriteid, animid, objid);
717 objs[id].objspecific.enemy.curr_step = 0;
718 objs[id].objspecific.enemy.spawn = spawn;
719 switch(objid) {
720 case OBJ_MINE_CROSSED: case OBJ_MINE_FLAT:
721 add_mine(id);
722 break;
723 case OBJ_JEEP: case OBJ_TRANSPORTER:
724 case OBJ_TANK_BIG: case OBJ_TANK_SMALL:
725 add_vehicle(id);
726 break;
727 default:
728 add_enemy(id);
730 return id;
733 static void remove_enemy(int id) {
734 enum objtype objid = objs[id].objtype;
735 switch(objid) {
736 case OBJ_JEEP: case OBJ_TRANSPORTER:
737 case OBJ_TANK_BIG: case OBJ_TANK_SMALL:
738 golist_remove(&go_vehicles, id);
739 break;
740 default:
741 golist_remove(&go_enemies, id);
743 gameobj_free(id);
746 static int enemy_fires(struct enemy *e) {
747 int i;
748 for(i = 0; i < ENEMY_MAX_SHOT; i++)
749 if(e->curr_step == e->spawn->shots[i]) return 1;
750 return 0;
753 static enum animation_id get_flash_animation_from_direction(enum direction dir) {
754 #define ANIMF(dir, anim) [dir] = anim
755 static const enum animation_id dir_to_anim[] = {
756 ANIMF(DIR_O, ANIM_FLASH_O),
757 ANIMF(DIR_NO, ANIM_FLASH_NO),
758 ANIMF(DIR_N, ANIM_FLASH_N),
759 ANIMF(DIR_NW, ANIM_FLASH_NW),
760 ANIMF(DIR_W, ANIM_FLASH_W),
761 ANIMF(DIR_SW, ANIM_FLASH_SW),
762 ANIMF(DIR_S, ANIM_FLASH_S),
763 ANIMF(DIR_SO, ANIM_FLASH_SO),
765 #undef ANIMF
766 return dir_to_anim[dir];
769 static int init_flash(vec2f *pos, enum direction dir) {
770 int id = gameobj_alloc();
771 gameobj_init(id, pos, &VEC(0, 0), SI_FLASH, get_flash_animation_from_direction(dir), OBJ_FLASH);
772 gameobj_init_bulletdata(id, 2);
773 return id;
776 static vec2f get_gameobj_pos(int obj_id) {
777 return objs[obj_id].pos;
780 static vec2f get_gameobj_center(int obj_id) {
781 vec2f res = objs[obj_id].pos;
782 vec2f add = get_sprite_center(spritemaps[objs[obj_id].spritemap_id]);
783 return vecadd(&res, &add);
786 static enum direction get_direction_from_vec(vec2f *vel);
787 static enum animation_id get_anim_from_direction(enum direction dir, int player, int throwing);
789 static enum weapon_id get_active_weapon_id(int player_no) {
790 return player_weapons[player_no][weapon_active[player_no]];
793 static const struct weapon* get_active_weapon(int player_no) {
794 return &weapons[get_active_weapon_id(player_no)];
796 static enum direction get_shotdirection_from_enemy(int curr_step, const struct enemy_spawn *spawn) {
797 const struct enemy_route* r = get_enemy_current_route(curr_step, spawn);
798 switch(r->shape) {
799 case ES_SOLDIER1_DOWN: case ES_SOLDIER2_DOWN:
800 return DIR_S;
801 case ES_SOLDIER1_LEFT: case ES_SOLDIER2_LEFT:
802 return DIR_W;
803 case ES_SOLDIER1_RIGHT: case ES_SOLDIER2_RIGHT:
804 return DIR_O;
805 default:
806 assert(0);
810 static void enemy_fire_bullet(int objid) {
811 struct gameobj* go = &objs[objid];
812 enum direction dir = get_shotdirection_from_enemy(go->objspecific.enemy.curr_step, go->objspecific.enemy.spawn);
813 vec2f from = get_gameobj_center(objid);
814 vec2f vel = get_vel_from_direction(dir, 1.75);
815 int id;
816 if(go->objspecific.enemy.spawn->weapon == EW_GUN) {
817 id = init_bullet(&from, &vel, 41);
818 if(id != -1) add_ebullet(id);
819 } else {
820 id = init_grenade(&from, &vel, 41);
821 if(id != -1) add_enemy_grenade(id);
825 static void fire_bullet(int player_no) {
826 const struct weapon *pw = get_active_weapon(player_no);
827 if(player_ammo[player_no][pw->ammo] == 0) return;
828 vec2f from = get_gameobj_center(player_ids[player_no]);
829 //get_anim_from_vel(0, objs[player].
830 vec2f to = get_gameobj_center(crosshair_id);
831 to.x += 4*SCALE - rand()%8*SCALE;
832 to.y += 4*SCALE - rand()%8*SCALE;
833 vec2f vel = velocity(&from, &to);
834 enum direction dir = get_direction_from_vec(&vel);
835 if(dir != DIR_INVALID) {
836 enum animation_id aid = get_anim_from_direction(dir, player_no, pw->ammo == AMMO_GRENADE);
837 if(aid != ANIM_INVALID) switch_anim(player_ids[player_no], aid);
838 anim_step curranim = objs[player_ids[player_no]].anim_curr;
839 if(curranim == ANIM_STEP_INIT) curranim = get_next_anim_frame(objs[player_ids[player_no]].animid, ANIM_STEP_INIT);
840 vec2f muzzle = muzzle_tab[curranim];
842 from = get_gameobj_pos(player_ids[player_no]);
843 from.x += muzzle.x * SCALE;
844 from.y += muzzle.y * SCALE;
846 if(pw->flags & WF_MUZZLEFLASH) {
847 static const vec2f flash_start[] = {
848 [DIR_O] = { 0.0, 1.0 },
849 [DIR_NO] = { 0.5, 6.0 },
850 [DIR_N] = { 1.0, 7.5 },
851 [DIR_NW] = { 6.0, 6.0 },
852 [DIR_W] = { 7.5, 1.0 },
853 [DIR_SW] = { 4.5, 0.0 },
854 [DIR_S] = { 1.0, 0.0 },
855 [DIR_SO] = { 0.0, 0.0 },
857 vec2f ffrom = from;
858 ffrom.x -= flash_start[dir].x * SCALE;
859 ffrom.y -= flash_start[dir].y * SCALE;
860 init_flash(&ffrom, dir);
862 vel = velocity(&from, &to);
864 float dist = veclength(&vel);
865 float speed = pw->bullet_speed * SCALE;
866 const uint16_t range_tab[] = {0, 80, 66, 80, 118, 118, 118, 118, 118, 118,
867 200, 200, 240, 240, 240, 240, 240, 240, 240, 240, 320 };
868 float range = range_tab[pw->range] * SCALE;
869 if(dist > range)
870 dist = range;
871 float steps = dist / speed;
872 float deg = atan2(vel.y, vel.x);
873 vel.x = cos(deg) * speed;
874 vel.y = sin(deg) * speed;
875 int id;
876 switch(pw->shot) {
877 case ST_LAUNCHER:
878 id = init_rocket(dir, &from, &vel, steps);
879 break;
880 case ST_BULLET:
881 id = init_bullet(&from, &vel, steps);
882 if(id != -1) add_pbullet(id);
883 break;
884 case ST_FLAMES:
885 id = init_flame(dir, &from, &vel, steps);
886 break;
887 case ST_GRENADE:
888 id = init_grenade(&from, &vel, steps);
889 add_grenade(id);
890 break;
891 default:
892 abort();
894 player_ammo[player_no][pw->ammo]--;
895 const WAVE_HEADER_COMPLETE *wf = wavesounds[pw->sound];
896 if(id != -1 && pw->sound != WS_NONE)
897 audio_play_wave_resource(wf);
900 static void init_game_objs() {
901 sblist_init(&go_players, 1, 4);
902 sblist_init(&go_player_bullets, 1, 32);
903 sblist_init(&go_flames, 1, 32);
904 sblist_init(&go_enemy_bullets, 1, 32);
905 sblist_init(&go_explosions, 1, 16);
906 sblist_init(&go_enemy_explosions, 1, 16);
907 sblist_init(&go_grenades, 1, 16);
908 sblist_init(&go_enemy_grenades, 1, 16);
909 sblist_init(&go_rockets, 1, 8);
910 sblist_init(&go_walls, 1, 32);
911 sblist_init(&go_enemies, 1, 32);
912 sblist_init(&go_vehicles, 1, 4);
913 sblist_init(&go_mines, 1, 4);
914 init_player(0);
915 init_crosshair();
916 init_map(current_map);
919 static int point_in_mask(vec2f *point, int obj_id) {
920 vec2f pos_in_pic = VEC((point->x - objs[obj_id].pos.x) / SCALE,
921 (point->y - objs[obj_id].pos.y) / SCALE);
922 const struct palpic *p = spritemaps[objs[obj_id].spritemap_id];
923 unsigned h = palpic_getspriteheight(p), w = palpic_getspritewidth(p);
924 if(pos_in_pic.x < 0 || pos_in_pic.y < 0 || pos_in_pic.x > w || pos_in_pic.y > h) return 0;
925 assert(objs[obj_id].anim_curr != ANIM_STEP_INIT);
926 anim_step curranim = objs[obj_id].anim_curr;
927 if(curranim == ANIM_STEP_INIT) curranim = get_next_anim_frame(objs[obj_id].animid, ANIM_STEP_INIT);
928 uint8_t *data = palpic_getspritedata(p, curranim);
929 if(data[(unsigned) pos_in_pic.y * w + (unsigned) pos_in_pic.x] != 0) return 1;
930 return 0;
933 static enum animation_id get_die_anim(unsigned id) {
934 switch(objs[id].objtype) {
935 case OBJ_P1:
936 return ANIM_P1_DIE;
937 case OBJ_P2:
938 return ANIM_P2_DIE;
939 case OBJ_JEEP:
940 return ANIM_JEEP_DESTROYED;
941 case OBJ_TANK_SMALL:
942 return ANIM_TANK_SMALL_DESTROYED;
943 case OBJ_TANK_BIG:
944 return ANIM_TANK_BIG_DESTROYED;
945 case OBJ_TRANSPORTER:
946 return ANIM_TRANSPORTER_DESTROYED;
947 case OBJ_ENEMY_BOMBER:
948 return ANIM_ENEMY_BOMBER_DIE;
949 case OBJ_ENEMY_SHOOTER:
950 return ANIM_ENEMY_GUNNER_DIE;
951 case OBJ_BUNKER1: case OBJ_BUNKER2: case OBJ_BUNKER3:
952 case OBJ_BUNKER4: case OBJ_BUNKER5:
953 return ANIM_BUNKER_DESTROYED;
954 case OBJ_GUNTURRET_MOVABLE_MACHINE:
955 return ANIM_GUNTURRET_MOVABLE_MACHINE_DESTROYED;
956 case OBJ_GUNTURRET_MOVABLE_MAN:
957 return ANIM_GUNTURRET_MOVABLE_MAN_DESTROYED;
958 default:
959 return ANIM_INVALID;
963 /* remove bullets that have reached their maximum number of steps */
964 static int remove_bullets(sblist *list) {
965 int res = 0;
966 uint8_t *item_id;
967 ssize_t li;
968 sblist_iter_counter2s(list, li, item_id) {
969 struct gameobj *bullet = &objs[*item_id];
970 if(bullet->objspecific.bullet.step_curr >= bullet->objspecific.bullet.step_max) {
971 gameobj_free(*item_id);
972 sblist_delete(list, li);
973 li--;
974 } else {
975 bullet->objspecific.bullet.step_curr++;
977 res = 1;
979 return res;
982 static int remove_explosives(sblist *list) {
983 int res = 0;
984 uint8_t *item_id;
985 ssize_t li;
986 sblist_iter_counter2s(list, li, item_id) {
987 struct gameobj *go = &objs[*item_id];
988 if(go->objspecific.bullet.step_curr >= go->objspecific.bullet.step_max) {
989 if(go->objtype == OBJ_GRENADE) init_grenade_explosion(&go->pos, list == &go_enemy_grenades);
990 else init_rocket_explosion(&go->pos);
991 gameobj_free(*item_id);
992 sblist_delete(list, li);
993 li--;
994 } else {
995 go->objspecific.bullet.step_curr++;
996 if(go->objtype == OBJ_GRENADE) {
997 if(go->objspecific.bullet.step_curr >= 32) go->animid = ANIM_GRENADE_SMALL;
998 else if(go->objspecific.bullet.step_curr >= 8) go->animid = ANIM_GRENADE_BIG;
1001 res = 1;
1003 return res;
1006 static int remove_offscreen_objects(sblist *list) {
1007 int res = 0;
1008 uint8_t *item_id;
1009 ssize_t li;
1010 sblist_iter_counter2s(list, li, item_id) {
1011 assert(obj_slot_used[*item_id]);
1012 struct gameobj *go = &objs[*item_id];
1013 assert((int) go->spritemap_id < SI_MAX);
1014 const struct palpic *p = spritemaps[go->spritemap_id];
1015 assert(p->spritecount);
1016 int h = palpic_getspriteheight(p), w = palpic_getspritewidth(p);
1017 if(go->pos.x < SCREEN_MIN_X-w*SCALE || go->pos.x > SCREEN_MAX_X ||
1018 go->pos.y < SCREEN_MIN_Y-h*SCALE || go->pos.y > SCREEN_MAX_Y) {
1019 res = 1;
1020 dprintf(2, "offscreen: removed gameobj %d\n", (int) *item_id);
1021 gameobj_free(*item_id);
1022 sblist_delete(list, li);
1023 li--;
1026 return res;
1029 static int is_death_anim(enum animation_id anim) {
1030 return anim == ANIM_ENEMY_BOMBER_DIE || anim == ANIM_ENEMY_GUNNER_DIE ||
1031 anim == ANIM_ENEMY_BURNT || anim == ANIM_P1_DIE || anim == ANIM_P2_DIE;
1034 // removes bullets and other objects if they collide. return 1 if anything happened
1035 static int hit_bullets(sblist *bullet_list, sblist *target_list) {
1036 uint8_t *bullet_id;
1037 ssize_t li;
1038 int res = 0;
1039 enum bulletsubtype {
1040 BS_BULLET = 0,
1041 BS_FLAME = 1,
1042 BS_GRENADE_EXPL = 2,
1043 BS_BIG_EXPL = 3,
1044 } bullet_subtybe = BS_BULLET;
1046 sblist_iter_counter2s(bullet_list, li, bullet_id) {
1047 struct gameobj *bullet = &objs[*bullet_id];
1048 if(bullet->objtype == OBJ_FLAME) bullet_subtybe = BS_FLAME;
1049 else if(bullet->objtype == OBJ_GRENADE_EXPLOSION) {
1050 /* grenade kills only in the explosion, not in the smoke phase */
1051 if(bullet->objspecific.bullet.step_curr > 22) continue;
1052 bullet_subtybe = BS_GRENADE_EXPL;
1053 } else if(bullet->objtype == OBJ_BIG_EXPLOSION) {
1054 bullet_subtybe = BS_BIG_EXPL;
1057 vec2f bullet_center = get_gameobj_center(*bullet_id);
1058 if(bullet_list == &go_player_bullets || bullet_list == &go_flames) {
1059 if(is_wall(&bullet_center) == WT_SOLID) goto remove_bullet;
1062 const float bullet_radius[] = { [BS_BULLET] = 1.f, [BS_FLAME] = 6.f,
1063 [BS_GRENADE_EXPL] = 16.f, [BS_BIG_EXPL] = 19.f };
1065 size_t lj;
1066 uint8_t *target_id;
1067 sblist_iter_counter2(target_list, lj, target_id) {
1068 struct gameobj *target = &objs[*target_id];
1069 if(is_death_anim(target->animid)) continue;
1070 vec2f temp = get_gameobj_center(*target_id);
1071 float dist1 = vecdist(&bullet_center, &temp) - bullet_radius[bullet_subtybe] * SCALE;
1072 vec2f newpos = vecadd(&bullet_center, &bullet->vel);
1073 float dist2 = vecdist(&newpos, &temp) - bullet_radius[bullet_subtybe] * SCALE;
1075 unsigned w = palpic_getspritewidth(spritemaps[target->spritemap_id]),
1076 h = palpic_getspriteheight(spritemaps[target->spritemap_id]);
1077 float longest_side_div2 = ((float) MAX(h, w) / 2) * SCALE;
1078 if(dist1 < 1.f*SCALE || dist2 < 1.f*SCALE) { dprintf(2, "hit1\n"); goto hit; }
1079 if(dist1 < longest_side_div2 || dist2 < longest_side_div2) {
1080 vec2f velquarter = VEC(bullet->vel.x * 0.25, bullet->vel.y * 0.25);
1081 vec2f point = bullet_center;
1082 size_t k;
1083 for(k = 0; k < 4; k++) {
1084 if(point_in_mask(&point, *target_id)) {
1085 hit:
1087 if(bullet_list == &go_player_bullets) {
1088 if(target_list == &go_vehicles) {
1089 audio_play_wave_resource(wavesounds[WS_DROPSHOT]);
1090 goto remove_bullet;
1092 objs[player_ids[0]].objspecific.playerdata.score += 50;
1093 } else if (bullet_list == &go_rockets) {
1094 init_rocket_explosion(&target->pos);
1095 goto remove_bullet;
1096 } else if(bullet->objtype == OBJ_GRENADE_EXPLOSION) {
1097 // grenade explosion has no effect on vehicles.
1098 if(target_list == &go_vehicles) goto next_bullet;
1100 enum animation_id death_anim = bullet_subtybe == BS_FLAME ? ANIM_ENEMY_BURNT : get_die_anim(*target_id);
1101 if(death_anim == ANIM_INVALID) {
1102 gameobj_free(*target_id);
1103 sblist_delete(target_list, lj);
1104 lj--;
1105 goto next_target;
1107 switch_anim(*target_id, death_anim);
1108 target->vel = VEC(0,0);
1109 if(target->objtype == OBJ_ENEMY_BOMBER || target->objtype == OBJ_ENEMY_SHOOTER) {
1110 const enum wavesound_id wid[] = { WS_SCREAM, WS_SCREAM2 };
1111 audio_play_wave_resource(wavesounds[wid[rand()%2]]);
1113 if(bullet_subtybe == BS_BULLET) {
1114 remove_bullet:
1115 gameobj_free(*bullet_id);
1116 sblist_delete(bullet_list, li);
1117 li--;
1118 goto next_bullet;
1119 } else break;
1121 point = vecadd(&point, &velquarter);
1124 next_target:;
1126 next_bullet:
1127 res = 1;
1129 return res;
1132 uint32_t tickcounter;
1134 static int advance_animations(void) {
1135 size_t i, obj_visited;
1136 int res = 0;
1137 for(i = 0, obj_visited = 0; obj_visited < obj_count && i < OBJ_MAX; i++) {
1138 if(!obj_slot_used[i]) continue;
1139 struct gameobj *go = &objs[i];
1140 if(go->anim_curr == ANIM_STEP_INIT || (go->vel.x != 0 || go->vel.y != 0) || (go->objtype != OBJ_P1 && go->objtype != OBJ_P2) || is_death_anim(go->animid)) {
1141 unsigned anim_delay = go->objtype == OBJ_BIG_EXPLOSION ? 8 : 4;
1142 if(go->anim_curr == ANIM_STEP_INIT || tickcounter % anim_delay == go->anim_frame) {
1143 anim_step last_anim = go->anim_curr;
1144 go->anim_curr = get_next_anim_frame(go->animid, go->anim_curr);
1145 if(last_anim != go->anim_curr) res = 1;
1148 obj_visited++;
1150 return res;
1153 static void switch_enemy_shape(int objid, const struct enemy_route* r) {
1154 switch_anim(objid, enemy_animation_lut[r->shape]);
1157 static void game_update_caption(void) {
1158 char buf [128];
1159 snprintf(buf, 128, "objs: %d, map x,y %d/%d, index %d, xoff %d, yoff %d, spawnscreen %d, line %d", (int) obj_count,
1160 (int)mapsquare.x, (int)mapsquare.y, (int)map->screen_map[mapsquare.y][mapsquare.x],
1161 (int)mapscreen_xoff, (int)mapscreen_yoff, (int)map_spawn_screen_index, (int) map_spawn_line);
1162 SDL_WM_SetCaption(buf, 0);
1164 static void(*update_caption)(void) = game_update_caption;
1166 static void game_tick(int force_redraw) {
1167 int need_redraw = force_redraw;
1168 size_t obj_visited;
1169 const int fps = 64;
1170 size_t i;
1171 if(mousebutton_down[MB_LEFT] > 1) {
1172 const int player_no = 0;
1173 const struct weapon *pw = get_active_weapon(player_no);
1174 //if(get_active_weapon_id(player_no) == WP_M134) __asm__("int3");
1175 if (pw->flags & WF_AUTOMATIC) {
1176 float shots_per_second = pw->rpm / 60.f;
1177 float shotinterval = fps / shots_per_second;
1178 if((int)((float)mousebutton_down[MB_LEFT] / shotinterval) != (int)((float)(mousebutton_down[MB_LEFT]-1) / shotinterval))
1179 fire_bullet(player_no);
1182 /* 1) remove bullets/rockets hitting walls (and spawn expl in the latter case)- TODO
1183 * 2) check player bullets against enemies, remove bullets and enemies
1184 * 3) check enemy bullets against players, remove bullets and players
1185 * 4) bullets: move*
1186 * 5) check grenades, remove and spawn exposions if necessary
1187 * 6) enemies: move, kill player on hit
1188 * 7) player: move
1191 // remove dead enemies
1192 sblist_iter_counter2(&go_enemies, li, &item_id) {
1193 if(objs[item_id].anim_curr == animations[objs[item_id].animid].last &&
1194 (objs[item_id].animid == ANIM_ENEMY_BOMBER_DIE || objs[item_id].animid == ANIM_ENEMY_GUNNER_DIE))
1197 if(advance_animations()) need_redraw = 1;
1198 if(hit_bullets(&go_player_bullets, &go_enemies)) need_redraw = 1;
1199 if(hit_bullets(&go_player_bullets, &go_vehicles)) need_redraw = 1;
1200 if(hit_bullets(&go_flames, &go_enemies)) need_redraw = 1;
1201 if(hit_bullets(&go_rockets, &go_enemies)) need_redraw = 1;
1202 if(hit_bullets(&go_rockets, &go_vehicles)) need_redraw = 1;
1203 if(hit_bullets(&go_explosions, &go_enemies)) need_redraw = 1;
1204 if(hit_bullets(&go_explosions, &go_vehicles)) need_redraw = 1;
1205 if(hit_bullets(&go_explosions, &go_mines)) need_redraw = 1;
1206 if(hit_bullets(&go_explosions, &go_players)) need_redraw = 1;
1207 if(hit_bullets(&go_enemy_explosions, &go_players)) need_redraw = 1;
1208 if(hit_bullets(&go_enemy_bullets, &go_players)) need_redraw = 1;
1209 if(remove_bullets(&go_player_bullets)) need_redraw = 1;
1210 if(remove_bullets(&go_flames)) need_redraw = 1;
1211 if(remove_bullets(&go_explosions)) need_redraw = 1;
1212 if(remove_bullets(&go_enemy_explosions)) need_redraw = 1;
1213 if(remove_bullets(&go_enemy_bullets)) need_redraw = 1;
1214 if(remove_explosives(&go_grenades)) need_redraw = 1;
1215 if(remove_explosives(&go_enemy_grenades)) need_redraw = 1;
1216 if(remove_explosives(&go_rockets)) need_redraw = 1;
1217 if(tickcounter % 2 == 0 && scroll_map()) need_redraw = 1;
1219 size_t obj_count_copy = obj_count;
1220 for(i = 0, obj_visited = 0; obj_visited < obj_count_copy && i < OBJ_MAX; i++) {
1221 if(obj_slot_used[i]) {
1222 struct gameobj *go = &objs[i];
1223 obj_visited++;
1224 if(go->anim_curr == ANIM_STEP_INIT) need_redraw = 1;
1225 if(go->objtype == OBJ_FLASH) {
1226 if(go->objspecific.bullet.step_curr >= go->objspecific.bullet.step_max) {
1227 gameobj_free(i);
1228 need_redraw = 1;
1229 continue;
1230 } else go->objspecific.bullet.step_curr++;
1231 } else if (go->objtype == OBJ_ENEMY_SHOOTER || go->objtype == OBJ_ENEMY_BOMBER) {
1232 if (tickcounter % 4 == go->anim_frame) {
1233 const struct enemy_route *rc = get_enemy_current_route(go->objspecific.enemy.curr_step, go->objspecific.enemy.spawn);
1234 if(rc->vel) {
1235 go->objspecific.enemy.curr_step++;
1236 if(enemy_fires(&go->objspecific.enemy)) {
1237 enemy_fire_bullet(i);
1239 const struct enemy_route *rn = get_enemy_current_route(go->objspecific.enemy.curr_step, go->objspecific.enemy.spawn);
1240 if(rn->shape != rc->shape) switch_enemy_shape(i, rn);
1243 if(!is_death_anim(go->animid)) go->vel = get_enemy_vel(go->objspecific.enemy.curr_step, go->objspecific.enemy.spawn);
1244 else go->vel = VEC(0, 0);
1246 int ismoving = 0;
1247 if(go->vel.x != 0 || go->vel.y != 0) {
1248 ismoving = 1;
1249 vec2f oldpos = go->pos;
1250 go->pos.x += go->vel.x;
1251 go->pos.y += go->vel.y;
1253 if(go->objtype == OBJ_P1 || go->objtype == OBJ_P2) {
1254 if(go->pos.y < SCREEN_MIN_Y) go->pos.y = SCREEN_MIN_Y;
1255 else if(go->pos.y+25*SCALE > SCREEN_MAX_Y) go->pos.y = SCREEN_MAX_Y-25*SCALE;
1256 if(go->pos.x < SCREEN_MIN_X) go->pos.x = SCREEN_MIN_X;
1257 else if(go->pos.x+32*SCALE > SCREEN_MAX_X) go->pos.x = SCREEN_MAX_X-32*SCALE;
1258 vec2f center = get_sprite_center(spritemaps[go->spritemap_id]);
1259 center = vecadd(&center, &go->pos);
1260 if(is_wall(&center)) {
1261 go->pos = oldpos;
1262 go->vel = VEC(0,0);
1266 need_redraw = 1;
1268 if((go->objtype == OBJ_ENEMY_BOMBER || go->objtype == OBJ_ENEMY_SHOOTER) &&
1269 go->anim_curr == animations[go->animid].last &&
1270 (go->animid == ANIM_ENEMY_BOMBER_DIE ||
1271 go->animid == ANIM_ENEMY_GUNNER_DIE ||
1272 go->animid == ANIM_ENEMY_BURNT)) {
1273 dprintf(2, "removed enemy from %.2f,%.2f\n", go->pos.x, go->pos.y);
1274 gameobj_free(i);
1275 golist_remove(&go_enemies, i);
1276 need_redraw = 1;
1277 continue;
1282 if(remove_offscreen_objects(&go_enemies)) need_redraw = 1;
1283 if(remove_offscreen_objects(&go_vehicles)) need_redraw = 1;
1284 if(remove_offscreen_objects(&go_mines)) need_redraw = 1;
1285 long ms_used = 0;
1286 struct timeval timer;
1287 gettimestamp(&timer);
1288 if(need_redraw) {
1289 draw_map();
1290 for(i = 0, obj_visited = 0; obj_visited < obj_count && i < OBJ_MAX; i++) {
1291 if(!obj_slot_used[i]) continue;
1292 struct gameobj *o = &objs[i];
1293 const prgb *palette = (o->objtype == OBJ_ENEMY_BOMBER || o->objtype == OBJ_ENEMY_SHOOTER) ? map->enemy_palette : 0;
1294 blit_sprite(o->pos.x, o->pos.y, &video,
1295 SCALE, spritemaps[o->spritemap_id],
1296 o->anim_curr == ANIM_STEP_INIT ? get_next_anim_frame(o->animid, o->anim_curr) : o->anim_curr, palette);
1297 obj_visited++;
1299 draw_status_bar();
1300 video_update_region(SCREEN_MIN_X ,SCREEN_MIN_Y , SCREEN_MAX_X - SCREEN_MIN_X, VMODE_H);
1303 ms_used = mspassed(&timer);
1304 //if(ms_used) printf("repaint took: ms_used %ld\n", ms_used);
1305 int res = audio_process();
1306 if(res == -1) music_restart();
1307 ms_used = mspassed(&timer);
1308 //if(ms_used) printf("audio processed: %d, ms_used %ld\n", res, ms_used);
1310 long sleepms = 1000/fps - ms_used;
1311 if(sleepms >= 0) SDL_Delay(sleepms);
1312 if(mousebutton_down[MB_LEFT]) mousebutton_down[MB_LEFT]++;
1314 tickcounter++;
1315 update_caption();
1318 enum cursor {
1319 c_down = 0,
1320 c_up,
1321 c_left,
1322 c_right,
1325 static enum cursor cursor_lut[] = {
1326 [SDLK_UP] = c_up,
1327 [SDLK_DOWN] = c_down,
1328 [SDLK_LEFT] = c_left,
1329 [SDLK_RIGHT] = c_right,
1330 [SDLK_w] = c_up,
1331 [SDLK_a] = c_left,
1332 [SDLK_s] = c_down,
1333 [SDLK_d] = c_right,
1336 static char cursors_pressed[] = {
1337 [c_up] = 0,
1338 [c_down] = 0,
1339 [c_left] = 0,
1340 [c_right] = 0,
1343 static vec2f get_vel_from_direction(enum direction dir, float speed) {
1344 #define VELLUT(a, b, c) [a] = VEC(b, c)
1345 static const vec2f vel_lut[] = {
1346 VELLUT(DIR_O, 1, 0),
1347 VELLUT(DIR_NO, 0.7071067769704655, -0.7071067769704655),
1348 VELLUT(DIR_N, 0, -1),
1349 VELLUT(DIR_NW, -0.7071067769704655, -0.7071067769704655),
1350 VELLUT(DIR_W, -1, 0),
1351 VELLUT(DIR_SW, -0.7071067769704655, 0.7071067769704655),
1352 VELLUT(DIR_S, 0, 1),
1353 VELLUT(DIR_SO, 0.7071067769704655, 0.7071067769704655),
1355 #undef VELLUT
1356 vec2f v = vel_lut[dir];
1357 v.x *= speed * SCALE;
1358 v.y *= speed * SCALE;
1359 return v;
1362 static vec2f get_vel_from_direction16(enum direction16 dir, float speed) {
1363 #define VELLUT(a, b, c) [a] = VEC(b, c)
1364 #define ANK90 0.7071067769704655
1365 #define GK90 ANK90
1366 #define ANK45 0.9238795042037964
1367 #define GK45 0.3826834261417389
1368 static const vec2f vel_lut[] = {
1369 VELLUT(DIR16_O, 1, 0),
1370 VELLUT(DIR16_ONO, ANK45, -GK45),
1371 VELLUT(DIR16_NO, ANK90, -ANK90),
1372 VELLUT(DIR16_NNO, GK45, -ANK45),
1373 VELLUT(DIR16_N, 0, -1),
1374 VELLUT(DIR16_NNW, -GK45, -ANK45),
1375 VELLUT(DIR16_NW, -ANK90, -ANK90),
1376 VELLUT(DIR16_WNW, -ANK45, -GK45),
1377 VELLUT(DIR16_W, -1, 0),
1378 VELLUT(DIR16_WSW, -ANK45, GK45),
1379 VELLUT(DIR16_SW, -ANK90, ANK90),
1380 VELLUT(DIR16_SSW, -GK45, ANK45),
1381 VELLUT(DIR16_S, 0, 1),
1382 VELLUT(DIR16_SSO, GK45, ANK45),
1383 VELLUT(DIR16_SO, ANK90, ANK90),
1384 VELLUT(DIR16_OSO, ANK45, GK45),
1386 #undef VELLUT
1387 vec2f v = vel_lut[dir];
1388 v.x *= speed * SCALE;
1389 v.y *= speed * SCALE;
1390 return v;
1394 static enum direction get_direction_from_vec(vec2f *vel) {
1395 float deg_org, deg = atan2(vel->y, vel->x);
1396 deg_org = deg;
1397 if(deg < 0) deg *= -1.f;
1398 else if(deg > 0) deg = M_PI + (M_PI - deg); // normalize atan2 result to scale from 0 to 2 pi
1399 int hexadrant = (int)(deg / ((1.0/16.0f)*2*M_PI));
1400 assert(hexadrant >= 0 && hexadrant < 16);
1401 static const enum direction rad_lut[] = {
1402 DIR_O,
1403 DIR_NO, DIR_NO,
1404 DIR_N, DIR_N,
1405 DIR_NW, DIR_NW,
1406 DIR_W, DIR_W,
1407 DIR_SW, DIR_SW,
1408 DIR_S, DIR_S,
1409 DIR_SO, DIR_SO,
1410 DIR_O
1412 return rad_lut[hexadrant];
1415 static enum direction get_direction_from_cursor(void) {
1416 enum direction dir = DIR_INVALID;
1417 if(cursors_pressed[c_up]) {
1418 if(cursors_pressed[c_left]) dir = DIR_NW;
1419 else if(cursors_pressed[c_right]) dir = DIR_NO;
1420 else dir = DIR_N;
1421 } else if (cursors_pressed[c_down]) {
1422 if(cursors_pressed[c_left]) dir = DIR_SW;
1423 else if(cursors_pressed[c_right]) dir = DIR_SO;
1424 else dir = DIR_S;
1425 } else if (cursors_pressed[c_left]) {
1426 dir = DIR_W;
1427 } else if (cursors_pressed[c_right]) {
1428 dir = DIR_O;
1430 return dir;
1433 static enum animation_id get_anim_from_direction(enum direction dir, int player_no, int throwing) {
1434 #define DIRMAP(a, b) [a] = b
1435 if(!throwing) {
1436 static const enum animation_id dir_map_p1[] = {
1437 DIRMAP(DIR_N, ANIM_P1_MOVE_N),
1438 DIRMAP(DIR_NW, ANIM_P1_MOVE_NW),
1439 DIRMAP(DIR_W, ANIM_P1_MOVE_W),
1440 DIRMAP(DIR_SW, ANIM_P1_MOVE_SW),
1441 DIRMAP(DIR_S, ANIM_P1_MOVE_S),
1442 DIRMAP(DIR_SO, ANIM_P1_MOVE_SO),
1443 DIRMAP(DIR_O, ANIM_P1_MOVE_O),
1444 DIRMAP(DIR_NO, ANIM_P1_MOVE_NO),
1446 static const enum animation_id dir_map_p2[] = {
1447 DIRMAP(DIR_N, ANIM_P2_MOVE_N),
1448 DIRMAP(DIR_NW, ANIM_P2_MOVE_NW),
1449 DIRMAP(DIR_W, ANIM_P2_MOVE_W),
1450 DIRMAP(DIR_SW, ANIM_P2_MOVE_SW),
1451 DIRMAP(DIR_S, ANIM_P2_MOVE_S),
1452 DIRMAP(DIR_SO, ANIM_P2_MOVE_SO),
1453 DIRMAP(DIR_O, ANIM_P2_MOVE_O),
1454 DIRMAP(DIR_NO, ANIM_P2_MOVE_NO),
1456 const enum animation_id *dir_map = player_no == 0 ? dir_map_p1 : dir_map_p2;
1457 return dir_map[dir];
1458 } else {
1459 static const enum animation_id dir_map_p1_g[] = {
1460 DIRMAP(DIR_N, ANIM_P1_THROW_N),
1461 DIRMAP(DIR_NW, ANIM_P1_THROW_NW),
1462 DIRMAP(DIR_W, ANIM_P1_THROW_W),
1463 DIRMAP(DIR_SW, ANIM_P1_THROW_SW),
1464 DIRMAP(DIR_S, ANIM_P1_THROW_S),
1465 DIRMAP(DIR_SO, ANIM_P1_THROW_SO),
1466 DIRMAP(DIR_O, ANIM_P1_THROW_O),
1467 DIRMAP(DIR_NO, ANIM_P1_THROW_NO),
1469 static const enum animation_id dir_map_p2_g[] = {
1470 DIRMAP(DIR_N, ANIM_P2_THROW_N),
1471 DIRMAP(DIR_NW, ANIM_P2_THROW_NW),
1472 DIRMAP(DIR_W, ANIM_P2_THROW_W),
1473 DIRMAP(DIR_SW, ANIM_P2_THROW_SW),
1474 DIRMAP(DIR_S, ANIM_P2_THROW_S),
1475 DIRMAP(DIR_SO, ANIM_P2_THROW_SO),
1476 DIRMAP(DIR_O, ANIM_P2_THROW_O),
1477 DIRMAP(DIR_NO, ANIM_P2_THROW_NO),
1479 const enum animation_id *dir_map = player_no == 0 ? dir_map_p1_g : dir_map_p2_g;
1480 return dir_map[dir];
1482 #undef DIRMAP
1485 #if 0
1486 static enum animation_id get_anim_from_cursor(void) {
1487 enum direction dir = get_direction_from_cursor();
1488 if(dir == DIR_INVALID) return ANIM_INVALID;
1489 return get_anim_from_direction(dir, 0, 0);
1491 /* playerno is either 0 or 1, not player_id! */
1492 static enum animation_id get_anim_from_vel(int player_no, vec2f *vel) {
1493 enum direction dir = get_direction_from_vec(vel);
1494 if(dir == DIR_INVALID) return ANIM_INVALID;
1495 return get_anim_from_direction(dir, player_no, 0);
1497 #endif
1499 static void switch_anim(int obj_id, int aid) {
1500 if(objs[obj_id].animid == aid) return;
1501 gameobj_start_anim(obj_id, aid);
1504 enum map_index choose_mission(void);
1505 //RcB: DEP "mission_select.c"
1506 #include "enemytag.c"
1507 int main() {
1508 video_init();
1509 clear_screen();
1510 //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1511 SDL_EnableKeyRepeat(100, 20);
1513 audio_init();
1515 /* background music for mission selection screen */
1516 music_play(TUNE_MAP);
1518 if((current_map = choose_mission()) == MI_INVALID) goto dun_goofed;
1520 music_play(TUNE_FIGHTING);
1522 SDL_ShowCursor(0);
1525 int startx = 10;
1526 int starty = 10;
1528 //redraw(surface, startx, starty);
1529 const float player_speed = 1.25f;
1530 const struct palpic* spritemap = spritemaps[SI_PLAYERS];
1531 struct { int *target; int dir; int max;} moves[] = {
1532 [c_up] = {&starty, SCALE * -1, VMODE_H - (palpic_getspriteheight(spritemap) * SCALE)},
1533 [c_down] = {&starty, SCALE, VMODE_H - (palpic_getspriteheight(spritemap) * SCALE)},
1534 [c_left] = {&startx, SCALE * -1, VMODE_W - (palpic_getspritewidth(spritemap) * SCALE)},
1535 [c_right] = {&startx, SCALE, VMODE_W - (palpic_getspritewidth(spritemap) * SCALE)},
1538 init_game_objs();
1539 int player_no = 0;
1540 int player_id = player_ids[player_no];
1542 game_tick(1);
1544 SDL_Event sdl_event;
1545 while(1) {
1546 unsigned need_redraw = 0;
1547 int weapon_inc = 0;
1548 while (SDL_PollEvent(&sdl_event)) {
1549 need_redraw = 0;
1550 switch (sdl_event.type) {
1551 case SDL_MOUSEMOTION:
1552 if((int)mousepos->x != sdl_event.motion.x || (int)mousepos->y != sdl_event.motion.y)
1553 need_redraw = 1;
1554 mousepos->x = sdl_event.motion.x;
1555 mousepos->y = sdl_event.motion.y;
1556 break;
1557 case SDL_MOUSEBUTTONDOWN:
1558 mousepos->x = sdl_event.button.x;
1559 mousepos->y = sdl_event.button.y;
1560 mousebutton_down[MB_LEFT] = 1;
1561 fire_bullet(player_no);
1562 break;
1563 case SDL_MOUSEBUTTONUP:
1564 mousepos->x = sdl_event.button.x;
1565 mousepos->y = sdl_event.button.y;
1566 mousebutton_down[MB_LEFT] = 0;
1567 break;
1568 case SDL_QUIT:
1569 dun_goofed:
1570 video_cleanup();
1571 return 0;
1572 case SDL_KEYDOWN:
1573 switch(sdl_event.key.keysym.sym) {
1574 case SDLK_w: case SDLK_a: case SDLK_s: case SDLK_d:
1575 case SDLK_UP:
1576 case SDLK_DOWN:
1577 case SDLK_RIGHT:
1578 case SDLK_LEFT:
1579 cursors_pressed[cursor_lut[sdl_event.key.keysym.sym]] = 1;
1580 check_anim: {
1581 enum direction dir = get_direction_from_cursor();
1582 if(dir != DIR_INVALID) {
1583 if(!mousebutton_down[MB_LEFT]) {
1584 // change animation according to cursors,
1585 // unless we're in automatic fire mode.
1586 enum animation_id aid = get_anim_from_direction(dir, player_no, 0);
1587 if(aid != ANIM_INVALID) switch_anim(player_id, aid);
1589 objs[player_id].vel = get_vel_from_direction(dir, player_speed);
1590 } else {
1591 objs[player_id].vel = VEC(0,0);
1594 break;
1595 case SDLK_RETURN:
1596 if((sdl_event.key.keysym.mod & KMOD_LALT) ||
1597 (sdl_event.key.keysym.mod & KMOD_RALT)) {
1598 toggle_fullscreen();
1599 SDL_Delay(1);
1600 game_tick(1);
1601 need_redraw = 1;
1603 break;
1604 case SDLK_KP_PLUS:
1605 weapon_inc = 1;
1606 goto toggle_weapon;
1607 case SDLK_KP_MINUS:
1608 if((int)player_weapons[player_no][0] == 0)
1609 weapon_inc = WP_MAX-1;
1610 else weapon_inc = -1;
1611 toggle_weapon:
1612 player_weapons[player_no][0] += weapon_inc;
1613 if(player_weapons[player_no][0] == WP_INVALID)
1614 player_weapons[player_no][0] = 0;
1615 printf("%s\n", weapon_name(player_weapons[player_no][0]));
1616 need_redraw = 1;
1617 break;
1618 default:
1619 break;
1621 break;
1622 case SDL_KEYUP:
1623 switch(sdl_event.key.keysym.sym) {
1624 case SDLK_e:
1625 enemy_tag_loop();
1626 update_caption = game_update_caption;
1627 break;
1628 case SDLK_c:
1629 clear_screen();
1630 video_update();
1631 need_redraw = 1;
1632 break;
1633 case SDLK_w: case SDLK_a: case SDLK_s: case SDLK_d:
1634 case SDLK_UP:
1635 case SDLK_DOWN:
1636 case SDLK_RIGHT:
1637 case SDLK_LEFT:
1638 cursors_pressed[cursor_lut[sdl_event.key.keysym.sym]] = 0;
1639 goto check_anim;
1640 case SDLK_ESCAPE:
1641 goto dun_goofed;
1642 default:
1643 break;
1645 default:
1646 break;
1649 unsigned i;
1650 for (i = 0; i < ARRAY_SIZE(cursors_pressed); i++) {
1651 if(cursors_pressed[i]) {
1652 *moves[i].target += moves[i].dir;
1653 if(*moves[i].target < 0) *moves[i].target = 0;
1654 if(*moves[i].target > moves[i].max) *moves[i].target = moves[i].max;
1655 need_redraw = 1;
1658 game_tick(need_redraw);
1661 return 0;