1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12
13 #include "debugconsole/console.h"
14 #include "gamesequence/gamesequence.h"
15 #include "gamesnd/gamesnd.h"
16 #include "globalincs/alphacolors.h"
17 #include "io/key.h"
18 #include "localization/localize.h"
19 #include "menuui/snazzyui.h"
20 #include "parse/parselo.h"
21 #include "playerman/player.h"
22 #include "popup/popup.h"
23 #include "stats/medals.h"
24 #include "ui/ui.h"
25
26 #ifndef NDEBUG
27 #include "cmdline/cmdline.h"
28 #endif
29
30 int Num_medals = 0;
31
32 // define for the medal information
33 SCP_vector<medal_stuff> Medals;
34
35 // coords for indiv medal bitmaps
36 static int Default_medal_coords[GR_NUM_RESOLUTIONS][NUM_MEDALS_FS2][2] = {
37 { // GR_640
38 { 89, 47 }, // eps. peg. lib
39 { 486, 47 }, // imp. order o' vasuda
40 { 129, 130 }, // dist flying cross
41 { 208, 132 }, // soc service
42 { 361, 131 }, // dist intel cross
43 { 439, 130 }, // order of galatea
44 { 64, 234 }, // meritorious unit comm.
45 { 153, 234 }, // medal of valor
46 { 239, 241 }, // gtva leg of honor
47 { 326, 240 }, // allied defense citation
48 { 411, 234 }, // neb campaign victory
49 { 494, 234 }, // ntf campaign victory
50 { 189, 80 }, // rank
51 { 283, 91 }, // wings
52 { 372, 76 }, // bronze kills badge
53 { 403, 76 }, // silver kills badge
54 { 435, 76 }, // gold kills badge
55 { 300, 152 }, // SOC unit crest
56 },
57 { // GR_1024
58 { 143, 75 }, // eps. peg. lib
59 { 777, 75 }, // imp. order o' vasuda
60 { 206, 208 }, // dist flying cross
61 { 333, 212 }, // soc service
62 { 578, 210 }, // dist intel cross
63 { 703, 208 }, // order of galatea
64 { 103, 374 }, // meritorious unit comm.
65 { 245, 374 }, // medal of valor
66 { 383, 386 }, // gtva leg of honor
67 { 522, 384 }, // allied defense citation
68 { 658, 374 }, // neb campaign victory
69 { 790, 374 }, // ntf campaign victory
70 { 302, 128 }, // rank
71 { 453, 146 }, // wings
72 { 595, 121 }, // bronze kills badge
73 { 646, 121 }, // silver kills badge
74 { 696, 121 }, // gold kills badge
75 { 480, 244 }, // SOC unit crest
76 }
77 };
78
79 // debriefing bitmaps
80 static const char *Default_debriefing_bitmaps[NUM_MEDALS_FS2] =
81 {
82 "DebriefMedal00",
83 "DebriefMedal01",
84 "DebriefMedal02",
85 "DebriefMedal03",
86 "DebriefMedal04",
87 "DebriefMedal05",
88 "DebriefMedal06",
89 "DebriefMedal07",
90 "DebriefMedal08",
91 "DebriefMedal09",
92 "DebriefMedal10",
93 "DebriefMedal11",
94 "DebriefRank##",
95 "DebriefWings##",
96 "DebriefBadge01",
97 "DebriefBadge02",
98 "DebriefBadge03",
99 "DebriefCrest"
100 };
101
102 // argh
103 typedef struct coord2dw {
104 int x,y,w;
105 } coord2dw;
106
107 // coords for the medal title
108 static int Default_medals_label_coords[GR_NUM_RESOLUTIONS][3] = {
109 { 241, 458, 300 }, // GR_640 x, y, w
110 { 386, 734, 480 } // GR_1024 x, y, w
111 };
112 static coord2dw Medals_label_coords[GR_NUM_RESOLUTIONS];
113
114 // coords for the player callsign
115 static int Default_medals_callsign_coords[GR_NUM_RESOLUTIONS][2] = {
116 { -1, 54 }, // we'll use -1 as a convention to center it
117 { -1, 89 }
118 };
119 static coord2d Medals_callsign_coords[GR_NUM_RESOLUTIONS];
120
121 #define MEDALS_NUM_BUTTONS 1
122 #define MEDALS_EXIT 0
123 ui_button_info Medals_buttons[GR_NUM_RESOLUTIONS][MEDALS_NUM_BUTTONS] = {
124 { // GR_640
125 ui_button_info("MEB_18", 574, 432, -1, -1, 18),
126 },
127 { // GR_1024
128 ui_button_info("2_MEB_18", 919, 691, -1, -1, 18),
129 }
130 };
131 static int Exit_button_hotspot_override = -1;
132
133 #define MEDALS_NUM_TEXT 1
134 UI_XSTR Medals_text[GR_NUM_RESOLUTIONS][MEDALS_NUM_TEXT] = {
135 { // GR_640
136 {"Exit", 1466, 587, 416, UI_XSTR_COLOR_PINK, -1, &Medals_buttons[GR_640][MEDALS_EXIT].button },
137 },
138 { // GR_1024
139 {"Exit", 1466, 943, 673, UI_XSTR_COLOR_PINK, -1, &Medals_buttons[GR_1024][MEDALS_EXIT].button },
140 },
141 };
142
143 static const char* Default_medals_background_filename = "MedalsDisplayEmpty";
144 static char Medals_background_filename[NAME_LENGTH];
145
146 static const char* Default_medals_mask_filename = "Medals-M";
147 static char Medals_mask_filename[NAME_LENGTH];
148
149 scoring_struct *Player_score=NULL;
150
151 int Medals_mode;
152 player *Medals_player;
153
154 void init_medal_bitmaps();
155 void init_snazzy_regions();
156 void blit_medals();
157
158 // -----------------------------------------------------------------------------
159 // Main medals screen state
160 //
161
162 static bitmap *Medals_mask;
163 int Medals_mask_w, Medals_mask_h;
164 static int Medals_bitmap_mask; // the mask for the medal case
165 static int Medals_bitmap; // the medal case itself
166 static int Rank_bm; // bitmap for the rank medal
167
168 static UI_WINDOW Medals_window;
169
170 typedef struct medal_display_info {
171 int bitmap; // image in the medal case
172 coord2d coords[GR_NUM_RESOLUTIONS]; // screen position (can now be defined by the table)
173 } medal_display_info;
174
175 static SCP_vector<medal_display_info> Medal_display_info;
176 static MENU_REGION *Medal_regions = NULL;
177
178 int Rank_medal_index = -1;
179
180
181 #define MEDAL_BITMAP_INIT (1<<0)
182 #define MASK_BITMAP_INIT (1<<1)
183 int Init_flags;
184
medal_stuff()185 medal_stuff::medal_stuff()
186 : num_versions(1), version_starts_at_1(false), available_from_start(false), kills_needed(0)
187 {
188 name[0] = '\0';
189 bitmap[0] = '\0';
190 debrief_bitmap[0] = '\0';
191 voice_base[0] = '\0';
192 }
193
get_display_name() const194 const char* medal_stuff::get_display_name() const {
195 if (!alt_name.empty()) {
196 return alt_name.c_str();
197 } else {
198 return name;
199 }
200 }
201
parse_medal_tbl()202 void parse_medal_tbl()
203 {
204 int i;
205
206 try
207 {
208 read_file_text("medals.tbl", CF_TYPE_TABLES);
209 reset_parse();
210
211 required_string("#Medals");
212
213 // special background information
214 if (optional_string("+Background Bitmap:")) {
215 stuff_string(Medals_background_filename, F_NAME, NAME_LENGTH);
216 }
217 else {
218 strcpy_s(Medals_background_filename, Default_medals_background_filename);
219 }
220
221 // special mask information
222 if (optional_string("+Mask Bitmap:")) {
223 stuff_string(Medals_mask_filename, F_NAME, NAME_LENGTH);
224 }
225 else {
226 strcpy_s(Medals_mask_filename, Default_medals_mask_filename);
227 }
228
229 // configurable hotspot for the exit button
230 if (optional_string("+Exit Button Hotspot Index:")) {
231 stuff_int(&Exit_button_hotspot_override);
232 }
233
234 // special positioning for player callsign
235 if (optional_string("+Callsign Position 640:")) {
236 stuff_int(&Medals_callsign_coords[GR_640].x);
237 stuff_int(&Medals_callsign_coords[GR_640].y);
238 }
239 else {
240 Medals_callsign_coords[GR_640].x = Default_medals_callsign_coords[GR_640][0];
241 Medals_callsign_coords[GR_640].y = Default_medals_callsign_coords[GR_640][1];
242 }
243 if (optional_string("+Callsign Position 1024:")) {
244 stuff_int(&Medals_callsign_coords[GR_1024].x);
245 stuff_int(&Medals_callsign_coords[GR_1024].y);
246 }
247 else {
248 Medals_callsign_coords[GR_1024].x = Default_medals_callsign_coords[GR_1024][0];
249 Medals_callsign_coords[GR_1024].y = Default_medals_callsign_coords[GR_1024][1];
250 }
251
252 // special positioning for medal label
253 if (optional_string("+Label Position 640:")) {
254 stuff_int(&Medals_label_coords[GR_640].x);
255 stuff_int(&Medals_label_coords[GR_640].y);
256 stuff_int(&Medals_label_coords[GR_640].w);
257 }
258 else {
259 Medals_label_coords[GR_640].x = Default_medals_label_coords[GR_640][0];
260 Medals_label_coords[GR_640].y = Default_medals_label_coords[GR_640][1];
261 Medals_label_coords[GR_640].w = Default_medals_label_coords[GR_640][2];
262 }
263 if (optional_string("+Label Position 1024:")) {
264 stuff_int(&Medals_label_coords[GR_1024].x);
265 stuff_int(&Medals_label_coords[GR_1024].y);
266 stuff_int(&Medals_label_coords[GR_1024].w);
267 }
268 else {
269 Medals_label_coords[GR_1024].x = Default_medals_label_coords[GR_1024][0];
270 Medals_label_coords[GR_1024].y = Default_medals_label_coords[GR_1024][1];
271 Medals_label_coords[GR_1024].w = Default_medals_label_coords[GR_1024][2];
272 }
273
274 // parse in all the medal names
275 Num_medals = 0;
276 while (required_string_either("#End", "$Name:"))
277 {
278 medal_stuff temp_medal;
279 medal_display_info temp_display;
280
281 required_string("$Name:");
282 stuff_string(temp_medal.name, F_NAME, NAME_LENGTH);
283
284 // is this rank? if so, save it
285 if (!stricmp(temp_medal.name, "Rank"))
286 Rank_medal_index = Num_medals;
287
288 if (optional_string("$Alt Name:")) {
289 stuff_string(temp_medal.alt_name, F_NAME);
290 }
291
292 required_string("$Bitmap:");
293 stuff_string(temp_medal.bitmap, F_NAME, MAX_FILENAME_LEN);
294
295 if (optional_string("+Position 640:")) {
296 stuff_int(&temp_display.coords[GR_640].x);
297 stuff_int(&temp_display.coords[GR_640].y);
298 }
299 else if (Num_medals < NUM_MEDALS_FS2) {
300 temp_display.coords[GR_640].x = Default_medal_coords[GR_640][Num_medals][0];
301 temp_display.coords[GR_640].y = Default_medal_coords[GR_640][Num_medals][1];
302 }
303 else {
304 Warning(LOCATION, "No default GR_640 position for medal '%s'!", temp_medal.name);
305 temp_display.coords[GR_640].x = 0;
306 temp_display.coords[GR_640].y = 0;
307 }
308 if (optional_string("+Position 1024:")) {
309 stuff_int(&temp_display.coords[GR_1024].x);
310 stuff_int(&temp_display.coords[GR_1024].y);
311 }
312 else if (Num_medals < NUM_MEDALS_FS2) {
313 temp_display.coords[GR_1024].x = Default_medal_coords[GR_1024][Num_medals][0];
314 temp_display.coords[GR_1024].y = Default_medal_coords[GR_1024][Num_medals][1];
315 }
316 else {
317 Warning(LOCATION, "No default GR_1024 position for medal '%s'!", temp_medal.name);
318 temp_display.coords[GR_1024].x = 0;
319 temp_display.coords[GR_1024].y = 0;
320 }
321
322 if (optional_string("+Debriefing Bitmap:")) {
323 stuff_string(temp_medal.debrief_bitmap, F_NAME, MAX_FILENAME_LEN);
324 }
325 else if (Num_medals < NUM_MEDALS_FS2) {
326 strcpy_s(temp_medal.debrief_bitmap, Default_debriefing_bitmaps[Num_medals]);
327 }
328 else {
329 Warning(LOCATION, "No default debriefing bitmap for medal '%s'!", temp_medal.name);
330 strcpy_s(temp_medal.debrief_bitmap, "");
331 }
332
333 required_string("$Num mods:");
334 stuff_int(&temp_medal.num_versions);
335
336 // this is dumb
337 temp_medal.version_starts_at_1 = (Num_medals == Rank_medal_index);
338 if (optional_string("+Version starts at 1:")) {
339 stuff_boolean(&temp_medal.version_starts_at_1);
340 }
341
342 if (optional_string("+Available From Start:")) {
343 stuff_boolean(&temp_medal.available_from_start);
344 }
345
346 // some medals are based on kill counts. When string +Num Kills: is present, we know that
347 // this medal is a badge and should be treated specially
348 if (optional_string("+Num Kills:")) {
349 char buf[MULTITEXT_LENGTH];
350 int persona;
351 stuff_int(&temp_medal.kills_needed);
352
353 if (optional_string("$Wavefile 1:"))
354 stuff_string(temp_medal.voice_base, F_NAME, MAX_FILENAME_LEN);
355
356 if (optional_string("$Wavefile 2:"))
357 stuff_string(temp_medal.voice_base, F_NAME, MAX_FILENAME_LEN);
358
359 if (optional_string("$Wavefile Base:"))
360 stuff_string(temp_medal.voice_base, F_NAME, MAX_FILENAME_LEN);
361
362 while (check_for_string("$Promotion Text:")) {
363 required_string("$Promotion Text:");
364 stuff_string(buf, F_MULTITEXT, sizeof(buf));
365 persona = -1;
366 if (optional_string("+Persona:")) {
367 stuff_int(&persona);
368 if (persona < 0) {
369 Warning(LOCATION, "Debriefing text for %s is assigned to an invalid persona: %i (must be 0 or greater).\n", temp_medal.name, persona);
370 continue;
371 }
372 }
373 temp_medal.promotion_text[persona] = SCP_string(buf);
374 }
375 if (temp_medal.promotion_text.find(-1) == temp_medal.promotion_text.end()) {
376 Warning(LOCATION, "%s medal is missing default debriefing text.\n", temp_medal.name);
377 temp_medal.promotion_text[-1] = "";
378 }
379 }
380
381 Medals.push_back(temp_medal);
382 Medal_display_info.push_back(temp_display);
383 Num_medals++;
384 }
385
386 required_string("#End");
387
388 // be sure that we know where the rank is
389 if (Rank_medal_index < 0)
390 {
391 Warning(LOCATION, "Could not find the 'Rank' medal!");
392 Rank_medal_index = 0;
393 }
394
395 // be sure that the badges kill numbers show up in order
396 //WMC - I don't think this is needed anymore due to my changes to post-mission functions
397 //but I'm keeping it here to be sure.
398 int prev_badge_kills = 0;
399 for (i = 0; i < Num_medals; i++)
400 {
401 if (Medals[i].kills_needed < prev_badge_kills && Medals[i].kills_needed != 0)
402 Error(LOCATION, "Badges must appear sorted by lowest kill # first in medals.tbl\nFind Allender for most information.");
403
404 if (Medals[i].kills_needed > 0)
405 prev_badge_kills = Medals[i].kills_needed;
406 }
407 }
408 catch (const parse::ParseException& e)
409 {
410 mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "medals.tbl", e.what()));
411 return;
412 }
413 }
414
415 // replacement for -gimmemedals
416 DCF(medals, "Grant or revoke medals")
417 {
418 int i;
419 int idx;
420
421 if (dc_optional_string_either("help", "--help"))
422 {
423 dc_printf ("Usage: medals all | clear | promote | demote | [index]\n");
424 dc_printf (" [index] -- index of medal to grant\n");
425 dc_printf (" with no parameters, displays the available medals\n");
426 return;
427 }
428
429 if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?"))
430 {
431 dc_printf("You have the following medals:\n");
432
433 for (i = 0; i < Num_medals; i++)
434 {
435 if (Player->stats.medal_counts[i] > 0)
436 dc_printf("%d %s\n", Player->stats.medal_counts[i], Medals[i].name);
437 }
438 dc_printf("%s\n", Ranks[Player->stats.rank].name);
439 return;
440 }
441
442 if (dc_optional_string("all")) {
443 for (i = 0; i < Num_medals; i++) {
444 Player->stats.medal_counts[i]++;
445 }
446 dc_printf("Granted all medals\n");
447 return;
448
449 } else if (dc_optional_string("clear")) {
450 for (i = 0; i < Num_medals; i++) {
451 Player->stats.medal_counts[i] = 0;
452 }
453 dc_printf("Cleared all medals\n");
454 return;
455
456 } else if (dc_optional_string("promote")) {
457 if (Player->stats.rank < MAX_FREESPACE2_RANK) {
458 Player->stats.rank++;
459 }
460 dc_printf("Promoted to %s\n", Ranks[Player->stats.rank].name);
461 return;
462
463 } else if (dc_optional_string("demote")) {
464 if (Player->stats.rank > 0) {
465 Player->stats.rank--;
466 }
467 dc_printf("Demoted to %s\n", Ranks[Player->stats.rank].name);
468 return;
469 }
470
471 if (dc_maybe_stuff_int(&idx)) {
472 if (idx < 0 || idx >= Num_medals)
473 {
474 dc_printf("Medal index %d is out of range\n", idx);
475 return;
476 }
477
478 dc_printf("Granted %s\n", Medals[idx].name);
479 Player->stats.medal_counts[idx]++;
480 return;
481 }
482
483 dc_printf("The following medals are available:\n");
484 for (i = 0; i < Num_medals; i++) {
485 dc_printf("%d: %s\n", i, Medals[i].name);
486 }
487 }
488
489
medal_main_init(player * pl,int mode)490 void medal_main_init(player *pl, int mode)
491 {
492 int idx;
493 char bitmap_buf[NAME_LENGTH];
494
495 Assert(pl != NULL);
496 Medals_player = pl;
497 Player_score = &Medals_player->stats;
498
499 #ifndef NDEBUG
500 if (Cmdline_gimme_all_medals){
501 for (idx=0; idx < Num_medals; idx++){
502 Medals_player->stats.medal_counts[idx] = 1;
503 }
504 }
505 #endif
506
507 for (idx=0; idx < Num_medals; idx++) {
508 if ((Medals[idx].available_from_start) && (Medals_player->stats.medal_counts[idx] < 1)) {
509 Medals_player->stats.medal_counts[idx] = 1;
510 }
511 }
512
513 Medals_mode = mode;
514 snazzy_menu_init();
515 Medals_window.create( 0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0 );
516
517 // maybe override which hotspot is used for the exit button
518 if (Exit_button_hotspot_override >= 0) {
519 for (idx = 0; idx < GR_NUM_RESOLUTIONS; idx++) {
520 Medals_buttons[idx][MEDALS_EXIT].hotspot = Exit_button_hotspot_override;
521 }
522 }
523
524 // create the interface buttons
525 for (idx=0; idx<MEDALS_NUM_BUTTONS; idx++) {
526 // create the object
527 Medals_buttons[gr_screen.res][idx].button.create(&Medals_window, "", Medals_buttons[gr_screen.res][idx].x, Medals_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
528
529 // set the sound to play when highlighted
530 Medals_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
531
532 // set the ani for the button
533 Medals_buttons[gr_screen.res][idx].button.set_bmaps(Medals_buttons[gr_screen.res][idx].filename);
534
535 // set the hotspot
536 Medals_buttons[gr_screen.res][idx].button.link_hotspot(Medals_buttons[gr_screen.res][idx].hotspot);
537 }
538
539 // add all xstrs
540 for (idx=0; idx<MEDALS_NUM_TEXT; idx++) {
541 Medals_window.add_XSTR(&Medals_text[gr_screen.res][idx]);
542 }
543
544 Init_flags = 0;
545
546 strcpy_s(bitmap_buf, Resolution_prefixes[gr_screen.res]);
547 strcat_s(bitmap_buf, Medals_background_filename);
548
549 Medals_bitmap = bm_load(bitmap_buf);
550 if (Medals_bitmap < 0) {
551 Warning(LOCATION, "Error loading medal background bitmap %s", bitmap_buf);
552 } else {
553 Init_flags |= MEDAL_BITMAP_INIT;
554 }
555
556 Medals_mask_w = -1;
557 Medals_mask_h = -1;
558
559 strcpy_s(bitmap_buf, Resolution_prefixes[gr_screen.res]);
560 strcat_s(bitmap_buf, Medals_mask_filename);
561
562 Medals_bitmap_mask = bm_load(bitmap_buf);
563 if (Medals_bitmap_mask < 0) {
564 Warning(LOCATION, "Error loading medal mask file %s", bitmap_buf);
565 } else {
566 Init_flags |= MASK_BITMAP_INIT;
567 Medals_mask = bm_lock(Medals_bitmap_mask, 8, BMP_AABITMAP | BMP_MASK_BITMAP);
568 bm_get_info(Medals_bitmap_mask, &Medals_mask_w, &Medals_mask_h);
569
570 init_medal_bitmaps();
571 }
572
573 init_snazzy_regions();
574
575 gr_set_color_fast(&Color_normal);
576
577 if (Medals_bitmap_mask >= 0)
578 Medals_window.set_mask_bmap(bitmap_buf);
579 }
580
blit_label(const char * label,int num)581 void blit_label(const char *label, int num)
582 {
583 int x, y, sw;
584 char text[256];
585
586 gr_set_color_fast(&Color_bright);
587
588 // translate medal names before displaying
589 // can't translate in table cuz the names are used in comparisons
590 // Medals now have alternate display names so this code can be disabled in the mod table
591 if (Lcl_gr && !Disable_built_in_translations) {
592 char translated_label[256];
593 strcpy_s(translated_label, label);
594 lcl_translate_medal_name_gr(translated_label);
595
596 // set correct string
597 if ( num > 1 ) {
598 sprintf_safe( text, NOX("%s (%d)"), translated_label, num );
599 } else {
600 sprintf_safe( text, "%s", translated_label );
601 }
602 } else if (Lcl_pl && !Disable_built_in_translations) {
603 char translated_label[256];
604 strcpy_s(translated_label, label);
605 lcl_translate_medal_name_pl(translated_label);
606
607 // set correct string
608 if ( num > 1 ) {
609 sprintf_safe( text, NOX("%s (%d)"), translated_label, num );
610 } else {
611 sprintf_safe( text, "%s", translated_label );
612 }
613 } else {
614 // set correct string
615 if ( num > 1 ) {
616 sprintf_safe( text, NOX("%s (%d)"), label, num );
617 } else {
618 sprintf_safe( text, "%s", label );
619 }
620 }
621
622 // find correct coords
623 gr_get_string_size(&sw, NULL, text);
624 x = Medals_label_coords[gr_screen.res].x + (Medals_label_coords[gr_screen.res].w - sw) / 2;
625 y = Medals_label_coords[gr_screen.res].y;
626
627 // do it
628 gr_string(x, y, text, GR_RESIZE_MENU);
629 }
630
blit_callsign()631 void blit_callsign()
632 {
633 int x, y;
634
635 gr_set_color_fast(&Color_normal);
636
637 // find correct coords
638 x = Medals_callsign_coords[gr_screen.res].x;
639 y = Medals_callsign_coords[gr_screen.res].y;
640
641 // nothing special, just do it.
642 // Goober5000 - from previous code revisions, I assume 0x8000 means center it on-screen...
643 // m!m - 0x8000 was removed so we need to calculate it ourself
644 if (x < 0)
645 {
646 int w;
647 gr_get_string_size(&w, NULL, Medals_player->callsign);
648
649 x = (gr_screen.clip_width_unscaled - w) / 2;
650 }
651 gr_string(x, y, Medals_player->callsign, GR_RESIZE_MENU);
652 }
653
medal_main_do()654 int medal_main_do()
655 {
656 int region,selected, k;
657
658 // If we don't have a mask, we don't have enough data to do anything with this screen.
659 if (Medals_bitmap_mask == -1) {
660 popup_game_feature_not_in_demo();
661 if (Medals_mode == MM_NORMAL)
662 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
663 // any calling popup function will know to close the screen down
664 return 0;
665 }
666
667 k = Medals_window.process();
668
669 // process an exit command
670 if ( (k == KEY_ESC) && (Medals_mode == MM_NORMAL) ) {
671 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
672 }
673
674 // draw the background medal display case
675 gr_reset_clip();
676 GR_MAYBE_CLEAR_RES(Medals_bitmap);
677 if (Medals_bitmap != -1) {
678 gr_set_bitmap(Medals_bitmap);
679 gr_bitmap(0,0,GR_RESIZE_MENU);
680 }
681
682 // check to see if a button was pressed
683 if ( (k == (KEY_CTRLED|KEY_ENTER)) || (Medals_buttons[gr_screen.res][MEDALS_EXIT].button.pressed()) ) {
684 gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
685 if (Medals_mode == MM_NORMAL) {
686 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
687 } else {
688 // any calling popup function will know to close the screen down
689 return 0;
690 }
691 }
692
693 // blit medals also takes care of blitting the rank insignia
694 blit_medals();
695 blit_callsign();
696
697 region = snazzy_menu_do((ubyte*)Medals_mask->data, Medals_mask_w, Medals_mask_h, Num_medals, Medal_regions, &selected);
698 if (region == Rank_medal_index)
699 {
700 blit_label(Ranks[Player_score->rank].name, 1);
701 }
702 else switch (region)
703 {
704 case ESC_PRESSED:
705 if (Medals_mode == MM_NORMAL) {
706 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
707 } else {
708 // any calling popup function will know to close the screen down
709 return 0;
710 }
711 break;
712
713 case -1:
714 break;
715
716 default:
717 if (Player_score->medal_counts[region] > 0) {
718 blit_label(Medals[region].get_display_name(), Player_score->medal_counts[region]);
719 }
720 break;
721 } // end switch
722
723 Medals_window.draw();
724
725 gr_flip();
726
727 return 1;
728 }
729
medal_main_close()730 void medal_main_close()
731 {
732 if (Init_flags & MEDAL_BITMAP_INIT)
733 bm_release(Medals_bitmap);
734
735 if (Init_flags & MASK_BITMAP_INIT) {
736 bm_unlock(Medals_bitmap_mask);
737 }
738
739 for (SCP_vector<medal_display_info>::iterator idx = Medal_display_info.begin(); idx != Medal_display_info.end(); ++idx) {
740 if (idx->bitmap >= 0){
741 bm_release(idx->bitmap);
742 }
743 idx->bitmap = -1;
744 }
745
746 delete[] Medal_regions;
747 Medal_regions = NULL;
748
749 Player_score = NULL;
750
751 Medals_window.destroy();
752
753 if (Init_flags & MASK_BITMAP_INIT) {
754 bm_release(Medals_bitmap_mask);
755 }
756
757 snazzy_menu_close();
758 }
759
760 // function to load in the medals for this player. It loads medals that the player has (known
761 // by whether or not a non-zero number is present in the player's medal array), then loads the
762 // rank bitmap
init_medal_bitmaps()763 void init_medal_bitmaps()
764 {
765 int idx;
766 Assert(Player_score);
767
768 for (idx=0; idx<Num_medals; idx++) {
769 Medal_display_info[idx].bitmap = -1;
770
771 if (Player_score->medal_counts[idx] > 0) {
772 int num_medals;
773 char filename[MAX_FILENAME_LEN], base[MAX_FILENAME_LEN];
774
775 // possibly load a different filename that is specified by the bitmap filename
776 // for this medal. if the player has > 1 of these types of medals, then determien
777 // which of the possible version to use based on the player's count of this medal
778 strcpy_s( filename, Medals[idx].bitmap );
779
780 _splitpath( filename, NULL, NULL, base, NULL );
781
782 num_medals = Player_score->medal_counts[idx];
783
784 // can't display more than the maximum number of version for this medal
785 if ( num_medals > Medals[idx].num_versions )
786 num_medals = Medals[idx].num_versions;
787
788 if ( num_medals > 1 ) {
789 // append the proper character onto the end of the medal filename. Base version
790 // has no character. next version is a, then b, etc.
791 char temp[MAX_FILENAME_LEN];
792 strcpy_s(temp, base);
793 sprintf_safe( base, "%s%c", temp, (num_medals-2)+'a');
794 }
795
796 // hi-res support
797 if (gr_screen.res == GR_1024) {
798 sprintf_safe( filename, "2_%s", base );
799 }
800
801 // base now contains the actual medal bitmap filename needed to load
802 // we don't need to pass extension to bm_load anymore, so just use the basename
803 // as is.
804 Medal_display_info[idx].bitmap = bm_load((gr_screen.res == GR_1024) ? filename : base);
805 Assert( Medal_display_info[idx].bitmap != -1 );
806 }
807 }
808
809 // load up rank insignia
810 if (gr_screen.res == GR_1024) {
811 char filename[NAME_LENGTH];
812 if (snprintf(filename, NAME_LENGTH, "2_%s", Ranks[Player_score->rank].bitmap) >= NAME_LENGTH) {
813 // Make sure the string is null terminated
814 filename[NAME_LENGTH - 1] = '\0';
815 }
816 Rank_bm = bm_load(filename);
817 } else {
818 Rank_bm = bm_load(Ranks[Player_score->rank].bitmap);
819 }
820 }
821
init_snazzy_regions()822 void init_snazzy_regions()
823 {
824 int idx;
825
826 // well, we need regions in an array (versus a vector), so...
827 Assert(Medal_regions == NULL);
828 Medal_regions = new MENU_REGION[Num_medals];
829
830 // snazzy regions for the medals/ranks, etc.
831 for (idx=0; idx<Num_medals; idx++) {
832 snazzy_menu_add_region(&Medal_regions[idx], "", idx, 0);
833 }
834 }
835
836 // blit the medals -- this includes the rank insignia
blit_medals()837 void blit_medals()
838 {
839 int idx;
840
841 for (idx=0; idx<Num_medals; idx++) {
842 if (idx != Rank_medal_index && Player_score->medal_counts[idx] > 0) {
843 #ifndef NDEBUG
844 // this can happen if gimmemedals was used on the medal screen
845 if (Medal_display_info[idx].bitmap < 0) {
846 continue;
847 }
848 #endif
849 gr_set_bitmap(Medal_display_info[idx].bitmap);
850 gr_bitmap(Medal_display_info[idx].coords[gr_screen.res].x, Medal_display_info[idx].coords[gr_screen.res].y, GR_RESIZE_MENU);
851 }
852 }
853
854 // now blit rank, since that "medal" doesn't get loaded (or drawn) the normal way
855 gr_set_bitmap(Rank_bm);
856 gr_bitmap(Medal_display_info[Rank_medal_index].coords[gr_screen.res].x, Medal_display_info[Rank_medal_index].coords[gr_screen.res].y, GR_RESIZE_MENU);
857 }
858
medals_info_lookup(const char * name)859 int medals_info_lookup(const char *name)
860 {
861 if ( !name ) {
862 return -1;
863 }
864
865 for (int i = 0; i < Num_medals; i++) {
866 if ( !stricmp(name, Medals[i].name) ) {
867 return i;
868 }
869 }
870
871 return -1;
872 }
873