1 #include "../lib/include/timelib.h"
2 #include "../lib/include/macros.h"
3 #include "../lib/include/sblist.h"
13 #include "direction.h"
18 #include "muzzle_tab.h"
19 #include "spritemaps.h"
23 #include "mapsprites.h"
26 #include "spawnmaps.h"
30 #ifndef IN_KDEVELOP_PARSER
31 #include "../lib/include/bitarray.h"
40 // 1 if button down, 0 if not, >1 to count ms pressed
41 unsigned long mousebutton_down
[] = {
46 #pragma RcB2 LINK "-lSDL"
48 static void get_last_move_event(SDL_Event
* e
) {
50 SDL_Event peek
[numpeek
];
51 SDL_Event
* last_event
= NULL
;
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
];
63 SDL_PeepEvents(peek
, i
+ 1, SDL_GETEVENT
, (uint32_t) ~0);
69 static vec2f
get_sprite_center(const struct palpic
*p
) {
70 assert(p
->spritecount
);
72 res
.x
= palpic_getspritewidth(p
) * SCALE
/ 2;
73 res
.y
= palpic_getspriteheight(p
) * SCALE
/ 2;
77 static int player_ids
[2];
78 static int player_kills
[2];
79 static int player_score
[2];
80 static int player_cash
[2];
81 static enum weapon_id player_weapons
[2][WP_MAX
];
82 static int weapon_count
[2];
83 static int weapon_active
[2]; // index into player_weapons[playerno]
84 static int player_ammo
[2][AMMO_MAX
];
85 static enum weapon_id
get_active_weapon_id(int player_no
);
86 static void switch_anim(int obj_id
, int aid
);
87 static vec2f
get_vel_from_direction(enum direction dir
, float speed
);
88 static vec2f
get_vel_from_direction16(enum direction16 dir
, float speed
);
90 static sblist go_player_bullets
;
91 static sblist go_enemy_bullets
;
92 static sblist go_explosions
;
93 static sblist go_enemy_explosions
;
94 static sblist go_enemies
;
95 static sblist go_players
;
96 static sblist go_flames
;
97 static sblist go_enemy_flames
;
98 static sblist go_rockets
;
99 static sblist go_grenades
;
100 static sblist go_enemy_grenades
;
101 static sblist go_vehicles
;
102 static sblist go_mines
;
103 static sblist go_turrets
;
104 static sblist go_bunkers
;
105 static sblist go_boss
;
106 static sblist go_crosshair
;
107 static sblist go_muzzleflash
;
108 static sblist go_blood
;
109 static void add_pbullet(uint8_t bullet_id
) {
110 sblist_add(&go_player_bullets
, &bullet_id
);
112 static void add_ebullet(uint8_t bullet_id
) {
113 sblist_add(&go_enemy_bullets
, &bullet_id
);
115 static void add_player(uint8_t player_id
) {
116 sblist_add(&go_players
, &player_id
);
118 static void add_enemy(uint8_t enem_id
) {
119 sblist_add(&go_enemies
, &enem_id
);
121 static void add_explosion(uint8_t expl_id
) {
122 sblist_add(&go_explosions
, &expl_id
);
124 static void add_enemy_explosion(uint8_t expl_id
) {
125 sblist_add(&go_enemy_explosions
, &expl_id
);
127 static void add_flame(uint8_t id
) {
128 sblist_add(&go_flames
, &id
);
130 static void add_grenade(uint8_t id
) {
131 sblist_add(&go_grenades
, &id
);
133 static void add_enemy_grenade(uint8_t id
) {
134 sblist_add(&go_enemy_grenades
, &id
);
136 static void add_rocket(uint8_t id
) {
137 sblist_add(&go_rockets
, &id
);
139 static void add_vehicle(uint8_t id
) {
140 sblist_add(&go_vehicles
, &id
);
142 static void add_mine(uint8_t id
) {
143 sblist_add(&go_mines
, &id
);
145 static void add_turret(uint8_t id
) {
146 sblist_add(&go_turrets
, &id
);
148 static void add_bunker(uint8_t id
) {
149 sblist_add(&go_bunkers
, &id
);
151 static void add_boss(uint8_t id
) {
152 sblist_add(&go_boss
, &id
);
154 static void add_crosshair(uint8_t id
) {
155 sblist_add(&go_crosshair
, &id
);
157 static void add_muzzleflash(uint8_t id
) {
158 sblist_add(&go_muzzleflash
, &id
);
160 static void add_blood(uint8_t id
) {
161 sblist_add(&go_blood
, &id
);
163 static void add_enemy_flame(uint8_t id
) {
164 sblist_add(&go_enemy_flames
, &id
);
166 static void golist_remove(sblist
*l
, uint8_t objid
) {
169 sblist_iter_counter2(l
, i
, itemid
) {
170 if(*itemid
== objid
) {
177 static int get_next_anim_frame(enum animation_id aid
, anim_step curr
) {
178 if(curr
== ANIM_STEP_INIT
) return animations
[aid
].first
;
180 if(curr
> animations
[aid
].last
) return animations
[aid
].first
;
184 #define SCREEN_MIN_X 64*SCALE
185 #define SCREEN_MAX_X VMODE_W - 64*SCALE
186 #define SCREEN_MIN_Y 0
187 #define SCREEN_MAX_Y 200*SCALE
189 static void draw_status_bar(void) {
190 enum weapon_id wid
= get_active_weapon_id(0);
192 sdl_rgb_t
*ptr
= (sdl_rgb_t
*) video
.mem
;
193 unsigned pitch
= video
.pitch
/4;
194 for(y
= SCREEN_MAX_Y
; y
< VMODE_H
; y
++)
195 for (x
= SCREEN_MIN_X
; x
< SCREEN_MAX_X
; x
++)
196 ptr
[y
*pitch
+ x
] = SRGB_BLACK
;
198 blit_sprite(((320 / 2) - (59 / 2)) * SCALE
, (200 + (40/2) - (16/2)) * SCALE
,
199 &video
, SCALE
, spritemaps
[SI_WEAPONS
], wid
, 0);
202 snprintf(buf
, 16, "%.6u", player_score
[0]);
203 font_print(SCREEN_MIN_X
+ 8, SCREEN_MAX_Y
+ 8, buf
, 6, 1 * SCALE
, PRGB(255,255,255));
206 enum map_index current_map
= MI_VIETNAM
;
207 const struct map
*map
;
208 const struct map_screen
* map_scr
;
209 const struct palpic
*map_bg
;
210 const struct palpic
*map_fg
;
211 const mapscreen_index
*map_bonus_indices
;
212 const struct map_fglayer
*map_bonus_scr
;
214 int mapscreen_yoff
, mapscreen_xoff
;
215 struct { int x
,y
; } mapsquare
;
216 enum map_scrolldir mapscrolldir
;
217 unsigned map_spawn_screen_index
;
218 unsigned map_spawn_line
;
219 unsigned map_spawn_current
;
221 static const int fps
= 64;
223 static void init_map(enum map_index mapindex
) {
224 map
= maps
[mapindex
];
225 map_scr
= map_screens
[mapindex
];
226 map_bg
= map_bg_sprites
[map
->maptype
];
227 map_fg
= map_fg_sprites
[map
->maptype
];
228 map_bonus_scr
= map_bonus_screens
[mapindex
];
229 map_bonus_indices
= map_bonus_layer_indices
[mapindex
];
234 mapscrolldir
= MS_UP
;
235 map_spawn_screen_index
= 0;
237 map_spawn_current
= 0;
240 static mapscreen_index
get_bonus_layer_index(mapscreen_index screen
) {
242 for (i
= 0; i
< map
->bonuslayer_count
; i
++)
243 if(map_bonus_indices
[i
] == screen
) return i
;
244 return MAPSCREEN_BLOCKED
;
247 static mapscreen_index
screen_to_mapscreen(int *x
, int *y
) {
248 *x
= ((int) *x
- SCREEN_MIN_X
) / SCALE
;
249 *y
= ((int) *y
- SCREEN_MIN_Y
) / SCALE
;
250 int yscr
= (*y
+ mapscreen_yoff
) / 192;
251 *y
= (*y
+ mapscreen_yoff
) - yscr
*192;
252 int xscr
= (*x
+ mapscreen_xoff
) / 192;
253 *x
= (*x
+ mapscreen_xoff
) - xscr
*192;
254 return map
->screen_map
[mapsquare
.y
+ yscr
][mapsquare
.x
+ xscr
];
257 static enum walltype
is_wall(vec2f
*pos
) {
260 mapscreen_index scr_idx
= screen_to_mapscreen(&x
, &y
);
261 /* can happen when a bullet goes partially off-screen */
262 if(scr_idx
== MAPSCREEN_BLOCKED
) return WT_NONE
;
263 uint8_t spriteno
= map_scr
[scr_idx
].fg
.fg
[y
/16][x
/16];
264 if(spriteno
&& walls
[map
->maptype
][spriteno
]) return walls
[map
->maptype
][spriteno
];
265 scr_idx
= get_bonus_layer_index(scr_idx
);
266 if(scr_idx
== MAPSCREEN_BLOCKED
) return WT_NONE
;
267 spriteno
= map_bonus_scr
[scr_idx
].fg
[y
/16][x
/16];
268 if(spriteno
&& walls
[map
->maptype
][spriteno
]) return walls
[map
->maptype
][spriteno
];
272 static void draw_map() {
274 unsigned map_off
= 192-mapscreen_yoff
;
275 unsigned vis_x
= 192-mapscreen_xoff
;
276 int x_iter_max16
= 192/16;
277 int x_iter_max64
= 192/64;
278 unsigned x_iter_start64
= x_iter_max64
- (vis_x
/ 64 + !!(vis_x
% 64));
279 unsigned x_iter_start16
= x_iter_max16
- (vis_x
/ 16 + !!(vis_x
% 16));
281 int x_screen_start64
= -(!!(vis_x
%64)*64-(vis_x
%64));
282 int x_screen_start16
= -(!!(vis_x
%16)*16-(vis_x
%16));
284 unsigned x_screen_iter
;
285 for(x_screen_iter
= 0; x_screen_iter
<= !!mapscreen_xoff
; x_screen_iter
++) {
286 mapscreen_index bonus_layer
;
288 x_screen_start16
= x_screen_start16
+(x_iter_max16
-x_iter_start16
)*16;
289 x_screen_start64
= x_screen_start64
+(x_iter_max64
-x_iter_start64
)*64;
290 x_iter_max16
= mapscreen_xoff
/ 16 + !!(mapscreen_xoff
% 16);
291 x_iter_max64
= mapscreen_xoff
/ 64 + !!(mapscreen_xoff
% 64);
294 assert(map
->screen_map
[mapsquare
.y
][mapsquare
.x
+x_screen_iter
] != MAPSCREEN_BLOCKED
);
298 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
)
299 for(mx
= x_iter_start64
, x
= SCREEN_MIN_X
+ x_screen_start64
*SCALE
; mx
< x_iter_max64
; mx
++, x
+= 64*SCALE
) {
300 spriteno
= map_scr
[map
->screen_map
[mapsquare
.y
][mapsquare
.x
+x_screen_iter
]].bg
.bg
[my
][mx
];
301 blit_sprite(x
, y
, &video
,
302 SCALE
, map_bg
, spriteno
, 0);
304 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
)
305 for(mx
= x_iter_start16
, x
= SCREEN_MIN_X
+ x_screen_start16
*SCALE
; mx
< x_iter_max16
; mx
++, x
+= 16*SCALE
) {
306 spriteno
= map_scr
[map
->screen_map
[mapsquare
.y
][mapsquare
.x
+x_screen_iter
]].fg
.fg
[my
][mx
];
307 if(spriteno
) blit_sprite(x
, y
, &video
, SCALE
, map_fg
, spriteno
, 0);
309 bonus_layer
= get_bonus_layer_index(map
->screen_map
[mapsquare
.y
][mapsquare
.x
+x_screen_iter
]);
310 if(bonus_layer
!= MAPSCREEN_BLOCKED
) {
311 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
)
312 for(mx
= x_iter_start16
, x
= SCREEN_MIN_X
+ x_screen_start16
*SCALE
; mx
< x_iter_max16
; mx
++, x
+= 16*SCALE
) {
313 spriteno
= map_bonus_scr
[bonus_layer
].fg
[my
][mx
];
314 if(spriteno
) blit_sprite(x
, y
, &video
, SCALE
, map_fg
, spriteno
, 0);
319 int yleft
= 200-map_off
;
320 if(yleft
> 192) yleft
= 192;
321 for(my
= 0, y
= SCREEN_MIN_Y
+ (map_off
* SCALE
); my
< yleft
/32+!!(yleft
%32); my
++, y
+=32*SCALE
)
322 for(mx
= x_iter_start64
, x
= SCREEN_MIN_X
+ x_screen_start64
*SCALE
; mx
< x_iter_max64
; mx
++, x
+= 64*SCALE
) {
323 spriteno
= map_scr
[map
->screen_map
[mapsquare
.y
+1][mapsquare
.x
+x_screen_iter
]].bg
.bg
[my
][mx
];
324 blit_sprite(x
, y
, &video
, SCALE
, map_bg
, spriteno
, 0);
326 for(my
= 0, y
= SCREEN_MIN_Y
+ (map_off
* SCALE
); my
< yleft
/16+!!(yleft
%16); my
++, y
+=16*SCALE
)
327 for(mx
= x_iter_start16
, x
= SCREEN_MIN_X
+ x_screen_start16
*SCALE
; mx
< x_iter_max16
; mx
++, x
+= 16*SCALE
) {
328 spriteno
= map_scr
[map
->screen_map
[mapsquare
.y
+1][mapsquare
.x
+x_screen_iter
]].fg
.fg
[my
][mx
];
329 if(spriteno
) blit_sprite(x
, y
, &video
, SCALE
, map_fg
, spriteno
, 0);
332 bonus_layer
= get_bonus_layer_index(map
->screen_map
[mapsquare
.y
+1][mapsquare
.x
+x_screen_iter
]);
333 if(bonus_layer
!= MAPSCREEN_BLOCKED
) {
334 for(my
= 0, y
= SCREEN_MIN_Y
+ (map_off
* SCALE
); my
< yleft
/16+!!(yleft
%16); my
++, y
+=16*SCALE
)
335 for(mx
= x_iter_start16
, x
= SCREEN_MIN_X
+ x_screen_start16
*SCALE
; mx
< x_iter_max16
; mx
++, x
+= 16*SCALE
) {
336 spriteno
= map_bonus_scr
[bonus_layer
].fg
[my
][mx
];
337 if(spriteno
) blit_sprite(x
, y
, &video
, SCALE
, map_fg
, spriteno
, 0);
341 /* this is never triggered when mapscreen_xoff != 0 */
342 if(mapscreen_yoff
> 192 - 8) {
343 for(mx
= 0, x
= SCREEN_MIN_X
+ x_screen_start64
*SCALE
; mx
< 3; mx
++, x
+= 64*SCALE
)
344 blit_sprite(x
, SCALE
*(192*2-mapscreen_yoff
), &video
,
345 SCALE
, map_bg
, map_scr
[map
->screen_map
[mapsquare
.y
+2][mapsquare
.x
]].bg
.bg
[0][mx
], 0);
346 for(mx
= 0, x
= SCREEN_MIN_X
+ x_screen_start16
*SCALE
; mx
< 12; mx
++, x
+= 16*SCALE
) {
347 spriteno
= map_scr
[map
->screen_map
[mapsquare
.y
+2][mapsquare
.x
]].fg
.fg
[0][mx
];
348 if(spriteno
) blit_sprite(x
, SCALE
*(192*2-mapscreen_yoff
), &video
, SCALE
, map_fg
, spriteno
, 0);
350 bonus_layer
= get_bonus_layer_index(map
->screen_map
[mapsquare
.y
+2][mapsquare
.x
]);
351 if(bonus_layer
!= MAPSCREEN_BLOCKED
) {
352 for(mx
= 0, x
= SCREEN_MIN_X
+ x_screen_start16
*SCALE
; mx
< 12; mx
++, x
+= 16*SCALE
) {
353 spriteno
= map_bonus_scr
[bonus_layer
].fg
[0][mx
];
354 if(spriteno
) blit_sprite(x
, SCALE
*(192*2-mapscreen_yoff
), &video
, SCALE
, map_fg
, spriteno
, 0);
361 #define VSCROLL_TRESHOLD (200-74)
362 #define HSCROLLR_TRESHOLD (54-6)
363 #define HSCROLLL_TRESHOLD (192-(78+3))
364 static int scroll_needed() {
365 struct gameobj
*player
= &objs
[player_ids
[0]];
366 if((mapscrolldir
== MS_UP
&& player
->pos
.y
- SCREEN_MIN_Y
< VSCROLL_TRESHOLD
*SCALE
) ||
367 (mapscrolldir
== MS_RIGHT
&& player
->pos
.x
- SCREEN_MIN_X
> HSCROLLR_TRESHOLD
*SCALE
) ||
368 (mapscrolldir
== MS_LEFT
&& player
->pos
.x
- SCREEN_MIN_X
< HSCROLLL_TRESHOLD
*SCALE
))
373 static void scroll_gameobjs(int scroll_step
) {
374 if(!scroll_step
) return;
375 unsigned i
, avail
= obj_count
;
376 for(i
= 0; i
< OBJ_MAX
&& avail
; i
++) {
377 if(!obj_slot_used
[i
]) continue;
379 if(objs
[i
].objtype
== OBJ_CROSSHAIR
) continue;
381 if(mapscrolldir
== MS_UP
)
382 objs
[i
].pos
.y
+= scroll_step
*SCALE
;
383 else if(mapscrolldir
== MS_LEFT
)
384 objs
[i
].pos
.x
+= scroll_step
*SCALE
;
385 else if(mapscrolldir
== MS_RIGHT
)
386 objs
[i
].pos
.x
-= scroll_step
*SCALE
;
390 static void next_screen() {
391 map_spawn_screen_index
++;
393 map_spawn_current
= 0;
396 static int init_enemy(const struct enemy_spawn
*spawn
);
397 static void handle_spawns(unsigned scrollstep
) {
398 assert(scrollstep
<= 192);
400 const struct enemy_spawn_screen
*spawn_map
= spawn_maps
[current_map
];
401 if(!spawn_map
[map_spawn_screen_index
].spawns
) goto done
;
402 for(i
= 0; i
< scrollstep
; i
++) {
403 while(map_spawn_current
< spawn_map
[map_spawn_screen_index
].num_spawns
&&
404 map_spawn_line
+i
>= spawn_map
[map_spawn_screen_index
].spawns
[map_spawn_current
].scroll_line
) {
405 init_enemy(&spawn_map
[map_spawn_screen_index
].spawns
[map_spawn_current
]);
410 map_spawn_line
+= scrollstep
;
413 static int scroll_map() {
416 if(scroll_needed()) {
417 if(mapscrolldir
== MS_UP
) {
418 mapscreen_yoff
-= scroll_step
;
419 if(mapscreen_yoff
< 0) {
421 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
] == MAPSCREEN_BLOCKED
) {
422 scroll_step
= -mapscreen_yoff
;
425 scroll_gameobjs(scroll_step
);
426 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
+1] == MAPSCREEN_BLOCKED
) {
427 mapscrolldir
= MS_LEFT
;
429 mapscrolldir
= MS_RIGHT
;
435 mapscreen_yoff
+= 192;
439 handle_spawns(scroll_step
);
440 scroll_gameobjs(scroll_step
);
442 } else if(mapscrolldir
== MS_LEFT
) {
443 mapscreen_xoff
-= scroll_step
;
444 if(mapscreen_xoff
< 0) {
446 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
] == MAPSCREEN_BLOCKED
) {
447 scroll_step
= -mapscreen_xoff
;
451 scroll_gameobjs(scroll_step
);
452 mapscrolldir
= MS_UP
;
455 mapscreen_xoff
+= 192;
460 } else if(mapscrolldir
== MS_RIGHT
) {
461 mapscreen_xoff
+= scroll_step
;
462 if(mapscreen_xoff
>= 192) {
464 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
+1] == MAPSCREEN_BLOCKED
) {
465 scroll_step
= mapscreen_xoff
- 192;
468 scroll_gameobjs(scroll_step
);
469 mapscrolldir
= MS_UP
;
473 mapscreen_xoff
-= 192;
482 static void init_player_once(int player_no
) {
483 player_score
[player_no
] = 0;
484 player_cash
[player_no
] = 10000;
487 static int init_player(int player_no
) {
488 assert(player_no
== 0 || player_no
== 1);
489 int pid
= gameobj_alloc();
490 gameobj_init(pid
, &VEC( SCREEN_MIN_X
, SCREEN_MAX_Y
- (25 * SCALE
) ), &VEC( 0, 0 ),
491 SI_PLAYERS
, player_no
== 0 ? ANIM_P1_MOVE_N
: ANIM_P2_MOVE_N
, player_no
== 0 ? OBJ_P1
: OBJ_P2
);
492 if(pid
== -1) return -1;
493 player_ids
[player_no
] = pid
;
494 weapon_active
[player_no
] = 0;
500 static vec2f
*mousepos
;
501 static int init_crosshair() {
502 int id
= gameobj_alloc();
503 gameobj_init(id
, &VEC(VMODE_W
/2, VMODE_H
/2), &VEC(0,0), SI_CROSSHAIR
, ANIM_CROSSHAIR
, OBJ_CROSSHAIR
);
504 if(id
== -1) return -1;
505 mousepos
= &objs
[id
].pos
;
509 static int init_blood(vec2f
*pos
) {
510 int id
= gameobj_alloc();
511 gameobj_init(id
, pos
, &VEC(0,0), SI_MISC
, ANIM_BLOOD
, OBJ_BLOOD
);
512 gameobj_init_bulletdata(id
, 4);
516 static int init_bullet(vec2f
*pos
, vec2f
*vel
, int steps
) {
517 int id
= gameobj_alloc();
518 gameobj_init(id
, pos
, vel
, SI_BULLET
, ANIM_BULLET
, OBJ_BULLET
);
519 gameobj_init_bulletdata(id
, steps
);
523 static int init_grenade(vec2f
*pos
, vec2f
*vel
, int steps
) {
524 int id
= gameobj_alloc();
525 gameobj_init(id
, pos
, vel
, SI_GRENADE
, ANIM_GRENADE_SMALL
, OBJ_GRENADE
);
526 gameobj_init_bulletdata(id
, steps
);
530 static int init_grenade_explosion(vec2f
*pos
, int from_enemy
) {
531 const int ticks_per_anim_frame
= 4;
532 const int expl_anim_frames
= 11;
533 vec2f grenade_center
= get_sprite_center(spritemaps
[SI_GRENADE_EXPLOSION
]);
534 vec2f mypos
= vecsub(pos
, &grenade_center
);
535 int id
= gameobj_alloc();
536 if(id
== -1) return -1;
537 gameobj_init(id
, &mypos
, &VEC(0,0), SI_GRENADE_EXPLOSION
, ANIM_GRENADE_EXPLOSION
, OBJ_GRENADE_EXPLOSION
);
538 gameobj_init_bulletdata(id
, expl_anim_frames
*ticks_per_anim_frame
-1);
539 audio_play_wave_resource(wavesounds
[WS_GRENADE_EXPLOSION
]);
540 if(!from_enemy
) add_explosion(id
);
541 else add_enemy_explosion(id
);
545 static int init_big_explosion(vec2f
*pos
) {
546 const int ticks_per_anim_frame
= 8;
547 const int expl_anim_frames
= 5;
548 vec2f rocket_center
= get_sprite_center(spritemaps
[SI_BIG_EXPLOSION
]);
549 vec2f mypos
= vecsub(pos
, &rocket_center
);
550 int id
= gameobj_alloc();
551 if(id
== -1) return -1;
552 gameobj_init(id
, &mypos
, &VEC(0,0), SI_BIG_EXPLOSION
, ANIM_BIG_EXPLOSION
, OBJ_BIG_EXPLOSION
);
553 gameobj_init_bulletdata(id
, expl_anim_frames
*ticks_per_anim_frame
-1);
554 audio_play_wave_resource(wavesounds
[WS_GRENADE_EXPLOSION
]);
559 static int init_rocket_explosion(vec2f
*pos
) {
560 vec2f ax
= vecadd(pos
, &VEC(-15*SCALE
, 9*SCALE
));
561 vec2f bx
= vecadd(pos
, &VEC(1*SCALE
, -6*SCALE
));
562 vec2f cx
= vecadd(pos
, &VEC(-8*SCALE
, -8*SCALE
));
563 vec2f dx
= vecadd(pos
, &VEC(8*SCALE
, 8*SCALE
));
565 ret
+= init_grenade_explosion(&ax
, 0) != -1;
566 ret
+= init_grenade_explosion(&bx
, 0) != -1;
567 ret
+= init_big_explosion(&cx
) != -1;
568 ret
+= init_big_explosion(&dx
) != -1;
572 static int init_flame(vec2f
*pos
, vec2f
*vel
, int steps
) {
573 int id
= gameobj_alloc();
574 if(id
== -1) return -1;
575 gameobj_init(id
, pos
, vel
, SI_FLAME
, ANIM_FLAME
, OBJ_FLAME
);
576 gameobj_init_bulletdata(id
, steps
);
580 static int init_player_flame(enum direction dir
, vec2f
*pos
, vec2f
*vel
, int steps
) {
581 static const vec2f flame_origin
[] = {
582 [DIR_O
] = { 4.0, 8.0 },
583 [DIR_NO
] = { 5.0, 11.0 },
584 [DIR_N
] = { 7.5, 12.0 },
585 [DIR_NW
] = { 10.0, 11.0 },
586 [DIR_W
] = { 11.0, 8.0 },
587 [DIR_SW
] = { 10.0, 5.0 },
588 [DIR_S
] = { 7.5, 3.0 },
589 [DIR_SO
] = { 4.0, 4.0 },
592 mypos
.x
-= flame_origin
[dir
].x
* SCALE
;
593 mypos
.y
-= flame_origin
[dir
].y
* SCALE
;
594 return init_flame(&mypos
, vel
, steps
);
597 static int init_rocket(enum direction dir
, vec2f
*pos
, vec2f
*vel
, int steps
) {
598 static const vec2f rocket_origin
[] = {
599 [DIR_N
] = { 1.0, 10.0 },
600 [DIR_S
] = { 1.0, 0.0 },
601 [DIR_O
] = { 0.0, 1.0 },
602 [DIR_W
] = { 10.0, 1.0 },
603 [DIR_NO
] = { 0.0, 7.0 },
604 [DIR_SO
] = { 0.0, 0.0 },
605 [DIR_SW
] = { 7.0, 0.0 },
606 [DIR_NW
] = { 7.0, 7.0 },
608 static const enum animation_id rocket_anim
[] = {
609 [DIR_N
] = ANIM_ROCKET_N
,
610 [DIR_S
] = ANIM_ROCKET_S
,
611 [DIR_O
] = ANIM_ROCKET_O
,
612 [DIR_W
] = ANIM_ROCKET_W
,
613 [DIR_NO
] = ANIM_ROCKET_NO
,
614 [DIR_SO
] = ANIM_ROCKET_SO
,
615 [DIR_SW
] = ANIM_ROCKET_SW
,
616 [DIR_NW
] = ANIM_ROCKET_NW
,
619 mypos
.x
-= rocket_origin
[dir
].x
* SCALE
;
620 mypos
.y
-= rocket_origin
[dir
].y
* SCALE
;
621 int id
= gameobj_alloc();
622 if(id
== -1) return -1;
623 gameobj_init(id
, &mypos
, vel
, SI_ROCKET
, rocket_anim
[dir
], OBJ_ROCKET
);
624 gameobj_init_bulletdata(id
, steps
);
630 static const struct enemy_route
* get_enemy_current_route(int curr_step
, const struct enemy_spawn
*spawn
) {
631 int i
= ENEMY_MAX_ROUTE
-1;
633 if(spawn
->route
[i
].shape
!= ES_INVALID
&&
634 curr_step
>= spawn
->route
[i
].start_step
)
635 return &spawn
->route
[i
];
639 static vec2f
get_enemy_vel(int curr_step
, const struct enemy_spawn
*spawn
) {
640 const struct enemy_route
*route
= get_enemy_current_route(curr_step
, spawn
);
641 return get_vel_from_direction16(route
->dir
, (float)route
->vel
/8.f
);
644 static const enum animation_id enemy_animation_lut
[] = {
645 [ES_SOLDIER1_DOWN
] = ANIM_ENEMY_GUNNER_DOWN
,
646 [ES_SOLDIER1_RIGHT
] = ANIM_ENEMY_GUNNER_RIGHT
,
647 [ES_SOLDIER1_LEFT
] = ANIM_ENEMY_GUNNER_LEFT
,
648 [ES_SOLDIER2_DOWN
] = ANIM_ENEMY_BOMBER_DOWN
,
649 [ES_SOLDIER2_RIGHT
] = ANIM_ENEMY_BOMBER_RIGHT
,
650 [ES_SOLDIER2_LEFT
] = ANIM_ENEMY_BOMBER_LEFT
,
651 [ES_JEEP
] = ANIM_JEEP
,
652 [ES_TANK_SMALL
] = ANIM_TANK_SMALL
,
653 [ES_TANK_BIG
] = ANIM_TANK_BIG
,
654 [ES_TRANSPORTER
] = ANIM_TRANSPORTER
,
655 [ES_GUNTURRET_MOVABLE_MACHINE
] = ANIM_GUNTURRET_MOVABLE_MACHINE_S
,
656 [ES_GUNTURRET_MOVABLE_MAN
] = ANIM_GUNTURRET_MOVABLE_MAN_S
,
657 [ES_MINE_FLAT
] = ANIM_MINE_FLAT
,
658 [ES_MINE_CROSS
] = ANIM_MINE_CROSSED
,
659 [ES_FLAMETURRET
] = ANIM_FLAMETURRET
,
660 [ES_GUNTURRET_FIXED_SOUTH
] = ANIM_GUNTURRET_FIXED_SOUTH
,
661 [ES_GUNTURRET_FIXED_NORTH
] = ANIM_GUNTURRET_FIXED_NORTH
,
662 [ES_BUNKER_1
] = ANIM_BUNKER1
,
663 [ES_BUNKER_2
] = ANIM_BUNKER2
,
664 [ES_BUNKER_3
] = ANIM_BUNKER3
,
665 [ES_BUNKER_4
] = ANIM_BUNKER4
,
666 [ES_BUNKER_5
] = ANIM_BUNKER5
,
669 static int init_enemy(const struct enemy_spawn
*spawn
) {
670 const enum objtype enemy_soldier_objtype_lut
[] = {
671 [0] = OBJ_ENEMY_SHOOTER
,
672 [1] = OBJ_ENEMY_BOMBER
674 const enum objtype enemy_objtype_lut
[] = {
675 [ES_JEEP
] = OBJ_JEEP
,
676 [ES_TANK_SMALL
] = OBJ_TANK_SMALL
,
677 [ES_TANK_BIG
] = OBJ_TANK_BIG
,
678 [ES_TRANSPORTER
] = OBJ_TRANSPORTER
,
679 [ES_GUNTURRET_MOVABLE_MACHINE
] = OBJ_GUNTURRET_MOVABLE_MACHINE
,
680 [ES_GUNTURRET_MOVABLE_MAN
] = OBJ_GUNTURRET_MOVABLE_MAN
,
681 [ES_MINE_FLAT
] = OBJ_MINE_FLAT
,
682 [ES_MINE_CROSS
] = OBJ_MINE_CROSSED
,
683 [ES_FLAMETURRET
] = OBJ_FLAMETURRET
,
684 [ES_GUNTURRET_FIXED_SOUTH
] = OBJ_GUNTURRET_FIXED_SOUTH
,
685 [ES_GUNTURRET_FIXED_NORTH
] = OBJ_GUNTURRET_FIXED_NORTH
,
686 [ES_BUNKER_1
] = OBJ_BUNKER1
,
687 [ES_BUNKER_2
] = OBJ_BUNKER2
,
688 [ES_BUNKER_3
] = OBJ_BUNKER3
,
689 [ES_BUNKER_4
] = OBJ_BUNKER4
,
690 [ES_BUNKER_5
] = OBJ_BUNKER5
,
691 [ES_BOSS
] = OBJ_BOSS
,
693 const enum animation_id boss_animation_lut
[] = {
707 const enum sprite_index enemy_soldier_sprite_lut
[] = {
708 [ET_ASIAN
] = SI_ENEMY_ASIAN
,
709 [ET_WESTERN
] = SI_ENEMY_WESTERN
,
711 const enum sprite_index enemy_sprite_lut
[] = {
712 [ES_JEEP
] = SI_VEHICLES_SMALL
,
713 [ES_TANK_SMALL
] = SI_VEHICLES_MEDIUM
,
714 [ES_TANK_BIG
] = SI_VEHICLES_BIG
,
715 [ES_TRANSPORTER
] = SI_VEHICLES_BIG
,
716 [ES_BUNKER_1
] = SI_BUNKERS
,
717 [ES_BUNKER_2
] = SI_BUNKERS
,
718 [ES_BUNKER_3
] = SI_BUNKERS
,
719 [ES_BUNKER_4
] = SI_BUNKERS
,
720 [ES_BUNKER_5
] = SI_BUNKERS
,
721 [ES_GUNTURRET_MOVABLE_MACHINE
] = SI_GUNTURRET
,
722 [ES_GUNTURRET_MOVABLE_MAN
] = SI_GUNTURRET
,
723 [ES_MINE_FLAT
] = SI_MINES
,
724 [ES_MINE_CROSS
] = SI_MINES
,
725 [ES_FLAMETURRET
] = SI_MINES
,
726 [ES_GUNTURRET_FIXED_SOUTH
] = SI_MINES
,
727 [ES_GUNTURRET_FIXED_NORTH
] = SI_MINES
,
728 [ES_BOSS
] = SI_BOSSES
,
731 vec2f spawnpos
= VEC(SCREEN_MIN_X
+ spawn
->x
*SCALE
, SCREEN_MIN_Y
+ spawn
->y
*SCALE
);
732 int id
= gameobj_alloc();
733 if(id
== -1) return -1;
734 const struct enemy_route
* route_curr
= get_enemy_current_route(0, spawn
);
735 vec2f vel
= get_enemy_vel(0, spawn
);
737 int is_soldier
= route_curr
->shape
<= ES_SOLDIER2_RIGHT
;
738 int is_boss
= route_curr
->shape
== ES_BOSS
;
739 enum sprite_index spriteid
;
741 enum animation_id animid
;
743 spriteid
= enemy_soldier_sprite_lut
[map
->enemy_type
];
744 objid
= enemy_soldier_objtype_lut
[spawn
->weapon
];
746 spriteid
= enemy_sprite_lut
[route_curr
->shape
];
747 objid
= enemy_objtype_lut
[route_curr
->shape
];
749 if(is_boss
) animid
= boss_animation_lut
[map
->boss_id
];
750 else animid
= enemy_animation_lut
[route_curr
->shape
];
752 gameobj_init(id
, &spawnpos
, &vel
, spriteid
, animid
, objid
);
753 objs
[id
].objspecific
.enemy
.curr_step
= 0;
754 objs
[id
].objspecific
.enemy
.spawn
= spawn
;
759 case OBJ_BUNKER1
: case OBJ_BUNKER2
: case OBJ_BUNKER3
:
760 case OBJ_BUNKER4
: case OBJ_BUNKER5
:
763 case OBJ_GUNTURRET_MOVABLE_MACHINE
:
764 case OBJ_GUNTURRET_MOVABLE_MAN
:
765 case OBJ_FLAMETURRET
: case OBJ_GUNTURRET_FIXED_NORTH
:
766 case OBJ_GUNTURRET_FIXED_SOUTH
:
769 case OBJ_MINE_CROSSED
: case OBJ_MINE_FLAT
:
772 case OBJ_JEEP
: case OBJ_TRANSPORTER
:
773 case OBJ_TANK_BIG
: case OBJ_TANK_SMALL
:
782 static void remove_enemy(int id
) {
783 enum objtype objid
= objs
[id
].objtype
;
785 case OBJ_JEEP
: case OBJ_TRANSPORTER
:
786 case OBJ_TANK_BIG
: case OBJ_TANK_SMALL
:
787 golist_remove(&go_vehicles
, id
);
790 golist_remove(&go_enemies
, id
);
795 static int enemy_fires(struct enemy
*e
) {
797 for(i
= 0; i
< ENEMY_MAX_SHOT
; i
++)
798 if(e
->curr_step
== e
->spawn
->shots
[i
]) return 1;
802 static enum animation_id
get_flash_animation_from_direction(enum direction dir
) {
803 #define ANIMF(dir, anim) [dir] = anim
804 static const enum animation_id dir_to_anim
[] = {
805 ANIMF(DIR_O
, ANIM_FLASH_O
),
806 ANIMF(DIR_NO
, ANIM_FLASH_NO
),
807 ANIMF(DIR_N
, ANIM_FLASH_N
),
808 ANIMF(DIR_NW
, ANIM_FLASH_NW
),
809 ANIMF(DIR_W
, ANIM_FLASH_W
),
810 ANIMF(DIR_SW
, ANIM_FLASH_SW
),
811 ANIMF(DIR_S
, ANIM_FLASH_S
),
812 ANIMF(DIR_SO
, ANIM_FLASH_SO
),
815 return dir_to_anim
[dir
];
818 static int init_flash(vec2f
*pos
, enum direction dir
) {
819 int id
= gameobj_alloc();
820 gameobj_init(id
, pos
, &VEC(0, 0), SI_FLASH
, get_flash_animation_from_direction(dir
), OBJ_FLASH
);
821 gameobj_init_bulletdata(id
, 2);
825 static vec2f
get_gameobj_pos(int obj_id
) {
826 return objs
[obj_id
].pos
;
829 static vec2f
get_gameobj_center(int obj_id
) {
830 vec2f res
= objs
[obj_id
].pos
;
831 vec2f add
= get_sprite_center(spritemaps
[objs
[obj_id
].spritemap_id
]);
832 return vecadd(&res
, &add
);
835 static enum direction
get_direction_from_vec(vec2f
*vel
);
836 static enum animation_id
get_anim_from_direction(enum direction dir
, int player
, int throwing
);
838 static enum direction16
get_direction16_from_direction(enum direction dir
) {
839 static const enum direction16 dir16_transl_tab
[] = {
840 [DIR_N
] = DIR16_N
, [DIR_NO
] = DIR16_NO
, [DIR_O
] = DIR16_O
, [DIR_SO
] = DIR16_SO
,
841 [DIR_S
] = DIR16_S
, [DIR_SW
] = DIR16_SW
, [DIR_W
] = DIR16_W
, [DIR_NW
] = DIR16_NW
,
843 assert(dir
!= DIR_INVALID
);
844 return dir16_transl_tab
[dir
];
847 static enum weapon_id
get_active_weapon_id(int player_no
) {
848 return player_weapons
[player_no
][weapon_active
[player_no
]];
851 static const struct weapon
* get_active_weapon(int player_no
) {
852 return &weapons
[get_active_weapon_id(player_no
)];
854 static enum direction16
get_shotdirection_from_enemy(int curr_step
, const struct enemy_spawn
*spawn
) {
855 const struct enemy_route
* r
= get_enemy_current_route(curr_step
, spawn
);
857 case ES_SOLDIER1_DOWN
: case ES_SOLDIER2_DOWN
:
859 case ES_SOLDIER1_LEFT
: case ES_SOLDIER2_LEFT
:
861 case ES_SOLDIER1_RIGHT
: case ES_SOLDIER2_RIGHT
:
868 static void enemy_fire_bullet(enum direction16 dir
, int steps
, enum enemy_weapon wpn
, vec2f
*pos
) {
869 vec2f vel
= get_vel_from_direction16(dir
, 1.75);
872 id
= init_bullet(pos
, &vel
, steps
);
873 if(id
!= -1) add_ebullet(id
);
874 } else if (wpn
== EW_GRENADE
) {
875 id
= init_grenade(pos
, &vel
, steps
);
876 if(id
!= -1) add_enemy_grenade(id
);
878 id
= init_flame(pos
, &vel
, steps
);
879 if(id
!= -1) add_enemy_flame(id
);
883 static int get_crosshair_id(void) {
884 assert(sblist_getsize(&go_crosshair
));
885 uint8_t *id
= sblist_get(&go_crosshair
, 0);
889 static void fire_bullet(int player_no
) {
891 const struct weapon
*pw
= get_active_weapon(player_no
);
892 if(player_ammo
[player_no
][pw
->ammo
] == 0) return;
893 vec2f from
= get_gameobj_center(player_ids
[player_no
]);
894 //get_anim_from_vel(0, objs[player].
895 vec2f to
= get_gameobj_center(get_crosshair_id());
896 to
.x
+= 4*SCALE
- rand()%8*SCALE
;
897 to
.y
+= 4*SCALE
- rand()%8*SCALE
;
898 vec2f vel
= velocity(&from
, &to
);
899 enum direction dir
= get_direction_from_vec(&vel
);
900 if(dir
!= DIR_INVALID
) {
901 enum animation_id aid
= get_anim_from_direction(dir
, player_no
, pw
->ammo
== AMMO_GRENADE
);
902 if(aid
!= ANIM_INVALID
) switch_anim(player_ids
[player_no
], aid
);
903 anim_step curranim
= objs
[player_ids
[player_no
]].anim_curr
;
904 if(curranim
== ANIM_STEP_INIT
) curranim
= get_next_anim_frame(objs
[player_ids
[player_no
]].animid
, ANIM_STEP_INIT
);
905 vec2f muzzle
= muzzle_tab
[curranim
];
907 from
= get_gameobj_pos(player_ids
[player_no
]);
908 from
.x
+= muzzle
.x
* SCALE
;
909 from
.y
+= muzzle
.y
* SCALE
;
911 if(pw
->flags
& WF_MUZZLEFLASH
) {
912 static const vec2f flash_start
[] = {
913 [DIR_O
] = { 0.0, 1.0 },
914 [DIR_NO
] = { 0.5, 6.0 },
915 [DIR_N
] = { 1.0, 7.5 },
916 [DIR_NW
] = { 6.0, 6.0 },
917 [DIR_W
] = { 7.5, 1.0 },
918 [DIR_SW
] = { 4.5, 0.0 },
919 [DIR_S
] = { 1.0, 0.0 },
920 [DIR_SO
] = { 0.0, 0.0 },
923 ffrom
.x
-= flash_start
[dir
].x
* SCALE
;
924 ffrom
.y
-= flash_start
[dir
].y
* SCALE
;
925 id
= init_flash(&ffrom
, dir
);
926 if(id
!= -1) add_muzzleflash(id
);
928 vel
= velocity(&from
, &to
);
930 float dist
= veclength(&vel
);
931 float speed
= pw
->bullet_speed
* SCALE
;
932 const uint16_t range_tab
[] = {0, 80, 66, 80, 118, 118, 118, 118, 118, 118,
933 200, 200, 240, 240, 240, 240, 240, 240, 240, 240, 320 };
934 float range
= range_tab
[pw
->range
] * SCALE
;
937 float steps
= dist
/ speed
;
938 float deg
= atan2(vel
.y
, vel
.x
);
939 vel
.x
= cos(deg
) * speed
;
940 vel
.y
= sin(deg
) * speed
;
943 id
= init_rocket(dir
, &from
, &vel
, steps
);
946 id
= init_bullet(&from
, &vel
, steps
);
947 if(id
!= -1) add_pbullet(id
);
950 id
= init_player_flame(dir
, &from
, &vel
, steps
);
951 if(id
!= -1) add_flame(id
);
954 id
= init_grenade(&from
, &vel
, steps
);
960 player_ammo
[player_no
][pw
->ammo
]--;
961 const WAVE_HEADER_COMPLETE
*wf
= wavesounds
[pw
->sound
];
962 if(id
!= -1 && pw
->sound
!= WS_NONE
)
963 audio_play_wave_resource(wf
);
966 static void init_game_objs() {
967 sblist_init(&go_crosshair
, 1, 4);
968 sblist_init(&go_players
, 1, 4);
969 sblist_init(&go_muzzleflash
, 1, 4);
970 sblist_init(&go_player_bullets
, 1, 32);
971 sblist_init(&go_flames
, 1, 32);
972 sblist_init(&go_enemy_bullets
, 1, 32);
973 sblist_init(&go_explosions
, 1, 16);
974 sblist_init(&go_enemy_explosions
, 1, 16);
975 sblist_init(&go_grenades
, 1, 16);
976 sblist_init(&go_enemy_grenades
, 1, 16);
977 sblist_init(&go_rockets
, 1, 8);
978 sblist_init(&go_enemies
, 1, 32);
979 sblist_init(&go_vehicles
, 1, 4);
980 sblist_init(&go_mines
, 1, 4);
981 sblist_init(&go_turrets
, 1, 8);
982 sblist_init(&go_bunkers
, 1, 4);
983 sblist_init(&go_boss
, 1, 4);
984 sblist_init(&go_blood
, 1, 16);
985 sblist_init(&go_enemy_flames
, 1, 16);
987 add_crosshair(init_crosshair());
988 init_map(current_map
);
991 static int point_in_mask(vec2f
*point
, int obj_id
) {
992 vec2f pos_in_pic
= VEC((point
->x
- objs
[obj_id
].pos
.x
) / SCALE
,
993 (point
->y
- objs
[obj_id
].pos
.y
) / SCALE
);
994 const struct palpic
*p
= spritemaps
[objs
[obj_id
].spritemap_id
];
995 unsigned h
= palpic_getspriteheight(p
), w
= palpic_getspritewidth(p
);
996 if(pos_in_pic
.x
< 0 || pos_in_pic
.y
< 0 || pos_in_pic
.x
> w
|| pos_in_pic
.y
> h
) return 0;
997 assert(objs
[obj_id
].anim_curr
!= ANIM_STEP_INIT
);
998 anim_step curranim
= objs
[obj_id
].anim_curr
;
999 if(curranim
== ANIM_STEP_INIT
) curranim
= get_next_anim_frame(objs
[obj_id
].animid
, ANIM_STEP_INIT
);
1000 uint8_t *data
= palpic_getspritedata(p
, curranim
);
1001 if(data
[(unsigned) pos_in_pic
.y
* w
+ (unsigned) pos_in_pic
.x
] != 0) return 1;
1005 static enum animation_id
get_die_anim(unsigned id
) {
1006 switch(objs
[id
].objtype
) {
1012 return ANIM_JEEP_DESTROYED
;
1013 case OBJ_TANK_SMALL
:
1014 return ANIM_TANK_SMALL_DESTROYED
;
1016 return ANIM_TANK_BIG_DESTROYED
;
1017 case OBJ_TRANSPORTER
:
1018 return ANIM_TRANSPORTER_DESTROYED
;
1019 case OBJ_ENEMY_BOMBER
:
1020 return ANIM_ENEMY_BOMBER_DIE
;
1021 case OBJ_ENEMY_SHOOTER
:
1022 return ANIM_ENEMY_GUNNER_DIE
;
1023 case OBJ_BUNKER1
: case OBJ_BUNKER2
: case OBJ_BUNKER3
:
1024 case OBJ_BUNKER4
: case OBJ_BUNKER5
:
1025 return ANIM_BUNKER_DESTROYED
;
1026 case OBJ_GUNTURRET_MOVABLE_MACHINE
:
1027 return ANIM_GUNTURRET_MOVABLE_MACHINE_DESTROYED
;
1028 case OBJ_GUNTURRET_MOVABLE_MAN
:
1029 return ANIM_GUNTURRET_MOVABLE_MAN_DESTROYED
;
1031 return ANIM_INVALID
;
1035 /* remove bullets that have reached their maximum number of steps */
1036 static int remove_bullets(sblist
*list
) {
1040 sblist_iter_counter2s(list
, li
, item_id
) {
1041 struct gameobj
*bullet
= &objs
[*item_id
];
1042 if(bullet
->objspecific
.bullet
.step_curr
>= bullet
->objspecific
.bullet
.step_max
) {
1043 gameobj_free(*item_id
);
1044 sblist_delete(list
, li
);
1047 bullet
->objspecific
.bullet
.step_curr
++;
1054 static int remove_explosives(sblist
*list
) {
1058 sblist_iter_counter2s(list
, li
, item_id
) {
1059 struct gameobj
*go
= &objs
[*item_id
];
1060 if(go
->objspecific
.bullet
.step_curr
>= go
->objspecific
.bullet
.step_max
) {
1061 if(go
->objtype
== OBJ_GRENADE
) init_grenade_explosion(&go
->pos
, list
== &go_enemy_grenades
);
1062 else init_rocket_explosion(&go
->pos
);
1063 gameobj_free(*item_id
);
1064 sblist_delete(list
, li
);
1067 go
->objspecific
.bullet
.step_curr
++;
1068 if(go
->objtype
== OBJ_GRENADE
) {
1069 if(go
->objspecific
.bullet
.step_curr
>= 32) go
->animid
= ANIM_GRENADE_SMALL
;
1070 else if(go
->objspecific
.bullet
.step_curr
>= 8) go
->animid
= ANIM_GRENADE_BIG
;
1078 static int remove_offscreen_objects(sblist
*list
) {
1082 sblist_iter_counter2s(list
, li
, item_id
) {
1083 assert(obj_slot_used
[*item_id
]);
1084 struct gameobj
*go
= &objs
[*item_id
];
1085 assert((int) go
->spritemap_id
< SI_MAX
);
1086 const struct palpic
*p
= spritemaps
[go
->spritemap_id
];
1087 assert(p
->spritecount
);
1088 int h
= palpic_getspriteheight(p
), w
= palpic_getspritewidth(p
);
1089 if(go
->pos
.x
< SCREEN_MIN_X
-w
*SCALE
|| go
->pos
.x
> SCREEN_MAX_X
||
1090 go
->pos
.y
< SCREEN_MIN_Y
-h
*SCALE
|| go
->pos
.y
> SCREEN_MAX_Y
) {
1092 dprintf(2, "offscreen: removed gameobj %d\n", (int) *item_id
);
1093 gameobj_free(*item_id
);
1094 sblist_delete(list
, li
);
1101 static int get_sprite_number(int id
) {
1102 struct gameobj
*o
= &objs
[id
];
1103 return o
->anim_curr
== ANIM_STEP_INIT
? get_next_anim_frame(o
->animid
, o
->anim_curr
) : o
->anim_curr
;
1106 static int is_death_anim(enum animation_id anim
) {
1108 case ANIM_ENEMY_BOMBER_DIE
:
1109 case ANIM_ENEMY_GUNNER_DIE
:
1110 case ANIM_ENEMY_BURNT
:
1113 case ANIM_GUNTURRET_MOVABLE_MAN_DESTROYED
:
1114 case ANIM_GUNTURRET_MOVABLE_MACHINE_DESTROYED
:
1115 case ANIM_BUNKER_DESTROYED
:
1116 case ANIM_JEEP_DESTROYED
:
1117 case ANIM_TANK_BIG_DESTROYED
:
1118 case ANIM_TANK_SMALL_DESTROYED
:
1119 case ANIM_TRANSPORTER_DESTROYED
:
1126 static int overlap(int id1
, int id2
) {
1127 struct gameobj
*o1
= &objs
[id1
];
1128 struct gameobj
*o2
= &objs
[id2
];
1129 const struct palpic
* p1
= spritemaps
[o1
->spritemap_id
];
1130 const struct palpic
* p2
= spritemaps
[o2
->spritemap_id
];
1132 vec2f vec
= velocity(&o1
->pos
, &o2
->pos
);
1133 float mh
= o1
->pos
.y
< o2
->pos
.y
? palpic_getspriteheight(p1
) : palpic_getspriteheight(p2
);
1134 float mw
= o1
->pos
.x
< o2
->pos
.x
? palpic_getspritewidth(p1
) : palpic_getspritewidth(p2
);
1135 mh
*=SCALE
, mw
*=SCALE
;
1136 return fabs(vec
.x
) < mw
&& fabs(vec
.y
) < mh
;
1139 static int masks_collide(int id1
, int id2
) {
1140 struct gameobj
*a
= &objs
[id1
];
1141 struct gameobj
*b
= &objs
[id2
];
1142 const struct palpic
* pa
= spritemaps
[a
->spritemap_id
];
1143 const struct palpic
* pb
= spritemaps
[b
->spritemap_id
];
1144 int ax
= a
->pos
.x
/SCALE
, ay
= a
->pos
.y
/SCALE
,
1145 aw
= palpic_getspritewidth(pa
), ah
= palpic_getspriteheight(pa
);
1146 int bx
= b
->pos
.x
/SCALE
, by
= b
->pos
.y
/SCALE
,
1147 bw
= palpic_getspritewidth(pb
), bh
= palpic_getspriteheight(pb
);
1149 /* x: normalized start position */
1150 int xx
= MIN(ax
, bx
), xy
= MIN(ay
, by
);
1151 /* normalize coordinates to 0 */
1152 ax
-= xx
, ay
-= xy
, bx
-= xx
, by
-= xy
;
1153 /* o: upper left point of the overlapping region */
1154 int ox
= MAX(ax
, bx
), oy
= MAX(ay
, by
);
1155 /* e: lower right point of the overlapping region */
1156 int ex
= MIN(ax
+aw
, bx
+bw
), ey
= MIN(ay
+ah
, by
+bh
);
1158 int oex
= ex
- ox
, oey
= ey
- oy
;
1159 /* (s)tart and (e)nd coordinates of the overlapping regions in both rects */
1160 int asx
= ox
- ax
, asy
= oy
- ay
;
1161 int bsx
= ox
- bx
, bsy
= oy
- by
;
1162 int aex
= asx
+ oex
, bex
= bsx
+ oex
;
1163 int aey
= asy
+ oey
, bey
= bsy
+ oey
;
1164 assert(asx
>= 0); assert(asy
>= 0);
1165 assert(bsx
>= 0); assert(bsy
>= 0);
1166 if(!aex
|| !bex
|| !aey
|| !bey
) return 0;
1167 assert(aex
> 0); assert(bex
> 0);
1168 assert(aey
> 0); assert(bey
> 0);
1170 const uint8_t *da
= palpic_getspritedata(pa
, get_sprite_number(id1
)),
1171 *db
= palpic_getspritedata(pb
, get_sprite_number(id2
));
1172 assert(da
); assert(db
);
1173 for(ya
= asy
, yb
= bsy
; ya
< aey
&& yb
< bey
; ya
++, yb
++)
1174 for(xa
= asx
, xb
= bsx
; xa
< aex
&& xb
< bex
; xa
++, xb
++)
1175 if(da
[ya
*aw
+xa
] && db
[yb
*bw
+xb
]) return 1;
1180 // removes bullets and other objects if they collide. return 1 if anything happened
1181 static int hit_bullets(sblist
*bullet_list
, sblist
*target_list
) {
1185 enum bulletsubtype
{
1188 BS_GRENADE_EXPL
= 2,
1191 } bullet_subtybe
= BS_BULLET
;
1193 sblist_iter_counter2s(bullet_list
, li
, bullet_id
) {
1194 struct gameobj
*bullet
= &objs
[*bullet_id
];
1195 if(bullet
->objtype
== OBJ_FLAME
) bullet_subtybe
= BS_FLAME
;
1196 else if(bullet
->objtype
== OBJ_GRENADE_EXPLOSION
) {
1197 /* grenade kills only in the explosion, not in the smoke phase */
1198 if(bullet
->objspecific
.bullet
.step_curr
> 22) continue;
1199 bullet_subtybe
= BS_GRENADE_EXPL
;
1200 } else if(bullet
->objtype
== OBJ_BIG_EXPLOSION
) {
1201 bullet_subtybe
= BS_BIG_EXPL
;
1202 } else if(bullet_list
== &go_vehicles
|| bullet_list
== &go_enemies
||
1203 bullet_list
== &go_boss
|| bullet_list
== &go_mines
) {
1204 bullet_subtybe
= BS_TOUCH
;
1207 vec2f bullet_center
= get_gameobj_center(*bullet_id
);
1208 if(bullet_list
== &go_player_bullets
|| bullet_list
== &go_flames
) {
1209 if(is_wall(&bullet_center
) == WT_SOLID
) goto remove_bullet
;
1212 const float bullet_radius
[] = { [BS_BULLET
] = 1.f
, [BS_FLAME
] = 6.f
,
1213 [BS_GRENADE_EXPL
] = 16.f
, [BS_BIG_EXPL
] = 19.f
, [BS_TOUCH
] = 8.f
, };
1217 sblist_iter_counter2(target_list
, lj
, target_id
) {
1218 struct gameobj
*target
= &objs
[*target_id
];
1219 if(is_death_anim(target
->animid
) && target_list
!= &go_vehicles
) continue;
1220 if(bullet_subtybe
== BS_TOUCH
&&
1221 overlap(*target_id
, *bullet_id
) &&
1222 masks_collide(*target_id
, *bullet_id
)) {
1223 dprintf(2, "hit2\n");
1224 if(bullet_list
== &go_boss
) return 2;
1228 vec2f temp
= get_gameobj_center(*target_id
);
1229 float dist1
= vecdist(&bullet_center
, &temp
) - bullet_radius
[bullet_subtybe
] * SCALE
;
1230 vec2f newpos
= vecadd(&bullet_center
, &bullet
->vel
);
1231 float dist2
= vecdist(&newpos
, &temp
) - bullet_radius
[bullet_subtybe
] * SCALE
;
1233 unsigned w
= palpic_getspritewidth(spritemaps
[target
->spritemap_id
]),
1234 h
= palpic_getspriteheight(spritemaps
[target
->spritemap_id
]);
1235 float longest_side_div2
= ((float) MAX(h
, w
) / 2) * SCALE
;
1236 if(dist1
< 1.f
*SCALE
|| dist2
< 1.f
*SCALE
) { dprintf(2, "hit1\n"); goto hit
; }
1237 if(dist1
< longest_side_div2
|| dist2
< longest_side_div2
) {
1238 vec2f velquarter
= VEC(bullet
->vel
.x
* 0.25, bullet
->vel
.y
* 0.25);
1239 vec2f point
= bullet_center
;
1241 for(k
= 0; k
< 4; k
++) {
1242 if(point_in_mask(&point
, *target_id
)) {
1245 if(bullet_list
== &go_player_bullets
) {
1246 if(target_list
== &go_vehicles
) {
1247 audio_play_wave_resource(wavesounds
[WS_DROPSHOT
]);
1250 player_score
[0] += 50;
1252 } else if (bullet_list
== &go_rockets
) {
1253 init_rocket_explosion(&target
->pos
);
1255 } else if(bullet
->objtype
== OBJ_GRENADE_EXPLOSION
) {
1256 // grenade explosion has no effect on vehicles and bunkers.
1257 if(target_list
== &go_vehicles
|| target_list
== &go_bunkers
)
1259 } else if(bullet
->objtype
== OBJ_MINE_CROSSED
|| bullet
->objtype
== OBJ_MINE_FLAT
) {
1260 init_big_explosion(&target
->pos
);
1263 enum animation_id death_anim
= (bullet_subtybe
== BS_FLAME
&& target_list
== &go_enemies
) ?
1264 ANIM_ENEMY_BURNT
: get_die_anim(*target_id
);
1265 if(death_anim
== ANIM_INVALID
) {
1266 gameobj_free(*target_id
);
1267 sblist_delete(target_list
, lj
);
1271 switch_anim(*target_id
, death_anim
);
1272 target
->vel
= VEC(0,0);
1273 if(target
->objtype
== OBJ_ENEMY_BOMBER
|| target
->objtype
== OBJ_ENEMY_SHOOTER
) {
1274 const enum wavesound_id wid
[] = { WS_SCREAM
, WS_SCREAM2
};
1275 audio_play_wave_resource(wavesounds
[wid
[rand()%2]]);
1276 if(bullet_subtybe
== BS_BULLET
) {
1277 vec2f bloodpos
= vecadd(&target
->pos
, &VEC(0, (2+rand()%7)*SCALE
));
1278 /* original displays at 0,8 but i prefer a random effect */
1279 //vec2f bloodpos = vecadd(&target->pos, &VEC(0, 8*SCALE));
1280 int id
= init_blood(&bloodpos
);
1281 if(id
!= -1) add_blood(id
);
1284 if(bullet_subtybe
== BS_BULLET
) {
1286 gameobj_free(*bullet_id
);
1287 sblist_delete(bullet_list
, li
);
1292 point
= vecadd(&point
, &velquarter
);
1303 uint32_t tickcounter
;
1305 static int advance_animations(void) {
1306 size_t i
, obj_visited
;
1308 for(i
= 0, obj_visited
= 0; obj_visited
< obj_count
&& i
< OBJ_MAX
; i
++) {
1309 if(!obj_slot_used
[i
]) continue;
1310 struct gameobj
*go
= &objs
[i
];
1311 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
)) {
1312 unsigned anim_delay
= go
->objtype
== OBJ_BIG_EXPLOSION
? 8 : 4;
1313 if(go
->anim_curr
== ANIM_STEP_INIT
|| tickcounter
% anim_delay
== go
->anim_frame
) {
1314 anim_step last_anim
= go
->anim_curr
;
1315 go
->anim_curr
= get_next_anim_frame(go
->animid
, go
->anim_curr
);
1316 if(last_anim
!= go
->anim_curr
) res
= 1;
1324 static void switch_enemy_shape(int objid
, const struct enemy_route
* r
) {
1325 switch_anim(objid
, enemy_animation_lut
[r
->shape
]);
1328 static void draw_golist(sblist
*list
) {
1331 sblist_iter_counter2(list
, i
, itemid
) {
1332 assert(obj_slot_used
[*itemid
]);
1333 struct gameobj
*o
= &objs
[*itemid
];
1334 const prgb
*palette
;
1335 switch(o
->objtype
) {
1337 // original blood color is bb5511 but that is hardly visible
1338 // palette = (prgb[]) {PRGB(0,0,0), PRGB(0xbb, 0x55, 0x11)};
1339 palette
= (const prgb
[]) {PRGB(0,0,0), PRGB(0xff, 0x0, 0x0)};
1341 case OBJ_ENEMY_BOMBER
: case OBJ_ENEMY_SHOOTER
:
1342 palette
= map
->enemy_palette
;
1347 blit_sprite(o
->pos
.x
, o
->pos
.y
, &video
,
1348 SCALE
, spritemaps
[o
->spritemap_id
],
1349 get_sprite_number(*itemid
),
1354 static void draw_gameobjs(void) {
1355 draw_golist(&go_mines
);
1356 draw_golist(&go_turrets
);
1357 draw_golist(&go_bunkers
);
1358 draw_golist(&go_vehicles
);
1359 draw_golist(&go_enemies
);
1360 draw_golist(&go_blood
);
1361 draw_golist(&go_enemy_bullets
);
1362 draw_golist(&go_boss
);
1363 draw_golist(&go_rockets
);
1364 draw_golist(&go_players
);
1365 draw_golist(&go_player_bullets
);
1366 draw_golist(&go_enemy_explosions
);
1367 draw_golist(&go_flames
);
1368 draw_golist(&go_enemy_flames
);
1369 draw_golist(&go_explosions
);
1370 draw_golist(&go_grenades
);
1371 draw_golist(&go_enemy_grenades
);
1372 draw_golist(&go_crosshair
);
1373 draw_golist(&go_muzzleflash
);
1376 static void process_soldiers(void) {
1378 sblist_iter(&go_enemies
, itemid
) {
1379 struct gameobj
*go
= &objs
[*itemid
];
1380 assert(go
->objtype
== OBJ_ENEMY_SHOOTER
|| go
->objtype
== OBJ_ENEMY_BOMBER
);
1381 if (tickcounter
% 4 == go
->anim_frame
) {
1382 const struct enemy_route
*rc
= get_enemy_current_route(go
->objspecific
.enemy
.curr_step
, go
->objspecific
.enemy
.spawn
);
1384 go
->objspecific
.enemy
.curr_step
++;
1385 if(enemy_fires(&go
->objspecific
.enemy
)) {
1386 vec2f from
= get_gameobj_center(*itemid
);
1387 enum direction16 dir
= get_shotdirection_from_enemy(go
->objspecific
.enemy
.curr_step
, go
->objspecific
.enemy
.spawn
);
1388 enemy_fire_bullet(dir
, 41, go
->objspecific
.enemy
.spawn
->weapon
, &from
);
1390 const struct enemy_route
*rn
= get_enemy_current_route(go
->objspecific
.enemy
.curr_step
, go
->objspecific
.enemy
.spawn
);
1391 if(rn
->shape
!= rc
->shape
) switch_enemy_shape(*itemid
, rn
);
1394 if(!is_death_anim(go
->animid
)) go
->vel
= get_enemy_vel(go
->objspecific
.enemy
.curr_step
, go
->objspecific
.enemy
.spawn
);
1395 else go
->vel
= VEC(0, 0);
1399 static enum animation_id
get_turret_anim_from_dir(int objid
, enum direction dir
) {
1400 static const enum animation_id man_anim
[] = {
1401 [DIR_N
] = ANIM_GUNTURRET_MOVABLE_MAN_N
,
1402 [DIR_NO
] = ANIM_GUNTURRET_MOVABLE_MAN_NO
,
1403 [DIR_O
] = ANIM_GUNTURRET_MOVABLE_MAN_O
,
1404 [DIR_SO
] = ANIM_GUNTURRET_MOVABLE_MAN_SO
,
1405 [DIR_S
] = ANIM_GUNTURRET_MOVABLE_MAN_S
,
1406 [DIR_SW
] = ANIM_GUNTURRET_MOVABLE_MAN_SW
,
1407 [DIR_W
] = ANIM_GUNTURRET_MOVABLE_MAN_W
,
1408 [DIR_NW
] = ANIM_GUNTURRET_MOVABLE_MAN_NW
,
1410 static const enum animation_id mach_anim
[] = {
1411 [DIR_N
] = ANIM_GUNTURRET_MOVABLE_MACHINE_N
,
1412 [DIR_NO
] = ANIM_GUNTURRET_MOVABLE_MACHINE_NO
,
1413 [DIR_O
] = ANIM_GUNTURRET_MOVABLE_MACHINE_O
,
1414 [DIR_SO
] = ANIM_GUNTURRET_MOVABLE_MACHINE_SO
,
1415 [DIR_S
] = ANIM_GUNTURRET_MOVABLE_MACHINE_S
,
1416 [DIR_SW
] = ANIM_GUNTURRET_MOVABLE_MACHINE_SW
,
1417 [DIR_W
] = ANIM_GUNTURRET_MOVABLE_MACHINE_W
,
1418 [DIR_NW
] = ANIM_GUNTURRET_MOVABLE_MACHINE_NW
,
1420 const enum animation_id
*lut
= objs
[objid
].objtype
== OBJ_GUNTURRET_MOVABLE_MAN
? man_anim
: mach_anim
;
1424 static int editor_mode
;
1425 static int process_turrets(sblist
* list
) {
1426 if(editor_mode
) return 0;
1429 sblist_iter(list
, itemid
) {
1430 struct gameobj
*go
= &objs
[*itemid
];
1431 if(is_death_anim(go
->animid
)) continue;
1432 enum direction16 dir
= DIR16_S
;
1433 enum enemy_weapon ew
= EW_GUN
;
1436 switch(go
->objtype
) {
1437 case OBJ_GUNTURRET_FIXED_NORTH
:
1439 case OBJ_GUNTURRET_FIXED_SOUTH
:
1440 from
= get_gameobj_center(*itemid
);
1444 enemy_fire_bullet(dir
, steps
, ew
, &from
);
1448 case OBJ_FLAMETURRET
:
1452 if(objs
[player_ids
[0]].pos
.y
<= (192-25)*SCALE
/2) dir
= DIR16_N
;
1454 from
.y
+= (dir
== DIR16_N
? -8*SCALE
: 8*SCALE
);
1456 /* abusing curr_step variable to save position in a burst of flames */
1457 if(go
->objspecific
.enemy
.curr_step
&& go
->objspecific
.enemy
.curr_step
< 16)
1459 else if(go
->objspecific
.enemy
.curr_step
)
1460 go
->objspecific
.enemy
.curr_step
= 0;
1462 if(objs
[player_ids
[0]].pos
.x
< go
->pos
.x
-32*SCALE
|| objs
[player_ids
[0]].pos
.x
> go
->pos
.x
+ (16+32)*SCALE
)
1466 go
->objspecific
.enemy
.curr_step
++;
1470 case OBJ_GUNTURRET_MOVABLE_MAN
:
1471 case OBJ_GUNTURRET_MOVABLE_MACHINE
: {
1472 from
= get_gameobj_center(*itemid
);
1473 vec2f p_center
= get_gameobj_center(player_ids
[0]);
1474 vec2f vel
= velocity(&from
, &p_center
);
1475 enum direction dir8
= get_direction_from_vec(&vel
);
1476 dir
= get_direction16_from_direction(dir8
);
1477 switch_anim(*itemid
, get_turret_anim_from_dir(*itemid
, dir8
));
1481 case OBJ_TANK_SMALL
:
1487 const vec2f
*bunkerpos
;
1488 const enum direction16
*bunkerdir
;
1489 unsigned count
, sec
;
1490 static const enum direction16 bunker5_dir
[] = {
1491 [0] = DIR16_N
, [1] = DIR16_NO
, [2] = DIR16_O
, [3] = DIR16_SO
,
1492 [4] = DIR16_S
, [5] = DIR16_SW
, [6] = DIR16_W
, [7] = DIR16_NW
,
1494 static const enum direction16 bunker1_dir
[] = {
1495 [0] = DIR16_NNW
, [1] = DIR16_NNO
, [2] = DIR16_NO
, [3] = DIR16_O
,
1496 [4] = DIR16_SO
, [5] = DIR16_SSO
, [6] = DIR16_SSW
, [7] = DIR16_SW
,
1497 [8] = DIR16_W
, [9] = DIR16_NW
,
1499 static const vec2f bunker5_pos
[] = {
1500 [0] = VEC(9,-8), [1] = VEC(18,-6), [2] = VEC(26,2), [3] = VEC(22,10),
1501 [4] = VEC(9,16), [5] = VEC(-5,13), [6] = VEC(-7,2), [7] = VEC(-5,-6),
1503 static const vec2f bunker2_pos
[] = {
1504 [0] = VEC(15,-2), [1] = VEC(27,3), [2] = VEC(31,12), [3] = VEC(28,23),
1505 [4] = VEC(15,27), [5] = VEC(2,23), [6] = VEC(0,12), [7] = VEC(4,2),
1507 static const vec2f bunker4_pos
[] = {
1508 [0] = VEC(13,0), [1] = VEC(25,1), [2] = VEC(29,10), [3] = VEC(26,21),
1509 [4] = VEC(13,25), [5] = VEC(0,21), [6] = VEC(-2,10), [7] = VEC(2,0),
1511 static const vec2f bunker1_pos
[] = {
1512 [0] = VEC(6,-6), [1] = VEC(18,-6), [2] = VEC(26,2), [3] = VEC(26,12),
1513 [4] = VEC(26,16), [5] = VEC(20,24), [6] = VEC(6,24), [7] = VEC(0,20),
1514 [8] = VEC(0,12), [9] = VEC(0,2),
1516 static const enum direction16 tank_dir
[] = {
1517 [0] = DIR16_WSW
, [1] = DIR16_OSO
, [2] = DIR16_WSW
, [3] = DIR16_OSO
,
1518 [4] = DIR16_WSW
, [5] = DIR16_OSO
, [6] = DIR16_SSW
, [7] = DIR16_SSO
,
1520 static const vec2f tank_pos
[] = {
1521 [0] = VEC(10,8), [1] = VEC(38,8), [2] = VEC(10,20), [3] = VEC(38,20),
1522 [4] = VEC(10,32), [5] = VEC(38,32), [6] = VEC(16,40), [7] = VEC(32,40),
1524 unsigned b
, dist
= 28;
1525 switch(go
->objtype
) {
1528 bunkerpos
= bunker1_pos
;
1529 bunkerdir
= bunker1_dir
;
1534 case OBJ_TANK_SMALL
:
1535 if(tickcounter
% 8) break;
1538 bunkerpos
= tank_pos
;
1539 bunkerdir
= tank_dir
;
1540 // FIXME: the last 2 shoots (6+7) should be fired at the same time
1541 goto bunker_circle_shoot
;
1543 if(tickcounter
% 8) break;
1546 bunkerpos
= bunker2_pos
;
1547 bunkerdir
= bunker5_dir
;
1548 goto bunker_circle_shoot
;
1550 if(tickcounter
% /*(fps*3.5)/8*/ 24) break;
1553 bunkerpos
= bunker4_pos
;
1554 bunkerdir
= bunker5_dir
;
1555 bunker_circle_shoot
:;
1556 b
= go
->objspecific
.enemy
.curr_step
;
1558 if(++go
->objspecific
.enemy
.curr_step
>= 8) go
->objspecific
.enemy
.curr_step
= 0;
1562 bunkerpos
= bunker2_pos
;
1563 bunkerdir
= bunker5_dir
;
1571 bunkerpos
= bunker5_pos
;
1572 bunkerdir
= bunker5_dir
;
1579 if(tickcounter
> (uint32_t) go
->objspecific
.enemy
.curr_step
+ fps
*sec
) {
1580 go
->objspecific
.enemy
.curr_step
= tickcounter
;
1582 for(; b
< count
; b
++) {
1584 from
.x
+= bunkerpos
[b
].x
*SCALE
;
1585 from
.y
+= bunkerpos
[b
].y
*SCALE
;
1586 enemy_fire_bullet(bunkerdir
[b
], dist
, ew
, &from
);
1598 static int move_gameobjs(void) {
1600 size_t i
, obj_visited
;
1601 for(i
= 0, obj_visited
= 0; obj_visited
< obj_count
&& i
< OBJ_MAX
; i
++) {
1602 if(obj_slot_used
[i
]) {
1603 struct gameobj
*go
= &objs
[i
];
1605 if(go
->anim_curr
== ANIM_STEP_INIT
) res
= 1;
1606 if(go
->vel
.x
!= 0 || go
->vel
.y
!= 0) {
1607 vec2f oldpos
= go
->pos
;
1608 go
->pos
.x
+= go
->vel
.x
;
1609 go
->pos
.y
+= go
->vel
.y
;
1611 if(go
->objtype
== OBJ_P1
|| go
->objtype
== OBJ_P2
) {
1612 if(go
->pos
.y
< SCREEN_MIN_Y
) go
->pos
.y
= SCREEN_MIN_Y
;
1613 else if(go
->pos
.y
+25*SCALE
> SCREEN_MAX_Y
) go
->pos
.y
= SCREEN_MAX_Y
-25*SCALE
;
1614 if(go
->pos
.x
< SCREEN_MIN_X
) go
->pos
.x
= SCREEN_MIN_X
;
1615 else if(go
->pos
.x
+32*SCALE
> SCREEN_MAX_X
) go
->pos
.x
= SCREEN_MAX_X
-32*SCALE
;
1616 vec2f center
= get_sprite_center(spritemaps
[go
->spritemap_id
]);
1617 center
= vecadd(¢er
, &go
->pos
);
1618 if(is_wall(¢er
)) {
1626 if((go
->objtype
== OBJ_ENEMY_BOMBER
|| go
->objtype
== OBJ_ENEMY_SHOOTER
) &&
1627 go
->anim_curr
== animations
[go
->animid
].last
&&
1628 (go
->animid
== ANIM_ENEMY_BOMBER_DIE
||
1629 go
->animid
== ANIM_ENEMY_GUNNER_DIE
||
1630 go
->animid
== ANIM_ENEMY_BURNT
)) {
1631 dprintf(2, "removed enemy from %.2f,%.2f\n", go
->pos
.x
, go
->pos
.y
);
1633 golist_remove(&go_enemies
, i
);
1643 static void game_update_caption(void) {
1645 snprintf(buf
, 128, "objs: %d, map x,y %d/%d, index %d, xoff %d, yoff %d, spawnscreen %d, line %d", (int) obj_count
,
1646 (int)mapsquare
.x
, (int)mapsquare
.y
, (int)map
->screen_map
[mapsquare
.y
][mapsquare
.x
],
1647 (int)mapscreen_xoff
, (int)mapscreen_yoff
, (int)map_spawn_screen_index
, (int) map_spawn_line
);
1648 SDL_WM_SetCaption(buf
, 0);
1650 static void(*update_caption
)(void) = game_update_caption
;
1652 /* returns 1 if level finished */
1653 static int game_tick(int force_redraw
) {
1654 int need_redraw
= force_redraw
;
1655 if(mousebutton_down
[MB_LEFT
] > 1) {
1656 const int player_no
= 0;
1657 const struct weapon
*pw
= get_active_weapon(player_no
);
1658 //if(get_active_weapon_id(player_no) == WP_M134) __asm__("int3");
1659 if (pw
->flags
& WF_AUTOMATIC
) {
1660 float shots_per_second
= pw
->rpm
/ 60.f
;
1661 float shotinterval
= fps
/ shots_per_second
;
1662 if((int)((float)mousebutton_down
[MB_LEFT
] / shotinterval
) != (int)((float)(mousebutton_down
[MB_LEFT
]-1) / shotinterval
))
1663 fire_bullet(player_no
);
1667 if(advance_animations()) need_redraw
= 1;
1668 if(hit_bullets(&go_player_bullets
, &go_enemies
)) need_redraw
= 1;
1669 if(hit_bullets(&go_player_bullets
, &go_vehicles
)) need_redraw
= 1;
1670 if(hit_bullets(&go_flames
, &go_enemies
)) need_redraw
= 1;
1671 if(hit_bullets(&go_enemy_flames
, &go_enemies
)) need_redraw
= 1;
1672 if(hit_bullets(&go_rockets
, &go_enemies
)) need_redraw
= 1;
1673 if(hit_bullets(&go_rockets
, &go_vehicles
)) need_redraw
= 1;
1674 if(hit_bullets(&go_explosions
, &go_enemies
)) need_redraw
= 1;
1675 if(hit_bullets(&go_explosions
, &go_vehicles
)) need_redraw
= 1;
1676 if(hit_bullets(&go_explosions
, &go_mines
)) need_redraw
= 1;
1677 if(hit_bullets(&go_explosions
, &go_turrets
)) need_redraw
= 1;
1678 if(hit_bullets(&go_explosions
, &go_bunkers
)) need_redraw
= 1;
1679 if(hit_bullets(&go_explosions
, &go_players
)) need_redraw
= 1;
1680 if(hit_bullets(&go_enemy_explosions
, &go_players
)) need_redraw
= 1;
1681 if(hit_bullets(&go_enemy_bullets
, &go_players
)) need_redraw
= 1;
1682 if(hit_bullets(&go_enemy_flames
, &go_players
)) need_redraw
= 1;
1683 if(hit_bullets(&go_vehicles
, &go_players
)) need_redraw
= 1;
1684 if(hit_bullets(&go_enemies
, &go_players
)) need_redraw
= 1;
1685 if(hit_bullets(&go_mines
, &go_players
)) need_redraw
= 1;
1687 int ret
, level_finished
= 0;
1688 if((ret
= hit_bullets(&go_boss
, &go_players
)) == 2) level_finished
= 1;
1689 else if(ret
== 1) need_redraw
= 1;
1691 if(remove_bullets(&go_player_bullets
)) need_redraw
= 1;
1692 if(remove_bullets(&go_flames
)) need_redraw
= 1;
1693 if(remove_bullets(&go_explosions
)) need_redraw
= 1;
1694 if(remove_bullets(&go_enemy_explosions
)) need_redraw
= 1;
1695 if(remove_bullets(&go_enemy_bullets
)) need_redraw
= 1;
1696 if(remove_bullets(&go_enemy_flames
)) need_redraw
= 1;
1697 if(remove_bullets(&go_muzzleflash
)) need_redraw
= 1;
1698 if(remove_bullets(&go_blood
)) need_redraw
= 1;
1699 if(remove_explosives(&go_grenades
)) need_redraw
= 1;
1700 if(remove_explosives(&go_enemy_grenades
)) need_redraw
= 1;
1701 if(remove_explosives(&go_rockets
)) need_redraw
= 1;
1702 if(tickcounter
% 2 == 0 && scroll_map()) need_redraw
= 1;
1705 if(tickcounter
% 4 == 0 && process_turrets(&go_turrets
)) need_redraw
= 1;
1706 if(tickcounter
% 4 == 0 && process_turrets(&go_bunkers
)) need_redraw
= 1;
1707 if(tickcounter
% 4 == 0 && process_turrets(&go_vehicles
)) need_redraw
= 1;
1709 if(move_gameobjs()) need_redraw
= 1;
1711 if(remove_offscreen_objects(&go_enemies
)) need_redraw
= 1;
1712 if(remove_offscreen_objects(&go_vehicles
)) need_redraw
= 1;
1713 if(remove_offscreen_objects(&go_mines
)) need_redraw
= 1;
1714 if(remove_offscreen_objects(&go_turrets
)) need_redraw
= 1;
1715 if(remove_offscreen_objects(&go_bunkers
)) need_redraw
= 1;
1718 struct timeval timer
;
1719 gettimestamp(&timer
);
1724 video_update_region(SCREEN_MIN_X
,SCREEN_MIN_Y
, SCREEN_MAX_X
- SCREEN_MIN_X
, VMODE_H
);
1727 ms_used
= mspassed(&timer
);
1728 //if(ms_used) printf("repaint took: ms_used %ld\n", ms_used);
1729 int res
= audio_process();
1730 if(res
== -1) music_restart();
1731 ms_used
= mspassed(&timer
);
1732 //if(ms_used) printf("audio processed: %d, ms_used %ld\n", res, ms_used);
1734 long sleepms
= 1000/fps
- ms_used
;
1735 if(sleepms
>= 0) SDL_Delay(sleepms
);
1736 if(mousebutton_down
[MB_LEFT
]) mousebutton_down
[MB_LEFT
]++;
1741 return level_finished
;
1751 static enum cursor cursor_lut
[] = {
1753 [SDLK_DOWN
] = c_down
,
1754 [SDLK_LEFT
] = c_left
,
1755 [SDLK_RIGHT
] = c_right
,
1762 static char cursors_pressed
[] = {
1769 static vec2f
get_vel_from_direction(enum direction dir
, float speed
) {
1770 #define VELLUT(a, b, c) [a] = VEC(b, c)
1771 static const vec2f vel_lut
[] = {
1772 VELLUT(DIR_O
, 1, 0),
1773 VELLUT(DIR_NO
, 0.7071067769704655, -0.7071067769704655),
1774 VELLUT(DIR_N
, 0, -1),
1775 VELLUT(DIR_NW
, -0.7071067769704655, -0.7071067769704655),
1776 VELLUT(DIR_W
, -1, 0),
1777 VELLUT(DIR_SW
, -0.7071067769704655, 0.7071067769704655),
1778 VELLUT(DIR_S
, 0, 1),
1779 VELLUT(DIR_SO
, 0.7071067769704655, 0.7071067769704655),
1782 vec2f v
= vel_lut
[dir
];
1783 v
.x
*= speed
* SCALE
;
1784 v
.y
*= speed
* SCALE
;
1788 static vec2f
get_vel_from_direction16(enum direction16 dir
, float speed
) {
1789 #define VELLUT(a, b, c) [a] = VEC(b, c)
1790 #define ANK90 0.7071067769704655
1792 #define ANK45 0.9238795042037964
1793 #define GK45 0.3826834261417389
1794 static const vec2f vel_lut
[] = {
1795 VELLUT(DIR16_O
, 1, 0),
1796 VELLUT(DIR16_ONO
, ANK45
, -GK45
),
1797 VELLUT(DIR16_NO
, ANK90
, -ANK90
),
1798 VELLUT(DIR16_NNO
, GK45
, -ANK45
),
1799 VELLUT(DIR16_N
, 0, -1),
1800 VELLUT(DIR16_NNW
, -GK45
, -ANK45
),
1801 VELLUT(DIR16_NW
, -ANK90
, -ANK90
),
1802 VELLUT(DIR16_WNW
, -ANK45
, -GK45
),
1803 VELLUT(DIR16_W
, -1, 0),
1804 VELLUT(DIR16_WSW
, -ANK45
, GK45
),
1805 VELLUT(DIR16_SW
, -ANK90
, ANK90
),
1806 VELLUT(DIR16_SSW
, -GK45
, ANK45
),
1807 VELLUT(DIR16_S
, 0, 1),
1808 VELLUT(DIR16_SSO
, GK45
, ANK45
),
1809 VELLUT(DIR16_SO
, ANK90
, ANK90
),
1810 VELLUT(DIR16_OSO
, ANK45
, GK45
),
1813 vec2f v
= vel_lut
[dir
];
1814 v
.x
*= speed
* SCALE
;
1815 v
.y
*= speed
* SCALE
;
1820 static enum direction
get_direction_from_vec(vec2f
*vel
) {
1821 float deg_org
, deg
= atan2(vel
->y
, vel
->x
);
1823 if(deg
< 0) deg
*= -1.f
;
1824 else if(deg
> 0) deg
= M_PI
+ (M_PI
- deg
); // normalize atan2 result to scale from 0 to 2 pi
1825 int hexadrant
= (int)(deg
/ ((1.0/16.0f
)*2*M_PI
));
1826 assert(hexadrant
>= 0 && hexadrant
< 16);
1827 static const enum direction rad_lut
[] = {
1838 return rad_lut
[hexadrant
];
1841 static enum direction
get_direction_from_cursor(void) {
1842 enum direction dir
= DIR_INVALID
;
1843 if(cursors_pressed
[c_up
]) {
1844 if(cursors_pressed
[c_left
]) dir
= DIR_NW
;
1845 else if(cursors_pressed
[c_right
]) dir
= DIR_NO
;
1847 } else if (cursors_pressed
[c_down
]) {
1848 if(cursors_pressed
[c_left
]) dir
= DIR_SW
;
1849 else if(cursors_pressed
[c_right
]) dir
= DIR_SO
;
1851 } else if (cursors_pressed
[c_left
]) {
1853 } else if (cursors_pressed
[c_right
]) {
1859 static enum animation_id
get_anim_from_direction(enum direction dir
, int player_no
, int throwing
) {
1860 #define DIRMAP(a, b) [a] = b
1862 static const enum animation_id dir_map_p1
[] = {
1863 DIRMAP(DIR_N
, ANIM_P1_MOVE_N
),
1864 DIRMAP(DIR_NW
, ANIM_P1_MOVE_NW
),
1865 DIRMAP(DIR_W
, ANIM_P1_MOVE_W
),
1866 DIRMAP(DIR_SW
, ANIM_P1_MOVE_SW
),
1867 DIRMAP(DIR_S
, ANIM_P1_MOVE_S
),
1868 DIRMAP(DIR_SO
, ANIM_P1_MOVE_SO
),
1869 DIRMAP(DIR_O
, ANIM_P1_MOVE_O
),
1870 DIRMAP(DIR_NO
, ANIM_P1_MOVE_NO
),
1872 static const enum animation_id dir_map_p2
[] = {
1873 DIRMAP(DIR_N
, ANIM_P2_MOVE_N
),
1874 DIRMAP(DIR_NW
, ANIM_P2_MOVE_NW
),
1875 DIRMAP(DIR_W
, ANIM_P2_MOVE_W
),
1876 DIRMAP(DIR_SW
, ANIM_P2_MOVE_SW
),
1877 DIRMAP(DIR_S
, ANIM_P2_MOVE_S
),
1878 DIRMAP(DIR_SO
, ANIM_P2_MOVE_SO
),
1879 DIRMAP(DIR_O
, ANIM_P2_MOVE_O
),
1880 DIRMAP(DIR_NO
, ANIM_P2_MOVE_NO
),
1882 const enum animation_id
*dir_map
= player_no
== 0 ? dir_map_p1
: dir_map_p2
;
1883 return dir_map
[dir
];
1885 static const enum animation_id dir_map_p1_g
[] = {
1886 DIRMAP(DIR_N
, ANIM_P1_THROW_N
),
1887 DIRMAP(DIR_NW
, ANIM_P1_THROW_NW
),
1888 DIRMAP(DIR_W
, ANIM_P1_THROW_W
),
1889 DIRMAP(DIR_SW
, ANIM_P1_THROW_SW
),
1890 DIRMAP(DIR_S
, ANIM_P1_THROW_S
),
1891 DIRMAP(DIR_SO
, ANIM_P1_THROW_SO
),
1892 DIRMAP(DIR_O
, ANIM_P1_THROW_O
),
1893 DIRMAP(DIR_NO
, ANIM_P1_THROW_NO
),
1895 static const enum animation_id dir_map_p2_g
[] = {
1896 DIRMAP(DIR_N
, ANIM_P2_THROW_N
),
1897 DIRMAP(DIR_NW
, ANIM_P2_THROW_NW
),
1898 DIRMAP(DIR_W
, ANIM_P2_THROW_W
),
1899 DIRMAP(DIR_SW
, ANIM_P2_THROW_SW
),
1900 DIRMAP(DIR_S
, ANIM_P2_THROW_S
),
1901 DIRMAP(DIR_SO
, ANIM_P2_THROW_SO
),
1902 DIRMAP(DIR_O
, ANIM_P2_THROW_O
),
1903 DIRMAP(DIR_NO
, ANIM_P2_THROW_NO
),
1905 const enum animation_id
*dir_map
= player_no
== 0 ? dir_map_p1_g
: dir_map_p2_g
;
1906 return dir_map
[dir
];
1912 static enum animation_id
get_anim_from_cursor(void) {
1913 enum direction dir
= get_direction_from_cursor();
1914 if(dir
== DIR_INVALID
) return ANIM_INVALID
;
1915 return get_anim_from_direction(dir
, 0, 0);
1917 /* playerno is either 0 or 1, not player_id! */
1918 static enum animation_id
get_anim_from_vel(int player_no
, vec2f
*vel
) {
1919 enum direction dir
= get_direction_from_vec(vel
);
1920 if(dir
== DIR_INVALID
) return ANIM_INVALID
;
1921 return get_anim_from_direction(dir
, player_no
, 0);
1925 static void switch_anim(int obj_id
, int aid
) {
1926 if(objs
[obj_id
].animid
== aid
) return;
1927 gameobj_start_anim(obj_id
, aid
);
1930 enum map_index
choose_mission(uint8_t* completed
);
1931 #pragma RcB2 DEP "mission_select.c"
1932 #include "enemytag.c"
1934 static uint8_t mission_completed
[MI_MAX
];
1938 static void game_delay(int s
) {
1939 /* wait for s frames while continuing to play music,
1940 * if s == -1 return as soon as music finished */
1942 if(x
== -1) x
= INT_MAX
;
1944 int r
= audio_process();
1945 if(s
== -1 && r
== -1) return;
1946 SDL_Delay(1000/fps
);
1952 static void finish_level(void) {
1953 music_play(TUNE_TITLE
);
1955 video_darken_screen();
1956 video_update_region(SCREEN_MIN_X
, SCREEN_MIN_Y
, SCREEN_MAX_X
- SCREEN_MIN_X
, VMODE_H
);
1957 #define STRLSZ(x) (x), sizeof(x)-1
1958 font_print(SCREEN_MIN_X
+40*SCALE
, SCREEN_MIN_Y
+13*SCALE
, STRLSZ("congratulations"), SCALE
, PRGB(255,255,255));
1959 font_print(SCREEN_MIN_X
+24*SCALE
, SCREEN_MIN_Y
+24*SCALE
, STRLSZ("you have completed"), SCALE
, PRGB(255,255,255));
1960 font_print(SCREEN_MIN_X
+48*SCALE
, SCREEN_MIN_Y
+35*SCALE
, STRLSZ("your mission"), SCALE
, PRGB(255,255,255));
1961 video_update_region(SCREEN_MIN_X
, SCREEN_MIN_Y
, SCREEN_MAX_X
- SCREEN_MIN_X
, VMODE_H
);
1963 /* FIXME: for some reason there's a crack noise when switching to "empty" */
1964 music_play(TUNE_EMPTY
);
1966 uint32_t bgbuf0
[8*SCALE
*((95-40)*SCALE
)];
1967 video_save_rect(SCREEN_MIN_X
+40*SCALE
,SCREEN_MIN_Y
+65*SCALE
,(95-40)*SCALE
,8*SCALE
,bgbuf0
);
1969 font_print(SCREEN_MIN_X
+40*SCALE
, SCREEN_MIN_Y
+65*SCALE
, STRLSZ("mission bonus"), SCALE
, PRGB(255,255,255));
1971 font_print(SCREEN_MIN_X
+32*SCALE
, SCREEN_MIN_Y
+89*SCALE
, STRLSZ("1."), SCALE
, PRGB(255,255,255));
1972 uint32_t bgbuf
[8*SCALE
*((159-64)*SCALE
)];
1973 video_save_rect(SCREEN_MIN_X
+64*SCALE
,SCREEN_MIN_Y
+89*SCALE
,(159-64)*SCALE
,8*SCALE
,bgbuf
);
1976 int dollars
= player_cash
[0];
1977 snprintf(buf
, sizeof buf
, " $%.6d", dollars
);
1978 font_print(SCREEN_MIN_X
+64*SCALE
, SCREEN_MIN_Y
+89*SCALE
, buf
, 12, SCALE
, PRGB(255,255,255));
1979 video_update_region(SCREEN_MIN_X
, SCREEN_MIN_Y
, SCREEN_MAX_X
- SCREEN_MIN_X
, VMODE_H
);
1984 dollars
+= maps
[current_map
]->rewardk
*500;
1985 video_restore_rect(SCREEN_MIN_X
+64*SCALE
,SCREEN_MIN_Y
+89*SCALE
,(159-64)*SCALE
,8*SCALE
,bgbuf
);
1986 snprintf(buf
, sizeof buf
, " $%.6d", dollars
);
1987 font_print(SCREEN_MIN_X
+64*SCALE
, SCREEN_MIN_Y
+89*SCALE
, buf
, 12, SCALE
, PRGB(255,255,255));
1988 video_update_region(SCREEN_MIN_X
, SCREEN_MIN_Y
, SCREEN_MAX_X
- SCREEN_MIN_X
, VMODE_H
);
1991 audio_play_wave_resource(wavesounds
[WS_MISSIONBONUS
]);
1993 game_delay(4.2*fps
);
1996 video_restore_rect(SCREEN_MIN_X
+40*SCALE
,SCREEN_MIN_Y
+65*SCALE
,(95-40)*SCALE
,8*SCALE
,bgbuf0
);
1997 font_print(SCREEN_MIN_X
+40*SCALE
, SCREEN_MIN_Y
+65*SCALE
, STRLSZ("revenge bonus"), SCALE
, PRGB(255,255,255));
1999 snprintf(buf
, sizeof buf
, "%.4d $%.6d", player_kills
[0], dollars
);
2000 font_print(SCREEN_MIN_X
+64*SCALE
, SCREEN_MIN_Y
+89*SCALE
, buf
, 12, SCALE
, PRGB(255,255,255));
2001 video_update_region(SCREEN_MIN_X
, SCREEN_MIN_Y
, SCREEN_MAX_X
- SCREEN_MIN_X
, VMODE_H
);
2004 while(player_kills
[0]) {
2007 video_restore_rect(SCREEN_MIN_X
+64*SCALE
,SCREEN_MIN_Y
+89*SCALE
,(159-64)*SCALE
,8*SCALE
,bgbuf
);
2008 snprintf(buf
, sizeof buf
, "%.4d $%.6d", player_kills
[0], dollars
);
2009 font_print(SCREEN_MIN_X
+64*SCALE
, SCREEN_MIN_Y
+89*SCALE
, buf
, 12, SCALE
, PRGB(255,255,255));
2010 video_update_region(SCREEN_MIN_X
, SCREEN_MIN_Y
, SCREEN_MAX_X
- SCREEN_MIN_X
, VMODE_H
);
2011 if(tickcounter
% 4 == 0) audio_play_wave_resource(wavesounds
[WS_COUNTDOWN
]);
2012 // FIXME: when no music is played, sound does not play either... maybe add "empty song"?
2015 music_play(TUNE_LEVEL_FINISHED
);
2018 mission_completed
[current_map
] = 1;
2019 player_cash
[0] = dollars
;
2022 #include "weapon_shop.c"
2027 //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
2028 SDL_EnableKeyRepeat(100, 20);
2031 init_player_once(0);
2037 /* background music for mission selection screen */
2038 music_play(TUNE_MAP
);
2040 if((current_map
= choose_mission(mission_completed
)) == MI_INVALID
) goto dun_goofed
;
2042 player_cash
[0] += maps
[current_map
]->rewardk
*500;
2046 music_play(TUNE_FIGHTING
);
2053 //redraw(surface, startx, starty);
2054 const float player_speed
= 1.25f
;
2055 const struct palpic
* spritemap
= spritemaps
[SI_PLAYERS
];
2056 struct { int *target
; int dir
; int max
;} moves
[] = {
2057 [c_up
] = {&starty
, SCALE
* -1, VMODE_H
- (palpic_getspriteheight(spritemap
) * SCALE
)},
2058 [c_down
] = {&starty
, SCALE
, VMODE_H
- (palpic_getspriteheight(spritemap
) * SCALE
)},
2059 [c_left
] = {&startx
, SCALE
* -1, VMODE_W
- (palpic_getspritewidth(spritemap
) * SCALE
)},
2060 [c_right
] = {&startx
, SCALE
, VMODE_W
- (palpic_getspritewidth(spritemap
) * SCALE
)},
2065 int player_id
= player_ids
[player_no
];
2069 SDL_Event sdl_event
;
2071 unsigned need_redraw
= 0;
2073 //if(tickcounter>64) { player_kills[0] = 150; finish_level();}
2074 while (SDL_PollEvent(&sdl_event
)) {
2076 switch (sdl_event
.type
) {
2077 case SDL_MOUSEMOTION
:
2078 if((int)mousepos
->x
!= sdl_event
.motion
.x
|| (int)mousepos
->y
!= sdl_event
.motion
.y
)
2080 mousepos
->x
= sdl_event
.motion
.x
;
2081 mousepos
->y
= sdl_event
.motion
.y
;
2083 case SDL_MOUSEBUTTONDOWN
:
2084 mousepos
->x
= sdl_event
.button
.x
;
2085 mousepos
->y
= sdl_event
.button
.y
;
2086 mousebutton_down
[MB_LEFT
] = 1;
2087 fire_bullet(player_no
);
2089 case SDL_MOUSEBUTTONUP
:
2090 mousepos
->x
= sdl_event
.button
.x
;
2091 mousepos
->y
= sdl_event
.button
.y
;
2092 mousebutton_down
[MB_LEFT
] = 0;
2099 switch(sdl_event
.key
.keysym
.sym
) {
2100 case SDLK_w
: case SDLK_a
: case SDLK_s
: case SDLK_d
:
2105 cursors_pressed
[cursor_lut
[sdl_event
.key
.keysym
.sym
]] = 1;
2107 enum direction dir
= get_direction_from_cursor();
2108 if(dir
!= DIR_INVALID
) {
2109 if(!mousebutton_down
[MB_LEFT
]) {
2110 // change animation according to cursors,
2111 // unless we're in automatic fire mode.
2112 enum animation_id aid
= get_anim_from_direction(dir
, player_no
, 0);
2113 if(aid
!= ANIM_INVALID
) switch_anim(player_id
, aid
);
2115 objs
[player_id
].vel
= get_vel_from_direction(dir
, player_speed
);
2117 objs
[player_id
].vel
= VEC(0,0);
2122 if((sdl_event
.key
.keysym
.mod
& KMOD_LALT
) ||
2123 (sdl_event
.key
.keysym
.mod
& KMOD_RALT
)) {
2124 toggle_fullscreen();
2136 weapon_active
[player_no
] += weapon_inc
;
2137 if(weapon_active
[player_no
] < 0)
2138 weapon_active
[player_no
] = weapon_count
[player_no
] - 1;
2139 else if(weapon_active
[player_no
] >= weapon_count
[player_no
])
2140 weapon_active
[player_no
] = 0;
2141 printf("%s\n", weapon_name(player_weapons
[player_no
][weapon_active
[player_no
]]));
2149 switch(sdl_event
.key
.keysym
.sym
) {
2154 update_caption
= game_update_caption
;
2161 case SDLK_w
: case SDLK_a
: case SDLK_s
: case SDLK_d
:
2166 cursors_pressed
[cursor_lut
[sdl_event
.key
.keysym
.sym
]] = 0;
2178 for (i
= 0; i
< ARRAY_SIZE(cursors_pressed
); i
++) {
2179 if(cursors_pressed
[i
]) {
2180 *moves
[i
].target
+= moves
[i
].dir
;
2181 if(*moves
[i
].target
< 0) *moves
[i
].target
= 0;
2182 if(*moves
[i
].target
> moves
[i
].max
) *moves
[i
].target
= moves
[i
].max
;
2186 if(game_tick(need_redraw
)) {
2189 goto mission_select
;