1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <SDL2/SDL.h>
4 #include "utils/log.h"
5 #include "utils/random.h"
6 #include "video/video.h"
7 #include "resources/ids.h"
8 #include "game/gui/text_render.h"
9 #include "resources/languages.h"
10 #include "game/protos/scene.h"
11 #include "game/scenes/vs.h"
12 #include "game/utils/settings.h"
13 #include "game/gui/menu_background.h"
14 #include "game/gui/dialog.h"
15 #include "game/game_state.h"
16 #include "controller/controller.h"
17 #include "controller/keyboard.h"
18
19 void cb_vs_spawn_object(object *parent, int id, vec2i pos, int g, void *userdata);
20 void cb_vs_destroy_object(object *parent, int id, void *userdata);
21
22 typedef struct vs_local_t {
23 object player1_portrait;
24 object player2_portrait;
25 object player1_har;
26 object player2_har;
27 surface arena_select_bg;
28 object arena_select;
29 int arena;
30 char vs_str[128];
31 dialog quit_dialog;
32 dialog too_pathetic_dialog;
33 } vs_local;
34
vs_is_netplay(scene * scene)35 int vs_is_netplay(scene *scene) {
36 if(game_state_get_player(scene->gs, 0)->ctrl->type == CTRL_TYPE_NETWORK ||
37 game_state_get_player(scene->gs, 1)->ctrl->type == CTRL_TYPE_NETWORK) {
38 return 1;
39 }
40 return 0;
41 }
42
vs_is_singleplayer(scene * scene)43 int vs_is_singleplayer(scene *scene) {
44 if(game_state_get_player(scene->gs, 1)->ctrl->type == CTRL_TYPE_AI) {
45 return 1;
46 }
47 return 0;
48 }
49
spawn_position(int index,int scientist)50 vec2i spawn_position(int index, int scientist) {
51 switch (index) {
52 case 0:
53 // top left gantry
54 if (scientist) {
55 return vec2i_create(90,80);
56 }
57 switch (rand_int(3)) {
58 case 0:
59 // middle
60 return vec2i_create(90,80);
61 case 1:
62 // left arm
63 return vec2i_create(30,80);
64 case 2:
65 // right arm
66 return vec2i_create(120,80);
67 }
68 break;
69 case 1:
70 // top right gantry
71 if (scientist) {
72 return vec2i_create(230,80);
73 }
74 switch (rand_int(3)) {
75 case 0:
76 // middle
77 return vec2i_create(230,80);
78 case 1:
79 // left arm
80 return vec2i_create(200,80);
81 case 2:
82 //right arm
83 return vec2i_create(260,80);
84 }
85 break;
86 case 2:
87 // middle left gantry
88 return vec2i_create(90, 118);
89 case 3:
90 // middle right gantry
91 return vec2i_create(230,118);
92 //return vec2i_create(280,118);
93 // only welder can use the following
94 case 4:
95 // bottom left gantry
96 return vec2i_create(90,150);
97 case 5:
98 // bottom right gantry
99 return vec2i_create(230,150);
100 default:
101 break;
102 }
103 // default
104 return vec2i_create(160, 200);
105 }
106
cb_vs_spawn_object(object * parent,int id,vec2i pos,int g,void * userdata)107 void cb_vs_spawn_object(object *parent, int id, vec2i pos, int g, void *userdata) {
108 scene *s = (scene*)userdata;
109
110 // Get next animation
111 bk_info *info = bk_get_info(&s->bk_data, id);
112 if(info != NULL) {
113 object *obj = malloc(sizeof(object));
114 object_create(obj, parent->gs, vec2i_add(pos, vec2f_to_i(parent->pos)), vec2f_create(0,0));
115 object_set_stl(obj, object_get_stl(parent));
116 object_set_animation(obj, &info->ani);
117 object_set_spawn_cb(obj, cb_vs_spawn_object, userdata);
118 object_set_destroy_cb(obj, cb_vs_destroy_object, userdata);
119 game_state_add_object(parent->gs, obj, RENDER_LAYER_MIDDLE, 0, 0);
120 }
121 }
122
cb_vs_destroy_object(object * parent,int id,void * userdata)123 void cb_vs_destroy_object(object *parent, int id, void *userdata) {
124 game_state_del_animation(parent->gs, id);
125 }
126
vs_free(scene * scene)127 void vs_free(scene *scene) {
128 vs_local *local = scene_get_userdata(scene);
129 game_player *player2 = game_state_get_player(scene->gs, 1);
130
131 dialog_free(&local->quit_dialog);
132 dialog_free(&local->too_pathetic_dialog);
133 surface_free(&local->arena_select_bg);
134 object_free(&local->player1_portrait);
135 object_free(&local->player2_portrait);
136 object_free(&local->player1_har);
137 object_free(&local->player2_har);
138 if (player2->selectable) {
139 object_free(&local->arena_select);
140 }
141 free(local);
142 }
143
vs_handle_action(scene * scene,int action)144 void vs_handle_action(scene *scene, int action) {
145 vs_local *local = scene_get_userdata(scene);
146 if(dialog_is_visible(&local->too_pathetic_dialog)) {
147 dialog_event(&local->too_pathetic_dialog, action);
148 } else if(dialog_is_visible(&local->quit_dialog)) {
149 dialog_event(&local->quit_dialog, action);
150 } else {
151 switch (action) {
152 case ACT_KICK:
153 case ACT_PUNCH:
154 game_state_set_next(scene->gs, SCENE_ARENA0+local->arena);
155 break;
156 case ACT_UP:
157 case ACT_LEFT:
158 if(game_state_get_player(scene->gs, 1)->selectable) {
159 local->arena--;
160 if (local->arena < 0) {
161 local->arena =4;
162 }
163 object_select_sprite(&local->arena_select, local->arena);
164 }
165 break;
166 case ACT_DOWN:
167 case ACT_RIGHT:
168 if(game_state_get_player(scene->gs, 1)->selectable) {
169 local->arena++;
170 if (local->arena > 4) {
171 local->arena = 0;
172 }
173 object_select_sprite(&local->arena_select, local->arena);
174 }
175 break;
176 }
177 }
178 }
179
vs_dynamic_tick(scene * scene,int paused)180 void vs_dynamic_tick(scene *scene, int paused) {
181 game_player *player1 = game_state_get_player(scene->gs, 0);
182 ctrl_event *i = NULL;
183 // Handle extra controller inputs
184 i = player1->ctrl->extra_events;
185 if (i) {
186 do {
187 if(i->type == EVENT_TYPE_ACTION) {
188 vs_handle_action(scene, i->event_data.action);
189 } else if (i->type == EVENT_TYPE_CLOSE) {
190 game_state_set_next(scene->gs, SCENE_MENU);
191 return;
192 }
193 } while((i = i->next));
194 }
195 }
196
vs_static_tick(scene * scene,int paused)197 void vs_static_tick(scene *scene, int paused) {
198 vs_local *local = scene->userdata;
199 if(dialog_is_visible(&local->too_pathetic_dialog)) {
200 dialog_tick(&local->too_pathetic_dialog);
201 } else if(dialog_is_visible(&local->quit_dialog)) {
202 dialog_tick(&local->quit_dialog);
203 }
204 }
205
vs_input_tick(scene * scene)206 void vs_input_tick(scene *scene) {
207 vs_local *local = scene->userdata;
208 ctrl_event *p1=NULL, *i;
209 game_player *player1 = game_state_get_player(scene->gs, 0);
210 controller_poll(player1->ctrl, &p1);
211 i = p1;
212 if (i) {
213 do {
214 if(i->type == EVENT_TYPE_ACTION) {
215 if (i->event_data.action == ACT_ESC) {
216 if(dialog_is_visible(&local->too_pathetic_dialog)) {
217 dialog_event(&local->too_pathetic_dialog, i->event_data.action);
218 } else if(dialog_is_visible(&local->quit_dialog)) {
219 dialog_event(&local->quit_dialog, i->event_data.action);
220 } else if(vs_is_singleplayer(scene) && player1->sp_wins != 0) {
221 // there's an active singleplayer campaign, confirm quitting
222 dialog_show(&local->quit_dialog, 1);
223 } else {
224 game_state_set_next(scene->gs, SCENE_MELEE);
225 }
226 } else {
227 vs_handle_action(scene, i->event_data.action);
228 }
229 } else if (i->type == EVENT_TYPE_CLOSE) {
230 game_state_set_next(scene->gs, SCENE_MENU);
231 }
232 } while((i = i->next));
233 }
234 controller_free_chain(p1);
235 }
236
vs_render(scene * scene)237 void vs_render(scene *scene) {
238 vs_local *local = scene_get_userdata(scene);
239
240 game_player *player1 = game_state_get_player(scene->gs, 0);
241 game_player *player2 = game_state_get_player(scene->gs, 1);
242
243 // player 1 HAR
244 object_render(&local->player1_har);
245
246 // player 2 HAR
247 object_render(&local->player2_har);
248
249 // player 1 portrait
250 object_render(&local->player1_portrait);
251
252 // player 2 portrait
253 object_render(&local->player2_portrait);
254
255
256 font_render_shadowed(&font_small, local->vs_str, 160-((strlen(local->vs_str)*font_small.w)/2), 3, COLOR_YELLOW, TEXT_SHADOW_RIGHT|TEXT_SHADOW_BOTTOM);
257
258
259 if (player2->selectable) {
260 // arena selection
261 video_render_sprite(&local->arena_select_bg, 55, 150, BLEND_ALPHA, 0);
262
263 object_render(&local->arena_select);
264
265 // arena name
266 font_render_wrapped(&font_small, lang_get(56+local->arena), 56+72, 153, (211-72)-4, COLOR_GREEN);
267
268 // arena description
269 font_render_wrapped(&font_small, lang_get(66+local->arena), 56+72, 160, (211-72)-4, COLOR_GREEN);
270 } else if (player2->pilot_id == PILOT_KREISSACK && settings_get()->gameplay.difficulty < 2) {
271 // kriessack, but not on Veteran or higher
272 font_render_wrapped(&font_small, lang_get(747), 59, 160, 200, COLOR_YELLOW);
273 } else {
274 font_render_wrapped(&font_small, lang_get(749+(11*player1->pilot_id)+player2->pilot_id), 59, 160, 150, COLOR_YELLOW);
275 font_render_wrapped(&font_small, lang_get(870+(11*player2->pilot_id)+player1->pilot_id), 320-(59+150), 180, 150, COLOR_YELLOW);
276 }
277 }
278
vs_render_overlay(scene * scene)279 void vs_render_overlay(scene *scene) {
280 vs_local *local = scene_get_userdata(scene);
281 if(dialog_is_visible(&local->quit_dialog)) {
282 dialog_render(&local->quit_dialog);
283 }
284
285 if(dialog_is_visible(&local->too_pathetic_dialog)) {
286 dialog_render(&local->too_pathetic_dialog);
287 }
288 }
289
vs_quit_dialog_clicked(dialog * dlg,dialog_result result)290 void vs_quit_dialog_clicked(dialog *dlg, dialog_result result){
291 scene *sc = dlg->userdata;
292 if(result == DIALOG_RESULT_YES_OK) {
293 game_state_set_next(sc->gs, SCENE_MELEE);
294 }
295 }
296
vs_too_pathetic_dialog_clicked(dialog * dlg,dialog_result result)297 void vs_too_pathetic_dialog_clicked(dialog *dlg, dialog_result result){
298 scene *sc = dlg->userdata;
299 game_state_set_next(sc->gs, SCENE_MENU);
300 }
301
vs_create(scene * scene)302 int vs_create(scene *scene) {
303 // Init local data
304 vs_local *local = malloc(sizeof(vs_local));
305 scene_set_userdata(scene, local);
306 game_player *player1 = game_state_get_player(scene->gs, 0);
307 game_player *player2 = game_state_get_player(scene->gs, 1);
308
309 const char *pilot1 = lang_get(20+player1->pilot_id);
310 const char *pilot2 = lang_get(20+player2->pilot_id);
311 snprintf(local->vs_str, 128, "%*.*s VS. %*.*s",
312 (int)strlen(pilot1)-1, (int)strlen(pilot1)-1, pilot1,
313 (int)strlen(pilot2)-1, (int)strlen(pilot2)-1, pilot2);
314
315 animation *ani;
316
317 palette *mpal = video_get_base_palette();
318 palette_set_player_color(mpal, 0, player1->colors[2], 0);
319 palette_set_player_color(mpal, 0, player1->colors[1], 1);
320 palette_set_player_color(mpal, 0, player1->colors[0], 2);
321 palette_set_player_color(mpal, 1, player2->colors[2], 0);
322 palette_set_player_color(mpal, 1, player2->colors[1], 1);
323 palette_set_player_color(mpal, 1, player2->colors[0], 2);
324 video_force_pal_refresh();
325
326 // HAR
327 ani = &bk_get_info(&scene->bk_data, 5)->ani;
328 object_create(&local->player1_har, scene->gs, vec2i_create(160,0), vec2f_create(0, 0));
329 object_set_animation(&local->player1_har, ani);
330 object_select_sprite(&local->player1_har, player1->har_id);
331
332 object_create(&local->player2_har, scene->gs, vec2i_create(160,0), vec2f_create(0, 0));
333 object_set_animation(&local->player2_har, ani);
334 object_select_sprite(&local->player2_har, player2->har_id);
335 object_set_direction(&local->player2_har, OBJECT_FACE_LEFT);
336 object_set_pal_offset(&local->player2_har, 48);
337
338 // PLAYER
339 ani = &bk_get_info(&scene->bk_data, 4)->ani;
340 object_create(&local->player1_portrait, scene->gs, vec2i_create(-10,150), vec2f_create(0, 0));
341 object_set_animation(&local->player1_portrait, ani);
342 object_select_sprite(&local->player1_portrait, player1->pilot_id);
343
344 object_create(&local->player2_portrait, scene->gs, vec2i_create(330,150), vec2f_create(0, 0));
345 object_set_animation(&local->player2_portrait, ani);
346 object_select_sprite(&local->player2_portrait, player2->pilot_id);
347 object_set_direction(&local->player2_portrait, OBJECT_FACE_LEFT);
348
349 // clone the left side of the background image
350 // Note! We are touching the scene-wide background surface!
351 surface_sub(&scene->bk_data.background, // DST Surface
352 &scene->bk_data.background, // SRC Surface
353 160, 0, // DST
354 0, 0, // SRC
355 160, 200, // Size
356 SUB_METHOD_MIRROR); // Flip the right side horizontally
357
358 if (player2->selectable) {
359 // player1 gets to choose, start at arena 0
360 local->arena = 0;
361 } else if (player2->pilot_id == PILOT_KREISSACK) {
362 // force arena 0 when fighting Kreissack in 1 player mode
363 local->arena = 0;
364 } else {
365 // pick a random arena for 1 player mode
366 local->arena = rand_int(5); // srand was done in melee
367 }
368
369 // Arena
370 if(player2->selectable) {
371 ani = &bk_get_info(&scene->bk_data, 3)->ani;
372 object_create(&local->arena_select, scene->gs, vec2i_create(59,155), vec2f_create(0, 0));
373 object_set_animation(&local->arena_select, ani);
374 object_select_sprite(&local->arena_select, local->arena);
375 }
376
377
378 // SCIENTIST
379 int scientistpos = rand_int(4);
380 vec2i scientistcoord = spawn_position(scientistpos, 1);
381 if (scientistpos % 2) {
382 scientistcoord.x += 50;
383 } else {
384 scientistcoord.x -= 50;
385 }
386 object *o_scientist = malloc(sizeof(object));
387 ani = &bk_get_info(&scene->bk_data, 8)->ani;
388 object_create(o_scientist, scene->gs, scientistcoord, vec2f_create(0, 0));
389 object_set_animation(o_scientist, ani);
390 object_select_sprite(o_scientist, 0);
391 object_set_direction(o_scientist, scientistpos % 2 ? OBJECT_FACE_LEFT : OBJECT_FACE_RIGHT);
392 game_state_add_object(scene->gs, o_scientist, RENDER_LAYER_MIDDLE, 0, 0);
393
394 // WELDER
395 int welderpos = rand_int(6);
396 // welder can't be on the same gantry or the same *side* as the scientist
397 // he also can't be on the same 'level'
398 // but he has 10 possible starting positions
399 while ((welderpos % 2) == (scientistpos % 2) || (scientistpos < 2 && welderpos < 2) || (scientistpos > 1 && welderpos > 1 && welderpos < 4)) {
400 welderpos = rand_int(6);
401 }
402 object *o_welder = malloc(sizeof(object));
403 ani = &bk_get_info(&scene->bk_data, 7)->ani;
404 object_create(o_welder, scene->gs, spawn_position(welderpos, 0), vec2f_create(0, 0));
405 object_set_animation(o_welder, ani);
406 object_select_sprite(o_welder, 0);
407 object_set_spawn_cb(o_welder, cb_vs_spawn_object, (void*)scene);
408 object_set_destroy_cb(o_welder, cb_vs_destroy_object, (void*)scene);
409 object_set_direction(o_welder, welderpos % 2 ? OBJECT_FACE_LEFT : OBJECT_FACE_RIGHT);
410 game_state_add_object(scene->gs, o_welder, RENDER_LAYER_MIDDLE, 0, 0);
411
412 // GANTRIES
413 object *o_gantry_a = malloc(sizeof(object));
414 ani = &bk_get_info(&scene->bk_data, 11)->ani;
415 object_create(o_gantry_a, scene->gs, vec2i_create(0,0), vec2f_create(0, 0));
416 object_set_animation(o_gantry_a, ani);
417 object_select_sprite(o_gantry_a, 0);
418 game_state_add_object(scene->gs, o_gantry_a, RENDER_LAYER_TOP, 0, 0);
419
420 object *o_gantry_b = malloc(sizeof(object));
421 object_create(o_gantry_b, scene->gs, vec2i_create(320,0), vec2f_create(0, 0));
422 object_set_animation(o_gantry_b, ani);
423 object_select_sprite(o_gantry_b, 0);
424 object_set_direction(o_gantry_b, OBJECT_FACE_LEFT);
425 game_state_add_object(scene->gs, o_gantry_b, RENDER_LAYER_TOP, 0, 0);
426
427 // Background tex
428 menu_background2_create(&local->arena_select_bg, 211, 50);
429
430 // Quit Dialog
431 dialog_create(&local->quit_dialog, DIALOG_STYLE_YES_NO, "ARE YOU SURE YOU WANT TO QUIT THIS GAME?", 72, 60);
432 local->quit_dialog.userdata = scene;
433 local->quit_dialog.clicked = vs_quit_dialog_clicked;
434
435 // Too Pathetic Dialog
436 char insult[512];
437 snprintf(insult, 512, lang_get(748), "Veteran", "Major Kreissack");
438 dialog_create(&local->too_pathetic_dialog, DIALOG_STYLE_OK, insult, 72, 60);
439 local->too_pathetic_dialog.userdata = scene;
440 local->too_pathetic_dialog.clicked = vs_too_pathetic_dialog_clicked;
441
442 if (player2->pilot_id == PILOT_KREISSACK && settings_get()->gameplay.difficulty < 2) {
443 // kriessack, but not on Veteran or higher
444 dialog_show(&local->too_pathetic_dialog, 1);
445 }
446
447 // Callbacks
448 scene_set_render_cb(scene, vs_render);
449 scene_set_render_overlay_cb(scene, vs_render_overlay);
450 scene_set_input_poll_cb(scene, vs_input_tick);
451 scene_set_dynamic_tick_cb(scene, vs_dynamic_tick);
452 scene_set_static_tick_cb(scene, vs_static_tick);
453 scene_set_free_cb(scene, vs_free);
454
455 // Pick renderer
456 video_select_renderer(VIDEO_RENDERER_HW);
457
458 return 0;
459 }
460