1 #include <SDL2/SDL.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <math.h>
6
7 #include "video/surface.h"
8 #include "video/video.h"
9 #include "game/scenes/arena.h"
10 #include "audio/stream.h"
11 #include "audio/audio.h"
12 #include "audio/music.h"
13 #include "game/utils/settings.h"
14 #include "game/objects/har.h"
15 #include "game/objects/scrap.h"
16 #include "game/objects/hazard.h"
17 #include "game/objects/arena_constraints.h"
18 #include "game/protos/object.h"
19 #include "game/utils/score.h"
20 #include "game/game_player.h"
21 #include "game/game_state.h"
22 #include "game/utils/ticktimer.h"
23 #include "game/gui/text_render.h"
24 #include "resources/languages.h"
25 #include "game/gui/menu.h"
26 #include "game/gui/menu_background.h"
27 #include "game/gui/textbutton.h"
28 #include "game/gui/textselector.h"
29 #include "game/gui/textslider.h"
30 #include "game/gui/label.h"
31 #include "game/gui/filler.h"
32 #include "game/gui/frame.h"
33 #include "game/gui/progressbar.h"
34 #include "controller/controller.h"
35 #include "controller/net_controller.h"
36 #include "resources/ids.h"
37 #include "utils/log.h"
38 #include "utils/random.h"
39
40 #define TEXT_COLOR color_create(186,250,250,255)
41
42 #define HAR1_START_POS 110
43 #define HAR2_START_POS 211
44
45 typedef struct arena_local_t {
46 guiframe *game_menu;
47
48 surface sur;
49 int menu_visible;
50 unsigned int state;
51 int ending_ticks;
52
53 component *health_bars[2];
54 component *endurance_bars[2];
55
56 chr_score player1_score;
57 chr_score player2_score;
58
59 int round;
60 int rounds;
61 int over;
62
63 object *player_rounds[2][4];
64
65 int rein_enabled;
66
67 sd_rec_file *rec;
68 int rec_last[2];
69 } arena_local;
70
71 void arena_maybe_sync(scene *scene, int need_sync);
72 void write_rec_move(scene *scene, game_player *player, int action);
73
74 // -------- Local callbacks --------
75
game_menu_quit(component * c,void * userdata)76 void game_menu_quit(component *c, void *userdata) {
77 scene *s = userdata;
78 chr_score_reset(game_player_get_score(game_state_get_player((s)->gs, 0)), 1);
79 chr_score_reset(game_player_get_score(game_state_get_player((s)->gs, 1)), 1);
80 game_state_set_next(s->gs, SCENE_MENU);
81 }
82
game_menu_return(component * c,void * userdata)83 void game_menu_return(component *c, void *userdata) {
84 arena_local *local = scene_get_userdata((scene*)userdata);
85 game_player *player1 = game_state_get_player(((scene*)userdata)->gs, 0);
86 controller_set_repeat(game_player_get_ctrl(player1), 1);
87 local->menu_visible = 0;
88 game_state_set_paused(((scene*)userdata)->gs, 0);
89 arena_maybe_sync(userdata, 1);
90 }
91
arena_music_slide(component * c,void * userdata,int pos)92 void arena_music_slide(component *c, void *userdata, int pos) {
93 music_set_volume(pos/10.0f);
94 }
95
arena_sound_slide(component * c,void * userdata,int pos)96 void arena_sound_slide(component *c, void *userdata, int pos) {
97 sound_set_volume(pos/10.0f);
98 }
99
arena_speed_slide(component * c,void * userdata,int pos)100 void arena_speed_slide(component *c, void *userdata, int pos) {
101 scene *sc = userdata;
102 game_state_set_speed(sc->gs, pos + 5);
103 }
104
scene_fight_anim_done(void * userdata)105 void scene_fight_anim_done(void *userdata) {
106 object *parent = userdata;
107 scene *scene = game_state_get_scene(parent->gs);
108 arena_local *arena = scene_get_userdata(scene);
109
110 // This will release HARs for action
111 arena->state = ARENA_STATE_FIGHTING;
112
113 // Custom object finisher callback requires that we
114 // mark object as finished manually, if necessary.
115 //parent->animation_state.finished = 1;
116 }
117
scene_fight_anim_start(void * userdata)118 void scene_fight_anim_start(void *userdata) {
119 // Start FIGHT animation
120 game_state *gs = userdata;
121 scene *scene = game_state_get_scene(gs);
122 animation *fight_ani = &bk_get_info(&scene->bk_data, 10)->ani;
123 object *fight = malloc(sizeof(object));
124 object_create(fight, gs, fight_ani->start_pos, vec2f_create(0,0));
125 object_set_stl(fight, bk_get_stl(&scene->bk_data));
126 object_set_animation(fight, fight_ani);
127 //object_set_finish_cb(fight, scene_fight_anim_done);
128 game_state_add_object(gs, fight, RENDER_LAYER_TOP, 0, 0);
129 ticktimer_add(&scene->tick_timer, 24, scene_fight_anim_done, fight);
130 }
131
scene_ready_anim_done(object * parent)132 void scene_ready_anim_done(object *parent) {
133 // Wait a moment before loading FIGHT animation
134 ticktimer_add(&game_state_get_scene(parent->gs)->tick_timer, 10, scene_fight_anim_start, parent->gs);
135
136 // Custom object finisher callback requires that we
137 // mark object as finished manually, if necessary.
138 parent->animation_state.finished = 1;
139 }
140
scene_youwin_anim_done(object * parent)141 void scene_youwin_anim_done(object *parent) {
142 // Custom object finisher callback requires that we
143 // mark object as finished manually, if necessary.
144 parent->animation_state.finished = 1;
145 }
146
scene_youwin_anim_start(void * userdata)147 void scene_youwin_anim_start(void *userdata) {
148 // Start FIGHT animation
149 game_state *gs = userdata;
150 scene *scene = game_state_get_scene(gs);
151 animation *youwin_ani = &bk_get_info(&scene->bk_data, 9)->ani;
152 object *youwin = malloc(sizeof(object));
153 object_create(youwin, gs, youwin_ani->start_pos, vec2f_create(0,0));
154 object_set_stl(youwin, bk_get_stl(&scene->bk_data));
155 object_set_animation(youwin, youwin_ani);
156 object_set_finish_cb(youwin, scene_youwin_anim_done);
157 game_state_add_object(gs, youwin, RENDER_LAYER_MIDDLE, 0, 0);
158
159 // This will release HARs for action
160 /*arena->state = ARENA_STATE_ENDING;*/
161 }
162
scene_youlose_anim_done(object * parent)163 void scene_youlose_anim_done(object *parent) {
164 // Custom object finisher callback requires that we
165 // mark object as finished manually, if necessary.
166 parent->animation_state.finished = 1;
167 }
168
scene_youlose_anim_start(void * userdata)169 void scene_youlose_anim_start(void *userdata) {
170 // Start FIGHT animation
171 game_state *gs = userdata;
172 scene *scene = game_state_get_scene(gs);
173 animation *youlose_ani = &bk_get_info(&scene->bk_data, 8)->ani;
174 object *youlose = malloc(sizeof(object));
175 object_create(youlose, gs, youlose_ani->start_pos, vec2f_create(0,0));
176 object_set_stl(youlose, bk_get_stl(&scene->bk_data));
177 object_set_animation(youlose, youlose_ani);
178 object_set_finish_cb(youlose, scene_youlose_anim_done);
179 game_state_add_object(gs, youlose, RENDER_LAYER_MIDDLE, 0, 0);
180
181 // This will release HARs for action
182 /*arena->state = ARENA_STATE_ENDING;*/
183 }
184
arena_repeat_controller(void * userdata)185 void arena_repeat_controller(void *userdata) {
186 game_state *gs = userdata;
187 game_player *player1 = game_state_get_player(gs, 0);
188 controller_set_repeat(game_player_get_ctrl(player1), 1);
189 }
190
is_netplay(scene * scene)191 int is_netplay(scene *scene) {
192 if(game_state_get_player(scene->gs, 0)->ctrl->type == CTRL_TYPE_NETWORK ||
193 game_state_get_player(scene->gs, 1)->ctrl->type == CTRL_TYPE_NETWORK) {
194 return 1;
195 }
196 return 0;
197 }
198
is_singleplayer(scene * scene)199 int is_singleplayer(scene *scene) {
200 if(game_state_get_player(scene->gs, 1)->ctrl->type == CTRL_TYPE_AI) {
201 return 1;
202 }
203 return 0;
204 }
205
is_demoplay(scene * scene)206 int is_demoplay(scene *scene) {
207 if(game_state_get_player(scene->gs, 0)->ctrl->type == CTRL_TYPE_AI &&
208 game_state_get_player(scene->gs, 1)->ctrl->type == CTRL_TYPE_AI) {
209 return 1;
210 }
211 return 0;
212 }
213
is_twoplayer(scene * scene)214 int is_twoplayer(scene *scene) {
215 if (!is_demoplay(scene) && !is_netplay(scene) && !is_singleplayer(scene)) {
216 return 1;
217 }
218 return 0;
219 }
220
arena_screengrab_winner(scene * sc)221 void arena_screengrab_winner(scene* sc) {
222 game_state *gs = sc->gs;
223
224 // take victory pose screenshot for the newsroom
225 har *h1 = object_get_userdata(game_state_get_player(gs, 0)->har);
226 if(h1->state == STATE_VICTORY || h1->state == STATE_DONE) {
227 har_screencaps_capture(
228 &game_state_get_player(gs, 0)->screencaps,
229 game_state_get_player(gs, 0)->har,
230 SCREENCAP_POSE);
231 } else {
232 har_screencaps_capture(
233 &game_state_get_player(gs, 1)->screencaps,
234 game_state_get_player(gs, 1)->har,
235 SCREENCAP_POSE);
236 }
237 }
238
arena_end(scene * sc)239 void arena_end(scene *sc) {
240 game_state *gs = sc->gs;
241 int next_id;
242
243 // Switch scene
244 if (is_demoplay(sc)) {
245 do {
246 next_id = rand_arena();
247 } while(next_id == sc->id);
248 game_state_set_next(gs, next_id);
249 }
250 else if (is_singleplayer(sc)) {
251 game_state_set_next(gs, SCENE_NEWSROOM);
252 } else if (is_twoplayer(sc)) {
253 game_state_set_next(gs, SCENE_MELEE);
254 } else {
255 game_state_set_next(gs, SCENE_MENU);
256 }
257 }
258
arena_reset(scene * sc)259 void arena_reset(scene *sc) {
260 arena_local *local = scene_get_userdata(sc);
261 local->round++;
262 local->state = ARENA_STATE_STARTING;
263
264 // Kill all hazards and projectiles
265 game_state_clear_hazards_projectiles(sc->gs);
266
267 // Initial har data
268 vec2i pos[2];
269 int dir[2] = {OBJECT_FACE_RIGHT, OBJECT_FACE_LEFT};
270 pos[0] = vec2i_create(HAR1_START_POS, ARENA_FLOOR);
271 pos[1] = vec2i_create(HAR2_START_POS, ARENA_FLOOR);
272
273 // init HARs
274 for(int i = 0; i < 2; i++) {
275 // Declare some vars
276 game_player *player = game_state_get_player(sc->gs, i);
277 object *har_obj = game_player_get_har(player);
278 har *h = object_get_userdata(har_obj);
279 h->state = STATE_STANDING;
280 har_set_ani(har_obj, ANIM_IDLE, 1);
281 h->health = h->health_max;
282 h->endurance = h->endurance_max;
283 h->air_attacked = 0;
284 object_set_pos(har_obj, pos[i]);
285 object_set_vel(har_obj, vec2f_create(0, 0));
286 object_set_gravity(har_obj, 1);
287 object_set_direction(har_obj, dir[i]);
288 chr_score_clear_done(&player->score);
289 }
290
291 sc->bk_data.sound_translation_table[3] = 23 + local->round; // NUMBER
292 // ROUND animation
293 animation *round_ani = &bk_get_info(&sc->bk_data, 6)->ani;
294 object *round = malloc(sizeof(object));
295 object_create(round, sc->gs, round_ani->start_pos, vec2f_create(0,0));
296 object_set_stl(round, sc->bk_data.sound_translation_table);
297 object_set_animation(round, round_ani);
298 object_set_finish_cb(round, scene_ready_anim_done);
299 game_state_add_object(sc->gs, round, RENDER_LAYER_TOP, 0, 0);
300
301 // Round number
302 animation *number_ani = &bk_get_info(&sc->bk_data, 7)->ani;
303 object *number = malloc(sizeof(object));
304 object_create(number, sc->gs, number_ani->start_pos, vec2f_create(0,0));
305 object_set_stl(number, sc->bk_data.sound_translation_table);
306 object_set_animation(number, number_ani);
307 object_select_sprite(number, local->round);
308 object_set_sprite_override(number, 1);
309 game_state_add_object(sc->gs, number, RENDER_LAYER_TOP, 0, 0);
310 }
311
arena_maybe_sync(scene * scene,int need_sync)312 void arena_maybe_sync(scene *scene, int need_sync) {
313 game_state *gs = scene->gs;
314 game_player *player1 = game_state_get_player(gs, 0);
315 game_player *player2 = game_state_get_player(gs, 1);
316
317 if(need_sync
318 && gs->role == ROLE_SERVER
319 && (player1->ctrl->type == CTRL_TYPE_NETWORK || player2->ctrl->type == CTRL_TYPE_NETWORK)) {
320
321 // some of the moves did something interesting and we should synchronize the peer
322 serial ser;
323 serial_create(&ser);
324 game_state_serialize(scene->gs, &ser);
325 if (player1->ctrl->type == CTRL_TYPE_NETWORK) {
326 controller_update(player1->ctrl, &ser);
327 }
328 if (player2->ctrl->type == CTRL_TYPE_NETWORK) {
329 controller_update(player2->ctrl, &ser);
330 }
331 serial_free(&ser);
332 }
333 }
334
arena_har_take_hit_hook(int hittee,af_move * move,scene * scene)335 void arena_har_take_hit_hook(int hittee, af_move *move, scene *scene) {
336 chr_score *score;
337 chr_score *otherscore;
338 object *hit_har;
339 har *h;
340
341 if (is_netplay(scene) && scene->gs->role == ROLE_CLIENT) {
342 return; // netplay clients do not keep score
343 }
344
345 if (hittee == 1) {
346 score = game_player_get_score(game_state_get_player(scene->gs, 0));
347 otherscore = game_player_get_score(game_state_get_player(scene->gs, 1));
348 hit_har = game_player_get_har(game_state_get_player(scene->gs, 1));
349 } else {
350 score = game_player_get_score(game_state_get_player(scene->gs, 1));
351 otherscore = game_player_get_score(game_state_get_player(scene->gs, 0));
352 hit_har = game_player_get_har(game_state_get_player(scene->gs, 0));
353 }
354 h = hit_har->userdata;
355 if (h->state == STATE_RECOIL) {
356 DEBUG("COMBO!");
357 }
358 chr_score_hit(score, move->points);
359 chr_score_interrupt(otherscore, object_get_pos(hit_har));
360 arena_maybe_sync(scene, 1);
361 }
362
arena_har_recover_hook(int player_id,scene * scene)363 void arena_har_recover_hook(int player_id, scene *scene) {
364 chr_score *score;
365 object *o_har;
366
367 if (is_netplay(scene) && scene->gs->role == ROLE_CLIENT) {
368 return; // netplay clients do not keep score
369 }
370
371 if (player_id == 0) {
372 score = game_player_get_score(game_state_get_player(scene->gs, 1));
373 o_har = game_player_get_har(game_state_get_player(scene->gs, 1));
374 } else {
375 score = game_player_get_score(game_state_get_player(scene->gs, 0));
376 o_har = game_player_get_har(game_state_get_player(scene->gs, 0));
377 }
378 if(chr_score_end_combo(score, object_get_pos(o_har))) {
379 arena_maybe_sync(scene, 1);
380 }
381 }
382
arena_har_hit_wall_hook(int player_id,int wall,scene * scene)383 void arena_har_hit_wall_hook(int player_id, int wall, scene *scene) {
384 object *o_har = game_player_get_har(game_state_get_player(scene->gs, player_id));
385 har *h = object_get_userdata(o_har);
386
387 int towards_wall = 0;
388 if(wall == 0 && o_har->vel.x <= 1) {
389 towards_wall = 1;
390 }
391 if(wall == 1 && o_har->vel.x >= 1) {
392 towards_wall = 1;
393 }
394
395 int on_air = 0;
396 if(o_har->pos.y < ARENA_FLOOR - 10) {
397 on_air = 1;
398 }
399
400 // The limit here is entirely guesswork, and might not be it at all
401 // However, it is a close enough guess.
402 // TODO: Find out how this really works.
403 int took_enough_damage = 0;
404 if(h->last_damage_value > 15) {
405 took_enough_damage = 1;
406 }
407
408 /*
409 * When hitting the lightning arena wall, the HAr needs to get hit by lightning thingy.
410 */
411 if (scene->id == SCENE_ARENA2
412 && on_air
413 && (h->state == STATE_FALLEN || h->state == STATE_RECOIL)
414 && towards_wall
415 && !h->is_grabbed
416 && took_enough_damage)
417 {
418 DEBUG("hit lightning wall %d", wall);
419 h->state = STATE_WALLDAMAGE;;
420
421 // Spawn wall animation
422 bk_info *info = bk_get_info(&scene->bk_data, 20+wall);
423 object *obj = malloc(sizeof(object));
424 object_create(obj, scene->gs, info->ani.start_pos, vec2f_create(0,0));
425 object_set_stl(obj, scene->bk_data.sound_translation_table);
426 object_set_animation(obj, &info->ani);
427 if(game_state_add_object(scene->gs, obj, RENDER_LAYER_BOTTOM, 1, 0) == 0) {
428
429 // spawn the electricity on top of the HAR
430 // TODO this doesn't track the har's position well...
431 info = bk_get_info(&scene->bk_data, 22);
432 object *obj2 = malloc(sizeof(object));
433 object_create(obj2, scene->gs, vec2i_create(o_har->pos.x, o_har->pos.y), vec2f_create(0, 0));
434 object_set_stl(obj2, scene->bk_data.sound_translation_table);
435 object_set_animation(obj2, &info->ani);
436 object_attach_to(obj2, o_har);
437 object_dynamic_tick(obj2);
438 game_state_add_object(scene->gs, obj2, RENDER_LAYER_TOP, 0, 0);
439 } else {
440 object_free(obj);
441 free(obj);
442 }
443 return;
444 }
445
446 /**
447 * On arena wall, the wall needs to pulse. Handle it here
448 */
449 if (scene->id == SCENE_ARENA4
450 && on_air
451 && (h->state == STATE_FALLEN || h->state == STATE_RECOIL)
452 && towards_wall
453 && !h->is_grabbed
454 && took_enough_damage)
455 {
456 DEBUG("hit desert wall %d", wall);
457 h->state = STATE_WALLDAMAGE;
458
459 // desert always shows the 'hit' animation when you touch the wall
460 bk_info *info = bk_get_info(&scene->bk_data, 20+wall);
461 object *obj = malloc(sizeof(object));
462 object_create(obj, scene->gs, info->ani.start_pos, vec2f_create(0,0));
463 object_set_stl(obj, scene->bk_data.sound_translation_table);
464 object_set_animation(obj, &info->ani);
465 object_set_custom_string(obj, "brwA1-brwB1-brwD1-brwE0-brwD4-brwC2-brwB2-brwA2");
466 if(game_state_add_object(scene->gs, obj, RENDER_LAYER_BOTTOM, 1, 0) != 0) {
467 object_free(obj);
468 free(obj);
469 }
470 }
471
472 /**
473 * On all other arenas, the HAR needs to hit the wall with dust flying around
474 */
475 if(scene->id != SCENE_ARENA4
476 && scene->id != SCENE_ARENA2
477 && on_air
478 && towards_wall
479 && (h->state == STATE_FALLEN || h->state == STATE_RECOIL)
480 && !h->is_grabbed
481 && took_enough_damage)
482 {
483 DEBUG("hit dusty wall %d", wall);
484 h->state = STATE_WALLDAMAGE;
485
486 int amount = rand_int(2) + 3;
487 for(int i = 0; i < amount; i++) {
488 int variance = rand_int(20) - 10;
489 int anim_no = rand_int(2) + 24;
490 DEBUG("XXX anim = %d, variance = %d", anim_no, variance);
491 int pos_y = o_har->pos.y - object_get_size(o_har).y + variance + i*25;
492 vec2i coord = vec2i_create(o_har->pos.x, pos_y);
493 object *dust = malloc(sizeof(object));
494 object_create(dust, scene->gs, coord, vec2f_create(0,0));
495 object_set_stl(dust, scene->bk_data.sound_translation_table);
496 object_set_animation(dust, &bk_get_info(&scene->bk_data, anim_no)->ani);
497 game_state_add_object(scene->gs, dust, RENDER_LAYER_MIDDLE, 0, 0);
498 }
499
500 // Wallhit sound
501 float d = ((float)o_har->pos.x) / 640.0f;
502 float pos_pan = d - 0.25f;
503 sound_play(68, 1.0f, pos_pan, 2.0f);
504 }
505
506 /**
507 * Handle generic collision stuff
508 */
509 if(on_air
510 && towards_wall
511 && !h->is_grabbed
512 && took_enough_damage
513 && (h->state == STATE_FALLEN
514 || h->state == STATE_RECOIL
515 || h->state == STATE_WALLDAMAGE))
516 {
517 // Set hit animation
518 object_set_animation(o_har, &af_get_move(h->af_data, ANIM_DAMAGE)->ani);
519 object_set_repeat(o_har, 0);
520 scene->gs->screen_shake_horizontal = 3*fabsf(o_har->vel.x);
521 // from MASTER.DAT
522 if(wall == 1) {
523 object_set_custom_string(o_har, "hQ10-x-3Q5-x-2L5-x-2M900");
524 o_har->vel.x = -2;
525 } else {
526 object_set_custom_string(o_har, "hQ10-x3Q5-x2L5-x2M900");
527 o_har->vel.x = 2;
528 }
529
530 object_dynamic_tick(o_har);
531
532 if(wall == 1) {
533 o_har->pos.x = ARENA_RIGHT_WALL - 2;
534 object_set_direction(o_har, OBJECT_FACE_RIGHT);
535 } else {
536 o_har->pos.x = ARENA_LEFT_WALL + 2;
537 object_set_direction(o_har, OBJECT_FACE_LEFT);
538 }
539 }
540 }
541
arena_har_defeat_hook(int player_id,scene * scene)542 void arena_har_defeat_hook(int player_id, scene *scene) {
543 game_state *gs = scene->gs;
544 arena_local *local = scene_get_userdata(scene);
545 int other_player_id = abs(player_id - 1);
546 game_player *player_winner = game_state_get_player(scene->gs, other_player_id);
547 game_player *player_loser = game_state_get_player(scene->gs, player_id);
548 object *winner = game_player_get_har(player_winner);
549 object *loser = game_player_get_har(player_loser);
550 har *winner_har = object_get_userdata(winner);
551 // XXX need a smarter way to detect if a player is networked or local
552 if(player_winner->ctrl->type != CTRL_TYPE_NETWORK &&
553 player_loser->ctrl->type == CTRL_TYPE_NETWORK) {
554 scene_youwin_anim_start(scene->gs);
555 } else if(player_winner->ctrl->type == CTRL_TYPE_NETWORK &&
556 player_loser->ctrl->type != CTRL_TYPE_NETWORK) {
557 scene_youlose_anim_start(scene->gs);
558 } else {
559 if (!is_singleplayer(scene)) {
560 // XXX in two player mode, "you win" should always be displayed
561 scene_youwin_anim_start(scene->gs);
562 } else {
563 if (player_id == 1) {
564 scene_youwin_anim_start(scene->gs);
565 } else {
566 scene_youlose_anim_start(scene->gs);
567 }
568 }
569 }
570 chr_score *score = game_player_get_score(game_state_get_player(gs, other_player_id));
571 object_select_sprite(local->player_rounds[other_player_id][score->rounds], 0);
572 score->rounds++;
573 if (score->rounds >= ceil(local->rounds/2.0f)) {
574 har_set_ani(winner, ANIM_VICTORY, 0);
575 chr_score_victory(score, winner_har->health);
576 winner_har->state = STATE_VICTORY;
577 local->over = 1;
578 if (is_singleplayer(scene)) {
579 player_winner->sp_wins |= 2 << player_loser->pilot_id;
580 if (player_loser->pilot_id == 10) {
581 // can't scrap/destruct kreissack
582 winner_har->state = STATE_DONE;
583 // major go boom
584 har_set_ani(loser, 47, 1);
585 }
586 }
587 } else {
588 har_set_ani(winner, ANIM_VICTORY, 0);
589 // can't do scrap/destruct except on final round
590 winner_har->state = STATE_DONE;
591 }
592 winner_har->executing_move = 1;
593 object_set_vel(loser, vec2f_create(0, 0));
594 object_set_vel(winner, vec2f_create(0, 0));
595 //object_set_gravity(loser, 0);
596 arena_maybe_sync(scene,
597 chr_score_interrupt(score, object_get_pos(winner)));
598 }
599
arena_maybe_turn_har(int player_id,scene * scene)600 void arena_maybe_turn_har(int player_id, scene* scene) {
601 int other_player_id = abs(player_id - 1);
602 object *obj_har1 = game_player_get_har(game_state_get_player(scene->gs, player_id));
603 object *obj_har2 = game_player_get_har(game_state_get_player(scene->gs, other_player_id));
604 if (obj_har1->pos.x > obj_har2->pos.x) {
605 object_set_direction(obj_har1, OBJECT_FACE_LEFT);
606 } else {
607 object_set_direction(obj_har1, OBJECT_FACE_RIGHT);
608 }
609
610 // there isn;t an idle event hook, so do the best we can...
611 har *har2 = obj_har2->userdata;
612 if ((har2->state == STATE_STANDING || har_is_crouching(har2) || har_is_walking(har2)) && !har2->executing_move) {
613 object_set_direction(obj_har2, object_get_direction(obj_har1) * -1);
614 }
615 }
616
arena_har_hook(har_event event,void * data)617 void arena_har_hook(har_event event, void *data) {
618 scene *scene = data;
619 int other_player_id = abs(event.player_id - 1);
620 arena_local *arena = scene_get_userdata(scene);
621 chr_score *score = game_player_get_score(game_state_get_player(scene->gs, event.player_id));
622 object *obj_har1 = game_player_get_har(game_state_get_player(scene->gs, event.player_id));
623 object *obj_har2 = game_player_get_har(game_state_get_player(scene->gs, other_player_id));
624 har *har1 = obj_har1->userdata;
625 har *har2 = obj_har2->userdata;
626 switch (event.type) {
627 case HAR_EVENT_WALK:
628 arena_maybe_turn_har(event.player_id, scene);
629 break;
630 case HAR_EVENT_AIR_TURN:
631 arena_maybe_turn_har(event.player_id, scene);
632 break;
633 case HAR_EVENT_TAKE_HIT:
634 if(af_get_move(har2->af_data, obj_har2->cur_animation->id)->category != CAT_CLOSE) {
635 arena_maybe_turn_har(event.player_id, scene);
636 }
637 arena_har_take_hit_hook(event.player_id, event.move, scene);
638 break;
639 case HAR_EVENT_HIT_WALL:
640 arena_har_hit_wall_hook(event.player_id, event.wall, scene);
641 break;
642 case HAR_EVENT_ATTACK:
643 if(object_is_airborne(obj_har1)) {
644 har1->air_attacked = 1;
645 DEBUG("AIR ATTACK %u", event.player_id);
646 } else {
647 // XXX this breaks the backwards razor spin and anything else using the 'ar' tag, so lets disable it for now
648 //arena_maybe_turn_har(event.player_id, scene);
649 }
650 break;
651 case HAR_EVENT_LAND:
652 if (har2->state == STATE_STANDING || har_is_crouching(har2) || har_is_walking(har2) || har2->executing_move) {
653 // if the other HAR is jumping or recoiling, don't flip the direction. This specifically is to fix jaguar ending up facing backwards after an overhead throw.
654 arena_maybe_turn_har(event.player_id, scene);
655 }
656 arena_maybe_sync(scene, 1);
657 DEBUG("LAND %u", event.player_id);
658 break;
659 case HAR_EVENT_AIR_ATTACK_DONE:
660 har1->air_attacked = 0;
661 arena_maybe_sync(scene, 1);
662 DEBUG("AIR_ATTACK_DONE %u", event.player_id);
663 break;
664 case HAR_EVENT_RECOVER:
665 arena_har_recover_hook(event.player_id, scene);
666 if(!object_is_airborne(obj_har1)) {
667 arena_maybe_turn_har(event.player_id, scene);
668 DEBUG("RECOVER %u", event.player_id);
669 }
670 break;
671 case HAR_EVENT_DEFEAT:
672 arena_har_defeat_hook(event.player_id, scene);
673 if (arena->state != ARENA_STATE_ENDING) {
674 arena->ending_ticks = 0;
675 arena->state = ARENA_STATE_ENDING;
676 }
677 break;
678 case HAR_EVENT_SCRAP:
679 chr_score_scrap(score);
680 break;
681 case HAR_EVENT_DESTRUCTION:
682 chr_score_destruction(score);
683 DEBUG("DESTRUCTION!");
684 break;
685 case HAR_EVENT_DONE:
686 chr_score_done(score);
687 DEBUG("DONE!");
688 break;
689 }
690 }
691
maybe_install_har_hooks(scene * scene)692 void maybe_install_har_hooks(scene *scene) {
693 object *obj_har1,*obj_har2;
694 obj_har1 = game_player_get_har(game_state_get_player(scene->gs, 0));
695 obj_har2 = game_player_get_har(game_state_get_player(scene->gs, 1));
696 har *har1, *har2;
697 har1 = obj_har1->userdata;
698 har2 = obj_har2->userdata;
699
700 if (scene->gs->role == ROLE_CLIENT) {
701 game_player *_player[2];
702 for(int i = 0; i < 2; i++) {
703 _player[i] = game_state_get_player(scene->gs, i);
704 }
705 if(game_player_get_ctrl(_player[0])->type == CTRL_TYPE_NETWORK) {
706 har_install_action_hook(har2, &net_controller_har_hook, _player[0]->ctrl);
707 }
708 if(game_player_get_ctrl(_player[1])->type == CTRL_TYPE_NETWORK) {
709 har_install_action_hook(har1, &net_controller_har_hook, _player[1]->ctrl);
710 }
711 }
712
713 har_install_hook(har1, &arena_har_hook, scene);
714 har_install_hook(har2, &arena_har_hook, scene);
715 }
716
717
718 // -------- Scene callbacks --------
719
arena_free(scene * scene)720 void arena_free(scene *scene) {
721 arena_local *local = scene_get_userdata(scene);
722
723 game_state_set_paused(scene->gs, 0);
724
725 if (local->rec) {
726 write_rec_move(scene, game_state_get_player(scene->gs, 0), ACT_STOP);
727 sd_rec_save(local->rec, scene->gs->init_flags->rec_file);
728 sd_rec_free(local->rec);
729 free(local->rec);
730 }
731
732 for(int i = 0; i < 2; i++) {
733 game_player *player = game_state_get_player(scene->gs, i);
734 game_player_set_har(player, NULL);
735 //game_player_set_ctrl(player, NULL);
736 controller_set_repeat(game_player_get_ctrl(player), 0);
737
738 for (int j = 0; j < 4; j++) {
739 if (j < ceil(local->rounds / 2.0f)) {
740 free(local->player_rounds[i][j]);
741 }
742 }
743 }
744
745 guiframe_free(local->game_menu);
746 surface_free(&local->sur);
747
748 music_stop();
749
750 // Free bar components
751 for(int i = 0; i < 2; i++) {
752 component_free(local->health_bars[i]);
753 component_free(local->endurance_bars[i]);
754 }
755
756 settings_save();
757
758 free(local);
759 }
760
write_rec_move(scene * scene,game_player * player,int action)761 void write_rec_move(scene *scene, game_player *player, int action) {
762 arena_local *local = scene_get_userdata(scene);
763 sd_rec_move move;
764 if (!local->rec) {
765 return;
766 }
767
768 move.tick = scene->gs->tick;
769 move.lookup_id = 2;
770 move.player_id = 0;
771 move.action = 0;
772
773 if (player == game_state_get_player(scene->gs, 1)) {
774 move.player_id = 1;
775 }
776
777 if (action & ACT_PUNCH) {
778 move.action |= SD_ACT_PUNCH;
779 }
780
781 if (action & ACT_KICK) {
782 move.action |= SD_ACT_KICK;
783 }
784
785 if (action & ACT_UP) {
786 move.action |= SD_ACT_UP;
787 }
788
789 if (action & ACT_DOWN) {
790 move.action |= SD_ACT_DOWN;
791 }
792
793 if (action & ACT_LEFT) {
794 move.action |= SD_ACT_LEFT;
795 }
796
797 if (action & ACT_RIGHT) {
798 move.action |= SD_ACT_RIGHT;
799 }
800
801 if (local->rec_last[move.player_id] == move.action) {
802 return;
803 }
804 local->rec_last[move.player_id] = move.action;
805
806 int ret;
807
808 if ((ret = sd_rec_insert_action(local->rec, local->rec->move_count, &move)) != SD_SUCCESS) {
809 DEBUG("recoding move failed %d", ret);
810 }
811 }
812
arena_handle_events(scene * scene,game_player * player,ctrl_event * i)813 int arena_handle_events(scene *scene, game_player *player, ctrl_event *i) {
814 int need_sync = 0;
815 arena_local *local = scene_get_userdata(scene);
816 if (i) {
817 do {
818 if(i->type == EVENT_TYPE_ACTION && i->event_data.action == ACT_ESC &&
819 player == game_state_get_player(scene->gs, 0)) {
820 // toggle menu
821 local->menu_visible = !local->menu_visible;
822 game_state_set_paused(scene->gs, local->menu_visible);
823 need_sync = 1;
824 controller_set_repeat(game_player_get_ctrl(player), !local->menu_visible);
825 controller_set_repeat(game_player_get_ctrl(game_state_get_player(scene->gs, 1)), !local->menu_visible);
826 DEBUG("local menu %d, controller repeat %d", local->menu_visible, game_player_get_ctrl(player)->repeat);
827 } else if(i->type == EVENT_TYPE_ACTION && local->menu_visible &&
828 (player->ctrl->type == CTRL_TYPE_KEYBOARD || player->ctrl->type == CTRL_TYPE_GAMEPAD) &&
829 i->event_data.action != ACT_ESC && /* take AST_ESC only from player 1 */
830 !is_demoplay(scene)
831 ) {
832 DEBUG("menu event %d", i->event_data.action);
833 // menu events
834 guiframe_action(local->game_menu, i->event_data.action);
835 } else if(i->type == EVENT_TYPE_ACTION) {
836 if (player->ctrl->type == CTRL_TYPE_NETWORK) {
837 do {
838 object_act(game_player_get_har(player), i->event_data.action);
839 write_rec_move(scene, player, i->event_data.action);
840 // Rewritten this way, we possible skipped some events before.
841 // We check if there is a next event, then check if it is EVENT_TYPE_ACTION
842 // and only then we move the event iterator.
843 // If conditions fail then we move to the next element at the end of the loop as usual.
844 // This change also simplified the loop condition, we now don't need to check i for NULL.
845 } while (i->next && i->next->type == EVENT_TYPE_ACTION && (i = i->next));
846 // always trigger a synchronization, since if the client's move did not actually happen, we want to rewind them ASAP
847 need_sync = 1;
848 } else {
849 need_sync += object_act(game_player_get_har(player), i->event_data.action);
850 write_rec_move(scene, player, i->event_data.action);
851 }
852 } else if (i->type == EVENT_TYPE_SYNC) {
853 DEBUG("sync");
854 game_state_unserialize(scene->gs, i->event_data.ser, player->ctrl->rtt);
855 maybe_install_har_hooks(scene);
856 } else if (i->type == EVENT_TYPE_CLOSE) {
857 if (player->ctrl->type == CTRL_TYPE_REC) {
858 game_state_set_next(scene->gs, SCENE_NONE);
859 } else {
860 game_state_set_next(scene->gs, SCENE_MENU);
861 }
862 return 0;
863 }
864 } while((i = i->next));
865 }
866 return need_sync;
867 }
868
arena_spawn_hazard(scene * scene)869 void arena_spawn_hazard(scene *scene) {
870 iterator it;
871 hashmap_iter_begin(&scene->bk_data.infos, &it);
872 hashmap_pair *pair = NULL;
873
874 if (is_netplay(scene) && scene->gs->role == ROLE_CLIENT) {
875 // only the server spawns hazards
876 return;
877 }
878
879 int changed = 0;
880
881 while((pair = iter_next(&it)) != NULL) {
882 bk_info *info = (bk_info*)pair->val;
883 if(info->probability > 1) {
884 if (rand_int(info->probability) == 1) {
885 // TODO don't spawn it if we already have this animation running
886 object *obj = malloc(sizeof(object));
887 object_create(obj, scene->gs, info->ani.start_pos, vec2f_create(0,0));
888 object_set_stl(obj, scene->bk_data.sound_translation_table);
889 object_set_animation(obj, &info->ani);
890 if (scene->id == SCENE_ARENA3 && info->ani.id == 0) {
891 // XXX fire pit orb has a bug whwre it double spawns. Use a custom animation string to avoid it
892 // it mioght be to do with the 'mp' tag, which we don't currently understand
893 object_set_custom_string(obj, "Z3-mx+160my+100m15mp10Z1-Z300");
894 }
895 /*object_set_spawn_cb(obj, cb_scene_spawn_object, (void*)scene);*/
896 /*object_set_destroy_cb(obj, cb_scene_destroy_object, (void*)scene);*/
897 hazard_create(obj, scene);
898 if (game_state_add_object(scene->gs, obj, RENDER_LAYER_BOTTOM, 1, 0) == 0) {
899 object_set_layers(obj, LAYER_HAZARD|LAYER_HAR);
900 object_set_group(obj, GROUP_PROJECTILE);
901 object_set_userdata(obj, &scene->bk_data);
902 if (info->ani.extra_string_count > 0) {
903 // For the desert, there's a bunch of extra animation strgins for
904 // the different plane formations.
905 // Pick one, rather than always use the first
906
907 int r = rand_int(info->ani.extra_string_count);
908 if (r > 0) {
909 str *s = vector_get(&info->ani.extra_strings, r);
910 object_set_custom_string(obj, str_c(s));
911 }
912 }
913
914 // XXX without this, the object does not unserialize correctly in netplay
915 object_dynamic_tick(obj);
916
917 DEBUG("Arena tick: Hazard with probability %d started.", info->probability, info->ani.id);
918 changed++;
919 } else {
920 object_free(obj);
921 free(obj);
922 }
923 }
924 }
925 }
926
927 arena_maybe_sync(scene, changed);
928 }
929
arena_dynamic_tick(scene * scene,int paused)930 void arena_dynamic_tick(scene *scene, int paused) {
931 arena_local *local = scene_get_userdata(scene);
932 game_state *gs = scene->gs;
933 game_player *player1 = game_state_get_player(gs, 0);
934 game_player *player2 = game_state_get_player(gs, 1);
935
936 if(!paused) {
937 object *obj_har[2];
938 har *hars[2];
939 for(int i = 0; i < 2; i++) {
940 obj_har[i] = game_player_get_har(game_state_get_player(scene->gs, i));
941 hars[i] = obj_har[i]->userdata;
942 }
943
944 // Handle scrolling score texts
945 chr_score_tick(game_player_get_score(game_state_get_player(scene->gs, 0)));
946 chr_score_tick(game_player_get_score(game_state_get_player(scene->gs, 1)));
947
948 // Set and tick all proggressbars
949 for(int i = 0; i < 2; i++) {
950 float hp = (float)hars[i]->health / (float)hars[i]->health_max;
951 float en = (float)hars[i]->endurance / (float)hars[i]->endurance_max;
952 progressbar_set_progress(local->health_bars[i], hp * 100);
953 progressbar_set_progress(local->endurance_bars[i], en * 100);
954 progressbar_set_flashing(local->endurance_bars[i], (en * 100 < 50), 8);
955 component_tick(local->health_bars[i]);
956 component_tick(local->endurance_bars[i]);
957 }
958
959 // RTT stuff
960 hars[0]->delay = ceil(player2->ctrl->rtt / 2.0f);
961 hars[1]->delay = ceil(player1->ctrl->rtt / 2.0f);
962
963 // Endings and beginnings
964 if(local->state != ARENA_STATE_ENDING && local->state != ARENA_STATE_STARTING) {
965 settings *setting = settings_get();
966 if (setting->gameplay.hazards_on) {
967 arena_spawn_hazard(scene);
968 }
969 }
970 if(local->state == ARENA_STATE_ENDING) {
971 chr_score *s1 = game_player_get_score(game_state_get_player(scene->gs, 0));
972 chr_score *s2 = game_player_get_score(game_state_get_player(scene->gs, 1));
973 if (player_frame_isset(obj_har[0], "be")
974 || player_frame_isset(obj_har[1], "be")
975 || chr_score_onscreen(s1)
976 || chr_score_onscreen(s2)) {
977 } else {
978 local->ending_ticks++;
979 }
980 if(local->ending_ticks == 18) {
981 arena_screengrab_winner(scene);
982 }
983 if(local->ending_ticks > 20) {
984 if (!local->over) {
985 arena_reset(scene);
986 } else {
987 arena_end(scene);
988 }
989 }
990 }
991
992 // Pour some rein!
993 if(local->rein_enabled) {
994 if(rand_float() > 0.65f) {
995 vec2i pos = vec2i_create(rand_int(NATIVE_W), -10);
996 for(int harnum = 0;harnum < game_state_num_players(gs);harnum++) {
997 object *h_obj = game_state_get_player(gs, harnum)->har;
998 har *h = object_get_userdata(h_obj);
999 // Calculate velocity etc.
1000 float rv = rand_float() - 0.5f;
1001 float velx = rv;
1002 float vely = -12 * sin(0 / 2 + rv);
1003
1004 // Make sure scrap has somekind of velocity
1005 // (to prevent floating scrap objects)
1006 if(vely < 0.1 && vely > -0.1) vely += 0.21;
1007
1008 // Create the object
1009 object *scrap = malloc(sizeof(object));
1010 int anim_no = rand_int(3) + ANIM_SCRAP_METAL;
1011 object_create(scrap, gs, pos, vec2f_create(velx, vely));
1012 object_set_animation(scrap, &af_get_move(h->af_data, anim_no)->ani);
1013 object_set_gravity(scrap, 0.4f);
1014 object_set_pal_offset(scrap, object_get_pal_offset(h_obj));
1015 object_set_layers(scrap, LAYER_SCRAP);
1016 object_set_shadow(scrap, 1);
1017 object_dynamic_tick(scrap);
1018 scrap_create(scrap);
1019 game_state_add_object(gs, scrap, RENDER_LAYER_TOP, 0, 0);
1020 }
1021 }
1022 }
1023 } // if(!paused)
1024
1025 int need_sync = 0;
1026 // allow enemy HARs to move during a network game
1027 need_sync += arena_handle_events(scene, player1, player1->ctrl->extra_events);
1028 need_sync += arena_handle_events(scene, player2, player2->ctrl->extra_events);
1029 arena_maybe_sync(scene, need_sync);
1030 }
1031
arena_static_tick(scene * scene,int paused)1032 void arena_static_tick(scene *scene, int paused) {
1033 arena_local *local = scene_get_userdata(scene);
1034 guiframe_tick(local->game_menu);
1035 }
1036
arena_input_tick(scene * scene)1037 void arena_input_tick(scene *scene) {
1038 game_player *player1 = game_state_get_player(scene->gs, 0);
1039 game_player *player2 = game_state_get_player(scene->gs, 1);
1040
1041 ctrl_event *p1 = NULL, *p2 = NULL;
1042 controller_poll(player1->ctrl, &p1);
1043 controller_poll(player2->ctrl, &p2);
1044
1045 int need_sync = 0;
1046 need_sync += arena_handle_events(scene, player1, p1);
1047 need_sync += arena_handle_events(scene, player2, p2);
1048 controller_free_chain(p1);
1049 controller_free_chain(p2);
1050 arena_maybe_sync(scene, need_sync);
1051 }
1052
arena_event(scene * scene,SDL_Event * e)1053 int arena_event(scene *scene, SDL_Event *e) {
1054 // ESC during demo mode jumps you back to the main menu
1055 if (e->type == SDL_KEYDOWN && is_demoplay(scene) && e->key.keysym.sym == SDLK_ESCAPE) {
1056 game_state_set_next(scene->gs, SCENE_MENU);
1057 }
1058 return 0;
1059 }
1060
arena_render_overlay(scene * scene)1061 void arena_render_overlay(scene *scene) {
1062 arena_local *local = scene_get_userdata(scene);
1063
1064 // Render bars
1065 game_player *player[2];
1066 object *obj[2];
1067
1068 char buf[40];
1069 #ifdef DEBUGMODE
1070 sprintf(buf, "%u", game_state_get_tick(scene->gs));
1071 font_render(&font_small, buf, 160, 0, TEXT_COLOR);
1072 sprintf(buf, "%u", rand_get_seed());
1073 font_render(&font_small, buf, 130, 8, TEXT_COLOR);
1074 #endif
1075 for(int i = 0; i < 2; i++) {
1076 player[i] = game_state_get_player(scene->gs, i);
1077 obj[i] = game_player_get_har(player[i]);
1078 }
1079 if(obj[0] != NULL && obj[1] != NULL) {
1080 // Render progress bar components
1081 for(int i = 0; i < 2; i++) {
1082 component_render(local->health_bars[i]);
1083 component_render(local->endurance_bars[i]);
1084 }
1085
1086 // Render HAR and pilot names
1087 font_render_shadowed(&font_small,
1088 lang_get(player[0]->pilot_id+20),
1089 5, 19,
1090 TEXT_COLOR,
1091 TEXT_SHADOW_RIGHT|TEXT_SHADOW_BOTTOM);
1092 font_render_shadowed(&font_small,
1093 lang_get((player[0]->har_id)+31),
1094 5, 26,
1095 TEXT_COLOR,
1096 TEXT_SHADOW_RIGHT|TEXT_SHADOW_BOTTOM);
1097
1098 int p2len = (strlen(lang_get(player[1]->pilot_id+20))-1) * font_small.w;
1099 int h2len = (strlen(lang_get((player[1]->har_id)+31))-1) * font_small.w;
1100 font_render_shadowed(&font_small,
1101 lang_get(player[1]->pilot_id+20),
1102 315-p2len, 19,
1103 TEXT_COLOR,
1104 TEXT_SHADOW_RIGHT|TEXT_SHADOW_BOTTOM);
1105 font_render_shadowed(&font_small,
1106 lang_get((player[1]->har_id)+31),
1107 315-h2len, 26,
1108 TEXT_COLOR,
1109 TEXT_SHADOW_RIGHT|TEXT_SHADOW_BOTTOM);
1110
1111 // Render score stuff
1112 chr_score_render(game_player_get_score(player[0]));
1113
1114 // Do not render player 2 score in 1 player mode
1115 if(game_player_get_selectable(player[1])) {
1116 chr_score_render(game_player_get_score(player[1]));
1117 }
1118
1119 // render ping, if player is networked
1120 if (player[0]->ctrl->type == CTRL_TYPE_NETWORK) {
1121 sprintf(buf, "ping %u", player[0]->ctrl->rtt);
1122 font_render(&font_small, buf, 5, 40, TEXT_COLOR);
1123 }
1124 if (player[1]->ctrl->type == CTRL_TYPE_NETWORK) {
1125 sprintf(buf, "ping %u", player[1]->ctrl->rtt);
1126 font_render(&font_small, buf, 315-(strlen(buf)*font_small.w), 40, TEXT_COLOR);
1127 }
1128
1129 for (int i = 0; i < 2; i++) {
1130 for (int j = 0; j < 4; j++) {
1131 if (local->player_rounds[i][j]) {
1132 object_render(local->player_rounds[i][j]);
1133 }
1134 }
1135 }
1136 }
1137
1138 // Render menu (if visible)
1139 if(local->menu_visible) {
1140 guiframe_render(local->game_menu);
1141 video_render_sprite(&local->sur, 10, 150, BLEND_ALPHA, 0);
1142 }
1143 }
1144
arena_get_state(scene * scene)1145 int arena_get_state(scene *scene) {
1146 arena_local *local = scene_get_userdata(scene);
1147 return local->state;
1148 }
1149
arena_set_state(scene * scene,int state)1150 void arena_set_state(scene *scene, int state) {
1151 arena_local *local = scene_get_userdata(scene);
1152 local->state = state;
1153 }
1154
arena_toggle_rein(scene * scene)1155 void arena_toggle_rein(scene *scene) {
1156 arena_local *local = scene_get_userdata(scene);
1157 local->rein_enabled = !local->rein_enabled;
1158 }
1159
arena_startup(scene * scene,int id,int * m_load,int * m_repeat)1160 void arena_startup(scene *scene, int id, int *m_load, int *m_repeat) {
1161 if(scene->bk_data.file_id == 64) {
1162 // Start up & repeat torches on arena startup
1163 switch(id) {
1164 case 1:
1165 case 2:
1166 case 3:
1167 case 4:
1168 *m_load = 1;
1169 *m_repeat = 1;
1170 return;
1171 }
1172 }
1173 }
1174
arena_create(scene * scene)1175 int arena_create(scene *scene) {
1176 settings *setting;
1177 arena_local *local;
1178
1179 // Load up settings
1180 setting = settings_get();
1181
1182 // Initialize Demo
1183 if(is_demoplay(scene)) {
1184 game_state_init_demo(scene->gs);
1185 }
1186
1187 // Handle music playback
1188 switch(scene->bk_data.file_id) {
1189 case 8: music_play(PSM_ARENA0); break;
1190 case 16: music_play(PSM_ARENA1); break;
1191 case 32: music_play(PSM_ARENA2); break;
1192 case 64: music_play(PSM_ARENA3); break;
1193 case 128: music_play(PSM_ARENA4); break;
1194 }
1195
1196 // Initialize local struct
1197 local = malloc(sizeof(arena_local));
1198 scene_set_userdata(scene, local);
1199
1200 // Set correct state
1201 local->state = ARENA_STATE_STARTING;
1202 local->ending_ticks = 0;
1203 local->rein_enabled = 0;
1204
1205 local->round = 0;
1206 switch (setting->gameplay.rounds) {
1207 case 0:
1208 local->rounds = 1;
1209 break;
1210 case 1:
1211 local->rounds = 3;
1212 break;
1213 case 2:
1214 local->rounds = 5;
1215 break;
1216 case 3:
1217 local->rounds = 7;
1218 break;
1219 default:
1220 local->rounds = 1;
1221 break;
1222 }
1223 local->over = 0;
1224
1225 // Initial har data
1226 vec2i pos[2];
1227 int dir[2] = {OBJECT_FACE_RIGHT, OBJECT_FACE_LEFT};
1228 pos[0] = vec2i_create(HAR1_START_POS, ARENA_FLOOR);
1229 pos[1] = vec2i_create(HAR2_START_POS, ARENA_FLOOR);
1230
1231 // init HARs
1232 for(int i = 0; i < 2; i++) {
1233 // Declare some vars
1234 game_player *player = game_state_get_player(scene->gs, i);
1235 object *obj = malloc(sizeof(object));
1236
1237 // load the player's colors into the palette
1238 palette *base_pal = video_get_base_palette();
1239 palette_set_player_color(base_pal, i, player->colors[2], 0);
1240 palette_set_player_color(base_pal, i, player->colors[1], 1);
1241 palette_set_player_color(base_pal, i, player->colors[0], 2);
1242 video_force_pal_refresh();
1243
1244 // Create object and specialize it as HAR.
1245 // Errors are unlikely here, but check anyway.
1246
1247 if (scene_load_har(scene, i, player->har_id)) {
1248 free(obj);
1249 return 1;
1250 }
1251
1252 object_create(obj, scene->gs, pos[i], vec2f_create(0,0));
1253 if(har_create(obj, scene->af_data[i], dir[i], player->har_id, player->pilot_id, i)) {
1254 return 1;
1255 }
1256
1257 // Set HAR to controller and game_player
1258 game_state_add_object(scene->gs, obj, RENDER_LAYER_MIDDLE, 0, 0);
1259
1260 // Set HAR for player
1261 game_player_set_har(player, obj);
1262 game_player_get_ctrl(player)->har = obj;
1263
1264 // Create round tokens
1265 for (int j = 0; j < 4; j++) {
1266 if (j < ceil(local->rounds / 2.0f)) {
1267 local->player_rounds[i][j] = malloc(sizeof(object));
1268 int xoff = 110 + 9 * j + 3 + j;
1269 if (i == 1) {
1270 xoff = 210 - 9 * j - 3 - j;
1271 }
1272 animation *ani = &bk_get_info(&scene->bk_data, 27)->ani;
1273 object_create(local->player_rounds[i][j], scene->gs, vec2i_create(xoff ,9), vec2f_create(0, 0));
1274 object_set_animation(local->player_rounds[i][j], ani);
1275 object_select_sprite(local->player_rounds[i][j], 1);
1276 } else {
1277 local->player_rounds[i][j] = NULL;
1278 }
1279 }
1280 }
1281
1282 // remove the keyboard hooks
1283
1284 game_player *_player[2];
1285 for(int i = 0; i < 2; i++) {
1286 _player[i] = game_state_get_player(scene->gs, i);
1287 }
1288 if(game_player_get_ctrl(_player[0])->type == CTRL_TYPE_NETWORK) {
1289 controller_clear_hooks(game_player_get_ctrl(_player[1]));
1290 }
1291 if(game_player_get_ctrl(_player[1])->type == CTRL_TYPE_NETWORK) {
1292 controller_clear_hooks(game_player_get_ctrl(_player[0]));
1293 }
1294
1295 controller_set_repeat(game_player_get_ctrl(_player[0]), 1);
1296 controller_set_repeat(game_player_get_ctrl(_player[1]), 1);
1297
1298 game_player_get_har(_player[0])->animation_state.enemy = game_player_get_har(_player[1]);
1299 game_player_get_har(_player[1])->animation_state.enemy = game_player_get_har(_player[0]);
1300
1301 maybe_install_har_hooks(scene);
1302
1303 // Arena menu text settings
1304 text_settings tconf;
1305 text_defaults(&tconf);
1306 tconf.font = FONT_BIG;
1307 tconf.cforeground = color_create(0, 121, 0, 255);
1308 tconf.halign = TEXT_CENTER;
1309
1310 // Arena menu
1311 local->menu_visible = 0;
1312 local->game_menu = guiframe_create(60, 5, 181, 117);
1313 component *menu = menu_create(11);
1314 menu_attach(menu, label_create(&tconf, "OPENOMF"));
1315 menu_attach(menu, filler_create());
1316 menu_attach(menu, filler_create());
1317 component *return_button = textbutton_create(&tconf, "RETURN TO GAME", COM_ENABLED, game_menu_return, scene);
1318 menu_attach(menu, return_button);
1319
1320 menu_attach(menu, textslider_create_bind(&tconf, "SOUND", 10, 1, arena_sound_slide, NULL, &setting->sound.sound_vol));
1321 menu_attach(menu, textslider_create_bind(&tconf, "MUSIC", 10, 1, arena_music_slide, NULL, &setting->sound.music_vol));
1322
1323 component *speed_slider = textslider_create_bind(&tconf, "SPEED", 10, 0, arena_speed_slide, scene, &setting->gameplay.speed);
1324 if(is_netplay(scene)) {
1325 component_disable(speed_slider, 1);
1326 }
1327 menu_attach(menu, speed_slider);
1328
1329 menu_attach(menu, textbutton_create(&tconf, "VIDEO OPTIONS", COM_DISABLED, NULL, NULL));
1330 menu_attach(menu, textbutton_create(&tconf, "HELP", COM_DISABLED, NULL, NULL));
1331 menu_attach(menu, textbutton_create(&tconf, "QUIT", COM_ENABLED, game_menu_quit, scene));
1332
1333 guiframe_set_root(local->game_menu, menu);
1334 guiframe_layout(local->game_menu);
1335 menu_select(menu, return_button);
1336
1337 // background for the 'help' at the bottom of the screen
1338 // TODO support rendering text onto it
1339 menu_background_create(&local->sur, 301, 37);
1340
1341 // Health and endurance bars
1342 local->health_bars[0] = progressbar_create(PROGRESSBAR_THEME_HEALTH, PROGRESSBAR_RIGHT, 100);
1343 component_layout(local->health_bars[0], 5, 5, 100, 8);
1344 local->health_bars[1] = progressbar_create(PROGRESSBAR_THEME_HEALTH, PROGRESSBAR_LEFT, 100);
1345 component_layout(local->health_bars[1], 215, 5, 100, 8);
1346 local->endurance_bars[0] = progressbar_create(PROGRESSBAR_THEME_ENDURANCE, PROGRESSBAR_RIGHT, 100);
1347 component_layout(local->endurance_bars[0], 5, 14, 100, 4);
1348 local->endurance_bars[1] = progressbar_create(PROGRESSBAR_THEME_ENDURANCE, PROGRESSBAR_LEFT, 100);
1349 component_layout(local->endurance_bars[1], 215, 14, 100, 4);
1350
1351 // Score positioning
1352 chr_score_set_pos(game_player_get_score(_player[0]), 5, 33, OBJECT_FACE_RIGHT);
1353 chr_score_set_pos(game_player_get_score(_player[1]), 315, 33, OBJECT_FACE_LEFT); // TODO: Set better coordinates for this
1354
1355 // Reset the score
1356 chr_score_reset(game_player_get_score(_player[0]), !is_singleplayer(scene));
1357 chr_score_reset(game_player_get_score(_player[1]), 1);
1358
1359 // Reset the win counter in single player mode
1360 if(is_singleplayer(scene)) {
1361 chr_score_reset_wins(game_player_get_score(_player[0]));
1362 chr_score_reset_wins(game_player_get_score(_player[1]));
1363 }
1364
1365 // Reset screencaps
1366 har_screencaps_reset(&_player[0]->screencaps);
1367 har_screencaps_reset(&_player[1]->screencaps);
1368
1369 // Set correct sounds for ready, round and number STL fields
1370 scene->bk_data.sound_translation_table[14] = 10; // READY
1371 scene->bk_data.sound_translation_table[15] = 16; // ROUND
1372 scene->bk_data.sound_translation_table[3] = 23 + local->round; // NUMBER
1373
1374 // Disable the floating ball disappearence sound in fire arena
1375 if(scene->id == SCENE_ARENA3) {
1376 scene->bk_data.sound_translation_table[20] = 0;
1377 }
1378
1379 if (local->rounds == 1) {
1380 // Start READY animation
1381 animation *ready_ani = &bk_get_info(&scene->bk_data, 11)->ani;
1382 object *ready = malloc(sizeof(object));
1383 object_create(ready, scene->gs, ready_ani->start_pos, vec2f_create(0,0));
1384 object_set_stl(ready, scene->bk_data.sound_translation_table);
1385 object_set_animation(ready, ready_ani);
1386 object_set_finish_cb(ready, scene_ready_anim_done);
1387 game_state_add_object(scene->gs, ready, RENDER_LAYER_TOP, 0, 0);
1388 } else {
1389 // ROUND
1390 animation *round_ani = &bk_get_info(&scene->bk_data, 6)->ani;
1391 object *round = malloc(sizeof(object));
1392 object_create(round, scene->gs, round_ani->start_pos, vec2f_create(0,0));
1393 object_set_stl(round, scene->bk_data.sound_translation_table);
1394 object_set_animation(round, round_ani);
1395 object_set_finish_cb(round, scene_ready_anim_done);
1396 game_state_add_object(scene->gs, round, RENDER_LAYER_TOP, 0, 0);
1397
1398 // Number
1399 animation *number_ani = &bk_get_info(&scene->bk_data, 7)->ani;
1400 object *number = malloc(sizeof(object));
1401 object_create(number, scene->gs, number_ani->start_pos, vec2f_create(0,0));
1402 object_set_stl(number, scene->bk_data.sound_translation_table);
1403 object_set_animation(number, number_ani);
1404 object_select_sprite(number, local->round);
1405 game_state_add_object(scene->gs, number, RENDER_LAYER_TOP, 0, 0);
1406 }
1407
1408 // Callbacks
1409 scene_set_event_cb(scene, arena_event);
1410 scene_set_free_cb(scene, arena_free);
1411 scene_set_dynamic_tick_cb(scene, arena_dynamic_tick);
1412 scene_set_static_tick_cb(scene, arena_static_tick);
1413 scene_set_startup_cb(scene, arena_startup);
1414 scene_set_input_poll_cb(scene, arena_input_tick);
1415 scene_set_render_overlay_cb(scene, arena_render_overlay);
1416
1417 // Pick renderer
1418 video_select_renderer(VIDEO_RENDERER_HW);
1419
1420 // initalize recording, if enabled
1421 if (scene->gs->init_flags->record == 1) {
1422 local->rec = malloc(sizeof(sd_rec_file));
1423 sd_rec_create(local->rec);
1424 for(int i = 0; i < 2; i++) {
1425 // Declare some vars
1426 game_player *player = game_state_get_player(scene->gs, i);
1427 DEBUG("player %d using har %d", i, player->har_id);
1428 local->rec->pilots[i].info.har_id = (unsigned char)player->har_id;
1429 local->rec->pilots[i].info.pilot_id = player->pilot_id;
1430 local->rec->pilots[i].info.color_1 = player->colors[2];
1431 local->rec->pilots[i].info.color_2 = player->colors[1];
1432 local->rec->pilots[i].info.color_3 = player->colors[0];
1433 memcpy(local->rec->pilots[i].info.name, lang_get(player->pilot_id+20), 18);
1434 }
1435 } else{
1436 local->rec = NULL;
1437 }
1438
1439 // All done!
1440 return 0;
1441 }
1442