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"
29 #ifndef IN_KDEVELOP_PARSER
30 #include "../lib/include/bitarray.h"
31 #include "weapon_sprites.c"
40 // 1 if button down, 0 if not, >1 to count ms pressed
41 unsigned long mousebutton_down
[] = {
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 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
);
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
) {
143 sblist_iter_counter2(l
, i
, itemid
) {
144 if(*itemid
== objid
) {
151 static int get_next_anim_frame(enum animation_id aid
, anim_step curr
) {
152 if(curr
== ANIM_STEP_INIT
) return animations
[aid
].first
;
154 if(curr
> animations
[aid
].last
) return animations
[aid
].first
;
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);
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);
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
];
208 mapscrolldir
= MS_UP
;
209 map_spawn_screen_index
= 0;
211 map_spawn_current
= 0;
214 static mapscreen_index
get_bonus_layer_index(mapscreen_index screen
) {
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 int is_wall(vec2f
*pos
) {
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 0;
237 uint8_t spriteno
= map_scr
[scr_idx
].fg
.fg
[y
/16][x
/16];
238 if(spriteno
&& walls
[map
->maptype
][spriteno
]) return 1;
239 scr_idx
= get_bonus_layer_index(scr_idx
);
240 if(scr_idx
== MAPSCREEN_BLOCKED
) return 0;
241 spriteno
= map_bonus_scr
[scr_idx
].fg
[y
/16][x
/16];
242 if(spriteno
&& walls
[map
->maptype
][spriteno
]) return 1;
246 static void draw_map() {
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
;
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);
268 assert(map
->screen_map
[mapsquare
.y
][mapsquare
.x
+x_screen_iter
] != MAPSCREEN_BLOCKED
);
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
))
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;
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
++;
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);
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
]);
383 map_spawn_line
+= scrollstep
;
386 static int scroll_map() {
389 if(scroll_needed()) {
390 if(mapscrolldir
== MS_UP
) {
391 mapscreen_yoff
-= scroll_step
;
392 if(mapscreen_yoff
< 0) {
394 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
] == MAPSCREEN_BLOCKED
) {
395 scroll_step
= -mapscreen_yoff
;
398 scroll_gameobjs(scroll_step
);
399 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
+1] == MAPSCREEN_BLOCKED
) {
400 mapscrolldir
= MS_LEFT
;
402 mapscrolldir
= MS_RIGHT
;
408 mapscreen_yoff
+= 192;
412 handle_spawns(scroll_step
);
413 scroll_gameobjs(scroll_step
);
415 } else if(mapscrolldir
== MS_LEFT
) {
416 mapscreen_xoff
-= scroll_step
;
417 if(mapscreen_xoff
< 0) {
419 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
] == MAPSCREEN_BLOCKED
) {
420 scroll_step
= -mapscreen_xoff
;
424 scroll_gameobjs(scroll_step
);
425 mapscrolldir
= MS_UP
;
428 mapscreen_xoff
+= 192;
433 } else if(mapscrolldir
== MS_RIGHT
) {
434 mapscreen_xoff
+= scroll_step
;
435 if(mapscreen_xoff
>= 192) {
437 if(map
->screen_map
[mapsquare
.y
][mapsquare
.x
+1] == MAPSCREEN_BLOCKED
) {
438 scroll_step
= mapscreen_xoff
- 192;
441 scroll_gameobjs(scroll_step
);
442 mapscrolldir
= MS_UP
;
446 mapscreen_xoff
-= 192;
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;
467 for(; i
< AMMO_MAX
; i
++)
468 player_ammo
[player_no
][i
] = 50000;
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;
479 mousepos
= &objs
[id
].pos
;
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
);
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
);
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
);
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
]);
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
));
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;
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 },
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
);
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
,
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
);
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;
597 if(spawn
->route
[i
].shape
!= ES_INVALID
&&
598 curr_step
>= spawn
->route
[i
].start_step
)
599 return &spawn
->route
[i
];
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
[] = {
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
;
705 enum animation_id animid
;
707 spriteid
= enemy_soldier_sprite_lut
[map
->enemy_type
];
708 objid
= enemy_soldier_objtype_lut
[spawn
->weapon
];
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
;
720 case OBJ_MINE_CROSSED
: case OBJ_MINE_FLAT
:
723 case OBJ_JEEP
: case OBJ_TRANSPORTER
:
724 case OBJ_TANK_BIG
: case OBJ_TANK_SMALL
:
733 static void remove_enemy(int id
) {
734 enum objtype objid
= objs
[id
].objtype
;
736 case OBJ_JEEP
: case OBJ_TRANSPORTER
:
737 case OBJ_TANK_BIG
: case OBJ_TANK_SMALL
:
738 golist_remove(&go_vehicles
, id
);
741 golist_remove(&go_enemies
, id
);
746 static int enemy_fires(struct enemy
*e
) {
748 for(i
= 0; i
< ENEMY_MAX_SHOT
; i
++)
749 if(e
->curr_step
== e
->spawn
->shots
[i
]) return 1;
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
),
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);
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
);
799 case ES_SOLDIER1_DOWN
: case ES_SOLDIER2_DOWN
:
801 case ES_SOLDIER1_LEFT
: case ES_SOLDIER2_LEFT
:
803 case ES_SOLDIER1_RIGHT
: case ES_SOLDIER2_RIGHT
:
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);
816 if(go
->objspecific
.enemy
.spawn
->weapon
== EW_GUN
) {
817 id
= init_bullet(&from
, &vel
, 41);
818 if(id
!= -1) add_ebullet(id
);
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 },
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
;
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
;
878 id
= init_rocket(dir
, &from
, &vel
, steps
);
881 id
= init_bullet(&from
, &vel
, steps
);
882 if(id
!= -1) add_pbullet(id
);
885 id
= init_flame(dir
, &from
, &vel
, steps
);
888 id
= init_grenade(&from
, &vel
, steps
);
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);
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;
933 static enum animation_id
get_die_anim(unsigned id
) {
934 switch(objs
[id
].objtype
) {
940 return ANIM_JEEP_DESTROYED
;
942 return ANIM_TANK_SMALL_DESTROYED
;
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
;
963 /* remove bullets that have reached their maximum number of steps */
964 static int remove_bullets(sblist
*list
) {
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
);
975 bullet
->objspecific
.bullet
.step_curr
++;
982 static int remove_explosives(sblist
*list
) {
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
);
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
;
1006 static int remove_offscreen_objects(sblist
*list
) {
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
) {
1020 dprintf(2, "offscreen: removed gameobj %d\n", (int) *item_id
);
1021 gameobj_free(*item_id
);
1022 sblist_delete(list
, li
);
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
) {
1039 enum bulletsubtype
{
1042 BS_GRENADE_EXPL
= 2,
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
)) 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
};
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
;
1083 for(k
= 0; k
< 4; k
++) {
1084 if(point_in_mask(&point
, *target_id
)) {
1087 if(bullet_list
== &go_player_bullets
) {
1088 if(target_list
== &go_vehicles
) {
1089 audio_play_wave_resource(wavesounds
[WS_DROPSHOT
]);
1092 objs
[player_ids
[0]].objspecific
.playerdata
.score
+= 50;
1093 } else if (bullet_list
== &go_rockets
) {
1094 init_rocket_explosion(&target
->pos
);
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
);
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
) {
1115 gameobj_free(*bullet_id
);
1116 sblist_delete(bullet_list
, li
);
1121 point
= vecadd(&point
, &velquarter
);
1132 uint32_t tickcounter
;
1134 static int advance_animations(void) {
1135 size_t i
, obj_visited
;
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;
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) {
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
;
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
1186 * 5) check grenades, remove and spawn exposions if necessary
1187 * 6) enemies: move, kill player on hit
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
];
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
) {
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
);
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);
1247 if(go
->vel
.x
!= 0 || go
->vel
.y
!= 0) {
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(¢er
, &go
->pos
);
1260 if(is_wall(¢er
)) {
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
);
1275 golist_remove(&go_enemies
, i
);
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;
1286 struct timeval timer
;
1287 gettimestamp(&timer
);
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
);
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
]++;
1325 static enum cursor cursor_lut
[] = {
1327 [SDLK_DOWN
] = c_down
,
1328 [SDLK_LEFT
] = c_left
,
1329 [SDLK_RIGHT
] = c_right
,
1336 static char cursors_pressed
[] = {
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),
1356 vec2f v
= vel_lut
[dir
];
1357 v
.x
*= speed
* SCALE
;
1358 v
.y
*= speed
* SCALE
;
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
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
),
1387 vec2f v
= vel_lut
[dir
];
1388 v
.x
*= speed
* SCALE
;
1389 v
.y
*= speed
* SCALE
;
1394 static enum direction
get_direction_from_vec(vec2f
*vel
) {
1395 float deg_org
, deg
= atan2(vel
->y
, vel
->x
);
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
[] = {
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
;
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
;
1425 } else if (cursors_pressed
[c_left
]) {
1427 } else if (cursors_pressed
[c_right
]) {
1433 static enum animation_id
get_anim_from_direction(enum direction dir
, int player_no
, int throwing
) {
1434 #define DIRMAP(a, b) [a] = b
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
];
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
];
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);
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"
1510 //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1511 SDL_EnableKeyRepeat(100, 20);
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
);
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
)},
1540 int player_id
= player_ids
[player_no
];
1544 SDL_Event sdl_event
;
1546 unsigned need_redraw
= 0;
1548 while (SDL_PollEvent(&sdl_event
)) {
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
)
1554 mousepos
->x
= sdl_event
.motion
.x
;
1555 mousepos
->y
= sdl_event
.motion
.y
;
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
);
1563 case SDL_MOUSEBUTTONUP
:
1564 mousepos
->x
= sdl_event
.button
.x
;
1565 mousepos
->y
= sdl_event
.button
.y
;
1566 mousebutton_down
[MB_LEFT
] = 0;
1573 switch(sdl_event
.key
.keysym
.sym
) {
1574 case SDLK_w
: case SDLK_a
: case SDLK_s
: case SDLK_d
:
1579 cursors_pressed
[cursor_lut
[sdl_event
.key
.keysym
.sym
]] = 1;
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
);
1591 objs
[player_id
].vel
= VEC(0,0);
1596 if((sdl_event
.key
.keysym
.mod
& KMOD_LALT
) ||
1597 (sdl_event
.key
.keysym
.mod
& KMOD_RALT
)) {
1598 toggle_fullscreen();
1608 if((int)player_weapons
[player_no
][0] == 0)
1609 weapon_inc
= WP_MAX
-1;
1610 else weapon_inc
= -1;
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]));
1623 switch(sdl_event
.key
.keysym
.sym
) {
1626 update_caption
= game_update_caption
;
1633 case SDLK_w
: case SDLK_a
: case SDLK_s
: case SDLK_d
:
1638 cursors_pressed
[cursor_lut
[sdl_event
.key
.keysym
.sym
]] = 0;
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
;
1658 game_tick(need_redraw
);