/* Copyright (C) 2015-2018 Night Dive Studios, LLC. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * $Source: r:/prj/cit/src/RCS/setup.c $ * $Revision: 1.141 $ * $Author: dc $ * $Date: 1994/11/23 00:05:51 $ */ #include #include // TODO: extract this into a compatibility header #ifdef _MSC_VER #ifndef F_OK #define F_OK 0 #endif #else #include #endif #ifdef SVGA_SUPPORT #include "fullscrn.h" #endif #include "archiveformat.h" #include "setup.h" #include "colors.h" #include "cybmem.h" #include "diffq.h" #include "gamewrap.h" #include "gr2ss.h" #include "hud.h" #include "init.h" #include "miscqvar.h" #include "player.h" #include "version.h" #include "wrapper.h" #include "verify.h" #include "sdl_events.h" #include "cybstrng.h" #include "gamestrn.h" #include "mainloop.h" #include "tools.h" #include "input.h" #include "game_screen.h" #include "hkeyfunc.h" #include "loops.h" #include "keydefs.h" #include "status.h" #include "cutsloop.h" #include "musicai.h" #include "palfx.h" #include "gamescr.h" #include "faketime.h" #include "2d.h" #include "splash.h" #include "splshpal.h" #include "tickcount.h" #include "MacTune.h" #include "Shock.h" #include "Xmi.h" #ifdef PLAYTEST #include #endif #define KEYBOARD_FOCUS_COLOR (RED_BASE + 3) #define NORMAL_ENTRY_COLOR (RED_BASE + 7) #define CURRENT_DIFF_COLOR (RED_BASE + 3) #define SELECTED_COLOR (RED_BASE) #define UNAVAILABLE_COLOR (RED_BASE + 11) uiSlab setup_slab; LGRegion setup_root_region; uchar play_intro_anim; uchar save_game_exists = FALSE; SetupMode setup_mode; SetupMode last_setup_mode; int intro_num; int splash_num; int diff_sum = 0; bool do_fades = false; ubyte valid_save; uchar setup_bio_started = FALSE; uchar start_first_time = TRUE; bool waiting_for_key = false; static uchar direct_into_cutscene = FALSE; extern char which_lang; extern uchar clear_player_data; extern char current_cutscene; extern char curr_vol_lev; extern char curr_sfx_vol; extern uchar fullscrn_vitals; extern uchar fullscrn_icons; extern uchar map_notes_on; extern uchar audiolog_setting; extern uchar mouseLefty; #ifdef AUDIOLOGS extern char curr_alog_vol; #endif errtype draw_difficulty_char(int char_num); errtype draw_difficulty_description(int which_cat, int color); errtype journey_newgame_func(void); errtype journey_continue_func(uchar draw_stuff); errtype draw_username(int color, char *string); //***************************************************************************** // DIFFICULTY #define DIFF_DONE_X1 119 #define DIFF_DONE_Y1 179 #define DIFF_DONE_X2 203 #define DIFF_DONE_Y2 198 #define DIFF_NAME_X 57 #define DIFF_NAME_Y 49 #define DIFF_NAME_X2 253 #define DIFF_NAME_Y2 65 #define DIFF_NAME_TEXT_X 124 #define DIFF_X_BASE 28 #define DIFF_W_BASE 156 #define DIFF_W_ELEM 32 #define DIFF_H_ELEM 20 #define DIFF_OPT_TOP 99 #define DIFF_OPT_DELTA 53 #define DIFF_OPT_HEIGHT 24 #define DIFF_STRING_OFFSET_Y 10 #define DIFF_STRING_OFFSET_X 13 #define DIFF_TITLE1_X1 12 #define DIFF_TITLE1_Y1 70 #define DIFF_TITLE1_X2 155 #define DIFF_TITLE1_Y2 93 #define DIFF_TITLE1_OPT_TOP 94 #define DIFF_TITLE1_OPT_BOTTOM 118 #define DIFF_TITLE1_LEFT1 25 #define DIFF_TITLE1_RIGHT1 45 #define DIFF_TITLE2_X1 169 #define DIFF_TITLE2_Y1 70 #define DIFF_TITLE2_X2 311 #define DIFF_TITLE2_Y2 93 #define DIFF_TITLE3_X1 12 #define DIFF_TITLE3_Y1 123 #define DIFF_TITLE3_X2 155 #define DIFF_TITLE3_Y2 146 #define DIFF_TITLE4_X1 169 #define DIFF_TITLE4_Y1 123 #define DIFF_TITLE4_X2 311 #define DIFF_TITLE4_Y2 146 #define DIFF_SIZE_X DIFF_TITLE1_RIGHT1 - DIFF_TITLE1_LEFT1 #define DIFF_SIZE_Y DIFF_TITLE1_OPT_BOTTOM - DIFF_TITLE1_OPT_TOP // why +2? #define build_diff_x(char_num) \ ((DIFF_X_BASE + (DIFF_W_ELEM * (char_num & 3)) + (((char_num >> 2) & 1) * DIFF_W_BASE)) + 2) #define build_diff_y(char_num) ((DIFF_OPT_TOP + ((char_num >> 3) * DIFF_OPT_DELTA)) + 2) #define NUM_DIFF_CATEGORIES 4 #define CATEGORY_STRING_BASE REF_STR_diffCategories #define DIFF_STRING_BASE REF_STR_diffStrings #define DIFF_NAME REF_STR_diffName #define DIFF_START REF_STR_diffStart #define FLASH_TIME (CIT_CYCLE / 8) #define COMPUTE_DIFF_STRING_X(wcat) (DIFF_X_BASE + (DIFF_W_BASE * (wcat & 1)) - DIFF_STRING_OFFSET_X) #define COMPUTE_DIFF_STRING_Y(wcat) (DIFF_OPT_TOP + ((wcat >> 1) * DIFF_OPT_DELTA) - DIFF_STRING_OFFSET_Y) #define REF_IMG_bmDifficultyScreen 0x26d0000 char curr_diff = 0; uchar start_selected = FALSE; short diff_titles_x[] = {DIFF_TITLE1_X1, DIFF_TITLE2_X1, DIFF_TITLE3_X1, DIFF_TITLE4_X1}; short diff_titles_y[] = {DIFF_TITLE1_Y1, DIFF_TITLE2_Y1, DIFF_TITLE3_Y1, DIFF_TITLE4_Y1}; char *valid_char_string = "0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz "; LGRect name_rect = {{DIFF_NAME_TEXT_X, DIFF_NAME_Y}, {DIFF_NAME_X2, DIFF_NAME_Y2}}; errtype compute_new_diff(void) { int i, new_sum = 0; for (i = 0; i < 4; i++) new_sum += player_struct.difficulty[i]; diff_sum = new_sum; return OK; } errtype difficulty_draw(uchar full) { int i; uiHideMouse(NULL); if (full) { draw_raw_res_bm_temp(REF_IMG_bmDifficultyScreen, 0, 0); status_bio_draw(); } setup_mode = SETUP_DIFFICULTY; for (i = 0; i < NUM_DIFF_CATEGORIES; i++) { if (i == curr_diff && !start_selected) gr_set_fcolor(KEYBOARD_FOCUS_COLOR); else gr_set_fcolor(NORMAL_ENTRY_COLOR); res_draw_string(RES_citadelFont, CATEGORY_STRING_BASE + i, diff_titles_x[i] + 12, diff_titles_y[i] + 2); } if (start_selected) gr_set_fcolor(KEYBOARD_FOCUS_COLOR); else gr_set_fcolor(NORMAL_ENTRY_COLOR); res_draw_string(RES_citadelFont, DIFF_START, DIFF_DONE_X1 + 13, DIFF_DONE_Y1 + 2); if (full) { for (i = 0; i < 16; i++) draw_difficulty_char(i); for (i = 0; i < 4; i++) draw_difficulty_description(i, NORMAL_ENTRY_COLOR); gr_set_fcolor(KEYBOARD_FOCUS_COLOR); res_draw_string(RES_citadelFont, DIFF_NAME, DIFF_NAME_X, DIFF_NAME_Y); draw_username(NORMAL_ENTRY_COLOR, player_struct.name); } uiShowMouse(NULL); return OK; } errtype draw_username(int color, char *string) { gr_set_fcolor(color); uiHideMouse(&name_rect); res_draw_text(RES_citadelFont, string, DIFF_NAME_TEXT_X, DIFF_NAME_Y); uiShowMouse(&name_rect); return OK; } void flash_username(void) { long flash_done; uiHideMouse(&name_rect); gr_set_fcolor(SELECTED_COLOR - 4); res_draw_string(RES_citadelFont, DIFF_NAME, DIFF_NAME_X, DIFF_NAME_Y); flash_done = *tmd_ticks + FLASH_TIME; while (TickCount() < flash_done) { SDLDraw(); } gr_set_fcolor(KEYBOARD_FOCUS_COLOR); res_draw_string(RES_citadelFont, DIFF_NAME, DIFF_NAME_X, DIFF_NAME_Y); uiShowMouse(&name_rect); } errtype draw_difficulty_line(int which_line) { int i; for (i = 0; i < 4; i++) draw_difficulty_char((which_line * 4) + i); draw_difficulty_description(which_line, NORMAL_ENTRY_COLOR); return OK; } errtype draw_difficulty_description(int which_cat, int color) { if (color != -1) gr_set_fcolor(color); res_draw_string(RES_smallTechFont, DIFF_STRING_BASE + (which_cat * 4) + player_struct.difficulty[which_cat], COMPUTE_DIFF_STRING_X(which_cat), COMPUTE_DIFF_STRING_Y(which_cat)); return OK; } // char_num 1-16 errtype draw_difficulty_char(int char_num) { char buff[] = "X"; uiHideMouse(NULL); if (player_struct.difficulty[char_num / 4] == char_num % 4) gr_set_fcolor(CURRENT_DIFF_COLOR); else gr_set_fcolor(NORMAL_ENTRY_COLOR); buff[0] = (char_num & 3) + '0'; res_draw_text(RES_citadelFont, buff, build_diff_x(char_num), build_diff_y(char_num)); uiShowMouse(NULL); return OK; } //***************************************************************************** //***************************************************************************** // JOURNEY #define JOURNEY_OPT1_TOP 66 #define JOURNEY_OPT1_BOT 88 #define JOURNEY_OPT2_TOP 97 #define JOURNEY_OPT2_BOT 119 #define JOURNEY_OPT3_TOP 129 #define JOURNEY_OPT3_BOT 160 #define JOURNEY_OPT4_TOP 160 #define JOURNEY_OPT4_BOT 182 #define SETUP_STRING_BASE REF_STR_journeyOpts #define NUM_SETUP_LINES 4 #define JOURNEY_OPT_LEFT 79 #define JOURNEY_OPT_RIGHT 247 #define REF_IMG_bmJourneyOnwards 0x26c0000 #ifdef DEMO char curr_setup_line = 1; #else char curr_setup_line = 0; #endif int journey_y[8] = {JOURNEY_OPT1_TOP, JOURNEY_OPT1_BOT, JOURNEY_OPT2_TOP, JOURNEY_OPT2_BOT, JOURNEY_OPT3_TOP, JOURNEY_OPT3_BOT, JOURNEY_OPT4_TOP, JOURNEY_OPT4_BOT}; short setup_tops[] = {JOURNEY_OPT1_TOP, JOURNEY_OPT2_TOP, JOURNEY_OPT3_TOP, JOURNEY_OPT4_TOP}; errtype journey_draw(char part) { char i; uiHideMouse(NULL); if (setup_bio_started) { status_bio_end(); setup_bio_started = FALSE; } // extract into buffer - AFTER we've stopped biorhythms (which used that buffer.....) if (part == 0) draw_raw_res_bm_temp(REF_IMG_bmJourneyOnwards, 0, 0); for (i = 0; i < NUM_SETUP_LINES; i++) { if ((part == 0) || (part - 1 == i)) { int col; if (i == curr_setup_line) col = KEYBOARD_FOCUS_COLOR; else col = NORMAL_ENTRY_COLOR; #ifdef DEMO if ((i == NUM_SETUP_LINES - 1) || (i == 0)) #else if (i == NUM_SETUP_LINES - 1) // why is NUM_SETUP_LINES-1 necessarily continue? #endif { #ifndef DEMO if (!save_game_exists) #endif col = UNAVAILABLE_COLOR; } gr_set_fcolor(col); res_draw_string(RES_citadelFont, SETUP_STRING_BASE + i, JOURNEY_OPT_LEFT + 15, setup_tops[i] + 4); } } uiShowMouse(NULL); setup_mode = SETUP_JOURNEY; return OK; } errtype journey_intro_func(uchar draw_stuff) { #ifdef DEMO uiShowMouse(NULL); // need to leave it hidden return OK; #else if (draw_stuff) res_draw_string(RES_citadelFont, SETUP_STRING_BASE, JOURNEY_OPT_LEFT + 15, JOURNEY_OPT1_TOP + 2); uiShowMouse(NULL); // need to leave it hidden MacTuneKillCurrentTheme(); return play_cutscene(START_CUTSCENE, FALSE); #endif } errtype journey_newgame_func(void) { clear_player_data = TRUE; DEBUG("Load object data"); object_data_load(); player_struct.level = 0xFF; DEBUG("Create initial game"); create_initial_game_func(0, 0, 0); INFO("Started new game!"); change_mode_func(0, 0, GAME_LOOP); return OK; } errtype journey_difficulty_func(uchar draw_stuff) { if (draw_stuff) res_draw_string(RES_citadelFont, SETUP_STRING_BASE + 1, JOURNEY_OPT_LEFT + 15, JOURNEY_OPT2_TOP + 2); uiShowMouse(NULL); difficulty_draw(TRUE); compute_new_diff(); status_bio_set(DIFF_BIO); status_bio_start(); setup_bio_started = TRUE; return OK; } //***************************************************************************** //***************************************************************************** // CREDITS #define CredResFnt (RES_coloraliasedFont) #define CredColor (GREEN_BASE + 4) #define CredResource (RES_credits) int credits_inp = 0; void *credits_txtscrn; int CreditsTune; // set this when game is won, then stats will be shown once before credits int WonGame_ShowStats = 0; // ticks: 0 wait forever int WaitForKey(ulong ticks) { ulong end_ticks = (ulong)TickCount(); ulong key_ticks = end_ticks + (!ticks ? 500 : (ticks * 1 / 8)); end_ticks = ticks ? end_ticks + ticks : 0; int ch; // wait for specified elapsed ticks or keypress for (;;) { // loop win or elevator music int i = 0; if (music_on && !IsPlaying(i)) { int track; if (WonGame_ShowStats) track = 0; else track = 1 + CreditsTune; if (track >= 0 && track < NumTracks) { // int volume = (int)curr_vol_lev * 127 / 100; //convert from 0-100 to 0-127 StartTrack(i, track); if (!WonGame_ShowStats) CreditsTune = (CreditsTune + 1) % 8; } } pump_events(); SDLDraw(); kbs_event ev = kb_next(); ch = ev.ascii; ticks = (ulong)TickCount(); if ((ch == 27 || ch == ' ' || ch == '\r') && ticks >= key_ticks) break; if (end_ticks && ticks >= end_ticks) break; } return ch; } void PrintWinStats(void) { char buf[256], buf_temp[256]; int x, y = 15; short w, h; grs_font *fon = gr_get_font(); gr_set_font(ResLock(RES_coloraliasedFont)); gr_clear(0); sprintf(buf, "CONGRATULATIONS!"); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12 * 2; sprintf(buf, "YOU HAVE COMPLETED SYSTEM SHOCK!"); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12; sprintf(buf, "HIT ESC TO VIEW CREDITS."); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12 * 2; sprintf(buf, "STATISTICS"); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12; // underline short x1 = SCONV_X((320 - w) / 2); short x2 = SCONV_X((320 - w) / 2 + w - 1); for (; x1 <= x2; x1++) { short y1 = SCONV_Y(y); short y2 = SCONV_Y(y + 1); y2 = y1 + (y2 - y1) / 3; for (; y1 <= y2; y1++) gr_set_pixel(GREEN_BASE + 4, x1, y1); } y += 4; sprintf(buf, "TIME: %u", player_struct.game_time); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12; sprintf(buf, "KILLS: %d", player_struct.num_victories); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12; sprintf(buf, "REGENERATIONS: %d", player_struct.num_deaths); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12; uint8_t stupid = 0; for (uint8_t i = 0; i < 4; i++) stupid += (player_struct.difficulty[i] * player_struct.difficulty[i]); sprintf(buf, "DIFFICULTY INDEX: %d", stupid); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); y += 12; int score; // death is 10 anti-kills, but you always keep at least a third of your kills. score = player_struct.num_victories - lg_min(player_struct.num_deaths * 10, player_struct.num_victories * 2 / 3); score = score * 10000; score = score - lg_min(score * 2 / 3, ((player_struct.game_time / (CIT_CYCLE * 36)) * 100)); score = score * (stupid + 1) / 37; // 9 * 4 + 1 is best difficulty factor if (stupid == 36) { score += 2222222; // secret kevin bonus } sprintf(buf, "SCORE: %d", score); gr_string_size(buf, &w, &h); ss_string(buf, (320 - w) / 2, y); ResUnlock(RES_coloraliasedFont); gr_set_font(fon); WaitForKey(0); } void PrintCredits(void) { // Credits display reverse-engineered from text resources int end = 0, line = 0, x, y = 15, columns = 1, cur_col = 0; int underline = 0, last_underline = 0; char buf[256]; gr_clear(0); while (!end) { get_string((RES_credits << 16) | line, buf, sizeof(buf)); line++; int len = strlen(buf); if (*buf == '^') { for (int i = 1; i < len; i++) { switch (buf[i]) { case 'E': WaitForKey(0); end = 1; break; case 'p': WaitForKey(200); break; case 'G': if (WaitForKey(2000) == 27) end = 1; gr_clear(0); y = 15; break; case 'N': break; // dunno case '1': columns = 1; cur_col = 0; break; case '2': columns = 2; cur_col = 0; break; case 'H': underline = 3; break; case 'T': underline = 2; break; case 'h': underline = 1; break; case 'c': underline = 0; break; case 'S': y += 10; break; case 'L': y = 15 * (buf[++i] - '0'); break; } } continue; } grs_font *fon = gr_get_font(); gr_set_font(ResLock(RES_coloraliasedFont)); short w, h; gr_string_size(buf, &w, &h); x = (columns == 1) ? (320 - w) / 2 : (cur_col == 0) ? 320 / 2 - 8 - w : 320 / 2 + 8; ss_string(buf, x, y); ResUnlock(RES_coloraliasedFont); gr_set_font(fon); if (underline) { short x1, x2, y1, y2; x1 = SCONV_X(x); x2 = SCONV_X(x + w - 1); for (; x1 <= x2; x1++) { y1 = SCONV_Y(y + h + 1); y2 = SCONV_Y(y + h + 2); if (underline == 2) y2 = y1 + (y2 - y1) / 2; else y2 = y1 + (y2 - y1) / 3; for (; y1 <= y2; y1++) gr_set_pixel(GREEN_BASE + 4, x1, y1); } if (underline == 3) { x1 = SCONV_X(x + 1); x2 = SCONV_X(x + w - 1 - 1); for (; x1 <= x2; x1++) { y1 = SCONV_Y(y + h + 1); y2 = SCONV_Y(y + h + 2); y1 = y1 + (y2 - y1) / 3; for (; y1 <= y2; y1++) gr_set_pixel(GREEN_BASE + 4, x1, y1); } } } if (columns == 1) { y += underline ? 14 : 11; underline = 0; } else { if (!cur_col) last_underline = underline; else { y += (underline || last_underline) ? 14 : 11; underline = 0; } cur_col ^= 1; } } } errtype journey_credits_func(uchar draw_stuff) { setup_mode = SETUP_CREDITS; if (draw_stuff) { if (music_on) { if (WonGame_ShowStats) { MacTuneLoadTheme("endloop", 0); } else { CreditsTune = 0; load_score_guts(7); // elevator } } if (WonGame_ShowStats) PrintWinStats(); PrintCredits(); WonGame_ShowStats = 0; journey_credits_done(); } else { if (credits_inp != 0) { journey_credits_done(); } } return OK; } void journey_credits_done(void) { kb_flush(); mouse_flush(); credits_inp = 0; uiShowMouse(NULL); journey_draw(0); if (music_on) MacTuneLoadTheme("titloop", 0); } //***************************************************************************** //***************************************************************************** // CONTINUE #define SG_SLOT_HT 17 #define SG_SLOT_WD 169 #define SG_SLOT_OFFSET_X 21 #define SG_SLOT_OFFSET_Y 6 #define SG_SLOT_X 79 #define SG_SLOT_Y 62 #define REF_IMG_bmContinueScreen 0x26e0000 char curr_sg = 0; errtype draw_sg_slot(int slot_num) { char temp[64]; short sz, x, y; uiHideMouse(NULL); if (curr_sg == slot_num) gr_set_fcolor(KEYBOARD_FOCUS_COLOR); else gr_set_fcolor(NORMAL_ENTRY_COLOR); // if slot_num == -1 highlight the curr_sg slot with the SELECTED_COLOR color if (slot_num == -1) { gr_set_fcolor(SELECTED_COLOR); slot_num = curr_sg; } if (valid_save & (1 << slot_num)) { sz = strlen(comments[slot_num]); strcpy(temp, comments[slot_num]); } else { get_string(REF_STR_UnusedSave, temp, 64); } gr_set_font(ResLock(RES_smallTechFont)); gr_string_size(temp, &x, &y); while ((x > SG_SLOT_WD - SG_SLOT_OFFSET_X) && (sz > 0)) { sz--; strcpy(temp, ""); strncpy(temp, comments[slot_num], sz); temp[sz] = '\0'; gr_string_size(temp, &x, &y); } ResUnlock(RES_smallTechFont); // was RES_CitadelFont res_draw_text(RES_smallTechFont, temp, SG_SLOT_X + SG_SLOT_OFFSET_X, SG_SLOT_Y + SG_SLOT_OFFSET_Y + (slot_num * SG_SLOT_HT)); uiShowMouse(NULL); return OK; } errtype draw_savegame_names(void) { int i; for (i = 0; i < NUM_SAVE_SLOTS; i++) draw_sg_slot(i); return OK; } errtype load_that_thar_game(int which_slot) { DEBUG("load_that_thar_game %i", which_slot); errtype retval; if (valid_save & (1 << which_slot)) { draw_sg_slot(-1); // highlight the current save game slot with SELECTED_COLOR clear_player_data = TRUE; // initializes the player struct in object_data_load object_data_load(); player_create_initial(); player_struct.level = 0xFF; // make sure we load textures Poke_SaveName(which_slot); change_mode_func(0, 0, GAME_LOOP); retval = load_game(save_game_name); if (retval != OK) { strcpy(comments[which_slot], "<< INVALID GAME >>"); uiHideMouse(NULL); journey_continue_func(TRUE); return retval; } gr2ss_override = OVERRIDE_ALL; // CC: This fixed popups cursors drawing tiny after loading } return OK; } errtype journey_continue_func(uchar draw_stuff) { #ifndef DEMO if (save_game_exists) { // draw_raw_res_bm_extract(REF_IMG_bmContinueScreen, 0, 0); // do what the above line does, but hack bitmap height Ref rid = REF_IMG_bmContinueScreen; int i = REFINDEX(rid); RefTable *rt = ResReadRefTable(REFID(rid)); if (RefIndexValid(rt, i)) { FrameDesc *f = RefLock(rid); grs_bitmap bm = f->bm; bm.h = 200; // SUPER HACK: resource reports 320 ss_bitmap(&bm, 0, 0); RefUnlock(rid); } ResFreeRefTable(rt); setup_mode = SETUP_CONTINUE; draw_savegame_names(); } #endif uiShowMouse(NULL); return OK; } //***************************************************************************** //***************************************************************************** // SETUP #define DO_FADES #define SECRET_MISSION_DIFFICULTY_QB 0xB0 #define ALT(x) ((x) | KB_FLAG_ALT) #define CFG_NAME_VAR "name" char diff_qvars[4] = {COMBAT_DIFF_QVAR, MISSION_DIFF_QVAR, PUZZLE_DIFF_QVAR, CYBER_DIFF_QVAR}; typedef errtype (*journey_func)(uchar draw_stuff); journey_func journey_funcs[4] = {journey_intro_func, journey_difficulty_func, journey_credits_func, journey_continue_func}; // if there are two different input events - only lets one call a journey_func uchar journey_lock = FALSE; void go_and_start_the_game_already(void) { INFO("New Journey"); char i; #ifdef GAMEONLY if (strlen(player_struct.name) == 0) { flash_username(); return; } #endif uiHideMouse(NULL); gr_set_fcolor(SELECTED_COLOR); res_draw_string(RES_citadelFont, DIFF_START, DIFF_DONE_X1 + 13, DIFF_DONE_Y1 + 2); uiShowMouse(NULL); journey_newgame_func(); #ifdef SVGA_SUPPORT QUESTVAR_SET(SCREENMODE_QVAR, convert_use_mode); #endif QUESTVAR_SET(MUSIC_VOLUME_QVAR, (curr_vol_lev * curr_vol_lev) / 100); QUESTVAR_SET(SFX_VOLUME_QVAR, (curr_sfx_vol * curr_sfx_vol) / 100); #ifdef AUDIOLOGS QUESTVAR_SET(ALOG_VOLUME_QVAR, (curr_alog_vol * curr_alog_vol) / 100); QUESTVAR_SET(ALOG_OPT_QVAR, audiolog_setting); #endif QUESTVAR_SET(FULLSCRN_ICON_QVAR, fullscrn_icons); QUESTVAR_SET(FULLSCRN_VITAL_QVAR, fullscrn_vitals); QUESTVAR_SET(AMAP_NOTES_QVAR, map_notes_on); QUESTVAR_SET(HUDCOLOR_QVAR, hud_color_bank); QUESTVAR_SET(SCREENMODE_QVAR, 3); QUESTVAR_SET(DCLICK_QVAR, FIX_UNIT / 3); for (i = 0; i < 4; i++) QUESTVAR_SET(diff_qvars[i], player_struct.difficulty[i]); if (QUESTVAR_GET(MISSION_DIFF_QVAR) == 3) hud_set(HUD_GAMETIME); strncpy(player_struct.version, SYSTEM_SHOCK_VERSION, 6); if (setup_bio_started) { status_bio_end(); setup_bio_started = FALSE; } gr2ss_override = OVERRIDE_ALL; // CC: This fixed popups cursors drawing tiny } uchar intro_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) { int which_one = -1; int i = 0; int old_diff; uchar diff_changed; #ifndef NO_DUMMIES intptr_t dummy = user_data; LGRegion *dummy2 = r; #endif if (ev->mouse_data.action & MOUSE_LDOWN) { // If in the splash screen, advance if (waiting_for_key) { waiting_for_key = false; return OK; } switch (setup_mode) { case SETUP_JOURNEY: if (!journey_lock) { if ((ev->pos.x > JOURNEY_OPT_LEFT) && (ev->pos.x < JOURNEY_OPT_RIGHT)) { while ((which_one == -1) && (i <= 6)) { if ((ev->pos.y > journey_y[i]) && (ev->pos.y < journey_y[i + 1])) which_one = i >> 1; else i += 2; } TRACE("%s: which_one = %d", __FUNCTION__, which_one); if (which_one != -1) { uiHideMouse(NULL); gr_set_fcolor(SELECTED_COLOR); journey_lock = TRUE; journey_funcs[which_one](TRUE); journey_lock = FALSE; } } } break; case SETUP_CREDITS: credits_inp = -1; break; case SETUP_CONTINUE: if ((ev->pos.x >= SG_SLOT_X) && (ev->pos.x <= SG_SLOT_X + SG_SLOT_WD) && (ev->pos.y >= SG_SLOT_Y) && (ev->pos.y <= SG_SLOT_Y + (NUM_SAVE_SLOTS * SG_SLOT_HT))) { char which = (ev->pos.y - SG_SLOT_Y) / SG_SLOT_HT; char old_sg = curr_sg; curr_sg = which; draw_sg_slot(old_sg); load_that_thar_game(which); } break; case SETUP_DIFFICULTY: diff_changed = FALSE; // given that these are all rectangles, i bet you could just get mouse pos and divide, eh? for (i = 0; i < 16; i++) { if ((ev->pos.x > (build_diff_x(i) - 2)) && (ev->pos.x < (build_diff_x(i) - 2 + DIFF_SIZE_X)) && (ev->pos.y > (build_diff_y(i) - 2)) && (ev->pos.y < (build_diff_y(i) - 2 + DIFF_SIZE_Y))) { old_diff = player_struct.difficulty[i / 4]; draw_difficulty_description(i / 4, 0); player_struct.difficulty[i / 4] = i % 4; TRACE("%s: difficulty %d set to %d (verify = %d)", __FUNCTION__, i / 4, i % 4, player_struct.difficulty[i / 4]); draw_difficulty_char(((i / 4) * 4) + old_diff); draw_difficulty_char(i); draw_difficulty_description(i / 4, NORMAL_ENTRY_COLOR); diff_changed = TRUE; } } if (diff_changed) compute_new_diff(); else if ((ev->pos.x > DIFF_DONE_X1) && (ev->pos.x < DIFF_DONE_X2) && (ev->pos.y > DIFF_DONE_Y1) && (ev->pos.y < DIFF_DONE_Y2)) go_and_start_the_game_already(); break; } } return TRUE; } uchar intro_key_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) { int code = ev->cooked_key_data.code & ~(KB_FLAG_DOWN | KB_FLAG_2ND); char old_diff, old_setup_line = curr_setup_line, n = 0; if (ev->cooked_key_data.code & KB_FLAG_DOWN) { // If in the splash screen, advance if (waiting_for_key) { waiting_for_key = false; return OK; } switch (setup_mode) { case SETUP_JOURNEY: switch (code) { case KEY_UP: n = NUM_SETUP_LINES - 2; // sneaky fallthrough action case KEY_DOWN: n++; curr_setup_line = (curr_setup_line + n) % NUM_SETUP_LINES; #ifdef DEMO if (curr_setup_line == NUM_SETUP_LINES - 1) // why is NUM_SETUP_LINES-1 necessarily continue? curr_setup_line = 2; if (curr_setup_line == 0) curr_setup_line = 1; #else if (curr_setup_line == NUM_SETUP_LINES - 1) // why is NUM_SETUP_LINES-1 necessarily continue? if (!save_game_exists) curr_setup_line = (curr_setup_line + n) % NUM_SETUP_LINES; #endif journey_draw(old_setup_line + 1); journey_draw(curr_setup_line + 1); break; case KEY_ENTER: if (!journey_lock) { uiHideMouse(NULL); gr_set_fcolor(SELECTED_COLOR); journey_lock = TRUE; journey_funcs[curr_setup_line](TRUE); journey_lock = FALSE; } break; } break; case SETUP_CREDITS: credits_inp = code; break; case SETUP_CONTINUE: switch (code) { case KEY_UP: case KEY_LEFT: n = NUM_SAVE_SLOTS - 2; case KEY_DOWN: case KEY_RIGHT: n++; old_diff = curr_sg; curr_sg = (curr_sg + n) % NUM_SAVE_SLOTS; draw_sg_slot(old_diff); draw_sg_slot(curr_sg); break; case KEY_ENTER: load_that_thar_game(curr_sg); break; case KEY_ESC: journey_draw(0); break; } break; case SETUP_DIFFICULTY: switch (code) { case ALT('X'): // Don't print the X when user ALT-X's out of the game case ALT('x'): break; case '-': case KEY_LEFT: n = NUM_DIFF_CATEGORIES - 2; // note sneaky -2 for fallthrough case '+': case KEY_RIGHT: n++; // n now NDC-1 or 1 if (!start_selected) { old_diff = player_struct.difficulty[curr_diff]; draw_difficulty_description(curr_diff, 0); player_struct.difficulty[curr_diff] = (player_struct.difficulty[curr_diff] + n) % NUM_DIFF_CATEGORIES; draw_difficulty_char(curr_diff * NUM_DIFF_CATEGORIES + player_struct.difficulty[curr_diff]); draw_difficulty_char(curr_diff * NUM_DIFF_CATEGORIES + old_diff); draw_difficulty_description(curr_diff, NORMAL_ENTRY_COLOR); compute_new_diff(); } break; case KEY_UP: case (KEY_TAB | KB_FLAG_SHIFT): n = NUM_DIFF_CATEGORIES - 2; // sneaky fallthrough case KEY_DOWN: case KEY_TAB: n++; // now -1 or 1 if (start_selected && n == 1) { start_selected = FALSE; curr_diff = 0; } else if (start_selected && n == NUM_DIFF_CATEGORIES - 1) { start_selected = FALSE; curr_diff = NUM_DIFF_CATEGORIES - 1; } else if ((curr_diff == NUM_DIFF_CATEGORIES - 1 && n == 1) || (curr_diff == 0 && n == NUM_DIFF_CATEGORIES - 1)) start_selected = TRUE; else { start_selected = FALSE; curr_diff = (curr_diff + n) % NUM_DIFF_CATEGORIES; } difficulty_draw(FALSE); break; case KEY_ENTER: go_and_start_the_game_already(); break; case KEY_ESC: journey_draw(0); break; default: { draw_username(0, player_struct.name); n = strlen(player_struct.name); short c = ev->cooked_key_data.code; if (kb_isprint(c) && (n < sizeof(player_struct.name)) && (((c & 0xff) >= 128 && (c & 0xff) <= 155) || ((c & 0xff) >= 160 && (c & 0xff) <= 165) || strchr(valid_char_string, c & 0xFF) != NULL)) { player_struct.name[n] = (c & 0xFF); player_struct.name[n + 1] = '\0'; } if (((c & 0xFF) == KEY_BS) && (n > 0)) player_struct.name[n - 1] = '\0'; draw_username(NORMAL_ENTRY_COLOR, player_struct.name); } break; } break; } } return (main_kb_callback(ev, r, user_data)); } errtype load_savegame_names(void) { int i; int file; valid_save = 0; DEBUG("Grabbing save game names"); for (i = 0; i < NUM_SAVE_SLOTS; i++) { Poke_SaveName(i); #ifdef __APPLE__ char full_save_game_name[1024]; sprintf(full_save_game_name, "%s%s", SDL_GetPrefPath("Interrupt", "SystemShock"), save_game_name); if (access(full_save_game_name, F_OK) != -1) { #else if (access(save_game_name, F_OK) != -1) { #endif file = ResOpenFile(save_game_name); if (ResInUse(OLD_SAVE_GAME_ID_BASE)) { #ifdef OLD_SG_FORMAT ResExtract(OLD_SAVE_GAME_ID_BASE, FORMAT_RAW, comments[i]); valid_save |= (1 << i); #else strcpy(comments[i], "<< BAD VERSION >>"); #endif } else { if (ResInUse(SAVELOAD_VERIFICATION_ID)) { int verify_cookie; ResExtract(SAVELOAD_VERIFICATION_ID, FORMAT_U32, &verify_cookie); switch (verify_cookie) { case OLD_VERIFY_COOKIE_VALID: // Uncomment these lines to reject Shock Floppy save games // sprintf(comments[i], "<< %s >>",get_temp_string(REF_STR_BadVersion + 1)); // break; case VERIFY_COOKIE_VALID: ResExtract(SAVE_GAME_ID_BASE, FORMAT_RAW, comments[i]); valid_save |= (1 << i); break; default: sprintf(comments[i], "<< %s >>", get_temp_string(REF_STR_BadVersion)); break; } } else sprintf(comments[i], "<< %s >>", get_temp_string(REF_STR_BadVersion)); } ResCloseFile(file); } else *(comments[i]) = '\0'; } return OK; } errtype setup_init(void) { #ifndef GAMEONLY int data[1]; int cnt; #endif generic_reg_init(TRUE, &setup_root_region, NULL, &setup_slab, intro_key_handler, intro_mouse_handler); #ifndef GAMEONLY cnt = 1; // if (config_get_value("intro", CONFIG_INT_TYPE, data, &cnt)) { physics_running = TRUE; time_passes = TRUE; _current_loop = SETUP_LOOP; } if (!config_get_raw(CFG_NAME_VAR, player_struct.name, 40)) strcpy(player_struct.name, get_temp_string(REF_STR_DefaultPlayName)); load_savegame_names(); #endif setup_mode = SETUP_JOURNEY; return OK; } void pause_for_key(ulong wait_time) { waiting_for_key = true; ulong wait_until = TickCount() + wait_time; while (waiting_for_key && ((ulong)TickCount() < wait_until)) { input_chk(); pump_events(); SDLDraw(); } waiting_for_key = false; } void splash_draw(bool show_splash) { int pal_file; if (!show_splash) return; // Need to load the splash palette file INFO("Loading splshpal.res"); pal_file = ResOpenFile("res/data/splshpal.res"); INFO("Loading splash.res"); splash_num = ResOpenFile("res/data/splash.res"); if (pal_file < 0) INFO("Could not open splshpal.res!"); uchar splash_pal[768]; ResExtract(RES_splashPalette, FORMAT_RAW, splash_pal); // Set initial palette gr_set_pal(0, 256, splash_pal); // Set screen mode #ifdef SVGA_SUPPORT change_svga_screen_mode(); #endif // clear the screen gr_clear(0); HotkeyContext = SETUP_CONTEXT; uiSetCurrentSlab(&setup_slab); // Draw Origin Logo #ifdef DO_FADES do_fades = true && pal_fx_on; #endif uiHideMouse(NULL); draw_full_res_bm(REF_IMG_bmOriginSplash, 0, 0, do_fades); pause_for_key(500); if (do_fades) palfx_fade_down(); // Draw LGS Logo uiHideMouse(NULL); draw_full_res_bm(REF_IMG_bmLGSplash, 0, 0, do_fades); pause_for_key(500); if (do_fades) palfx_fade_down(); // Draw System Shock title uiHideMouse(NULL); draw_full_res_bm(REF_IMG_bmSystemShockTitle, 0, 0, do_fades); pause_for_key(500); if (do_fades) palfx_fade_down(); // Original palette gr_set_pal(0, 256, ppall); } void setup_loop(void) { bool draw_stuff = FALSE; // loop title music int i = 0; if (music_on && !IsPlaying(i)) { int track = 0; if (track >= 0 && track < NumTracks) { // int volume = (int)curr_vol_lev * 127 / 100; //convert from 0-100 to 0-127 StartTrack(i, track); } } if (last_setup_mode != setup_mode) { uiHideMouse(NULL); gr_clear(0xFF); draw_stuff = TRUE; } last_setup_mode = setup_mode; switch (setup_mode) { case SETUP_DIFFICULTY: difficulty_draw(draw_stuff); break; case SETUP_JOURNEY: journey_draw(draw_stuff); break; case SETUP_CONTINUE: journey_continue_func(draw_stuff); break; case SETUP_CREDITS: journey_credits_func(draw_stuff); break; } } // if these don't get reset, sticky residue of any old game sticks around void empty_slate(void) { flush_resource_cache(); extern uint _fr_glob_flags; _fr_glob_flags = 0; extern uchar *shodan_bitmask; shodan_bitmask = NULL; extern uchar alternate_death; alternate_death = FALSE; extern uiSlab fullscreen_slab; extern uiSlab main_slab; uiPopSlabCursor(&fullscreen_slab); uiPopSlabCursor(&main_slab); extern uchar fire_slam; fire_slam = FALSE; extern short inventory_page; inventory_page = 0; extern short inv_last_page; inv_last_page = -1; extern void physics_zero_all_controls(void); physics_zero_all_controls(); extern int mlook_enabled; mlook_enabled = 0; extern uchar weapon_button_up; weapon_button_up = TRUE; } void setup_start(void) { int do_i_svg = -1, i_invuln = 0; empty_slate(); player_struct.name[0] = 0; for (int i = 0; i < 4; i++) player_struct.difficulty[i] = 2; MacTuneKillCurrentTheme(); // Check to see whether or not to play the intro cut scene #ifdef GAMEONLY load_savegame_names(); #endif save_game_exists = (valid_save != 0); if (setup_mode != SETUP_CREDITS) { if (!save_game_exists && start_first_time) { play_intro_anim = TRUE; } else { play_intro_anim = FALSE; } setup_mode = SETUP_JOURNEY; } if (!start_first_time) closedown_game(TRUE); start_first_time = FALSE; #ifdef GADGET // got rid of pointer type mismatch since one was a region and the other a gadget // someone should probably go and figure it out _current_root = NULL; #endif _current_3d_flag = ANIM_UPDATE; _current_fr_context = NULL; _current_view = &setup_root_region; static_change_copy(); message_info(""); #ifdef SVGA_SUPPORT change_svga_screen_mode(); #endif // clear the screen gr_clear(0); HotkeyContext = SETUP_CONTEXT; uiSetCurrentSlab(&setup_slab); // flush the keyboard and mouse - so we don't read old events kb_flush(); mouse_flush(); intro_num = ResOpenFile("res/data/intro.res"); // slam in the right palette load_da_palette(); if (do_i_svg != -1) { #ifdef PLAYTEST player_invulnerable = i_invuln; #endif uiShowMouse(NULL); } else if (!play_intro_anim) { uiShowMouse(NULL); switch (setup_mode) { case SETUP_DIFFICULTY: difficulty_draw(TRUE); break; case SETUP_JOURNEY: journey_draw(0); break; default: break; } direct_into_cutscene = FALSE; } else { direct_into_cutscene = TRUE; play_cutscene(START_CUTSCENE, FALSE); } if (music_on) MacTuneLoadTheme("titloop", 0); CaptureMouse(false); } void setup_exit(void) { ResCloseFile(intro_num); ResCloseFile(splash_num); #ifdef PALFX_FADES if (pal_fx_on) palfx_fade_down(); else { gr_set_fcolor(BLACK); gr_rect(0, 0, 320, 200); } #endif // must get rid of mouse - to maintain hidden mouse after loop if (!direct_into_cutscene) uiHideMouse(NULL); direct_into_cutscene = FALSE; MacTuneKillCurrentTheme(); } //*****************************************************************************