scummvm/engines/icb/options_manager_pc.cpp

6610 lines
195 KiB
C++
Raw Normal View History

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1999-2000 Revolution Software Ltd.
* This code is based on source code created by Revolution Software,
* used with permission.
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#define FORBIDDEN_SYMBOL_EXCEPTION_time
#include "engines/icb/icb.h"
2020-10-06 07:51:20 +00:00
#include "engines/icb/options_manager_pc.h"
#include "engines/icb/movie_pc.h"
#include "engines/icb/cluster_manager_pc.h"
#include "engines/icb/keyboard.h"
#include "engines/icb/actor_view_pc.h"
#include "engines/icb/mission_functions.h"
#include "engines/icb/text_sprites.h"
#include "engines/icb/mission.h"
#include "engines/icb/global_objects_psx.h"
#include "engines/icb/res_man.h"
#include "common/keyboard.h"
#include "common/system.h"
#include "common/events.h"
#include "common/textconsole.h"
#include "common/file.h"
#include "common/memstream.h"
namespace ICB {
#define OPTIONS_FONT_NAME "futura.pcfont"
#define MAX_BYTESIZE_OF_PROFILE_INFO (2 * 1024)
#define MAX_BYTESIZE_OF_CONTROL_INFO (512)
// Global options controller instance
OptionsManager *g_theOptionsManager;
// Temporary thumbnail direct draw surface
uint32 g_thumbSurfaceID = 0;
// Colours used for drawing
_rgb g_drawColour;
_rgb g_drawColourDark;
_rgb g_drawSelected;
_rgb g_drawSelectedDark;
#define BASE 0x100
#define BASE_DARK 0x101
#define SELECTED 0x200
#define SELECTED_DARK 0x201
// Coordinates for the actor drawn to the control configuration screen
#define CTRL_ACTOR_X -20
#define CTRL_ACTOR_Y -15
#define CTRL_ACTOR_Z -100
#define REFRESH_LIMITER 15000
// External declaration in header
_MOVIESLOT g_movieLibrary[TOTAL_NUMBER_OF_MOVIES];
// To limit slot access to within sensible range
uint32 g_largestValidSlotID = 0;
// So we can provide a sensible default slot selection by modified date
uint32 g_lastAccessedSlot = 0;
bool8 g_mainMenuLoadPlease = FALSE8;
bool8 g_resetToTitleScreen = FALSE8;
uint32 g_missionNumber = 0;
// Controlling variables for title screen timeout movies
uint32 g_titleScreenSecondsElapsed = 0;
uint32 g_titleScreenInitialCount = 0;
uint32 g_titleScreenAutoDelay = 60;
// Force screen full refresh
bool8 g_forceRefresh = FALSE8;
int32 g_skipBackgroundDrawOverFrames = 0;
bool8 g_videoOptionsCheat = FALSE8;
// Private for the title background movie handling
MovieManager *g_personalSequenceManager;
// Need this prototype
void Init_play_movie(const char *param0, bool8 param1);
uint32 GetFileSz(const char *path);
// Translation tweaks
2021-02-26 16:39:50 +00:00
_linked_data_file *LoadTranslatedFile(const char *session, const char *mission);
// Death text functions and defines
#define MAX_DEATH_TEXT 4
#define DEATH_TEXT_RETRYS 10
bool8 usedDeathText[MAX_DEATH_TEXT];
void InitDeathText() {
2021-03-07 08:56:34 +00:00
for (int32 i = 0; i < MAX_DEATH_TEXT; i++) {
usedDeathText[i] = 0;
}
}
// Debug timers
uint32 movieTime;
uint32 movieblitTime;
uint32 drawTime;
void ResetTitleScreenTimeout(void) {
g_titleScreenSecondsElapsed = 0;
g_titleScreenInitialCount = 0;
g_titleScreenAutoDelay = 60;
}
void InitialiseGlobalColours() {
g_drawColour.red = 46;
g_drawColour.green = 87;
g_drawColour.blue = 156;
g_drawColourDark.red = 22;
g_drawColourDark.green = 42;
g_drawColourDark.blue = 76;
g_drawSelected.red = 202;
g_drawSelected.green = 0;
g_drawSelected.blue = 0;
g_drawSelectedDark.red = 90;
g_drawSelectedDark.green = 0;
g_drawSelectedDark.blue = 0;
}
void MakeFullSaveFilename(uint32 slot_id, char *buff) {
// Construct full actual filename
sprintf(buff, "saves/ICBgame%02d.index", slot_id);
}
void MakeFullThumbFilename(uint32 slot_id, char *buff) {
// Construct full actual filename
sprintf(buff, "saves/ICBgame%02d.thumb", slot_id);
}
void InitialiseMovieLibrary() {
// The game intro is always visible
g_movieLibrary[0].visible = TRUE8;
// Zero all other flags
for (uint32 i = 1; i < TOTAL_NUMBER_OF_MOVIES; i++) {
g_movieLibrary[i].visible = FALSE8;
}
// Setup filename mappings (only done here)
strcpy(g_movieLibrary[0].filename, "m01int2");
strcpy(g_movieLibrary[1].filename, "m01intro");
strcpy(g_movieLibrary[2].filename, "m01accel");
strcpy(g_movieLibrary[3].filename, "m01cable");
strcpy(g_movieLibrary[4].filename, "m01chasm");
strcpy(g_movieLibrary[5].filename, "m01cut");
strcpy(g_movieLibrary[6].filename, "m01robot");
strcpy(g_movieLibrary[7].filename, "m01robt2");
strcpy(g_movieLibrary[8].filename, "m01robt3");
strcpy(g_movieLibrary[9].filename, "m01outro");
strcpy(g_movieLibrary[10].filename, "m02tortu");
strcpy(g_movieLibrary[11].filename, "m02intro");
strcpy(g_movieLibrary[12].filename, "m02outro");
strcpy(g_movieLibrary[13].filename, "m03tortu");
strcpy(g_movieLibrary[14].filename, "m03intro");
strcpy(g_movieLibrary[15].filename, "m03bomb");
strcpy(g_movieLibrary[16].filename, "m03cord");
strcpy(g_movieLibrary[17].filename, "m03shoot");
strcpy(g_movieLibrary[18].filename, "m03c1sep");
strcpy(g_movieLibrary[19].filename, "m03outro");
strcpy(g_movieLibrary[20].filename, "m04tortu");
strcpy(g_movieLibrary[21].filename, "m04intro");
strcpy(g_movieLibrary[22].filename, "m04lift1");
strcpy(g_movieLibrary[23].filename, "m04lift2");
strcpy(g_movieLibrary[24].filename, "m04spec");
strcpy(g_movieLibrary[25].filename, "m04zapp");
strcpy(g_movieLibrary[26].filename, "m04tube");
strcpy(g_movieLibrary[27].filename, "m04nag");
strcpy(g_movieLibrary[28].filename, "m04outro");
strcpy(g_movieLibrary[29].filename, "m05tortu");
strcpy(g_movieLibrary[30].filename, "m05intro");
strcpy(g_movieLibrary[31].filename, "m05outro");
strcpy(g_movieLibrary[32].filename, "m07tortu");
strcpy(g_movieLibrary[33].filename, "m07intro");
strcpy(g_movieLibrary[34].filename, "m07sam");
strcpy(g_movieLibrary[35].filename, "m07doors");
strcpy(g_movieLibrary[36].filename, "m07outro");
strcpy(g_movieLibrary[37].filename, "m08intro");
strcpy(g_movieLibrary[38].filename, "m08betr");
strcpy(g_movieLibrary[39].filename, "m08tortu");
strcpy(g_movieLibrary[40].filename, "m08outro");
strcpy(g_movieLibrary[41].filename, "m10intro");
strcpy(g_movieLibrary[42].filename, "m10luk");
strcpy(g_movieLibrary[43].filename, "m10exp");
strcpy(g_movieLibrary[44].filename, "m10miss");
strcpy(g_movieLibrary[45].filename, "m10coptr");
strcpy(g_movieLibrary[46].filename, "m10outro");
}
void Movie_ID_to_name(uint32 id, char *buff) {
if (id >= TOTAL_NUMBER_OF_MOVIES)
Fatal_error("Movies information out-of-date!");
strcpy(buff, g_movieLibrary[id].filename);
}
int32 Movie_name_to_ID(char *name) {
for (uint32 i = 0; i < TOTAL_NUMBER_OF_MOVIES; i++) {
// Check all movies in the library
if (strcmp(name, g_movieLibrary[i].filename) == 0)
return i;
}
// This movie isn't viewable from the title screen menu
return -1;
}
void SaveThumbnail(uint32 slot_id) {
Common::WriteStream *stream;
char buff[128];
MakeFullThumbFilename(slot_id, buff);
stream = openDiskWriteStream(buff);
if (!stream)
Fatal_error("SaveThumbnail() failed to open a file");
// First off, check the thumb surface is valid
if (!g_thumbSurfaceID)
Fatal_error("SaveThumbnail() cannot save a null surface");
// Lock the directdraw surface (working buffer)
uint8 *surface_address = surface_manager->Lock_surface(g_thumbSurfaceID);
uint32 pitch = surface_manager->Get_pitch(g_thumbSurfaceID);
uint32 *u32surfPtr = (uint32 *)surface_address;
// Now we need to write the 64 by 48 image data out
for (uint32 i = 0; i < 48; i++) {
for (uint32 j = 0; j < 64; j++) {
stream->writeUint32LE(u32surfPtr[j]);
if (stream->err())
Fatal_error("SaveThumbnail() failed writing");
}
surface_address += pitch;
}
// Release it now
surface_manager->Unlock_surface(g_thumbSurfaceID);
delete stream;
}
void LoadThumbnail(uint32 slot_id, uint32 to_surface_id) {
char buff[128];
MakeFullThumbFilename(slot_id, buff);
// If no file exists then do nothing
if (!checkFileExists(buff)) // amode = 0
return;
Common::SeekableReadStream *stream = openDiskFileForBinaryStreamRead(buff);
if (!stream)
Fatal_error("LoadThumbnail() failed to open a file");
// First off, check the thumb surface is valid
if (!to_surface_id)
Fatal_error("LoadThumbnail() cannot read to a null surface");
// Lock the directdraw surface (working buffer)
uint8 *surface_address = surface_manager->Lock_surface(to_surface_id);
uint32 pitch = surface_manager->Get_pitch(to_surface_id);
// Now we need to read the 64 by 48 image data into the surface
for (uint32 i = 0; i < 48; i++) {
if (stream->read(surface_address, sizeof(uint32) * 64) != 64 * sizeof(uint32))
Fatal_error("LoadThumbnail() failed reading");
surface_address += pitch;
}
// Release it now
surface_manager->Unlock_surface(to_surface_id);
delete stream;
}
void LoadAMovieShot(uint32 slot_id, uint32 to_surface_id) {
char thbFile[128];
uint32 thbFileHash = NULL_HASH;
char art2DCluster[MAXLEN_CLUSTER_URL];
uint32 art2DClusterHash = NULL_HASH;
// Make the correct filename for this pic when clustered up
if (slot_id < 10)
sprintf(thbFile, "images\\pc\\movie0%d.thb", slot_id);
else
sprintf(thbFile, "images\\pc\\movie%d.thb", slot_id);
uint32 fo, fs;
// Now see if it exists in the cluster
if (!DoesClusterContainFile(pxVString("a\\2dart"), HashString(thbFile), fo, fs)) {
// If no file exists then fill the surface with black
surface_manager->Clear_surface(to_surface_id);
return;
}
// Set this up for resman and open the thb file
sprintf(art2DCluster, ICON_CLUSTER_PATH);
uint8 *data = (uint8 *)rs1->Res_open(thbFile, thbFileHash, art2DCluster, art2DClusterHash);
// First off, check the thumb surface is valid
if (!to_surface_id)
Fatal_error("LoadAMovieShot() cannot read to a null surface");
uint8 *surface_address = surface_manager->Lock_surface(to_surface_id);
uint32 pitch = surface_manager->Get_pitch(to_surface_id);
// Now we need to read the 100 by 56 image data into the surface
for (uint32 i = 0; i < 56; i++) {
for (uint32 j = 0; j < 100; j++) {
*surface_address++ = *data++;
*surface_address++ = *data++;
*surface_address++ = *data++;
*surface_address++ = *data++;
}
surface_address += (pitch - 400);
}
// Release it now
surface_manager->Unlock_surface(to_surface_id);
}
OptionsManager::OptionsManager() {
m_global_text = NULL; // Clear global text pointer
InitialiseGlobalColours();
m_inGame = FALSE8;
m_useDirtyRects = FALSE8;
m_gameover = FALSE8;
m_haveControl = FALSE8;
m_thatsEnoughTa = FALSE8;
m_autoAnimating = 0;
m_autoAnimating2 = 0;
m_optionsBox.left = 0;
m_optionsBox.right = 0;
m_optionsBox.top = 0;
m_optionsBox.bottom = 0;
m_over_n_Frames = 0;
m_grower = 0;
m_lipLength = 0;
m_box.left = m_box.right = m_box.top = m_box.bottom = 0;
m_targetBox.left = m_targetBox.right = m_targetBox.top = m_targetBox.bottom = 0;
m_interFrames = 0;
m_widthIncrements = 0;
m_bottomIncrements = 0;
m_topIncrements = 0;
m_warpDirection = TRUE8;
memset(m_fontName, 0, ENGINE_STRING_LEN);
m_font_file = NULL;
m_fontPalette = NULL;
m_fontHeight = 20;
m_currentSprite = NULL;
m_selectedShade.red = 202;
m_selectedShade.green = 0;
m_selectedShade.blue = 0;
m_selectedShade.alpha = 0;
// A useful rect to have
m_fullscreen.left = 0;
m_fullscreen.right = SCREEN_WIDTH;
m_fullscreen.top = 0;
m_fullscreen.bottom = SCREEN_DEPTH;
m_global_text = NULL;
// Explicitly set alpha:
m_drawColour.alpha = 0;
// Set default draw colour (nice pale blue)
SetDrawColour(BASE);
m_activeMenu = INGAME_TOP;
memset(m_slots, 0, TOTAL_NUMBER_OF_GAME_SLOTS * sizeof(_SLOT *));
m_slotOffset = 0;
// m_movieOffset set further down
m_M_TOP_selected = _NEWGAME;
m_M_EXTRA_selected = MOVIES;
m_M_MOVIE_selected = MOVIE01;
m_M_PLAYSELECT_selected = M01;
m_M_PROFILES_selected = CORD;
m_IG_TOP_selected = CONTINUE;
m_OPTION_selected = VIDEO_SETTINGS;
m_AUDIO_selected = MUSIC_VOLUME;
2021-03-13 11:37:20 +00:00
m_CONTROL_selected = METHOD;
m_GAMESLOT_selected = SLOT1;
m_SAVECONFIRM_selected = YEY;
m_QUIT_selected = YES;
m_GAMEOVER_selected = RESTORE;
// Initialise these to zero so we know if it's safe to kill them
m_myScreenSurfaceID = 0;
m_mySlotSurface1ID = 0;
for (uint32 i = 0; i < 8; i++) {
m_thumbSurfaceIDs[i] = 0;
m_grayThumbSurfaceIDs[i] = 0;
}
memset(m_movieSurfaceIDs, 0, 24 * sizeof(uint32));
memset(m_grayMovieSurfaceIDs, 0, 24 * sizeof(uint32));
m_profileSurface = 0;
m_movieRect.left = m_movieRect.right = m_movieRect.top = m_movieRect.bottom = 0;
m_colourKey = 0xFF00FF00;
m_moveLimiter = FALSE8;
m_alterLimiter = FALSE8;
m_choiceLimiter = FALSE8;
m_controlPage1 = FALSE8;
m_controlAnimCursor = 0;
m_move_sfx_channel = 0;
m_choose_sfx_channel = 0;
m_canSave = FALSE8;
m_cursorPos = 0;
memset(m_editBuffer, '\0', MAX_LABEL_LENGTH);
memset(m_defaultSlotName, '\0', MAX_LABEL_LENGTH);
m_editing = FALSE8;
m_defaultWiper = FALSE8;
m_emptySlotFlag = FALSE8;
m_timePlayed = 0;
m_paging = FALSE8;
m_pageleft = FALSE8;
// Rectangle enclosing the slot information (that animates when paging)
m_slotBoundingRect.left = 86; // 73
m_slotBoundingRect.right = 553; // 550
m_slotBoundingRect.top = 128;
m_slotBoundingRect.bottom = 375;
2021-03-13 13:31:49 +00:00
m_slotsAnimOffBy = 0;
m_pageOn_from.left = m_pageOn_from.right = m_pageOn_from.bottom = m_pageOn_from.top = 0;
m_pageOn_dest.left = m_pageOn_dest.right = m_pageOn_dest.bottom = m_pageOn_dest.top = 0;
m_pageOff_from.left = m_pageOff_from.right = m_pageOff_from.bottom = m_pageOff_from.top = 0;
m_pageOff_dest.left = m_pageOff_dest.right = m_pageOff_dest.bottom = m_pageOff_dest.top = 0;
m_letJoystickQuitEdit = FALSE8;
m_awaitingKeyPress = FALSE8;
m_assignFlash = 0;
// m_creditControl set further down
// m_crediter handled in its constructor
m_slideshowActive = FALSE8;
m_slideLimiter = FALSE8;
m_currentSlide = 0;
m_slideWadger = 0;
m_slideFillColour = 0;
m_margin = 0;
m_profileRect.left = m_profileRect.right = m_profileRect.bottom = m_profileRect.top = 0;
m_profileScrollingOffset = 0;
m_profileScrollingLine = 0;
m_lastLineDisplayed = FALSE8;
m_profileScrolling = 0;
// Initialise movie library
InitialiseMovieLibrary();
m_movieOffset = 0;
m_creditControl = FALSE8;
InitDeathText();
}
OptionsManager::~OptionsManager() {
// Just in case
DestroySlots();
}
void OptionsManager::MakeAllSurfii() {
uint32 i;
if (surface_manager) {
// Create full screen spare surface (used by bink or the faded game screen)
m_myScreenSurfaceID = surface_manager->Create_new_surface("Title screen", SCREEN_WIDTH, SCREEN_DEPTH, SYSTEM);
surface_manager->Set_transparent_colour_key(m_myScreenSurfaceID, m_colourKey);
surface_manager->Fill_surface(m_myScreenSurfaceID, m_colourKey);
// And one more surface for blit scrolling (initialise to transparent)
m_mySlotSurface1ID = surface_manager->Create_new_surface("Slot scroller 1", SCREEN_WIDTH, SCREEN_DEPTH, SYSTEM);
surface_manager->Set_transparent_colour_key(m_mySlotSurface1ID, m_colourKey);
surface_manager->Fill_surface(m_mySlotSurface1ID, m_colourKey);
// Setup a surface for the profile text
m_profileSurface = surface_manager->Create_new_surface("Profile scroller", 285, 240, SYSTEM);
surface_manager->Set_transparent_colour_key(m_profileSurface, 0);
// Create slot thumbnail surfii
for (i = 0; i < 8; i++) {
m_thumbSurfaceIDs[i] = surface_manager->Create_new_surface(pxVString("Thumb %d", i), 64, 48, SYSTEM);
m_grayThumbSurfaceIDs[i] = surface_manager->Create_new_surface(pxVString("Gray thumb %d", i), 64, 48, SYSTEM);
}
// Create movie thumbnail surfii
for (i = 0; i < 24; i++) {
m_movieSurfaceIDs[i] = surface_manager->Create_new_surface(pxVString("MovieLib thumb %d", i), 100, 56, SYSTEM);
m_grayMovieSurfaceIDs[i] = surface_manager->Create_new_surface(pxVString("Gray MovieLib thumb", i), 100, 56, SYSTEM);
}
} else
Fatal_error("OptionsManager::MakeAllSurfii() function called when surface_manager is NULL");
}
void OptionsManager::KillAllSurfii() {
uint32 i;
if (m_myScreenSurfaceID != 0) {
surface_manager->Kill_surface(m_myScreenSurfaceID);
m_myScreenSurfaceID = 0;
}
if (m_mySlotSurface1ID != 0) {
surface_manager->Kill_surface(m_mySlotSurface1ID);
m_mySlotSurface1ID = 0;
}
if (g_thumbSurfaceID != 0) {
surface_manager->Kill_surface(g_thumbSurfaceID);
g_thumbSurfaceID = 0;
}
if (m_profileSurface != 0) {
surface_manager->Kill_surface(m_profileSurface);
m_profileSurface = 0;
}
for (i = 0; i < 8; i++) {
if (m_thumbSurfaceIDs[i] != 0) {
surface_manager->Kill_surface(m_thumbSurfaceIDs[i]);
m_thumbSurfaceIDs[i] = 0;
}
if (m_grayThumbSurfaceIDs[i] != 0) {
surface_manager->Kill_surface(m_grayThumbSurfaceIDs[i]);
m_grayThumbSurfaceIDs[i] = 0;
}
}
for (i = 0; i < 24; i++) {
if (m_movieSurfaceIDs[i] != 0) {
surface_manager->Kill_surface(m_movieSurfaceIDs[i]);
m_movieSurfaceIDs[i] = 0;
}
if (m_grayMovieSurfaceIDs[i] != 0) {
surface_manager->Kill_surface(m_grayMovieSurfaceIDs[i]);
m_grayMovieSurfaceIDs[i] = 0;
}
}
}
void OptionsManager::StartInGameOptions() {
// Pauses the game and presents the in game options
2021-03-13 16:34:02 +00:00
g_stub->Push_stub_mode(__pause_menu);
if (g_theSpeechManager)
g_theSpeechManager->PauseSpeech();
if (g_theMusicManager)
g_theMusicManager->StopMusic();
InitialiseInGameOptions();
}
2021-03-07 08:56:34 +00:00
int32 GetDeathText() {
int32 i;
int32 t;
// Have 10 attempts at finding a random one not visited recently
i = 0;
while (i < DEATH_TEXT_RETRYS) {
t = (g_icb->getRandomSource()->getRandomNumber(MAX_DEATH_TEXT - 1));
if (!usedDeathText[t]) {
usedDeathText[t] = 1;
return (t + 1);
}
i++;
}
// Now we may well have used them all so reinit
InitDeathText();
// Choose any of them and set it's flag
t = (g_icb->getRandomSource()->getRandomNumber(MAX_DEATH_TEXT - 1));
usedDeathText[t] = 1;
return (t + 1);
}
void OptionsManager::StartGameOverOptions() {
bool8 regularPlayerDeath = TRUE8;
// Have we died under irregular circumstances?
c_game_object *playerObj = (c_game_object *)MS->objects->Fetch_item_by_number(MS->player.Fetch_player_id());
int32 state = playerObj->GetVariable("state");
if (playerObj->GetIntegerVariable(state) == 2)
regularPlayerDeath = FALSE8;
InitialiseSounds();
LoadGlobalTextFile();
LoadBitmapFont();
// Error check
if (g_mission == NULL)
Fatal_error("OptionsManager::StartGameOverOptions() needs to know what mission is running (ie Can't have mission == NULL)");
// Figure out what mission is running
const char *mn = g_mission->Fetch_tiny_mission_name();
switch (mn[2]) {
case '1':
g_missionNumber = 1;
break;
case '2':
g_missionNumber = 2;
break;
case '3':
g_missionNumber = 3;
break;
case '4':
g_missionNumber = 4;
break;
case '5':
g_missionNumber = 5;
break;
case '7':
g_missionNumber = 7;
break;
case '8':
2021-03-13 18:20:24 +00:00
if (g_globalScriptVariables->GetVariable("mission9") == 0)
g_missionNumber = 8;
else
g_missionNumber = 9;
break;
case '0':
g_missionNumber = 10;
break;
default:
Fatal_error("Couldn't resolve what mission we are currently running (how SHIT is that?) - sorry");
}
// So we know what logic to run
m_inGame = TRUE8;
m_gameover = TRUE8;
// Menu to start with
m_activeMenu = GAME_OVER;
// Initially selected option (first on the list)
m_GAMEOVER_selected = (GAMEOVER_CHOICES)0;
m_thatsEnoughTa = FALSE8;
// Get font height
SetCharacterSprite('W');
m_fontHeight = m_currentSprite->height;
InitialiseSlots();
// Set default draw colour
SetDrawColour(BASE);
MakeAllSurfii();
if (regularPlayerDeath) {
// Copy the screen so we can refresh
surface_manager->Blit_surface_to_surface(working_buffer_id, m_myScreenSurfaceID, &m_fullscreen, &m_fullscreen);
// Make the screen go red
BloodScreen();
} else {
// Black screen
surface_manager->Clear_surface(m_myScreenSurfaceID);
}
// Refresh our faded screen first of all
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
// Now play some death speech
char deathSpeech[128];
2021-03-07 08:56:34 +00:00
int32 ds = 5;
if (g_missionNumber < 9)
ds = GetDeathText();
sprintf(deathSpeech, "player_death%d", ds);
SayLineOfSpeech(HashString(deathSpeech));
// Stop the engine sounds
PauseSounds();
// Take control
m_haveControl = TRUE8;
m_useDirtyRects = TRUE8;
}
void OptionsManager::CycleInGameOptionsLogic() {
PollInput();
// Quit
if ((m_thatsEnoughTa) && (m_autoAnimating < 0)) {
// Refresh entire screen
surface_manager->Clear_surface(working_buffer_id);
2021-03-13 16:34:02 +00:00
g_stub->Update_screen();
// Free up any resources we have used
KillAllSurfii();
DestroySlots();
// An extra pop needed to return control to gamescript
if (g_resetToTitleScreen) {
g_resetToTitleScreen = FALSE8;
2021-03-13 16:34:02 +00:00
g_stub->Pop_stub_mode();
} else {
// Resume the engines sounds
UnpauseSounds();
g_theSpeechManager->ResumeSpeech();
}
// Then quit
2021-03-13 16:34:02 +00:00
g_stub->Pop_stub_mode();
m_haveControl = FALSE8;
return;
}
// Refresh our faded screen first of all
if (g_forceRefresh == TRUE8) {
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
g_forceRefresh = FALSE8;
}
if (!AnimateBracketsToBox(m_warpDirection))
DrawInGameOptionsScreen();
}
void OptionsManager::ForceInGameScreenRefresh() { g_forceRefresh = TRUE8; }
void OptionsManager::CycleGameOverLogic() {
// Leave input unpolled until speech finished
if (g_theSpeechManager->IsPlaying() == FALSE8)
PollInput();
// Quit
if (m_thatsEnoughTa) {
// Free up any resources we have used
KillAllSurfii();
DestroySlots();
// An extra pop needed to return control to gamescript
if (g_resetToTitleScreen) {
g_resetToTitleScreen = FALSE8;
2021-03-13 16:34:02 +00:00
g_stub->Pop_stub_mode();
}
// Then quit
2021-03-13 16:34:02 +00:00
g_stub->Pop_stub_mode();
m_haveControl = FALSE8;
// Specifically for the Restart Mission command but can't hurt anyway
UnpauseSounds();
return;
}
// Refresh our faded screen first of all
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
// Leave screen alone until speech finished
if (g_theSpeechManager->IsPlaying() == FALSE8)
DrawGameOverScreen();
}
void OptionsManager::SetDesiredOptionsBoxParameters(uint32 width, uint32 height, uint32 nFrames) {
// Size of options box that we want to display centred in the screen
m_optionsBox.left = (SCREEN_WIDTH / 2) - (width / 2);
m_optionsBox.right = (SCREEN_WIDTH / 2) + (width / 2);
m_optionsBox.top = (SCREEN_DEPTH / 2) - (height / 2) - 60;
m_optionsBox.bottom = (SCREEN_DEPTH / 2) + (height / 2) - 60;
// Set the number of frames to animate over
m_over_n_Frames = nFrames;
// Initial animating box dimensions (screen centre-point)
m_box.left = SCREEN_WIDTH / 2;
m_box.right = SCREEN_WIDTH / 2;
m_box.top = (SCREEN_DEPTH / 2) - 60;
m_box.bottom = (SCREEN_DEPTH / 2) - 60;
}
void OptionsManager::LoadTitleScreenMovie() {
// Initialise background movie (looping)
pxString filename;
filename.Format("gmovies\\title.bik");
filename.ConvertPath();
// For maximum playback performance on the title screen play this movie directly from memory
// Clean background res_man
rs_bg->Res_purge_all();
if (!g_personalSequenceManager->registerMovie(filename, FALSE8, TRUE8)) {
Fatal_error(pxVString("Couldn't register the title screen movie: %s", (const char *)filename));
}
// Calculate movie blitting rectangle
uint32 movieWidth = g_personalSequenceManager->getMovieWidth();
uint32 movieHeight = g_personalSequenceManager->getMovieHeight();
m_movieRect.left = 0;
m_movieRect.top = 0;
if (movieWidth != SCREEN_WIDTH) {
m_movieRect.left = (SCREEN_WIDTH / 2) - (movieWidth / 2);
}
if (movieHeight != SCREEN_DEPTH) {
m_movieRect.top = (SCREEN_DEPTH / 2) - (movieHeight / 2);
}
m_movieRect.right = m_movieRect.left + movieWidth;
m_movieRect.bottom = m_movieRect.top + movieHeight;
}
void OptionsManager::UnloadTitleScreenMovie() {
// Release bink from playing the background movie
g_personalSequenceManager->kill();
// Clean background res_man
rs_bg->Res_purge_all();
}
void OptionsManager::StartMainOptions(void) {
LoadBitmapFont();
LoadGlobalTextFile();
InitialiseSlots();
// So we know what logic to run
m_inGame = FALSE8;
m_gameover = FALSE8;
// Need to calculate bracket dimesions
uint32 int32estWidth = 0;
const char *msg = NULL;
2021-03-07 08:56:34 +00:00
for (uint32 i = 0; i < NUMBER_OF_MAIN_TOP_CHOICES; i++) {
switch (i) {
case 0:
msg = GetTextFromReference(HashString("opt_newgame"));
break;
case 1:
msg = GetTextFromReference(HashString("opt_loadgame"));
break;
case 2:
msg = GetTextFromReference(HashString("opt_options"));
break;
case 3:
msg = GetTextFromReference(HashString("opt_extras"));
break;
case 4:
msg = GetTextFromReference(HashString("opt_exitgame"));
break;
}
uint32 width;
width = CalculateStringWidth(msg);
if (width > int32estWidth)
int32estWidth = width;
}
// Get font height
SetCharacterSprite('W');
m_fontHeight = m_currentSprite->height;
// Size of the bracketed box we want to make
SetDesiredOptionsBoxParameters(int32estWidth + 30, 5 * (m_fontHeight + 10), 20);
// Set default draw colour
SetDrawColour(BASE);
m_activeMenu = MAIN_TOP;
// Initialise our animatable box to the correct size with lips extended
m_box = m_optionsBox;
m_lipLength = 10;
m_interFrames = -1;
LoadTitleScreenMovie();
surface_manager->Clear_surface(working_buffer_id);
// Get surfaces to use
MakeAllSurfii();
// Need to ensure that that global timer variable is set to zero
2021-03-13 18:20:24 +00:00
g_globalScriptVariables->SetVariable("missionelapsedtime", 0);
// Initial selection
m_M_TOP_selected = _NEWGAME;
// Whey-hey!
LoadVisibleMovieShots();
// New game or load from title screen (determines how to quit - ie continue gamescript or set mission)
g_mainMenuLoadPlease = FALSE8;
// The game intro is always visible
g_movieLibrary[0].visible = TRUE8;
InitialiseSounds();
m_slideshowActive = FALSE8;
m_thatsEnoughTa = FALSE8;
// Take control
m_haveControl = TRUE8;
m_useDirtyRects = FALSE8;
ResetTitleScreenTimeout();
}
void OptionsManager::InitialiseInGameOptions(void) {
InitialiseSounds();
PlayChosenFX();
Poll_Sound_Engine();
LoadGlobalTextFile();
LoadBitmapFont();
// So we know what logic to run
m_inGame = TRUE8;
m_gameover = FALSE8;
// Menu to start with
m_activeMenu = INGAME_TOP;
// Initially selected option (first on the list)
m_IG_TOP_selected = (IN_GAME_TOP_CHOICES)0;
m_thatsEnoughTa = FALSE8;
InitialiseSlots();
// Find out what mission we are in and set default slot name to match
const char *mn = g_mission->Fetch_tiny_mission_name();
switch (mn[2]) {
case '1':
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_investigatemine")), MAX_LABEL_LENGTH - 1);
break;
case '2':
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_securityhq")), MAX_LABEL_LENGTH - 1);
break;
case '3':
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_landtrain")), MAX_LABEL_LENGTH - 1);
break;
case '4':
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_containment")), MAX_LABEL_LENGTH - 1);
break;
case '5':
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_refinery")), MAX_LABEL_LENGTH - 1);
break;
case '7':
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_mainlandbase")), MAX_LABEL_LENGTH - 1);
break;
case '8':
2021-03-13 18:20:24 +00:00
if (g_globalScriptVariables->GetVariable("mission9") == 0)
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_islandbase")), MAX_LABEL_LENGTH - 1);
else
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_escape")), MAX_LABEL_LENGTH - 1);
break;
case '0':
strncpy(m_defaultSlotName, GetTextFromReference(HashString("opt_submarine")), MAX_LABEL_LENGTH - 1);
break;
default:
Fatal_error("Couldn't resolve what mission we are currently running (how SHIT is that?) - sorry");
}
// Ensure string is terminated (dem translations arg)
m_defaultSlotName[MAX_LABEL_LENGTH - 1] = 0;
// Get number of game ticks at ~12fps
2021-03-13 18:20:24 +00:00
int32 ticks = g_globalScriptVariables->GetVariable("missionelapsedtime");
// Convert to seconds played
m_timePlayed = (uint32)((float)ticks / 12.0f);
// Need to calculate bracket dimesions
uint32 int32estWidth = 0;
const char *msg = NULL;
2021-03-07 08:56:34 +00:00
for (uint32 i = 0; i < NUMBER_OF_IN_GAME_TOP_CHOICES; i++) {
switch (i) {
case 0:
msg = GetTextFromReference(HashString("opt_continue"));
break;
case 1:
msg = GetTextFromReference(HashString("opt_savegame"));
break;
case 2:
msg = GetTextFromReference(HashString("opt_loadgame"));
break;
case 3:
msg = GetTextFromReference(HashString("opt_options"));
break;
case 4:
msg = GetTextFromReference(HashString("opt_quit"));
break;
}
uint32 width;
width = CalculateStringWidth(msg);
if (width > int32estWidth)
int32estWidth = width;
}
// Get font height
SetCharacterSprite('W');
m_fontHeight = m_currentSprite->height;
// Size of the bracketed box we want to make
SetDesiredOptionsBoxParameters(int32estWidth + 30, NUMBER_OF_IN_GAME_TOP_CHOICES * (m_fontHeight + 10), 20);
m_interFrames = -1;
m_lipLength = 0;
// Set default draw colour
SetDrawColour(BASE);
MakeAllSurfii();
// Copy the faded screen so we can refresh
surface_manager->Blit_surface_to_surface(working_buffer_id, m_myScreenSurfaceID, &m_fullscreen, &m_fullscreen);
GrabThumbnailImage();
DarkenScreen();
// Update the working buffer with the faded game screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
// Is this a bad time to save the game
m_canSave = MS->Can_save();
// Stop the engine sounds
PauseSounds();
// Take control
m_haveControl = TRUE8;
m_useDirtyRects = TRUE8;
}
void OptionsManager::GrabThumbnailImage() {
// Here we need to get a thumbnail image of the current screen
g_thumbSurfaceID = surface_manager->Create_new_surface("Thumbnail", 64, 48, SYSTEM);
// Byte size of a pixel (ie 32-bit)
uint32 PIXELSIZE = 4;
uint32 r_total;
uint32 g_total;
uint32 b_total;
// The target surface to stored averaged pixel data
uint8 *dest_address = surface_manager->Lock_surface(g_thumbSurfaceID);
uint32 dest_pitch = surface_manager->Get_pitch(g_thumbSurfaceID);
// For screen surface access
uint8 *surface_address = surface_manager->Lock_surface(m_myScreenSurfaceID);
uint32 pitch = surface_manager->Get_pitch(m_myScreenSurfaceID);
uint32 blocksize = 10 * PIXELSIZE;
// Look at 10x10 portions of the screen in turn
for (uint32 row = 0; row < 48; row++) {
for (uint32 col = 0; col < 64; col++) {
r_total = 0;
g_total = 0;
b_total = 0;
// Gather pixel information
for (uint32 r = 0; r < 10; r++) {
for (uint32 c = 0; c < 10; c++) {
// Read pixel components to additive totals
r_total += *surface_address++;
g_total += *surface_address++;
b_total += *surface_address++;
surface_address++;
}
// Next line in this portion please
surface_address += (pitch - blocksize);
}
// Cheers, now move the surface pointer to first pixel in next portion
surface_address -= (pitch * 10);
surface_address += blocksize;
// Now record the average at the destination pointer
*dest_address++ = (uint8)(r_total / 100);
*dest_address++ = (uint8)(g_total / 100);
*dest_address++ = (uint8)(b_total / 100);
dest_address++;
}
// Need to move destination pointer to next line
dest_address += (dest_pitch - (64 * PIXELSIZE));
// Need to move source pointer to next line
surface_address += ((pitch * 10) - (640 * PIXELSIZE));
}
surface_manager->Unlock_surface(m_myScreenSurfaceID);
surface_manager->Unlock_surface(g_thumbSurfaceID);
}
void OptionsManager::DrawGameOverScreen() {
const char *msg = NULL;
switch (m_activeMenu) {
case GAME_OVER:
uint8 *ad;
uint32 pitch;
ad = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
// Game Over Dude
msg = GetTextFromReference(HashString("opt_gameover"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
msg = GetTextFromReference(HashString("opt_loadgame"));
DisplayText(ad, pitch, msg, 0, 150, (m_GAMEOVER_selected == RESTORE) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_restart"));
DisplayText(ad, pitch, msg, 0, 170, (m_GAMEOVER_selected == RESTART) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_quit"));
2021-03-13 13:31:49 +00:00
DisplayText(ad, pitch, msg, 0, 190, (m_GAMEOVER_selected == GAMEOVER) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
break;
case DEAD_LOAD:
DrawMainLoadScreen();
break;
case DEAD_QUIT:
DrawQuitGameConfirmScreen();
2020-09-20 17:44:03 +00:00
break;
default:
break;
}
}
void OptionsManager::DrawInGameOptionsScreen() {
bool8 animating;
uint32 pitch;
uint8 *surface_address;
const char *msg = NULL;
uint32 temp;
pxString str;
int32 icon_x, icon_y;
// Are we trying to quit options
if (m_thatsEnoughTa) {
AnimateThoseBrackets(FALSE8);
} else {
switch (m_activeMenu) {
case INGAME_TOP:
animating = AnimateThoseBrackets(TRUE8);
if (!animating) {
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
// Calculate proper positions from lengths once font is defined properly
msg = GetTextFromReference(HashString("opt_continue"));
DisplayText(surface_address, pitch, msg, 0, 130, (m_IG_TOP_selected == CONTINUE) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_savegame"));
if (m_canSave)
DisplayText(surface_address, pitch, msg, 0, 150, (m_IG_TOP_selected == SAVE_GAME) ? SELECTEDFONT : NORMALFONT, TRUE8);
else
DisplayText(surface_address, pitch, msg, 0, 150, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_loadgame"));
DisplayText(surface_address, pitch, msg, 0, 170, (m_IG_TOP_selected == LOAD_GAME) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_options"));
DisplayText(surface_address, pitch, msg, 0, 190, (m_IG_TOP_selected == OPTIONS) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_quit"));
DisplayText(surface_address, pitch, msg, 0, 210, (m_IG_TOP_selected == QUIT) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
SetDrawColour(BASE_DARK);
// Inner lining top
Draw_vertical_line(m_box.left, m_box.top, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right, m_box.top, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.top, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
// Inner lining bottom
Draw_vertical_line(m_box.left, m_box.bottom - m_lipLength, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right, m_box.bottom - m_lipLength, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.bottom, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
SetDrawColour(BASE);
// Outer lining top
Draw_vertical_line(m_box.left - 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left - 1, m_box.top - 1, m_box.right - m_box.left + 2, &m_drawColour, surface_address, pitch);
// Out lining bottom
Draw_vertical_line(m_box.left - 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.bottom + 1, m_box.right - m_box.left + 1, &m_drawColour, surface_address, pitch);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
}
break;
case INGAME_OPTIONS:
DrawGameOptions();
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
break;
case INGAME_VIDEO:
DrawVideoSettings();
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
break;
case INGAME_AUDIO:
DrawAudioSettings();
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
break;
case INGAME_CONTROLS:
DrawControllerConfiguration();
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
break;
case INGAME_SAVE:
// Set default drawing colour
SetDrawColour(BASE);
// The slots themselves
if (!m_paging) {
// Regular
DrawGameSlots(m_slotOffset);
} else {
// Animating
AnimateSlotsPaging();
}
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_savegame"));
DisplayText(surface_address, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
icon_x = 40;
icon_y = (SCREEN_DEPTH / 2) - (m_fontHeight / 2);
// Draw the non-selectable paging icons
if (m_slotOffset != 0) {
temp = CalculateStringWidth("<");
DrawRectangle((bool8)(m_paging && m_pageleft), icon_x, icon_y, temp + 18, m_fontHeight - 2, surface_address, pitch);
DisplayText(surface_address, pitch, "<", icon_x + 10, icon_y - 2, (m_paging && m_pageleft) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
if (m_slotOffset < TOTAL_NUMBER_OF_GAME_SLOTS - NUMBER_OF_VISIBLE_GAME_SLOTS) {
temp = CalculateStringWidth(">");
icon_x = SCREEN_WIDTH - icon_x - temp - 18;
DrawRectangle((bool8)(m_paging && !m_pageleft), icon_x, icon_y, temp + 18, m_fontHeight - 2, surface_address, pitch);
DisplayText(surface_address, pitch, ">", icon_x + 10, icon_y - 2, (m_paging && !m_pageleft) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(surface_address, pitch, msg, 0, 378, (m_GAMESLOT_selected == RETURN) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
break;
case INGAME_LOAD:
// Set default drawing colour
SetDrawColour(BASE);
// The slots themselves
if (!m_paging) {
// Regular
DrawGameSlots(m_slotOffset);
} else {
// Animating
AnimateSlotsPaging();
}
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_loadgame"));
DisplayText(surface_address, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
icon_x = 40;
icon_y = (SCREEN_DEPTH / 2) - (m_fontHeight / 2);
// Draw the non-selectable paging icons
if (m_slotOffset != 0) {
temp = CalculateStringWidth("<");
DrawRectangle((bool8)(m_paging && m_pageleft), icon_x, icon_y, temp + 18, m_fontHeight - 2, surface_address, pitch);
DisplayText(surface_address, pitch, "<", icon_x + 10, icon_y - 2, (m_paging && m_pageleft) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
if (m_slotOffset < TOTAL_NUMBER_OF_GAME_SLOTS - NUMBER_OF_VISIBLE_GAME_SLOTS) {
temp = CalculateStringWidth(">");
icon_x = SCREEN_WIDTH - icon_x - temp - 18;
DrawRectangle((bool8)(m_paging && !m_pageleft), icon_x, icon_y, temp + 18, m_fontHeight - 2, surface_address, pitch);
DisplayText(surface_address, pitch, ">", icon_x + 10, icon_y - 2, (m_paging && !m_pageleft) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(surface_address, pitch, msg, 0, 378, (m_GAMESLOT_selected == RETURN) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
break;
case INGAME_QUIT:
DrawQuitGameConfirmScreen();
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
break;
case INGAME_SAVECONFIRM:
DrawOverwriteSaveConfirmScreen();
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_gamepaused"));
DisplayText(surface_address, pitch, msg, 0, SCREEN_DEPTH - 50, NORMALFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
2020-09-20 17:44:03 +00:00
break;
default:
break;
}
}
// All done thanks
}
void OptionsManager::DrawQuitGameConfirmScreen(uint32 surface_id) {
const char *msg = NULL;
uint32 halfScreen = SCREEN_WIDTH / 2;
uint32 temp;
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
if (m_activeMenu == MAIN_QUIT) {
msg = GetTextFromReference(HashString("opt_exitgame"));
} else {
msg = GetTextFromReference(HashString("opt_quit"));
}
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
msg = GetTextFromReference(HashString("opt_confirm"));
DisplayText(ad, pitch, msg, 0, 140, NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_yes"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, 172, (bool8)(m_QUIT_selected == YES) ? SELECTEDFONT : NORMALFONT, FALSE8);
DisplayText(ad, pitch, "/", 0, 172, NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_no"));
DisplayText(ad, pitch, msg, halfScreen + 10, 172, (bool8)(m_QUIT_selected == NO) ? SELECTEDFONT : NORMALFONT, FALSE8);
// Unlock the working buffer
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::DrawOverwriteSaveConfirmScreen(uint32 surface_id) {
const char *msg = NULL;
uint32 halfScreen = SCREEN_WIDTH / 2;
uint32 temp;
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
msg = GetTextFromReference(HashString("questttl"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
msg = GetTextFromReference(HashString("okoverquest"));
DisplayText(ad, pitch, msg, 0, 140, NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_yes"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, 172, (bool8)(m_SAVECONFIRM_selected == YEY) ? SELECTEDFONT : NORMALFONT, FALSE8);
DisplayText(ad, pitch, "/", 0, 172, NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_no"));
DisplayText(ad, pitch, msg, halfScreen + 10, 172, (bool8)(m_SAVECONFIRM_selected == NAY) ? SELECTEDFONT : NORMALFONT, FALSE8);
// Unlock the working buffer
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::DrawMainOptionsScreen(uint32 surface_id) {
uint32 pitch;
uint8 *surface_address;
const char *msg = NULL;
// With bink compression updating only portions of the screen per cycle we
// need to draw things that beint32 on top to another surface then blit with
// transparency at the end of each cycle
switch (m_activeMenu) {
case MAIN_TOP: // Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
// Calculate proper positions from lengths once font is defined properly
msg = GetTextFromReference(HashString("opt_newgame"));
DisplayText(surface_address, pitch, msg, 0, 120, (m_M_TOP_selected == _NEWGAME) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_loadgame"));
DisplayText(surface_address, pitch, msg, 0, 140, (m_M_TOP_selected == _LOAD_GAME) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_options"));
DisplayText(surface_address, pitch, msg, 0, 160, (m_M_TOP_selected == _OPTIONS) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_extras"));
#ifdef PC_DEMO
DisplayText(surface_address, pitch, msg, 0, 180, PALEFONT, TRUE8);
#else
DisplayText(surface_address, pitch, msg, 0, 180, (m_M_TOP_selected == _EXTRAS) ? SELECTEDFONT : NORMALFONT, TRUE8);
#endif
msg = GetTextFromReference(HashString("opt_exitgame"));
DisplayText(surface_address, pitch, msg, 0, 220, (m_M_TOP_selected == _EXIT_GAME) ? SELECTEDFONT : NORMALFONT, TRUE8);
SetDrawColour(BASE_DARK);
// Inner lining top
Draw_vertical_line(m_box.left, m_box.top, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right, m_box.top, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.top, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
// Inner lining bottom
Draw_vertical_line(m_box.left, m_box.bottom - m_lipLength, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right, m_box.bottom - m_lipLength, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.bottom, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
SetDrawColour(BASE);
// Outer lining top
Draw_vertical_line(m_box.left - 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left - 1, m_box.top - 1, m_box.right - m_box.left + 2, &m_drawColour, surface_address, pitch);
// Out lining bottom
Draw_vertical_line(m_box.left - 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.bottom + 1, m_box.right - m_box.left + 1, &m_drawColour, surface_address, pitch);
DisplayText(surface_address, pitch, "Copyright 2000 Revolution Software", 0, SCREEN_DEPTH - 100, PALEFONT, TRUE8);
// Unlock the working buffer
surface_manager->Unlock_surface(surface_id);
break;
case MAIN_LOAD:
DrawMainLoadScreen();
break;
case MAIN_OPTIONS:
DrawGameOptions();
break;
case MAIN_VIDEO:
DrawVideoSettings();
break;
case MAIN_AUDIO:
DrawAudioSettings();
break;
case MAIN_CONTROLS:
DrawControllerConfiguration();
break;
case MAIN_EXTRAS:
DrawExtrasScreen();
break;
case MAIN_MOVIES:
DrawMovieScreen();
break;
case MAIN_PLAYSELECT:
DrawPlaySelectScreen();
break;
case MAIN_PROFILES:
DrawProfileSelectScreen();
break;
case MAIN_A_PROFILE:
DrawProfileScreen();
break;
case MAIN_QUIT:
DrawQuitGameConfirmScreen(surface_id);
2020-09-20 17:44:03 +00:00
break;
default:
break;
}
}
void OptionsManager::CycleMainOptionsLogic() {
uint32 totalTime = GetMicroTimer();
if (g_titleScreenInitialCount == 0) {
g_titleScreenInitialCount = g_system->getMillis();
}
// Calculate seconds elapsed
g_titleScreenSecondsElapsed = (g_system->getMillis() - g_titleScreenInitialCount) / 1000;
// No demo movie for russian or polish thanks
if (g_theClusterManager->GetLanguage() == T_RUSSIAN || g_theClusterManager->GetLanguage() == T_POLISH)
g_titleScreenSecondsElapsed = 0;
// Hack for dreamcatcher, don't play demo movie either
if (g_theClusterManager->GetLanguage() == T_ENGLISH)
g_titleScreenSecondsElapsed = 0;
if (g_titleScreenSecondsElapsed >= g_titleScreenAutoDelay) {
// Reset timer count
g_titleScreenInitialCount = 0;
// Half next delay length
g_titleScreenAutoDelay = (uint32)(g_titleScreenAutoDelay / 2);
// Catch lower limit
if (g_titleScreenAutoDelay < 5)
g_titleScreenAutoDelay = 5;
// Bink can only handle one movie at a time
UnloadTitleScreenMovie();
// Reset menu to top-level
m_activeMenu = MAIN_TOP;
m_M_TOP_selected = _NEWGAME;
m_box = m_optionsBox;
m_lipLength = 10;
m_interFrames = -1;
m_slideshowActive = FALSE8;
m_thatsEnoughTa = FALSE8;
// Play movie here
Init_play_movie("demo", FALSE8);
// Go and play the movie
return;
}
// Sometimes we need to shutdown the title screen movie so check if we need to reload it
if (g_personalSequenceManager->busy() == FALSE8) {
LoadTitleScreenMovie();
}
PollInput();
// Quit
if (m_thatsEnoughTa) {
// Free up any resources we have used
KillAllSurfii();
DestroySlots();
UnloadTitleScreenMovie();
// Then quit
if (!g_mainMenuLoadPlease) {
// If we want a new game then just pop to gamescript
2021-03-13 16:34:02 +00:00
g_stub->Pop_stub_mode();
} else {
// When loading a game this sets the right gamescript position
2021-03-13 16:34:02 +00:00
g_stub->Set_current_stub_mode(__mission_and_console);
}
UnpauseSounds();
m_haveControl = FALSE8;
return;
}
if (m_slideshowActive) {
DrawSlideShow();
} else {
if (g_skipBackgroundDrawOverFrames != 0)
return;
movieTime = GetMicroTimer();
// Now we need to draw a frame of the title movie as our backdrop (to its own private buffer)
g_personalSequenceManager->drawFrame(m_myScreenSurfaceID);
movieTime = GetMicroTimer() - movieTime;
movieblitTime = GetMicroTimer();
// Now blit the movie frame without transparency to the working buffer
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_movieRect, &m_movieRect, 0);
movieblitTime = GetMicroTimer() - movieblitTime;
drawTime = GetMicroTimer();
// And draw the options on top
if (!AnimateBracketsToBox(m_warpDirection, working_buffer_id))
DrawMainOptionsScreen(working_buffer_id);
drawTime = GetMicroTimer() - drawTime;
}
totalTime = GetMicroTimer() - totalTime;
}
void OptionsManager::CycleLogic() {
Poll_Sound_Engine();
// Have we been asked to force refresh the background
if (g_skipBackgroundDrawOverFrames > 0) {
g_skipBackgroundDrawOverFrames--;
return;
}
// Res_open every cycle to ensure it stays in memory
LoadGlobalTextFile();
if (m_gameover) {
CycleGameOverLogic();
} else {
if (m_inGame)
CycleInGameOptionsLogic();
else
CycleMainOptionsLogic();
}
}
void OptionsManager::PlayMoveFX() {
if (g_theFxManager) {
g_theFxManager->Stop(m_move_sfx_channel);
g_theFxManager->SetVolume(m_move_sfx_channel, GetSfxVolume());
// Begin playback
g_theFxManager->Play(m_move_sfx_channel);
}
}
void OptionsManager::PlayChosenFX() {
if (g_theFxManager) {
g_theFxManager->Stop(m_choose_sfx_channel);
g_theFxManager->SetVolume(m_choose_sfx_channel, GetSfxVolume());
// Begin playback
g_theFxManager->Play(m_choose_sfx_channel);
}
}
void OptionsManager::InitialiseSounds() {
if (g_theFxManager) {
m_move_sfx_channel = 22;
m_choose_sfx_channel = 23;
// Unload samples if channels have one
g_theFxManager->Unregister(m_move_sfx_channel);
g_theFxManager->Unregister(m_choose_sfx_channel);
uint32 b_offset, sz;
if (!DoesClusterContainFile(pxVString("g\\samples.clu"), HashString("options_select.wav"), b_offset, sz))
Fatal_error(pxVString("Couldn't find options_select.wav in global sample cluster"));
// Pass sample name only if we're running from clusters
g_theFxManager->Register(m_move_sfx_channel, "options_select.wav", 0, b_offset);
if (!DoesClusterContainFile(pxVString("g\\samples.clu"), HashString("options_choose.wav"), b_offset, sz))
Fatal_error(pxVString("Couldn't find options_choose.wav in global sample cluster"));
// Pass sample name only if we're running from clusters
g_theFxManager->Register(m_choose_sfx_channel, "options_choose.wav", 0, b_offset);
}
}
void OptionsManager::MoveSelected(bool8 _down_) {
if (m_activeMenu != MAIN_A_PROFILE) {
if (m_moveLimiter)
return;
}
m_moveLimiter = TRUE8;
int32 currentlySelected;
2021-03-13 18:20:24 +00:00
int32 demo = g_globalScriptVariables->GetVariable("demo");
ResetTitleScreenTimeout();
// Need to switch on the menu currently active
switch (m_activeMenu) {
case MAIN_TOP:
currentlySelected = m_M_TOP_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
#ifdef PC_DEMO
if (currentlySelected == _EXTRAS) {
if (_down_)
currentlySelected++;
else
currentlySelected--;
}
#endif
if (currentlySelected < 0)
m_M_TOP_selected = (MAIN_TOP_CHOICES)(NUMBER_OF_MAIN_TOP_CHOICES - 1);
else
m_M_TOP_selected = (MAIN_TOP_CHOICES)(currentlySelected % NUMBER_OF_MAIN_TOP_CHOICES);
break;
case DEAD_LOAD:
case INGAME_SAVE:
case INGAME_LOAD:
case MAIN_LOAD:
currentlySelected = m_GAMESLOT_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_GAMESLOT_selected = (GAMESLOT_CHOICES)(NUMBER_OF_GAMESLOT_CHOICES - 1);
else
m_GAMESLOT_selected = (GAMESLOT_CHOICES)(currentlySelected % NUMBER_OF_GAMESLOT_CHOICES);
break;
case MAIN_EXTRAS:
currentlySelected = m_M_EXTRA_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
2021-03-14 07:59:48 +00:00
if (g_px->game_completed) {
if (currentlySelected < 0)
m_M_EXTRA_selected = (M_EXTRA_CHOICES)(NUMBER_OF_EXTRA_CHOICES - 1);
else
m_M_EXTRA_selected = (M_EXTRA_CHOICES)(currentlySelected % NUMBER_OF_EXTRA_CHOICES);
} else {
if (currentlySelected < 0)
m_M_EXTRA_selected = (M_EXTRA_CHOICES)(NUMBER_OF_EXTRA_CHOICES - 1);
else {
if (_down_ && currentlySelected == SLIDESHOW)
m_M_EXTRA_selected = CREDITS;
else if (!_down_ && currentlySelected == PROFILES)
m_M_EXTRA_selected = MOVIES;
else
m_M_EXTRA_selected = (M_EXTRA_CHOICES)(currentlySelected % NUMBER_OF_EXTRA_CHOICES);
}
}
break;
case MAIN_PLAYSELECT:
currentlySelected = m_M_PLAYSELECT_selected;
moveme:
if (_down_)
currentlySelected++;
else
currentlySelected--;
// Wraparound
if (currentlySelected < 0)
currentlySelected = NUMBER_OF_PLAYSELECT_CHOICES - 1;
else
currentlySelected = currentlySelected % NUMBER_OF_PLAYSELECT_CHOICES;
if (demo) {
if (currentlySelected != M01 && currentlySelected != M04 && currentlySelected != M07 && currentlySelected != CANCEL)
goto moveme;
}
m_M_PLAYSELECT_selected = (M_PLAYSELECT_CHOICES)currentlySelected;
break;
case MAIN_MOVIES: // Can't move up or down from the paging icons
if (m_M_MOVIE_selected == PAGELEFT || m_M_MOVIE_selected == PAGERIGHT)
break;
currentlySelected = m_M_MOVIE_selected;
// Moving up
if (!_down_) {
// To move up from the top row
if (currentlySelected < 4) {
// Not allowed to move
return;
} else {
// Moving up from the back button
if (currentlySelected == NOTHANKS) {
// Hardcode this behaviour
currentlySelected = MOVIE10;
} else {
// Up one row by all other means
currentlySelected -= 4;
}
}
} else {
// To move down from the back button
if (currentlySelected == NOTHANKS) {
// Not allowed to move
return;
} else {
// Moving down from the bottom row
if (currentlySelected > MOVIE08) {
currentlySelected = NOTHANKS;
} else {
// Down one row by all other means
currentlySelected += 4;
}
}
}
// As movie total is dynamic then we need to provide further code here to catch the arse end
if (currentlySelected != NOTHANKS) {
if (currentlySelected + m_movieOffset >= TOTAL_NUMBER_OF_MOVIES) {
// Ban this illegal move
currentlySelected = m_M_MOVIE_selected;
return;
}
}
// Avoid playing a sound if we haven't changed the selection
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)currentlySelected) {
return;
}
// Assign new selection
m_M_MOVIE_selected = (M_MOVIE_CHOICES)currentlySelected;
break;
case MAIN_PROFILES:
currentlySelected = m_M_PROFILES_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_M_PROFILES_selected = (M_PROFILES_CHOICES)(M_NUMBER_OF_PROFILE_CHOICES - 1);
else
m_M_PROFILES_selected = (M_PROFILES_CHOICES)(currentlySelected % M_NUMBER_OF_PROFILE_CHOICES);
break;
case MAIN_A_PROFILE:
if (_down_) {
// Can we scroll down at all
if (m_lastLineDisplayed)
return;
m_profileScrolling = 1;
m_profileScrollingOffset += 2;
if (m_profileScrollingOffset == 20) {
// Down a line
m_profileScrollingOffset = 0;
m_profileScrollingLine++;
}
} else {
// Can we scroll up at all
if (m_profileScrollingLine == -1 && m_profileScrollingOffset == 0)
return;
m_profileScrolling = -1;
m_profileScrollingOffset -= 2;
if (m_profileScrollingOffset == -2) {
// Up a line
m_profileScrollingOffset = 18;
m_profileScrollingLine--;
}
}
return;
case DEAD_QUIT:
case INGAME_QUIT:
case MAIN_QUIT:
currentlySelected = m_QUIT_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_QUIT_selected = (QUIT_CHOICES)(NUMBER_OF_QUIT_CHOICES - 1);
else
m_QUIT_selected = (QUIT_CHOICES)(currentlySelected % NUMBER_OF_QUIT_CHOICES);
break;
case INGAME_SAVECONFIRM:
currentlySelected = m_SAVECONFIRM_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_SAVECONFIRM_selected = (SAVECONFIRM_CHOICES)(NUMBER_OF_SAVECONFIRM_CHOICES - 1);
else
m_SAVECONFIRM_selected = (SAVECONFIRM_CHOICES)(currentlySelected % NUMBER_OF_SAVECONFIRM_CHOICES);
break;
case INGAME_TOP:
currentlySelected = m_IG_TOP_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
// Is save game disabled
if (m_canSave == FALSE8) {
if (currentlySelected == SAVE_GAME) {
if (_down_)
currentlySelected++;
else
currentlySelected--;
}
}
if (currentlySelected < 0)
m_IG_TOP_selected = (IN_GAME_TOP_CHOICES)(NUMBER_OF_IN_GAME_TOP_CHOICES - 1);
else
m_IG_TOP_selected = (IN_GAME_TOP_CHOICES)(currentlySelected % NUMBER_OF_IN_GAME_TOP_CHOICES);
break;
case MAIN_OPTIONS:
case INGAME_OPTIONS:
currentlySelected = m_OPTION_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_OPTION_selected = (OPTION_CHOICES)(NUMBER_OPTION_CHOICES - 1);
else
m_OPTION_selected = (OPTION_CHOICES)(currentlySelected % NUMBER_OPTION_CHOICES);
break;
case MAIN_VIDEO:
case INGAME_VIDEO:
currentlySelected = m_VIDEO_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (g_videoOptionsCheat == FALSE8) {
// Illegal selections without cheat
if (currentlySelected == SHADOWS || currentlySelected == FRAMELIMITER) {
if (_down_ == TRUE8)
currentlySelected = LEAVE;
else
2021-03-06 12:02:40 +00:00
currentlySelected = SUBTITLES;
}
}
if (currentlySelected < 0)
m_VIDEO_selected = (VIDEO_CHOICES)(NUMBER_OF_VIDEO_CHOICES - 1);
else
m_VIDEO_selected = (VIDEO_CHOICES)(currentlySelected % NUMBER_OF_VIDEO_CHOICES);
break;
case MAIN_AUDIO:
case INGAME_AUDIO:
currentlySelected = m_AUDIO_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_AUDIO_selected = (AUDIO_CHOICES)(NUMBER_OF_AUDIO_CHOICES - 1);
else
m_AUDIO_selected = (AUDIO_CHOICES)(currentlySelected % NUMBER_OF_AUDIO_CHOICES);
break;
case MAIN_CONTROLS:
case INGAME_CONTROLS:
currentlySelected = m_CONTROL_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_CONTROL_selected = (CONTROL_CHOICES)(NUMBER_OF_CONTROL_CHOICES - 1);
else
m_CONTROL_selected = (CONTROL_CHOICES)(currentlySelected % NUMBER_OF_CONTROL_CHOICES);
break;
case GAME_OVER:
currentlySelected = m_GAMEOVER_selected;
if (_down_)
currentlySelected++;
else
currentlySelected--;
if (currentlySelected < 0)
m_GAMEOVER_selected = (GAMEOVER_CHOICES)(NUMBER_OF_GAMEOVER_CHOICES - 1);
else
m_GAMEOVER_selected = (GAMEOVER_CHOICES)(currentlySelected % NUMBER_OF_GAMEOVER_CHOICES);
break;
default:
return;
}
PlayMoveFX();
Poll_Sound_Engine();
}
void OptionsManager::AlterSelected(bool8 _right_) {
uint32 currentlySelected;
ResetTitleScreenTimeout();
// Need to switch on the menu currently active
switch (m_activeMenu) {
case DEAD_QUIT:
case INGAME_QUIT:
case MAIN_QUIT:
if (m_alterLimiter)
break;
m_alterLimiter = TRUE8;
MoveSelected(TRUE8);
break;
case DEAD_LOAD:
case INGAME_LOAD:
case INGAME_SAVE:
case MAIN_LOAD:
if (m_alterLimiter)
break;
m_alterLimiter = TRUE8;
if (m_paging)
break;
// Initialise blitting rectangles
m_pageOn_from = m_slotBoundingRect;
m_pageOn_dest = m_slotBoundingRect;
m_pageOff_from = m_slotBoundingRect;
m_pageOff_dest = m_slotBoundingRect;
if (!_right_) {
m_pageleft = TRUE8;
m_pageOn_dest.left = m_pageOn_dest.right = 0;
if (m_slotOffset > 0) {
m_paging = TRUE8;
LoadPagingThumbnails(m_slotOffset - NUMBER_OF_VISIBLE_GAME_SLOTS);
LoadVisibleThumbnails();
} else
return;
} else {
m_pageleft = FALSE8;
m_pageOn_dest.left = m_pageOn_dest.right = SCREEN_WIDTH - 1;
if (m_slotOffset != (TOTAL_NUMBER_OF_GAME_SLOTS - NUMBER_OF_VISIBLE_GAME_SLOTS)) {
m_paging = TRUE8;
LoadPagingThumbnails(m_slotOffset + NUMBER_OF_VISIBLE_GAME_SLOTS);
LoadVisibleThumbnails();
} else
return;
}
// Special case move
PlayChosenFX();
Poll_Sound_Engine();
break;
case INGAME_SAVECONFIRM:
if (m_alterLimiter)
break;
m_alterLimiter = TRUE8;
MoveSelected(TRUE8);
break;
case MAIN_MOVIES:
if (m_alterLimiter)
break;
m_alterLimiter = TRUE8;
// Can't move left or right from the back button
if (m_M_MOVIE_selected == NOTHANKS)
break;
currentlySelected = m_M_MOVIE_selected;
// Move right
if (_right_) {
if (currentlySelected == PAGERIGHT) {
// Not allowed to move but might be nice if this actually paged here
DoChoice();
break;
} else {
if (currentlySelected == PAGELEFT) {
currentlySelected = MOVIE05;
} else {
// Last column
if (currentlySelected % 4 == 3) {
// The icon is unavailable if we're on the last page
if (m_movieOffset >= TOTAL_NUMBER_OF_MOVIES - M_NUMBER_OF_VISIBLE_MOVIE_SLOTS)
break;
// Select the paging icon and auto-page
m_M_MOVIE_selected = PAGERIGHT;
DoChoice();
break;
} else {
currentlySelected++;
}
}
}
} else {
if (currentlySelected == PAGELEFT) {
// Not allowed to move but might be nice if this actually paged here
DoChoice();
break;
} else {
if (currentlySelected == PAGERIGHT) {
currentlySelected = MOVIE08;
} else {
if (currentlySelected % 4 == 0) {
// The icon is unavailable if we're on the first page
if (m_movieOffset == 0)
break;
// Select the paging icon and auto-page
m_M_MOVIE_selected = PAGELEFT;
DoChoice();
break;
} else {
currentlySelected--;
}
}
}
}
// As movie total is dynamic then we need to provide further code here
if (currentlySelected < NOTHANKS) {
if (currentlySelected + m_movieOffset >= TOTAL_NUMBER_OF_MOVIES) {
// Ban this illegal move
currentlySelected = m_M_MOVIE_selected;
break;
}
}
// Special case move
if (m_M_MOVIE_selected != (M_MOVIE_CHOICES)currentlySelected) {
PlayMoveFX();
Poll_Sound_Engine();
}
// Assign new selection
m_M_MOVIE_selected = (M_MOVIE_CHOICES)currentlySelected;
break;
case MAIN_VIDEO:
case INGAME_VIDEO:
if (m_alterLimiter)
break;
m_alterLimiter = TRUE8;
switch (m_VIDEO_selected) {
case SUBTITLES:
2021-03-14 07:59:48 +00:00
if (g_px->on_screen_text)
g_px->on_screen_text = FALSE8;
else
2021-03-14 07:59:48 +00:00
g_px->on_screen_text = TRUE8;
// Chosen noise please
PlayChosenFX();
Poll_Sound_Engine();
return;
case SHADOWS:
if (_right_) {
2021-03-14 07:59:48 +00:00
if (g_px->actorShadows == 3)
g_px->actorShadows = -1;
else
2021-03-14 07:59:48 +00:00
g_px->actorShadows++;
} else {
2021-03-14 07:59:48 +00:00
if (g_px->actorShadows == -1)
g_px->actorShadows = 3;
else
2021-03-14 07:59:48 +00:00
g_px->actorShadows--;
}
// Chosen noise please
PlayChosenFX();
Poll_Sound_Engine();
return;
case FRAMELIMITER:
if (_right_) {
2021-03-13 16:34:02 +00:00
if (g_stub->cycle_speed < 200)
g_stub->cycle_speed += 10;
else if (g_stub->cycle_speed < 951)
g_stub->cycle_speed += 50;
} else {
2021-03-13 16:34:02 +00:00
if (g_stub->cycle_speed > 200)
g_stub->cycle_speed -= 50;
else if (g_stub->cycle_speed > 10)
g_stub->cycle_speed -= 10;
}
PlayChosenFX();
Poll_Sound_Engine();
return;
2020-09-20 17:44:03 +00:00
default:
break;
}
break;
case MAIN_AUDIO:
case INGAME_AUDIO:
switch (m_AUDIO_selected) {
case MUSIC_VOLUME:
if (_right_) {
if (GetMusicVolume() < 128)
SetMusicVolume(GetMusicVolume() + 1);
} else {
if (GetMusicVolume() > 0)
SetMusicVolume(GetMusicVolume() - 1);
}
// Update it
if (g_theMusicManager) {
g_theMusicManager->SetMusicVolume(GetMusicVolume());
g_personalSequenceManager->setVolume(GetMusicVolume());
g_theSequenceManager->setVolume(GetMusicVolume());
}
break;
case SPEECH_VOLUME:
if (_right_) {
if (GetSpeechVolume() < 128)
SetSpeechVolume(GetSpeechVolume() + 1);
} else {
if (GetSpeechVolume() > 0)
SetSpeechVolume(GetSpeechVolume() - 1);
}
// Update it
if (g_theSpeechManager) {
g_theSpeechManager->SetSpeechVolume(GetSpeechVolume());
}
break;
case SFX_VOLUME:
if (_right_) {
if (GetSfxVolume() < 128)
SetSfxVolume(GetSfxVolume() + 1);
} else {
if (GetSfxVolume() > 0)
SetSfxVolume(GetSfxVolume() - 1);
}
break;
2020-09-20 17:44:03 +00:00
default:
break;
}
break;
case MAIN_CONTROLS:
case INGAME_CONTROLS:
if (m_alterLimiter)
break;
m_alterLimiter = TRUE8;
switch (m_CONTROL_selected) {
2021-03-07 20:57:29 +00:00
case METHOD:
if (g_icb_session->player.Get_control_mode() == ACTOR_RELATIVE)
g_icb_session->player.Set_control_mode(SCREEN_RELATIVE);
else
g_icb_session->player.Set_control_mode(ACTOR_RELATIVE);
break;
default:
break;
}
PlayChosenFX();
Poll_Sound_Engine();
break;
2020-09-20 17:44:03 +00:00
default:
break;
}
}
void OptionsManager::MakeGrayScaleThumbnail(uint32 src_surface_id, uint32 dst_surface_id, uint32 w, uint32 h) {
// The target surface
uint8 *dst_address = surface_manager->Lock_surface(dst_surface_id);
uint32 dst_pitch = surface_manager->Get_pitch(dst_surface_id);
// The source surface
uint8 *src_address = surface_manager->Lock_surface(src_surface_id);
uint32 src_pitch = surface_manager->Get_pitch(src_surface_id);
uint32 width = w * 4;
// Loop over all pixels
for (uint32 row = 0; row < h; row++) {
for (uint32 col = 0; col < w; col++) {
uint8 r = *src_address++;
uint8 g = *src_address++;
uint8 b = *src_address++;
src_address++;
uint8 gray = (uint8)((r + g + b) / 3);
*dst_address++ = gray;
*dst_address++ = gray;
*dst_address++ = gray;
dst_address++;
}
src_address += (src_pitch - width);
dst_address += (dst_pitch - width);
}
surface_manager->Unlock_surface(src_surface_id);
surface_manager->Unlock_surface(dst_surface_id);
}
void OptionsManager::LoadVisibleThumbnails() {
// Load the four visible slot thumbnails to our first four slot surfaces
LoadThumbnail(m_slotOffset + SLOT1, m_thumbSurfaceIDs[SLOT1]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[SLOT1], m_grayThumbSurfaceIDs[SLOT1]);
LoadThumbnail(m_slotOffset + SLOT2, m_thumbSurfaceIDs[SLOT2]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[SLOT2], m_grayThumbSurfaceIDs[SLOT2]);
LoadThumbnail(m_slotOffset + SLOT3, m_thumbSurfaceIDs[SLOT3]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[SLOT3], m_grayThumbSurfaceIDs[SLOT3]);
LoadThumbnail(m_slotOffset + SLOT4, m_thumbSurfaceIDs[SLOT4]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[SLOT4], m_grayThumbSurfaceIDs[SLOT4]);
}
void OptionsManager::LoadVisibleMovieShots() {
// Load all movie pics for the selected page
LoadAMovieShot(m_movieOffset + MOVIE01, m_movieSurfaceIDs[MOVIE01]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE01], m_grayMovieSurfaceIDs[MOVIE01], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE02, m_movieSurfaceIDs[MOVIE02]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE02], m_grayMovieSurfaceIDs[MOVIE02], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE03, m_movieSurfaceIDs[MOVIE03]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE03], m_grayMovieSurfaceIDs[MOVIE03], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE04, m_movieSurfaceIDs[MOVIE04]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE04], m_grayMovieSurfaceIDs[MOVIE04], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE05, m_movieSurfaceIDs[MOVIE05]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE05], m_grayMovieSurfaceIDs[MOVIE05], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE06, m_movieSurfaceIDs[MOVIE06]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE06], m_grayMovieSurfaceIDs[MOVIE06], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE07, m_movieSurfaceIDs[MOVIE07]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE07], m_grayMovieSurfaceIDs[MOVIE07], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE08, m_movieSurfaceIDs[MOVIE08]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE08], m_grayMovieSurfaceIDs[MOVIE08], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE09, m_movieSurfaceIDs[MOVIE09]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE09], m_grayMovieSurfaceIDs[MOVIE09], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE10, m_movieSurfaceIDs[MOVIE10]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE10], m_grayMovieSurfaceIDs[MOVIE10], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE11, m_movieSurfaceIDs[MOVIE11]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE11], m_grayMovieSurfaceIDs[MOVIE11], 100, 56);
LoadAMovieShot(m_movieOffset + MOVIE12, m_movieSurfaceIDs[MOVIE12]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[MOVIE12], m_grayMovieSurfaceIDs[MOVIE12], 100, 56);
}
void OptionsManager::LoadPagingThumbnails(uint32 slotOffset) {
// Load the four visible slot thumbnails to our first four slot surfaces
LoadThumbnail(slotOffset + SLOT1, m_thumbSurfaceIDs[4]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[4], m_grayThumbSurfaceIDs[4]);
LoadThumbnail(slotOffset + SLOT2, m_thumbSurfaceIDs[5]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[5], m_grayThumbSurfaceIDs[5]);
LoadThumbnail(slotOffset + SLOT3, m_thumbSurfaceIDs[6]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[6], m_grayThumbSurfaceIDs[6]);
LoadThumbnail(slotOffset + SLOT4, m_thumbSurfaceIDs[7]);
MakeGrayScaleThumbnail(m_thumbSurfaceIDs[7], m_grayThumbSurfaceIDs[7]);
}
void OptionsManager::LoadPagingMovieShots(uint32 slotOffset) {
// Load all movie pics for the selected page
LoadAMovieShot(slotOffset + MOVIE01, m_movieSurfaceIDs[12]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[12], m_grayMovieSurfaceIDs[12], 100, 56);
LoadAMovieShot(slotOffset + MOVIE02, m_movieSurfaceIDs[13]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[13], m_grayMovieSurfaceIDs[13], 100, 56);
LoadAMovieShot(slotOffset + MOVIE03, m_movieSurfaceIDs[14]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[14], m_grayMovieSurfaceIDs[14], 100, 56);
LoadAMovieShot(slotOffset + MOVIE04, m_movieSurfaceIDs[15]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[15], m_grayMovieSurfaceIDs[15], 100, 56);
LoadAMovieShot(slotOffset + MOVIE05, m_movieSurfaceIDs[16]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[16], m_grayMovieSurfaceIDs[16], 100, 56);
LoadAMovieShot(slotOffset + MOVIE06, m_movieSurfaceIDs[17]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[17], m_grayMovieSurfaceIDs[17], 100, 56);
LoadAMovieShot(slotOffset + MOVIE07, m_movieSurfaceIDs[18]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[18], m_grayMovieSurfaceIDs[18], 100, 56);
LoadAMovieShot(slotOffset + MOVIE08, m_movieSurfaceIDs[19]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[19], m_grayMovieSurfaceIDs[19], 100, 56);
LoadAMovieShot(slotOffset + MOVIE09, m_movieSurfaceIDs[20]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[20], m_grayMovieSurfaceIDs[20], 100, 56);
LoadAMovieShot(slotOffset + MOVIE10, m_movieSurfaceIDs[21]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[21], m_grayMovieSurfaceIDs[21], 100, 56);
LoadAMovieShot(slotOffset + MOVIE11, m_movieSurfaceIDs[22]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[22], m_grayMovieSurfaceIDs[22], 100, 56);
LoadAMovieShot(slotOffset + MOVIE12, m_movieSurfaceIDs[23]);
MakeGrayScaleThumbnail(m_movieSurfaceIDs[23], m_grayMovieSurfaceIDs[23], 100, 56);
}
void OptionsManager::OnEscapeKey() {
// Simply select the correct menu choice and perform a simulated button press
switch (m_activeMenu) {
case MAIN_TOP:
m_M_TOP_selected = _EXIT_GAME;
break;
case DEAD_LOAD:
case INGAME_SAVE:
case INGAME_LOAD:
case MAIN_LOAD:
m_GAMESLOT_selected = RETURN;
break;
case INGAME_OPTIONS:
case MAIN_OPTIONS:
m_OPTION_selected = DO_BACK;
break;
case INGAME_VIDEO:
case MAIN_VIDEO:
m_VIDEO_selected = LEAVE;
break;
case INGAME_AUDIO:
case MAIN_AUDIO:
m_AUDIO_selected = DO_ONE;
break;
case INGAME_CONTROLS:
case MAIN_CONTROLS:
m_CONTROL_selected = DONE;
break;
case MAIN_EXTRAS:
m_M_EXTRA_selected = ALLDONE;
break;
case MAIN_MOVIES:
m_M_MOVIE_selected = NOTHANKS;
break;
case MAIN_PLAYSELECT:
m_M_PLAYSELECT_selected = CANCEL;
break;
case MAIN_PROFILES:
m_M_PROFILES_selected = RET;
break;
case MAIN_A_PROFILE:
break;
case INGAME_TOP:
m_IG_TOP_selected = CONTINUE;
break;
case GAME_OVER:
2021-03-13 13:31:49 +00:00
m_GAMEOVER_selected = GAMEOVER;
break;
// Do nothing for (critical) menus not in the list ie quit confirm screens
default:
return;
}
DoChoice();
}
void OptionsManager::DoChoice() {
if (m_choiceLimiter)
return;
m_choiceLimiter = TRUE8;
2020-09-21 04:59:09 +00:00
LRECT r;
char buff[128];
ResetTitleScreenTimeout();
// For play mission gamescript bookmarks
const char *actualMissions[] = {g_m01, g_m02, g_m03, g_m04, g_m05, g_m07, g_m08, g_m09, g_m10};
// Need to switch on the menu currently active
switch (m_activeMenu) {
case MAIN_TOP:
switch (m_M_TOP_selected) {
case _NEWGAME:
m_thatsEnoughTa = TRUE8;
break;
case _LOAD_GAME:
GetCentredRectFotText(GetTextFromReference(HashString("opt_loadgame")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 7);
m_activeMenu = MAIN_LOAD;
// Display the page containing the last modified save slot and select it by default
m_slotOffset = (g_lastAccessedSlot / NUMBER_OF_VISIBLE_GAME_SLOTS) * NUMBER_OF_VISIBLE_GAME_SLOTS;
m_GAMESLOT_selected = (GAMESLOT_CHOICES)(g_lastAccessedSlot % NUMBER_OF_VISIBLE_GAME_SLOTS);
LoadVisibleThumbnails();
break;
case _OPTIONS:
GetCentredRectFotText(GetTextFromReference(HashString("opt_options")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 8);
m_activeMenu = MAIN_OPTIONS;
m_OPTION_selected = VIDEO_SETTINGS;
break;
case _EXTRAS:
GetCentredRectFotText(GetTextFromReference(HashString("opt_extras")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 7);
m_activeMenu = MAIN_EXTRAS;
m_M_EXTRA_selected = MOVIES;
break;
case _EXIT_GAME:
GetCentredRectFotText(GetTextFromReference(HashString("opt_exitgame")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 7);
m_activeMenu = MAIN_QUIT;
m_QUIT_selected = NO;
break;
}
break;
case MAIN_EXTRAS:
switch (m_M_EXTRA_selected) {
case MOVIES:
m_activeMenu = MAIN_MOVIES;
m_M_MOVIE_selected = MOVIE01;
LoadVisibleMovieShots();
break;
case SLIDESHOW:
InitialiseSlideShow();
break;
case PLAYSELECT:
m_activeMenu = MAIN_PLAYSELECT;
m_M_PLAYSELECT_selected = M01;
break;
case PROFILES:
m_activeMenu = MAIN_PROFILES;
m_M_PROFILES_selected = CORD;
break;
case CREDITS:
2021-03-13 16:34:02 +00:00
g_stub->Push_stub_mode(__credits);
break;
case ALLDONE:
m_warpDirection = FALSE8;
m_activeMenu = MAIN_TOP;
m_M_TOP_selected = _EXTRAS;
break;
}
break;
case MAIN_PLAYSELECT:
switch (m_M_PLAYSELECT_selected) {
case M01:
case M02:
case M03:
case M04:
case M05:
case M07:
case M08:
case M09:
case M10:
UnloadTitleScreenMovie();
// Run to correct position in gamescript
gs.Run_to_bookmark(actualMissions[m_M_PLAYSELECT_selected]);
g_mainMenuLoadPlease = FALSE8;
m_thatsEnoughTa = TRUE8;
break;
case CANCEL:
m_activeMenu = MAIN_EXTRAS;
m_M_EXTRA_selected = PLAYSELECT;
break;
}
break;
case DEAD_LOAD:
case INGAME_LOAD:
case MAIN_LOAD: // Refresh entire screen
if (m_useDirtyRects)
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
switch (m_GAMESLOT_selected) {
case SLOT1:
case SLOT2:
case SLOT3:
case SLOT4:
if (m_slots[m_slotOffset + m_GAMESLOT_selected] != NULL) {
MakeFullSaveFilename(m_slotOffset + m_GAMESLOT_selected, buff);
// LOAD THIS INDEX FILE:
if (Load_game(buff) != __LOAD_OK)
Fatal_error("Load game failed.");
// Return to game
m_autoAnimating = 0;
m_thatsEnoughTa = TRUE8;
if (m_activeMenu == MAIN_LOAD)
g_mainMenuLoadPlease = TRUE8;
}
break;
case RETURN:
m_warpDirection = FALSE8;
if (m_activeMenu == INGAME_LOAD) {
m_activeMenu = INGAME_TOP;
m_IG_TOP_selected = LOAD_GAME;
} else if (m_activeMenu == MAIN_LOAD) {
m_activeMenu = MAIN_TOP;
m_M_TOP_selected = _LOAD_GAME;
} else {
m_activeMenu = GAME_OVER;
m_GAMEOVER_selected = RESTORE;
}
break;
}
break;
case MAIN_OPTIONS:
case INGAME_OPTIONS:
switch (m_OPTION_selected) {
case VIDEO_SETTINGS:
if (m_activeMenu == INGAME_OPTIONS) {
// Refresh entire screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
m_activeMenu = INGAME_VIDEO;
} else
m_activeMenu = MAIN_VIDEO;
m_VIDEO_selected = SUBTITLES;
break;
case AUDIO_SETTINGS:
if (m_activeMenu == INGAME_OPTIONS) {
// Refresh entire screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
m_activeMenu = INGAME_AUDIO;
} else
m_activeMenu = MAIN_AUDIO;
m_AUDIO_selected = MUSIC_VOLUME;
break;
case CONTROLS:
if (m_activeMenu == INGAME_OPTIONS) {
// Refresh entire screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
m_activeMenu = INGAME_CONTROLS;
} else
m_activeMenu = MAIN_CONTROLS;
2021-03-13 11:37:20 +00:00
m_CONTROL_selected = METHOD;
InitialiseControlsScreen();
break;
case DO_BACK:
m_warpDirection = FALSE8;
if (m_activeMenu == INGAME_OPTIONS) {
m_activeMenu = INGAME_TOP;
m_IG_TOP_selected = OPTIONS;
} else {
m_activeMenu = MAIN_TOP;
m_M_TOP_selected = _OPTIONS;
}
break;
}
break;
case MAIN_VIDEO:
case INGAME_VIDEO:
switch (m_VIDEO_selected) {
case LEAVE:
if (m_activeMenu == MAIN_VIDEO)
m_activeMenu = MAIN_OPTIONS;
else {
// Refresh entire screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
m_activeMenu = INGAME_OPTIONS;
}
m_OPTION_selected = VIDEO_SETTINGS;
break;
// No other option should play a sound
default:
return;
}
break;
case MAIN_AUDIO:
case INGAME_AUDIO:
switch (m_AUDIO_selected) {
case DO_ONE:
if (m_activeMenu == MAIN_AUDIO)
m_activeMenu = MAIN_OPTIONS;
else {
// Refresh entire screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
m_activeMenu = INGAME_OPTIONS;
}
m_OPTION_selected = AUDIO_SETTINGS;
break;
// No other option should play a sound
default:
return;
}
break;
case MAIN_MOVIES:
switch (m_M_MOVIE_selected) {
case MOVIE01:
case MOVIE02:
case MOVIE03:
case MOVIE04:
case MOVIE05:
case MOVIE06:
case MOVIE07:
case MOVIE08:
case MOVIE09:
case MOVIE10:
case MOVIE11:
case MOVIE12:
UnloadTitleScreenMovie();
PlayMovie(m_movieOffset + m_M_MOVIE_selected);
// Mute chosen SFX on playing a movie
return;
case NOTHANKS:
m_activeMenu = MAIN_EXTRAS;
m_M_EXTRA_selected = MOVIES;
break;
case PAGELEFT:
if (m_paging)
break;
// Initialise blitting rectangles
m_pageOn_from = m_slotBoundingRect;
m_pageOn_dest = m_slotBoundingRect;
m_pageOff_from = m_slotBoundingRect;
m_pageOff_dest = m_slotBoundingRect;
m_pageleft = TRUE8;
m_pageOn_dest.left = m_pageOn_dest.right = 0;
if (m_movieOffset > 0) {
m_paging = TRUE8;
LoadPagingMovieShots(m_movieOffset - M_NUMBER_OF_VISIBLE_MOVIE_SLOTS);
LoadVisibleMovieShots();
}
break;
case PAGERIGHT:
if (m_paging)
break;
// Initialise blitting rectangles
m_pageOn_from = m_slotBoundingRect;
m_pageOn_dest = m_slotBoundingRect;
m_pageOff_from = m_slotBoundingRect;
m_pageOff_dest = m_slotBoundingRect;
m_pageleft = FALSE8;
m_pageOn_dest.left = m_pageOn_dest.right = SCREEN_WIDTH - 1;
if (m_movieOffset < (TOTAL_NUMBER_OF_MOVIES - M_NUMBER_OF_VISIBLE_MOVIE_SLOTS)) {
m_paging = TRUE8;
LoadPagingMovieShots(m_movieOffset + M_NUMBER_OF_VISIBLE_MOVIE_SLOTS);
LoadVisibleMovieShots();
}
break;
}
break;
case MAIN_PROFILES:
switch (m_M_PROFILES_selected) {
// Do the same thing for all profiles
case CORD:
case CHI:
case GREGOR:
case NAGAROV:
case LUKYAN:
case KEIFFER:
case TOLSTOV:
case ALEXANDRA:
case OLIAKOV:
case SPECTRE:
m_activeMenu = MAIN_A_PROFILE;
InitialiseAProfile();
break;
case RET:
m_activeMenu = MAIN_EXTRAS;
m_M_EXTRA_selected = PROFILES;
break;
}
break;
case MAIN_A_PROFILE: // Can only ever return from a profile screen
m_activeMenu = MAIN_PROFILES;
DrawWidescreenBorders();
// This will auto remember which profile was chosen (to return to)
break;
case MAIN_QUIT:
switch (m_QUIT_selected) {
case NO:
m_warpDirection = FALSE8;
m_activeMenu = MAIN_TOP;
m_M_TOP_selected = _EXIT_GAME;
break;
case YES: {
Common::Event event;
event.type = Common::EVENT_QUIT;
g_system->getEventManager()->pushEvent(event);
break;
}
}
break;
case INGAME_TOP:
switch (m_IG_TOP_selected) {
case OPTIONS:
GetCentredRectFotText(GetTextFromReference(HashString("opt_options")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 8);
m_activeMenu = INGAME_OPTIONS;
m_OPTION_selected = VIDEO_SETTINGS;
break;
case SAVE_GAME:
GetCentredRectFotText(GetTextFromReference(HashString("opt_savegame")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 7);
m_activeMenu = INGAME_SAVE;
// Display the page containing the last modified save slot and select it by default
m_slotOffset = (g_lastAccessedSlot / NUMBER_OF_VISIBLE_GAME_SLOTS) * NUMBER_OF_VISIBLE_GAME_SLOTS;
m_GAMESLOT_selected = (GAMESLOT_CHOICES)(g_lastAccessedSlot % NUMBER_OF_VISIBLE_GAME_SLOTS);
LoadVisibleThumbnails();
break;
case LOAD_GAME:
GetCentredRectFotText(GetTextFromReference(HashString("opt_loadgame")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 7);
m_activeMenu = INGAME_LOAD;
// Display the page containing the last modified save slot and select it by default
m_slotOffset = (g_lastAccessedSlot / NUMBER_OF_VISIBLE_GAME_SLOTS) * NUMBER_OF_VISIBLE_GAME_SLOTS;
m_GAMESLOT_selected = (GAMESLOT_CHOICES)(g_lastAccessedSlot % NUMBER_OF_VISIBLE_GAME_SLOTS);
LoadVisibleThumbnails();
break;
case CONTINUE:
SetDrawColour(BASE_DARK);
m_thatsEnoughTa = TRUE8;
break;
case QUIT:
GetCentredRectFotText(GetTextFromReference(HashString("opt_quit")), r, 80);
SetTargetBox(r.left, r.right, r.top, r.bottom, 7);
m_activeMenu = INGAME_QUIT;
m_QUIT_selected = NO;
break;
}
break;
case MAIN_CONTROLS:
case INGAME_CONTROLS:
m_configLimiter = TRUE8;
m_assignFlash = 0;
switch (m_CONTROL_selected) {
// Set the selected function to unassigned
case DONE:
if (m_inGame) {
// Refresh entire screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
m_activeMenu = INGAME_OPTIONS;
} else {
m_activeMenu = MAIN_OPTIONS;
}
m_OPTION_selected = CONTROLS;
break;
// Don't play sound on the remaining options
default:
return;
}
break;
case INGAME_SAVE: // Refresh entire screen
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_fullscreen, &m_fullscreen);
switch (m_GAMESLOT_selected) {
case SLOT1:
case SLOT2:
case SLOT3:
case SLOT4: // Only EditSlotLabel() can quit editing
if (m_editing == TRUE8)
return;
// Set this flag so that EditSlotLabel() gets called for this slot
m_editing = TRUE8;
m_letJoystickQuitEdit = FALSE8;
// If the slot contains data, copy the label into our editing buffer else use default name
if (m_slots[m_slotOffset + m_GAMESLOT_selected] != NULL) {
// Wish to edit an existing slot label
strcpy(m_editBuffer, m_slots[m_slotOffset + m_GAMESLOT_selected]->label);
// Record this ex-time played so we restore if if the action is cancelled
m_emptySlotFlag = m_slots[m_slotOffset + m_GAMESLOT_selected]->secondsPlayed;
// Overwrite with current timeplayed
m_slots[m_slotOffset + m_GAMESLOT_selected]->secondsPlayed = m_timePlayed;
m_defaultWiper = FALSE8;
} else {
// Need to create a new slot here
m_slots[m_slotOffset + m_GAMESLOT_selected] = new _SLOT;
m_slots[m_slotOffset + m_GAMESLOT_selected]->secondsPlayed = m_timePlayed;
memset(m_slots[m_slotOffset + m_GAMESLOT_selected]->label, 0, MAX_LABEL_LENGTH);
strcpy(m_editBuffer, m_defaultSlotName);
m_defaultWiper = TRUE8;
// By setting this to zero it can be used to identify this newly created slot on cencel (ie need a delete)
m_emptySlotFlag = 0;
}
// This slot instantly becomes the most recently modified file so update default selection explicitly
g_lastAccessedSlot = m_slotOffset + m_GAMESLOT_selected;
// Get the current character position
m_cursorPos = strlen(m_editBuffer);
if (m_cursorPos >= MAX_LABEL_LENGTH - 1)
m_cursorPos = MAX_LABEL_LENGTH - 2;
Clear_key_buffer();
break;
case RETURN:
m_warpDirection = FALSE8;
m_activeMenu = INGAME_TOP;
m_IG_TOP_selected = CONTINUE;
break;
}
break;
case INGAME_SAVECONFIRM:
switch (m_SAVECONFIRM_selected) {
case NAY:
m_activeMenu = INGAME_SAVE;
// Cancellation code
if (m_emptySlotFlag == 0) {
// Slot was previously empty so delete on cancellation
delete m_slots[m_slotOffset + m_GAMESLOT_selected];
m_slots[m_slotOffset + m_GAMESLOT_selected] = NULL;
} else {
// Just need to restore time played to cancel
m_slots[m_slotOffset + m_GAMESLOT_selected]->secondsPlayed = m_emptySlotFlag;
}
memset(m_editBuffer, '\0', MAX_LABEL_LENGTH);
m_choiceLimiter = TRUE8;
ForceInGameScreenRefresh();
break;
case YEY:
m_activeMenu = INGAME_SAVE;
// Save over slot code
// Remove cursor from the end of the buffer
m_editBuffer[m_cursorPos] = '\0';
// Then set the label to equal the buffer
strcpy(m_slots[m_slotOffset + m_GAMESLOT_selected]->label, m_editBuffer);
// Now actually save the game
MakeFullSaveFilename(m_slotOffset + m_GAMESLOT_selected, buff);
g_mission->Save_game_position(buff, m_slots[m_slotOffset + m_GAMESLOT_selected]->label, m_slots[m_slotOffset + m_GAMESLOT_selected]->secondsPlayed);
// Aint32 with a thumbnail
SaveThumbnail(m_slotOffset + m_GAMESLOT_selected);
LoadVisibleThumbnails();
memset(m_editBuffer, '\0', MAX_LABEL_LENGTH);
m_GAMESLOT_selected = RETURN;
m_choiceLimiter = TRUE8;
ForceInGameScreenRefresh();
break;
}
break;
case INGAME_QUIT:
switch (m_QUIT_selected) {
case NO:
m_warpDirection = FALSE8;
m_activeMenu = INGAME_TOP;
m_IG_TOP_selected = QUIT;
break;
case YES:
m_thatsEnoughTa = TRUE8;
m_autoAnimating = 0;
#ifndef PC_DEMO
if (gs.Running_from_gamescript()) {
// Reset
gs.Restart_game_script();
g_resetToTitleScreen = TRUE8;
} else
#endif
{
// Shutdown if no gamescript
Common::Event event;
event.type = Common::EVENT_QUIT;
g_system->getEventManager()->pushEvent(event);
}
break;
}
break;
case GAME_OVER:
switch (m_GAMEOVER_selected) {
case RESTORE:
m_activeMenu = DEAD_LOAD;
// Display the page containing the last modified save slot and select it by default
m_slotOffset = (g_lastAccessedSlot / NUMBER_OF_VISIBLE_GAME_SLOTS) * NUMBER_OF_VISIBLE_GAME_SLOTS;
m_GAMESLOT_selected = (GAMESLOT_CHOICES)(g_lastAccessedSlot % NUMBER_OF_VISIBLE_GAME_SLOTS);
LoadVisibleThumbnails();
break;
case RESTART: // We need to restart the mission that we're currently on
// Nearly had me this - Load mission includes a six!
if (g_missionNumber > 5)
g_missionNumber--;
// Mission 8-9 special cases
if (g_missionNumber == 7) {
2021-03-13 18:20:24 +00:00
g_globalScriptVariables->SetVariable("mission9", 0);
}
if (g_missionNumber == 8) {
2021-03-13 18:20:24 +00:00
g_globalScriptVariables->SetVariable("mission9", 1);
}
// Need to preserve global timer variable
int32 tv;
2021-03-13 18:20:24 +00:00
tv = g_globalScriptVariables->GetVariable("missionelapsedtime");
// Use this function and then quit
RestartMission();
2021-03-13 18:20:24 +00:00
g_globalScriptVariables->SetVariable("missionelapsedtime", tv);
m_thatsEnoughTa = TRUE8;
break;
2021-03-13 13:31:49 +00:00
case GAMEOVER:
m_activeMenu = DEAD_QUIT;
m_QUIT_selected = NO;
break;
}
break;
case DEAD_QUIT:
switch (m_QUIT_selected) {
case NO:
m_activeMenu = GAME_OVER;
2021-03-13 13:31:49 +00:00
m_GAMEOVER_selected = GAMEOVER;
break;
case YES:
m_thatsEnoughTa = TRUE8;
if (gs.Running_from_gamescript()) {
// Reset
gs.Restart_game_script();
g_resetToTitleScreen = TRUE8;
} else {
// Shutdown if no gamescript
Common::Event event;
event.type = Common::EVENT_QUIT;
g_system->getEventManager()->pushEvent(event);
}
break;
}
break;
default:
return;
}
PlayChosenFX();
Poll_Sound_Engine();
}
void OptionsManager::PlayMovie(uint32 id) {
// Are we allowed to view this movie yet
if (g_movieLibrary[id].visible == FALSE8)
return;
char buff[32];
// Get the filename
Movie_ID_to_name(id, buff);
bool8 cancelret = TRUE8;
// Ensure correct CD is in the drive here so we can provide a cancel option
switch (buff[2]) {
case '1':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION1);
break;
case '2':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION2);
break;
case '3':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION3);
break;
case '4':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION4);
break;
case '5':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION5);
break;
case '7':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION7);
break;
case '8':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION8);
break;
case '9':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION9);
break;
case '0':
cancelret = g_theClusterManager->CheckDiscInsertedWithCancel(MISSION10);
break;
}
// User cancelled so do nowt
if (cancelret == TRUE8)
return;
// Register and play
Init_play_movie(buff, FALSE8);
// Don't draw the background this cycle
g_skipBackgroundDrawOverFrames = 5;
}
bool8 OptionsManager::VerifyLabel() {
// Do not allow empty labels as that's just silly
if (m_cursorPos == 0)
return FALSE8;
// Could add further restrictions here to the label names ie pure whitespace
// but the problem is how to inform the user that the label is invalid easily.
// Another difficulty arises when the length exceeds expected limits but NOT
// before the imposed 22 character limit is reached ie all capital w's...
// Perhaps I need to watch this everytime a character is input to the label
// and refuse any further input if the string exceeds the slot blitting box.
return TRUE8;
}
void OptionsManager::EditSlotLabel() {
char c;
2021-03-07 08:56:34 +00:00
static int32 flash = 0;
char buff[128];
2021-03-07 08:56:34 +00:00
int32 id = m_slotOffset + m_GAMESLOT_selected;
if (KeyWaiting()) {
ReadKey(&c);
// Shall we quit
if (c == Common::KEYCODE_RETURN) {
// Not allowed an empty name 'cos thats daft
if (!VerifyLabel())
return;
MakeFullSaveFilename(id, buff);
// Now check to see if the file exists (overwrite used slot)
if (checkFileExists(buff)) { // amode = 0
// Slot is in use so do confirm prompt screen
m_activeMenu = INGAME_SAVECONFIRM;
m_SAVECONFIRM_selected = NAY;
m_editing = FALSE8;
ForceInGameScreenRefresh();
return;
}
// Slot empty so just save without prompting
// Remove cursor from the end of the buffer
m_editBuffer[m_cursorPos] = '\0';
// Then set the label to equal the buffer
strcpy(m_slots[id]->label, m_editBuffer);
// Now actually save the game
g_mission->Save_game_position(buff, m_slots[id]->label, m_slots[id]->secondsPlayed);
// Aint32 with a thumbnail
SaveThumbnail(id);
LoadVisibleThumbnails();
memset(m_editBuffer, '\0', MAX_LABEL_LENGTH);
m_editing = FALSE8;
m_GAMESLOT_selected = RETURN;
} else
// Delete key
if (c == Common::KEYCODE_BACKSPACE) {
if (m_cursorPos > 0) {
m_editBuffer[m_cursorPos] = '\0';
m_cursorPos--;
m_editBuffer[m_cursorPos] = '\0';
}
// Cancel this
m_defaultWiper = FALSE8;
} else if ((c < 32) || (c > 'z')) {
// Ignore these keypresses
} else {
// I like this behaviour of overwriting the entire name on first key
if (m_defaultWiper) {
// Wipe contents
memset(m_editBuffer, '\0', MAX_LABEL_LENGTH);
m_cursorPos = 0;
m_defaultWiper = FALSE8;
}
// Only add the character if we have room left
if (m_cursorPos < MAX_LABEL_LENGTH - 2) {
m_editBuffer[m_cursorPos++] = c;
}
}
}
// Cursor display
flash++;
if (flash < 7)
m_editBuffer[m_cursorPos] = '_';
else
m_editBuffer[m_cursorPos] = ' ';
// Cycling
if (flash == 14)
flash = 0;
}
void OptionsManager::InitialiseSlots() {
char buff[128];
// Set all slots by default to empty
for (uint32 i = 0; i < TOTAL_NUMBER_OF_GAME_SLOTS; i++) {
m_slots[i] = NULL;
// Now look for any valid game files and set corresponding info
// Get filename
MakeFullSaveFilename(i, buff);
// If no file exists then end iteration
if (!checkFileExists(buff))
continue;
g_lastAccessedSlot = 0;
// This slot is in use so record largest id
g_largestValidSlotID = i;
// Now we must get the label and time from the file and set accordingly
Common::SeekableReadStream *stream = openDiskFileForBinaryStreamRead(buff);
if (stream == NULL)
Fatal_error(pxVString("Failed to open save file: %s", buff));
// Get storage for this slot
m_slots[i] = new _SLOT;
// Now access the first two pieces of data that we need for the slot data
stream->read(m_slots[i]->label, MAX_LABEL_LENGTH);
m_slots[i]->secondsPlayed = stream->readUint32LE();
delete stream;
}
// Display the page containing the last modified save slot
m_slotOffset = (g_lastAccessedSlot / NUMBER_OF_VISIBLE_GAME_SLOTS) * NUMBER_OF_VISIBLE_GAME_SLOTS;
m_GAMESLOT_selected = (GAMESLOT_CHOICES)(g_lastAccessedSlot % NUMBER_OF_VISIBLE_GAME_SLOTS);
}
void OptionsManager::DestroySlots() {
for (uint32 i = 0; i < TOTAL_NUMBER_OF_GAME_SLOTS; i++) {
if (m_slots[i] != NULL) {
delete m_slots[i];
m_slots[i] = NULL;
}
}
}
void OptionsManager::DrawGameSlots(uint32 slotOffset, uint32 surface_id /* = working_buffer_id*/) {
const char *msg = NULL;
uint32 temp;
pxString str;
uint32 h = 130;
uint32 id_wPos = 150;
uint32 nm_wPos = 240;
uint32 vspacing = 62;
// Need refresh when editing
if (m_editing)
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &m_slotBoundingRect, &m_slotBoundingRect);
// Lock the directdraw surface (working buffer)
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
// Loop through the visible slots on the screen and do the shit
for (int32 vslot = 0; vslot < NUMBER_OF_VISIBLE_GAME_SLOTS; vslot++) {
GAMESLOT_CHOICES slot = (GAMESLOT_CHOICES)vslot;
// Display slot id
str.Format("%d.", slotOffset + slot + 1);
temp = CalculateStringWidth(str);
DisplayText(ad, pitch, str, id_wPos - temp, h, (bool8)(m_GAMESLOT_selected == slot) ? SELECTEDFONT : NORMALFONT, FALSE8);
// Now draw the bounding rectangle for a thumbnail image
DrawRectangle((bool8)(m_GAMESLOT_selected == slot), id_wPos + 9, h - 1, 65, 49, ad, pitch);
// Followed by label and time played if the slot is in use
if (m_slots[slotOffset + slot] == NULL) {
if ((m_editing) && (m_GAMESLOT_selected == slot)) {
// Unlock the surface for the thumbnail blit
surface_manager->Unlock_surface(surface_id);
EditSlotLabel();
// Lock it back up again and continue
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
DisplayText(ad, pitch, m_editBuffer, nm_wPos, h, (bool8)(m_GAMESLOT_selected == slot) ? SELECTEDFONT : NORMALFONT, FALSE8);
} else {
msg = GetTextFromReference(HashString("opt_empty"));
DisplayText(ad, pitch, msg, nm_wPos, h, (bool8)(m_GAMESLOT_selected == slot) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
DisplayText(ad, pitch, "00:00:00", nm_wPos, h + 18, (bool8)(m_GAMESLOT_selected == slot) ? SELECTEDFONT : NORMALFONT, FALSE8);
} else {
if ((m_editing) && (m_GAMESLOT_selected == slot)) {
// Unlock the surface for the thumbnail blit
surface_manager->Unlock_surface(surface_id);
EditSlotLabel();
// Lock it back up again and continue
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
DisplayText(ad, pitch, m_editBuffer, nm_wPos, h, (bool8)(m_GAMESLOT_selected == slot) ? SELECTEDFONT : NORMALFONT, FALSE8);
} else {
msg = m_slots[slotOffset + slot]->label;
DisplayText(ad, pitch, msg, nm_wPos, h, (bool8)(m_GAMESLOT_selected == slot) ? SELECTEDFONT : NORMALFONT, FALSE8);
// The thumbnail
2020-09-21 04:59:09 +00:00
LRECT dest;
dest.left = id_wPos + 10;
dest.right = dest.left + 64;
dest.top = h;
dest.bottom = dest.top + 48;
// Unlock the surface for the thumbnail blit
surface_manager->Unlock_surface(surface_id);
// Are we to draw current thumbnails or the preloaded next thumbnails (paging animation)
if (slotOffset == m_slotOffset) {
if (m_GAMESLOT_selected == slot)
surface_manager->Blit_surface_to_surface(m_thumbSurfaceIDs[slot], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayThumbSurfaceIDs[slot], surface_id, NULL, &dest);
} else {
if (m_GAMESLOT_selected == slot)
surface_manager->Blit_surface_to_surface(m_thumbSurfaceIDs[slot + 4], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayThumbSurfaceIDs[slot + 4], surface_id, NULL, &dest);
}
// Lock it back up again and continue
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
}
uint32 sec = 0;
if (m_slots[slotOffset + slot])
sec = m_slots[slotOffset + slot]->secondsPlayed;
uint32 min = sec / 60;
uint32 hrs = min / 60;
// Get residual seconds
sec = sec % 60;
str.Format("%d%d:%d%d:%d%d", (hrs < 10) ? 0 : (hrs / 10), (hrs % 10), ((min % 60) < 10) ? 0 : ((min % 60) / 10), (min % 60) % 10, (sec < 10) ? 0 : sec / 10,
(sec < 10) ? sec : sec % 10);
DisplayText(ad, pitch, str, nm_wPos, h + 18, (bool8)(m_GAMESLOT_selected == slot) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
h += vspacing;
}
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::DrawMovieSlots(uint32 offset, uint32 surface_id /* = working_buffer_id*/) {
const char *msg = NULL;
2020-09-21 04:59:09 +00:00
LRECT dest;
uint32 selectedMovie;
uint32 selectedMovieSlot;
// Now draw the movie slots as three rows of four and the size ratio is 32\5 to that of
// original movies(rendered 640x356 pixels) giving thumnail dimensions of 100x56
uint32 firstRowY = 130;
uint8 *ad;
uint32 pitch;
for (uint32 row = 0; row < 3; row++) {
uint32 firstColumnX = 89;
selectedMovieSlot = (row * 4);
selectedMovie = selectedMovieSlot + offset;
// Check we don't exceed the total number of movies (which is dynamic at the moment)
if (selectedMovie == TOTAL_NUMBER_OF_MOVIES)
break;
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
DrawRectangle((bool8)(m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot), firstColumnX, firstRowY, 101, 57, ad, pitch);
surface_manager->Unlock_surface(surface_id);
// The piccy
dest.left = firstColumnX + 1;
dest.right = dest.left + 100;
dest.top = firstRowY + 1;
dest.bottom = dest.top + 56;
if (g_movieLibrary[selectedMovie].visible) {
if (offset == m_movieOffset) {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
} else {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
}
}
firstColumnX += 120;
selectedMovie++;
selectedMovieSlot++;
// Check we don't exceed the total number of movies
if (selectedMovie == TOTAL_NUMBER_OF_MOVIES)
break;
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
DrawRectangle((bool8)(m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot), firstColumnX, firstRowY, 101, 57, ad, pitch);
surface_manager->Unlock_surface(surface_id);
// The piccy
dest.left = firstColumnX + 1;
dest.right = dest.left + 100;
dest.top = firstRowY + 1;
dest.bottom = dest.top + 56;
if (g_movieLibrary[selectedMovie].visible) {
if (offset == m_movieOffset) {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
} else {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
}
}
firstColumnX += 120;
selectedMovie++;
selectedMovieSlot++;
// Check we don't exceed the total number of movies
if (selectedMovie == TOTAL_NUMBER_OF_MOVIES)
break;
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
DrawRectangle((bool8)(m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot), firstColumnX, firstRowY, 101, 57, ad, pitch);
surface_manager->Unlock_surface(surface_id);
// The piccy
dest.left = firstColumnX + 1;
dest.right = dest.left + 100;
dest.top = firstRowY + 1;
dest.bottom = dest.top + 56;
if (g_movieLibrary[selectedMovie].visible) {
if (offset == m_movieOffset) {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
} else {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
}
}
firstColumnX += 120;
selectedMovie++;
selectedMovieSlot++;
// Check we don't exceed the total number of movies
if (selectedMovie == TOTAL_NUMBER_OF_MOVIES)
break;
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
DrawRectangle((bool8)(m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot), firstColumnX, firstRowY, 101, 57, ad, pitch);
surface_manager->Unlock_surface(surface_id);
// The piccy
dest.left = firstColumnX + 1;
dest.right = dest.left + 100;
dest.top = firstRowY + 1;
dest.bottom = dest.top + 56;
if (g_movieLibrary[selectedMovie].visible) {
if (offset == m_movieOffset) {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot], surface_id, NULL, &dest);
} else {
if (m_M_MOVIE_selected == (M_MOVIE_CHOICES)selectedMovieSlot)
surface_manager->Blit_surface_to_surface(m_movieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
else
surface_manager->Blit_surface_to_surface(m_grayMovieSurfaceIDs[selectedMovieSlot + 12], surface_id, NULL, &dest);
}
}
firstRowY += 75;
}
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
// Display movies name if allowed
if (m_M_MOVIE_selected < NOTHANKS) {
uint32 selectMovie = m_M_MOVIE_selected + offset;
// Is this movie availble
if (g_movieLibrary[selectMovie].visible) {
// Get the nice name to go with the nice piccy
if (selectMovie < 10)
msg = GetTextFromReference(HashString(pxVString("opt_movie0%d", selectMovie)));
else
msg = GetTextFromReference(HashString(pxVString("opt_movie%d", selectMovie)));
if (msg)
DisplayText(ad, pitch, msg, 0, 355, PALEFONT, TRUE8);
else
DisplayText(ad, pitch, "???", 0, 355, PALEFONT, TRUE8);
} else {
// Nope, you'll have to play the game more dude
DisplayText(ad, pitch, "???", 0, 355, PALEFONT, TRUE8);
}
}
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::DrawMainLoadScreen(uint32 surface_id) {
const char *msg = NULL;
uint32 temp;
pxString str;
int32 icon_x = 60;
int32 icon_y = (SCREEN_DEPTH / 2) - (m_fontHeight / 2);
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
SetDrawColour(BASE);
msg = GetTextFromReference(HashString("opt_loadgame"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
// Draw the non-selectable paging icons
if (m_slotOffset != 0) {
temp = CalculateStringWidth("<");
DrawRectangle((bool8)(m_paging && m_pageleft), icon_x, icon_y, temp + 18, m_fontHeight - 2, ad, pitch);
DisplayText(ad, pitch, "<", icon_x + 10, icon_y - 2, (m_paging && m_pageleft) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
if (m_slotOffset < TOTAL_NUMBER_OF_GAME_SLOTS - NUMBER_OF_VISIBLE_GAME_SLOTS) {
temp = CalculateStringWidth(">");
icon_x = SCREEN_WIDTH - icon_x - temp - 18;
DrawRectangle((bool8)(m_paging && !m_pageleft), icon_x, icon_y, temp + 18, m_fontHeight - 2, ad, pitch);
DisplayText(ad, pitch, ">", icon_x + 10, icon_y - 2, (m_paging && !m_pageleft) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, 378, (m_GAMESLOT_selected == RETURN) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(surface_id);
// Now the slots themselves
if (!m_paging) {
// Regular
DrawGameSlots(m_slotOffset, surface_id);
} else {
// Animating
AnimateSlotsPaging();
}
}
void OptionsManager::DrawMovieScreen(uint32 surface_id) {
const char *msg = NULL;
uint32 temp;
int32 icon_x = 40;
int32 icon_y = (SCREEN_DEPTH / 2) - (m_fontHeight / 2);
// Draw the slots themselves
if (!m_paging) {
// Regular
DrawMovieSlots(m_movieOffset, surface_id);
} else {
// Animating
AnimateSlotsPaging();
}
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
SetDrawColour(BASE);
msg = GetTextFromReference(HashString("opt_movies"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
// Draw the selectable paging icons after the slots
// Only draw the page left icon if we can page left
if (m_movieOffset != 0) {
temp = CalculateStringWidth("<");
DrawRectangle((bool8)(m_M_MOVIE_selected == PAGELEFT), icon_x, icon_y, temp + 18, m_fontHeight - 2, ad, pitch);
DisplayText(ad, pitch, "<", icon_x + 10, icon_y - 2, (m_M_MOVIE_selected == PAGELEFT) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
// Only draw the page right icon if we can page right
if (m_movieOffset < TOTAL_NUMBER_OF_MOVIES - M_NUMBER_OF_VISIBLE_MOVIE_SLOTS) {
temp = CalculateStringWidth(">");
icon_x = SCREEN_WIDTH - icon_x - temp - 18;
DrawRectangle((bool8)(m_M_MOVIE_selected == PAGERIGHT), icon_x, icon_y, temp + 18, m_fontHeight - 2, ad, pitch);
DisplayText(ad, pitch, ">", icon_x + 10, icon_y - 2, (m_M_MOVIE_selected == PAGERIGHT) ? SELECTEDFONT : NORMALFONT, FALSE8);
}
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, 385, (m_M_MOVIE_selected == NOTHANKS) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::DrawExtrasScreen(uint32 surface_id) {
const char *msg = NULL;
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
SetDrawColour(BASE);
msg = GetTextFromReference(HashString("opt_extras"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
msg = GetTextFromReference(HashString("opt_movies"));
DisplayText(ad, pitch, msg, 0, 130, (m_M_EXTRA_selected == MOVIES) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_slideshow"));
2021-03-14 07:59:48 +00:00
if (g_px->game_completed)
DisplayText(ad, pitch, msg, 0, 150, (m_M_EXTRA_selected == SLIDESHOW) ? SELECTEDFONT : NORMALFONT, TRUE8);
else
DisplayText(ad, pitch, msg, 0, 150, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_playselect"));
2021-03-14 07:59:48 +00:00
if (g_px->game_completed)
DisplayText(ad, pitch, msg, 0, 170, (m_M_EXTRA_selected == PLAYSELECT) ? SELECTEDFONT : NORMALFONT, TRUE8);
else
DisplayText(ad, pitch, msg, 0, 170, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_profiles"));
2021-03-14 07:59:48 +00:00
if (g_px->game_completed)
DisplayText(ad, pitch, msg, 0, 190, (m_M_EXTRA_selected == PROFILES) ? SELECTEDFONT : NORMALFONT, TRUE8);
else
DisplayText(ad, pitch, msg, 0, 190, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_credits"));
DisplayText(ad, pitch, msg, 0, 210, (m_M_EXTRA_selected == CREDITS) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, 240, (m_M_EXTRA_selected == ALLDONE) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::DrawWidescreenBorders() {
Fill_rect(0, 0, SCREEN_WIDTH, 67, 0);
Fill_rect(0, SCREEN_DEPTH - 67, SCREEN_WIDTH, SCREEN_DEPTH, 0);
}
void OptionsManager::DrawPlaySelectScreen(uint32 surface_id) {
const char *msg = NULL;
// Some of this is disabled for demos
2021-03-13 18:20:24 +00:00
int32 demo = g_globalScriptVariables->GetVariable("demo");
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
SetDrawColour(BASE);
msg = GetTextFromReference(HashString("opt_playselect"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
if (demo) {
msg = GetTextFromReference(HashString("opt_investigatemine"));
DisplayText(ad, pitch, msg, 0, 130, (m_M_PLAYSELECT_selected == M01) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_securityhq"));
DisplayText(ad, pitch, msg, 0, 150, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_landtrain"));
DisplayText(ad, pitch, msg, 0, 170, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_containment"));
DisplayText(ad, pitch, msg, 0, 190, (m_M_PLAYSELECT_selected == M04) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_refinery"));
DisplayText(ad, pitch, msg, 0, 210, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_mainlandbase"));
DisplayText(ad, pitch, msg, 0, 230, (m_M_PLAYSELECT_selected == M07) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_islandbase"));
DisplayText(ad, pitch, msg, 0, 250, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_escape"));
DisplayText(ad, pitch, msg, 0, 270, PALEFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_submarine"));
DisplayText(ad, pitch, msg, 0, 290, PALEFONT, TRUE8);
} else {
msg = GetTextFromReference(HashString("opt_investigatemine"));
DisplayText(ad, pitch, msg, 0, 130, (m_M_PLAYSELECT_selected == M01) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_securityhq"));
DisplayText(ad, pitch, msg, 0, 150, (m_M_PLAYSELECT_selected == M02) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_landtrain"));
DisplayText(ad, pitch, msg, 0, 170, (m_M_PLAYSELECT_selected == M03) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_containment"));
DisplayText(ad, pitch, msg, 0, 190, (m_M_PLAYSELECT_selected == M04) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_refinery"));
DisplayText(ad, pitch, msg, 0, 210, (m_M_PLAYSELECT_selected == M05) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_mainlandbase"));
DisplayText(ad, pitch, msg, 0, 230, (m_M_PLAYSELECT_selected == M07) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_islandbase"));
DisplayText(ad, pitch, msg, 0, 250, (m_M_PLAYSELECT_selected == M08) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_escape"));
DisplayText(ad, pitch, msg, 0, 270, (m_M_PLAYSELECT_selected == M09) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_submarine"));
DisplayText(ad, pitch, msg, 0, 290, (m_M_PLAYSELECT_selected == M10) ? SELECTEDFONT : NORMALFONT, TRUE8);
}
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, 330, (m_M_PLAYSELECT_selected == CANCEL) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::DrawProfileSelectScreen(uint32 surface_id) {
const char *msg = NULL;
uint8 *ad = surface_manager->Lock_surface(surface_id);
uint32 pitch = surface_manager->Get_pitch(surface_id);
SetDrawColour(BASE);
msg = GetTextFromReference(HashString("opt_profiles"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
msg = "Cord";
DisplayText(ad, pitch, msg, 0, 130, (m_M_PROFILES_selected == CORD) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Chi";
DisplayText(ad, pitch, msg, 0, 150, (m_M_PROFILES_selected == CHI) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Gregor";
DisplayText(ad, pitch, msg, 0, 170, (m_M_PROFILES_selected == GREGOR) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Nagarov";
DisplayText(ad, pitch, msg, 0, 190, (m_M_PROFILES_selected == NAGAROV) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Lukyan";
DisplayText(ad, pitch, msg, 0, 210, (m_M_PROFILES_selected == LUKYAN) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Keiffer";
DisplayText(ad, pitch, msg, 0, 230, (m_M_PROFILES_selected == KEIFFER) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Tolstov";
DisplayText(ad, pitch, msg, 0, 250, (m_M_PROFILES_selected == TOLSTOV) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Alexandra";
DisplayText(ad, pitch, msg, 0, 270, (m_M_PROFILES_selected == ALEXANDRA) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Oliakov";
DisplayText(ad, pitch, msg, 0, 290, (m_M_PROFILES_selected == OLIAKOV) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = "Spectre";
DisplayText(ad, pitch, msg, 0, 310, (m_M_PROFILES_selected == SPECTRE) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, 350, (m_M_PROFILES_selected == RET) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(surface_id);
}
void OptionsManager::InitialiseAProfile() {
// Check for outfit select
2021-03-07 08:56:34 +00:00
int32 outfit_cheat = 0;
if (Read_DI_keys(Common::KEYCODE_1))
outfit_cheat = 1;
if (Read_DI_keys(Common::KEYCODE_2))
outfit_cheat = 2;
if (Read_DI_keys(Common::KEYCODE_3))
outfit_cheat = 3;
if (Read_DI_keys(Common::KEYCODE_4))
outfit_cheat = 4;
// Need to calculate printing margin
m_margin = 0;
const char *msg = NULL;
// This ensures correct spacing for any translations
2021-03-07 08:56:34 +00:00
for (uint32 i = 0; i < 5; i++) {
switch (i) {
case 0:
msg = GetTextFromReference(HashString("prf_name"));
break;
case 1:
msg = GetTextFromReference(HashString("prf_age"));
break;
case 2:
msg = GetTextFromReference(HashString("prf_height"));
break;
case 3:
msg = GetTextFromReference(HashString("prf_weight"));
break;
case 4:
msg = GetTextFromReference(HashString("prf_profile"));
break;
}
uint32 width;
width = CalculateStringWidth(msg);
if (width > m_margin)
m_margin = width;
}
// Cool now add some spacing
m_margin += 10;
// Rectangle describing the profile text box
m_profileRect.left = m_margin + 20;
m_profileRect.right = m_profileRect.left + 285;
m_profileRect.top = 188;
m_profileRect.bottom = m_profileRect.top + 184;
// Initialise scrolling offsets
m_profileScrollingOffset = 0;
m_profileScrollingLine = -1;
// Setup the character drawing stuff here using m_M_PROFILES_selected ...
const char *character_name = NULL;
const char *outfit_name = "outfit";
const char *anim_name = "walk";
int16 rx = 0;
int16 ry = 0;
int16 rz = 0;
switch (m_M_PROFILES_selected) {
case CORD:
character_name = "cord";
if (outfit_cheat == 1)
outfit_name = "flack_jacket";
else if (outfit_cheat == 2)
outfit_name = "arctic_wear";
else if (outfit_cheat == 3)
outfit_name = "tech_suit";
else if (outfit_cheat == 4)
outfit_name = "vest";
else
outfit_name = "casual_wear";
rx = -55;
ry = -23;
rz = -348;
break;
case CHI:
character_name = "chi";
if (outfit_cheat == 1)
outfit_name = "casual_wear";
else if (outfit_cheat == 2)
outfit_name = "menial_wear";
else
outfit_name = "arctic_wear";
rx = -55;
ry = -26;
rz = -348;
break;
case GREGOR:
character_name = "gregor";
outfit_name = "combats";
rx = -55;
ry = -18;
rz = -210;
break;
case NAGAROV:
character_name = "nagarov";
outfit_name = "uniform";
rx = -55;
ry = -20;
rz = -308;
break;
case LUKYAN:
character_name = "lukyan";
outfit_name = "uniform";
rx = -55;
ry = -17;
rz = -210;
break;
case KEIFFER:
character_name = "keiffer";
rx = -55;
ry = -23;
rz = -348;
break;
case TOLSTOV:
character_name = "scientist_contact";
outfit_name = "labcoat";
rx = -55;
ry = -20;
rz = -338;
break;
case ALEXANDRA:
character_name = "scientists_daughter";
rx = -55;
ry = -30;
rz = -368;
break;
case OLIAKOV:
character_name = "oliakov";
rx = -55;
ry = -17;
rz = -280;
break;
case SPECTRE:
character_name = "spectre";
rx = -28;
ry = -10;
break;
default:
Fatal_error("Can't draw unknown character profile!");
}
InitActorView(character_name, outfit_name, "unarmed", anim_name, rx, ry, rz);
// Ensure these flags are set for the polgon renderer
_drawActor = 1;
_drawPolys = 1;
_drawTxture = 1;
_drawBbox = 0;
_drawWfrm = 0;
_drawLit = 1;
}
void OptionsManager::DrawProfileScreen(uint32 surface_id) {
// Timing code to lock the refresh rate at a constant
uint32 t = GetMicroTimer();
const char *msg = NULL;
const char *prefix = NULL;
uint32 temp;
pxString label;
// Need this mainly for rabview reasons
DrawWidescreenBorders();
// Draw the actor first up
ActorViewDraw();
// Whose profile are we drawing
switch (m_M_PROFILES_selected) {
case CORD:
prefix = "prf_cord_";
break;
case CHI:
prefix = "prf_chi_";
break;
case GREGOR:
prefix = "prf_gregor_";
break;
case NAGAROV:
prefix = "prf_nagarov_";
break;
case LUKYAN:
prefix = "prf_lukyan_";
break;
case KEIFFER:
prefix = "prf_keiffer_";
break;
case TOLSTOV:
prefix = "prf_tolstov_";
break;
case ALEXANDRA:
prefix = "prf_alexandra_";
break;
case OLIAKOV:
prefix = "prf_oliakov_";
break;
case SPECTRE:
prefix = "prf_spectre_";
break;
default:
Fatal_error("Can't draw unknown profile!");
}
uint32 leftMargin = 20;
// Now we need to parse the info string word by word writing to the screen until we
// need a new line
// Get the whole string
label.Format("%s%s", prefix, "info");
msg = GetTextFromReference(HashString(label));
if (msg == NULL)
msg = "Please update 'globals\\translations\\' files";
// Get some storage from the stack
uint8 theData[MAX_BYTESIZE_OF_PROFILE_INFO];
// Zero out our memory
memset(theData, 0, MAX_BYTESIZE_OF_PROFILE_INFO);
// Make a personal copy
memcpy(theData, msg, strlen(msg) + 1);
// Get a pointer to our memory
char *ptr = (char *)theData;
// Split the text into words (overwrite spaces with terminators)
2021-03-07 08:56:34 +00:00
int32 i = 0;
uint32 numberOfWords = 1;
while (ptr[i]) {
// Found a space?
if (ptr[i] == ' ') {
// Watch for multiple spaces!
do {
ptr[i] = 0;
i++;
} while (ptr[i] == ' ');
numberOfWords++;
} else
i++;
}
// Positional cursors initialised
uint32 xp = 0;
uint32 yp;
if (m_profileScrollingLine != -1)
yp = 20 - m_profileScrollingOffset;
else
yp = 40 - m_profileScrollingOffset;
uint32 RIGHT_HAND_MARGIN = surface_manager->Get_width(m_profileSurface);
uint32 BOTTOM_EDGE_LIMIT = surface_manager->Get_height(m_profileSurface) - 20;
uint32 SPACE_PIXELWIDTH = 5;
uint32 cur = 0;
// Black is the colour key for this surface
surface_manager->Clear_surface(m_profileSurface);
uint8 *ad = surface_manager->Lock_surface(m_profileSurface);
uint32 pitch = surface_manager->Get_pitch(m_profileSurface);
2021-03-07 08:56:34 +00:00
int32 line;
if (m_profileScrollingLine != -1)
line = 0;
else
line = -1;
// Now we're in a position to display it word by word
for (uint32 w = 0; w < numberOfWords; w++) {
// Safety check
if (cur >= sizeof(theData))
break;
// Print a word
if (line >= m_profileScrollingLine)
DisplayText(ad, pitch, (const char *)(ptr + cur), xp, yp, NORMALFONT, FALSE8);
// Move cursor(s)
xp += CalculateStringWidth((const char *)(ptr + cur)) + SPACE_PIXELWIDTH;
// Last word already printed so break
if (w + 1 == numberOfWords) {
// Do we need to scroll a little more to display the last line properly
if (yp < BOTTOM_EDGE_LIMIT - 39)
m_lastLineDisplayed = TRUE8;
else
m_lastLineDisplayed = FALSE8;
break;
}
// Point to next word
cur += strlen((const char *)(ptr + cur));
while (!ptr[cur])
cur++;
// Calculate pixel length of NEXT word
uint32 nextw = CalculateStringWidth((const char *)(ptr + cur));
// Check for line ends
if (xp + nextw > RIGHT_HAND_MARGIN) {
// Need to go to next line
if (line >= m_profileScrollingLine)
yp += 20;
xp = 0;
line++;
// Check we've some screen left
if (yp > BOTTOM_EDGE_LIMIT) {
// Can't draw anymore
m_lastLineDisplayed = FALSE8;
break;
}
}
}
// Now do the fadey bit
FadeStrip(0, 28, RIGHT_HAND_MARGIN + 1, TRUE8, ad, pitch);
FadeStrip(0, surface_manager->Get_height(m_profileSurface) - 43, RIGHT_HAND_MARGIN + 1, FALSE8, ad, pitch);
surface_manager->Unlock_surface(m_profileSurface);
2020-09-21 04:59:09 +00:00
LRECT srcr;
srcr.left = 0;
srcr.top = 28;
srcr.right = m_profileRect.right - m_profileRect.left;
srcr.bottom = srcr.top + m_profileRect.bottom - m_profileRect.top;
// Now blit the sucker to it's house with transparency
surface_manager->Blit_surface_to_surface(m_profileSurface, working_buffer_id, &srcr, &m_profileRect, DDBLT_KEYSRC);
// Draw to the correct surface now
ad = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
// Draw the headings
msg = GetTextFromReference(HashString("prf_name"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, leftMargin + m_margin - temp - 10, 90, PALEFONT, FALSE8);
msg = GetTextFromReference(HashString("prf_age"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, leftMargin + m_margin - temp - 10, 110, PALEFONT, FALSE8);
msg = GetTextFromReference(HashString("prf_height"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, leftMargin + m_margin - temp - 10, 130, PALEFONT, FALSE8);
msg = GetTextFromReference(HashString("prf_weight"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, leftMargin + m_margin - temp - 10, 150, PALEFONT, FALSE8);
msg = GetTextFromReference(HashString("prf_profile"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, leftMargin + m_margin - temp - 10, 200, PALEFONT, FALSE8);
// Now the vitals for this character (using margin value for safety)
label.Format("%s%s", prefix, "name");
msg = GetTextFromReference(HashString(label));
DisplayText(ad, pitch, msg, leftMargin + m_margin, 90, NORMALFONT, FALSE8);
label.Format("%s%s", prefix, "age");
msg = GetTextFromReference(HashString(label));
DisplayText(ad, pitch, msg, leftMargin + m_margin, 110, NORMALFONT, FALSE8);
label.Format("%s%s", prefix, "height");
msg = GetTextFromReference(HashString(label));
DisplayText(ad, pitch, msg, leftMargin + m_margin, 130, NORMALFONT, FALSE8);
label.Format("%s%s", prefix, "weight");
msg = GetTextFromReference(HashString(label));
DisplayText(ad, pitch, msg, leftMargin + m_margin, 150, NORMALFONT, FALSE8);
// Draw the paging indicators
uint32 tlx = leftMargin + m_margin - 25;
uint32 tly = 338;
if (!(m_profileScrollingLine == -1 && m_profileScrollingOffset == 0))
DrawPageIndicator(tlx, tly, TRUE8, (bool8)(m_moveLimiter && m_profileScrolling < 0 ? TRUE8 : FALSE8), ad, pitch);
if (m_lastLineDisplayed == FALSE8)
DrawPageIndicator(tlx, tly + 12, FALSE8, (bool8)(m_moveLimiter && m_profileScrolling > 0 ? TRUE8 : FALSE8), ad, pitch);
// This is permanently selected on these profile screens
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, SCREEN_DEPTH - 90, SELECTEDFONT, TRUE8);
surface_manager->Unlock_surface(surface_id);
// Timing code to lock the refresh rate at a constant
t = GetMicroTimer() - t;
int32 r = 40000 - t;
if (t < 40000) {
g_system->delayMillis(r / 1000);
}
}
void OptionsManager::FadeStrip(uint32 x, uint32 y, uint32 w, bool8 up, uint8 *ad, uint32 pitch) {
uint8 subtractive[8];
uint32 *safe_ad = (uint32 *)ad;
uint8 initialFade = 0xE6; // 230
uint8 finalFade = 0x05; // 5
// Move to starting pixel
safe_ad += (y * (pitch / 4));
safe_ad += x;
uint8 *pixels = (uint8 *)safe_ad;
if (up) {
// Initial fade amount == 230
subtractive[4] = subtractive[0] = initialFade;
subtractive[5] = subtractive[1] = initialFade;
subtractive[6] = subtractive[2] = initialFade;
subtractive[7] = subtractive[3] = 0x00;
} else {
// Initial fade amount == 5
subtractive[4] = subtractive[0] = finalFade;
subtractive[5] = subtractive[1] = finalFade;
subtractive[6] = subtractive[2] = finalFade;
subtractive[7] = subtractive[3] = 0x00;
}
#if 1
2021-03-07 08:56:34 +00:00
for (uint32 lines = 0; lines < 15; lines++) {
for (uint32 xPos = 0; xPos < w; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Subtract from RGB components
2021-03-07 08:56:34 +00:00
for (int32 i = 0; i < 3; i++) {
pixel[i] = MAX(0, pixel[i] - subtractive[i]);
}
}
// Next line
pixels += pitch;
// Change fade amounts
if (up) {
subtractive[4] = subtractive[0] = (uint8)(initialFade - (lines * 15));
subtractive[5] = subtractive[1] = (uint8)(initialFade - (lines * 15));
subtractive[6] = subtractive[2] = (uint8)(initialFade - (lines * 15));
subtractive[7] = subtractive[3] = 0x00;
} else {
subtractive[4] = subtractive[0] = (uint8)(finalFade + (lines * 15));
subtractive[5] = subtractive[1] = (uint8)(finalFade + (lines * 15));
subtractive[6] = subtractive[2] = (uint8)(finalFade + (lines * 15));
subtractive[7] = subtractive[3] = 0x00;
}
}
#else
2021-03-07 08:56:34 +00:00
int32 pixelPairs = w / 2;
for (uint32 lines = 0; lines < 15; lines++) {
_asm {
lea edi, subtractive ; // Load the address of the blend colour block
mov ecx, pixelPairs ; // Pixel Counter (2 pixels at a time mind)
mov esi, safe_ad ; // Load the address of the pixels
movq MM0, [edi] ; // Put address of the blend colour block into MMX register
subtractive_fade_loop:
movq MM1, [esi] ; // Load 2 pixels
psubusb MM1, MM0 ; // Do the subtract
movq [esi], MM1 ; // Store the result
add esi, 8 ; // Move pixel pointer on
dec ecx ; // Reduce counter
jne subtractive_fade_loop ; // On to the next 2 pixels
EMMS ; // Clear/Set MMX/FPU flag
}
safe_ad += (pitch / 4);
// Change fade amounts
if (up) {
subtractive[4] = subtractive[0] = (uint8)(initialFade - (lines * 15));
subtractive[5] = subtractive[1] = (uint8)(initialFade - (lines * 15));
subtractive[6] = subtractive[2] = (uint8)(initialFade - (lines * 15));
subtractive[7] = subtractive[3] = 0x00;
} else {
subtractive[4] = subtractive[0] = (uint8)(finalFade + (lines * 15));
subtractive[5] = subtractive[1] = (uint8)(finalFade + (lines * 15));
subtractive[6] = subtractive[2] = (uint8)(finalFade + (lines * 15));
subtractive[7] = subtractive[3] = 0x00;
}
}
#endif
}
void OptionsManager::DrawPageIndicator(uint32 x, uint32 y, bool8 up, bool8 selected, uint8 *ad, uint32 pitch) {
_rgb col;
if (selected) {
col.red = 202;
col.green = 0;
col.blue = 0;
} else {
col.red = 254;
col.green = 254;
col.blue = 254;
}
if (up) {
Draw_horizontal_line(x + 7, y, 1, &col, ad, pitch);
Draw_horizontal_line(x + 6, y + 1, 3, &col, ad, pitch);
Draw_horizontal_line(x + 5, y + 2, 5, &col, ad, pitch);
Draw_horizontal_line(x + 4, y + 3, 7, &col, ad, pitch);
Draw_horizontal_line(x + 3, y + 4, 9, &col, ad, pitch);
Draw_horizontal_line(x + 2, y + 5, 11, &col, ad, pitch);
Draw_horizontal_line(x + 1, y + 6, 13, &col, ad, pitch);
} else {
Draw_horizontal_line(x + 7, y + 6, 1, &col, ad, pitch);
Draw_horizontal_line(x + 6, y + 5, 3, &col, ad, pitch);
Draw_horizontal_line(x + 5, y + 4, 5, &col, ad, pitch);
Draw_horizontal_line(x + 4, y + 3, 7, &col, ad, pitch);
Draw_horizontal_line(x + 3, y + 2, 9, &col, ad, pitch);
Draw_horizontal_line(x + 2, y + 1, 11, &col, ad, pitch);
Draw_horizontal_line(x + 1, y, 13, &col, ad, pitch);
}
}
void OptionsManager::GetKeyAssignment() {
uint32 keypressed = Get_DI_key_press();
// Change selected function using the enter key (so ensure this doesn't get immediately assigned)
if ((keypressed == Common::KEYCODE_RETURN) && m_configLimiter) {
// Now allowed to assign a key
m_configLimiter = FALSE8;
return;
}
2021-02-13 21:44:45 +00:00
if (m_configLimiter) {
// Now allowed to assign a button
m_configLimiter = FALSE8;
g_system->delayMillis(200);
return;
}
m_assignFlash++;
if (m_assignFlash == 10)
m_assignFlash = 0;
}
void OptionsManager::InitialiseControlsScreen() {
const char *msg = NULL;
// Need to calculate printing margin
m_margin = 0;
2021-03-07 08:56:34 +00:00
uint32 margin = 0;
2021-03-07 20:56:44 +00:00
// This ensures correct spacing for any translations (assuming this is the longest heading thang)
msg = GetTextFromReference(HashString("opt_controlmethod"));
margin = CalculateStringWidth(msg);
if (m_margin < margin)
m_margin = margin;
// This is the offending line in german version so add hack here
msg = GetTextFromReference(HashString("opt_turnright"));
margin = CalculateStringWidth(msg);
if (m_margin < margin)
m_margin = margin;
// Cool now add some spacing
m_margin += 15;
m_controlPage1 = TRUE8;
}
void OptionsManager::DrawControllerConfiguration() {
const char *msg = NULL;
uint32 halfScreen = SCREEN_WIDTH / 2;
uint32 temp;
pxString sentence;
2020-09-21 04:59:09 +00:00
LRECT repairRect;
// Only perform dirty rectangle refresh in-game (ie no movie)
if (m_useDirtyRects) {
// Calculate dirty rectangle
repairRect.left = 10;
repairRect.right = halfScreen + 130;
repairRect.top = 130;
repairRect.bottom = 340;
// We need to refresh a rectangle only
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
// Should use the actor's bounding box really but as that is more aften than not incorrect...
repairRect.left = halfScreen + 131;
repairRect.right = SCREEN_WIDTH;
repairRect.top = 79;
repairRect.bottom = 420;
// We need to refresh a rectangle only
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
}
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_controls"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
msg = GetTextFromReference(HashString("opt_controlmethod"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, m_margin - temp, 155, (m_CONTROL_selected == METHOD) ? SELECTEDFONT : NORMALFONT, FALSE8);
if (g_icb_session->player.Get_control_mode() == SCREEN_RELATIVE)
msg = GetTextFromReference(HashString("opt_screenrelative"));
else
msg = GetTextFromReference(HashString("opt_actorrelative"));
DisplayText(ad, pitch, msg, m_margin + 5, 155, NORMALFONT, FALSE8);
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, m_margin + 5, 385, (bool8)(m_CONTROL_selected == DONE) ? SELECTEDFONT : NORMALFONT, FALSE8);
surface_manager->Unlock_surface(working_buffer_id);
}
void OptionsManager::DrawGameOptions() {
const char *msg = NULL;
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_options"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
msg = GetTextFromReference(HashString("opt_videosettings"));
DisplayText(ad, pitch, msg, 0, 130, (m_OPTION_selected == VIDEO_SETTINGS) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_audiosettings"));
DisplayText(ad, pitch, msg, 0, 150, (m_OPTION_selected == AUDIO_SETTINGS) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_controls"));
DisplayText(ad, pitch, msg, 0, 170, (m_OPTION_selected == CONTROLS) ? SELECTEDFONT : NORMALFONT, TRUE8);
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, 205, (m_OPTION_selected == DO_BACK) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
}
void OptionsManager::DrawAudioSettings() {
const char *msg = NULL;
uint32 halfScreen = SCREEN_WIDTH / 2;
uint32 temp;
2020-09-21 04:59:09 +00:00
LRECT repairRect;
// Need to blit a dirty rect
if (m_useDirtyRects) {
if (m_AUDIO_selected != DO_ONE) {
repairRect.left = halfScreen + 2;
repairRect.right = halfScreen + 128;
repairRect.top = 142 + (m_AUDIO_selected * 35);
repairRect.bottom = repairRect.top + 16;
// We need to refresh a rectangle only
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
}
}
// Filled rectangles must be outside of a lock
Fill_rect(halfScreen + 3, 143, halfScreen + 127, 157, 0x172B4E);
if (GetMusicVolume() > 2)
Fill_rect(halfScreen + 2, 142, halfScreen + GetMusicVolume(), 158, 0xFEFEFE);
Fill_rect(halfScreen + 3, 178, halfScreen + 127, 192, 0x172B4E);
if (GetSpeechVolume() > 2)
Fill_rect(halfScreen + 2, 177, halfScreen + GetSpeechVolume(), 193, 0xFEFEFE);
Fill_rect(halfScreen + 3, 213, halfScreen + 127, 227, 0x172B4E);
if (GetSfxVolume() > 2)
Fill_rect(halfScreen + 2, 212, halfScreen + GetSfxVolume(), 228, 0xFEFEFE);
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_audiosettings"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
DrawRectangle((bool8)(m_AUDIO_selected == MUSIC_VOLUME), halfScreen, 140, 129, 19, ad, pitch);
DrawRectangle((bool8)(m_AUDIO_selected == SPEECH_VOLUME), halfScreen, 175, 129, 19, ad, pitch);
DrawRectangle((bool8)(m_AUDIO_selected == SFX_VOLUME), halfScreen, 210, 129, 19, ad, pitch);
SetDrawColour(BASE);
msg = GetTextFromReference(HashString("opt_musicvolume"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, 140, (m_AUDIO_selected == MUSIC_VOLUME) ? SELECTEDFONT : NORMALFONT, FALSE8);
msg = GetTextFromReference(HashString("opt_speechvolume"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, 175, (m_AUDIO_selected == SPEECH_VOLUME) ? SELECTEDFONT : NORMALFONT, FALSE8);
msg = GetTextFromReference(HashString("opt_sfxvolume"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, 210, (m_AUDIO_selected == SFX_VOLUME) ? SELECTEDFONT : NORMALFONT, FALSE8);
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, 255, (m_AUDIO_selected == DO_ONE) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
}
void OptionsManager::DrawVideoSettings() {
const char *msg = NULL;
uint32 halfScreen = SCREEN_WIDTH / 2;
uint32 temp;
2020-09-21 04:59:09 +00:00
LRECT repairRect;
// Need to blit a dirty rect
if (m_useDirtyRects) {
if (m_VIDEO_selected != LEAVE) {
repairRect.left = halfScreen;
repairRect.right = SCREEN_WIDTH;
repairRect.top = 130 + (m_VIDEO_selected * 20);
repairRect.bottom = repairRect.top + 20;
// We need to refresh a rectangle only
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
}
}
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
msg = GetTextFromReference(HashString("opt_videosettings"));
DisplayText(ad, pitch, msg, 0, 80, NORMALFONT, TRUE8, TRUE8);
uint32 hite = 130;
// Subtitles
msg = GetTextFromReference(HashString("opt_subtitles"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, hite, (m_VIDEO_selected == SUBTITLES) ? SELECTEDFONT : NORMALFONT, FALSE8);
2021-03-14 07:59:48 +00:00
if (g_px->on_screen_text)
msg = GetTextFromReference(HashString("opt_on"));
else
msg = GetTextFromReference(HashString("opt_off"));
DisplayText(ad, pitch, msg, halfScreen, hite, NORMALFONT, FALSE8);
hite += 20;
if (g_videoOptionsCheat == TRUE8) {
hite += 20;
// Shadows
msg = GetTextFromReference(HashString("opt_shadows"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, hite, (m_VIDEO_selected == SHADOWS) ? SELECTEDFONT : NORMALFONT, FALSE8);
2021-03-14 07:59:48 +00:00
if (g_px->actorShadows == -1)
msg = GetTextFromReference(HashString("opt_shadows_simple"));
2021-03-14 07:59:48 +00:00
else if (g_px->actorShadows == 1)
msg = GetTextFromReference(HashString("opt_shadows_1"));
2021-03-14 07:59:48 +00:00
else if (g_px->actorShadows == 2)
msg = GetTextFromReference(HashString("opt_shadows_2"));
2021-03-14 07:59:48 +00:00
else if (g_px->actorShadows == 3)
msg = GetTextFromReference(HashString("opt_shadows_3"));
else
msg = GetTextFromReference(HashString("opt_shadows_off"));
DisplayText(ad, pitch, msg, halfScreen, hite, NORMALFONT, FALSE8);
hite += 20;
// Frame limiter
msg = GetTextFromReference(HashString("opt_framelimiter"));
temp = CalculateStringWidth(msg);
DisplayText(ad, pitch, msg, halfScreen - temp - 10, hite, (m_VIDEO_selected == FRAMELIMITER) ? SELECTEDFONT : NORMALFONT, FALSE8);
char msg2[6];
2021-03-13 16:34:02 +00:00
sprintf(msg2, "%d%%", g_stub->cycle_speed);
DisplayText(ad, pitch, msg2, halfScreen, hite, NORMALFONT, FALSE8);
}
hite += 45;
// Back
msg = GetTextFromReference(HashString("opt_back"));
DisplayText(ad, pitch, msg, 0, hite, (m_VIDEO_selected == LEAVE) ? SELECTEDFONT : NORMALFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
}
void OptionsManager::AnimateSlotsPaging() {
2021-03-07 08:56:34 +00:00
int32 boxWidth = m_slotBoundingRect.right - m_slotBoundingRect.left;
int32 inc = 50;
uint32 t = 0;
2020-09-21 04:59:09 +00:00
LRECT repairRect;
bool8 saveRestoreScreen = (bool8)(m_activeMenu != MAIN_MOVIES);
// Only perform dirty rectangle refresh in-game (ie no movie)
if (m_useDirtyRects) {
// Time lock code
t = GetMicroTimer();
// Calculate dirty rectangle
repairRect.left = 0;
repairRect.right = SCREEN_WIDTH;
repairRect.top = m_slotBoundingRect.top - 1;
repairRect.bottom = m_slotBoundingRect.bottom;
// We need to refresh a rectangle only
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
}
// Clean the surfaces
surface_manager->Fill_surface(m_mySlotSurface1ID, m_colourKey);
if (m_pageleft) {
// We want to decrement the slot offset
// Should we slow the animation down
if (m_pageOn_dest.right > (m_slotBoundingRect.right - 10)) {
m_pageOn_dest.right += 1;
} else
m_pageOn_dest.right += inc;
// Limit checking
if (m_pageOn_dest.right > m_slotBoundingRect.right)
m_pageOn_dest.right = m_slotBoundingRect.right;
// Is the rect still partially hidden outside the screen
if (m_pageOn_dest.right > boxWidth) {
m_pageOn_dest.left = m_pageOn_dest.right - boxWidth;
m_pageOn_from.left = m_slotBoundingRect.left;
} else {
m_pageOn_dest.left = 0;
// Now set the source blit rectangle to match
m_pageOn_from.left = m_slotBoundingRect.left + (boxWidth - m_pageOn_dest.right);
}
if (m_pageOn_from.left < m_pageOn_from.right) {
// Which slots are we animating
if (saveRestoreScreen) {
// Draw the new slots to this blank surface
DrawGameSlots(m_slotOffset - NUMBER_OF_VISIBLE_GAME_SLOTS, m_mySlotSurface1ID);
} else {
DrawMovieSlots(m_movieOffset - M_NUMBER_OF_VISIBLE_MOVIE_SLOTS, m_mySlotSurface1ID);
}
// Blit this surface the the screen with animating offsets and transparency
surface_manager->Blit_surface_to_surface(m_mySlotSurface1ID, working_buffer_id, &m_pageOn_from, &m_pageOn_dest, DDBLT_KEYSRC);
}
surface_manager->Fill_surface(m_mySlotSurface1ID, m_colourKey);
2021-03-13 13:31:49 +00:00
m_pageOff_dest.left += m_slotsAnimOffBy * inc;
m_pageOff_dest.right = m_pageOff_dest.left + boxWidth;
if (m_pageOff_dest.right > SCREEN_WIDTH - 1) {
m_pageOff_dest.right = SCREEN_WIDTH - 1;
m_pageOff_from.right = m_slotBoundingRect.left + ((SCREEN_WIDTH - 1) - m_pageOff_dest.left);
}
if (m_pageOff_from.left < m_pageOff_from.right) {
// Which slots are we animating
if (saveRestoreScreen) {
// Draw the current slots to this blank surface
DrawGameSlots(m_slotOffset, m_mySlotSurface1ID);
} else {
DrawMovieSlots(m_movieOffset, m_mySlotSurface1ID);
}
// Blit this surface the the screen with animating offsets and transparency
surface_manager->Blit_surface_to_surface(m_mySlotSurface1ID, working_buffer_id, &m_pageOff_from, &m_pageOff_dest, DDBLT_KEYSRC);
}
// Are we finished
if (m_pageOn_dest.right == m_slotBoundingRect.right) {
// Stop animating and alter slot offset
m_paging = FALSE8;
2021-03-13 13:31:49 +00:00
m_slotsAnimOffBy = 0;
if (saveRestoreScreen) {
m_slotOffset -= NUMBER_OF_VISIBLE_GAME_SLOTS;
LoadVisibleThumbnails();
} else {
m_movieOffset -= M_NUMBER_OF_VISIBLE_MOVIE_SLOTS;
// Check we move the selection from the paging icon for the first page (which is now hidden)
if (m_movieOffset == 0)
m_M_MOVIE_selected = MOVIE05;
LoadVisibleMovieShots();
}
return;
}
// Decrement counter
2021-03-13 13:31:49 +00:00
m_slotsAnimOffBy++;
} else {
// We want to increment the slot offset
// Should we slow the animation down
if (m_pageOn_dest.left < (m_slotBoundingRect.left + 10)) {
m_pageOn_dest.left -= 1;
} else
m_pageOn_dest.left -= inc;
// Limit checking
if (m_pageOn_dest.left < m_slotBoundingRect.left)
m_pageOn_dest.left = m_slotBoundingRect.left;
// Is the rect still partially hidden outside the screen
if (m_pageOn_dest.left < (SCREEN_WIDTH - 1) - boxWidth) {
m_pageOn_dest.right = m_pageOn_dest.left + boxWidth;
m_pageOn_from.right = m_slotBoundingRect.right;
} else {
m_pageOn_dest.right = (SCREEN_WIDTH - 1);
// Now set the source blit rectangle to match
m_pageOn_from.right = m_slotBoundingRect.left + ((SCREEN_WIDTH - 1) - m_pageOn_dest.left);
}
if (m_pageOn_from.left < m_pageOn_from.right) {
if (saveRestoreScreen) {
// Draw the new slots to this blank surface
DrawGameSlots(m_slotOffset + NUMBER_OF_VISIBLE_GAME_SLOTS, m_mySlotSurface1ID);
} else {
DrawMovieSlots(m_movieOffset + M_NUMBER_OF_VISIBLE_MOVIE_SLOTS, m_mySlotSurface1ID);
}
// Blit this surface the the screen with animating offsets and transparency
surface_manager->Blit_surface_to_surface(m_mySlotSurface1ID, working_buffer_id, &m_pageOn_from, &m_pageOn_dest, DDBLT_KEYSRC);
}
surface_manager->Fill_surface(m_mySlotSurface1ID, m_colourKey);
2021-03-13 13:31:49 +00:00
m_pageOff_dest.right -= m_slotsAnimOffBy * inc;
m_pageOff_dest.left = m_pageOff_dest.right - boxWidth;
if (m_pageOff_dest.left < 0) {
m_pageOff_dest.left = 0;
m_pageOff_from.left = m_slotBoundingRect.left + (boxWidth - m_pageOff_dest.right);
}
if (m_pageOff_from.left < m_pageOff_from.right) {
if (saveRestoreScreen) {
// Draw the current slots to this blank surface
DrawGameSlots(m_slotOffset, m_mySlotSurface1ID);
} else {
DrawMovieSlots(m_movieOffset, m_mySlotSurface1ID);
}
// Blit this surface the the screen with animating offsets and transparency
surface_manager->Blit_surface_to_surface(m_mySlotSurface1ID, working_buffer_id, &m_pageOff_from, &m_pageOff_dest, DDBLT_KEYSRC);
}
// Are we finished
if (m_pageOn_dest.left == m_slotBoundingRect.left) {
// Stop animating and alter slot offset
m_paging = FALSE8;
2021-03-13 13:31:49 +00:00
m_slotsAnimOffBy = 0;
if (saveRestoreScreen) {
m_slotOffset += NUMBER_OF_VISIBLE_GAME_SLOTS;
LoadVisibleThumbnails();
} else {
m_movieOffset += M_NUMBER_OF_VISIBLE_MOVIE_SLOTS;
// Is the selection valid on this new page
if ((m_M_MOVIE_selected < NOTHANKS) && (m_M_MOVIE_selected + m_movieOffset >= TOTAL_NUMBER_OF_MOVIES))
m_M_MOVIE_selected = (M_MOVIE_CHOICES)((TOTAL_NUMBER_OF_MOVIES % M_NUMBER_OF_VISIBLE_MOVIE_SLOTS) - 1);
// Check we move the selection from the paging icon for the last page (which is now hidden)
if (m_movieOffset >= TOTAL_NUMBER_OF_MOVIES - M_NUMBER_OF_VISIBLE_MOVIE_SLOTS)
m_M_MOVIE_selected = MOVIE08;
LoadVisibleMovieShots();
}
return;
}
// Decrement counter
2021-03-13 13:31:49 +00:00
m_slotsAnimOffBy++;
}
if (m_useDirtyRects) {
// Lock refresh time
t = GetMicroTimer() - t;
int32 r = REFRESH_LIMITER - t;
if (t < REFRESH_LIMITER) {
g_system->delayMillis(r / 1000);
}
}
}
void OptionsManager::SetTargetBox(uint32 x1, uint32 x2, uint32 y1, uint32 y2, uint32 nFrames) {
m_targetBox.left = x1;
m_targetBox.right = x2;
m_targetBox.top = y1;
m_targetBox.bottom = y2;
m_interFrames = nFrames;
m_warpDirection = TRUE8;
m_autoAnimating2 = 0;
int32 growBy = (m_targetBox.left - m_optionsBox.left);
m_widthIncrements = (growBy / m_interFrames);
if (m_widthIncrements == 0) {
if (growBy < 0)
m_widthIncrements = -1;
else
m_widthIncrements = 1;
}
growBy = (m_targetBox.top - m_optionsBox.top);
m_topIncrements = (growBy / m_interFrames);
if (m_topIncrements == 0) {
if (growBy < 0)
m_topIncrements = -1;
else
m_topIncrements = 1;
}
growBy = (m_targetBox.bottom - m_optionsBox.bottom);
m_bottomIncrements = (growBy / m_interFrames);
if (m_bottomIncrements == 0) {
if (growBy < 0)
m_bottomIncrements = -1;
else
m_bottomIncrements = 1;
}
}
bool8 OptionsManager::AnimateBracketsToBox(bool8 forwards, uint32 surface_id) {
uint32 pitch;
uint8 *surface_address;
uint32 t = 0;
2020-09-21 04:59:09 +00:00
LRECT repairRect;
// Check if we still need to animate the brackets
if (forwards) {
// Are we finished
if (m_autoAnimating2 > m_interFrames) {
m_interFrames = -1;
return FALSE8;
}
} else {
// Are we finished
if (m_autoAnimating2 < 0) {
m_interFrames = -1;
return FALSE8;
}
}
// Only perform dirty rectangle refresh in-game (ie no movie)
if (m_useDirtyRects) {
// Time lock code
t = GetMicroTimer();
// Calculate dirty rectangle (needs to be quite fat to cope with other languages)
repairRect.left = m_targetBox.left - 50;
repairRect.right = m_targetBox.right + 50;
repairRect.top = m_targetBox.top - 1;
repairRect.bottom = m_targetBox.bottom + m_optionsBox.bottom - m_optionsBox.top + 5;
// We need to refresh a rectangle only
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
}
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(surface_id);
pitch = surface_manager->Get_pitch(surface_id);
// Vertical bracket lips are drawn from top to bottom
Draw_vertical_line(m_box.left - 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.left - 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.top - 1, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.bottom + 1, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
// Unlock the working buffer
surface_manager->Unlock_surface(surface_id);
// Apply modifications with clipping
if (forwards) {
// Catch last frame
if (m_autoAnimating2 == m_interFrames) {
m_box.left = m_targetBox.left;
m_box.right = m_targetBox.right;
m_box.top = m_targetBox.top;
m_box.bottom = m_targetBox.bottom;
// Final refresh
if (m_useDirtyRects)
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
} else {
// Check we don't exceed our desired limits
if (m_box.top + m_topIncrements < m_targetBox.top)
m_box.top = m_targetBox.top;
else
m_box.top += m_topIncrements;
if (m_box.bottom + m_bottomIncrements < m_targetBox.bottom)
m_box.bottom = m_targetBox.bottom;
else
m_box.bottom += m_bottomIncrements;
if (m_box.left + m_widthIncrements < m_targetBox.left)
m_box.left = m_targetBox.left;
else
m_box.left += m_widthIncrements;
if (m_box.right - m_widthIncrements > m_targetBox.right)
m_box.right = m_targetBox.right;
else
m_box.right -= m_widthIncrements;
}
} else {
// Catch last frame
if (m_autoAnimating2 == 0) {
m_box.left = m_optionsBox.left;
m_box.right = m_optionsBox.right;
m_box.top = m_optionsBox.top;
m_box.bottom = m_optionsBox.bottom;
// Final refresh
if (m_useDirtyRects)
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
} else {
// Check we don't exceed our desired limits
if (m_box.top - m_topIncrements > m_optionsBox.top)
m_box.top = m_optionsBox.top;
else
m_box.top -= m_topIncrements;
if (m_box.bottom - m_bottomIncrements > m_optionsBox.bottom)
m_box.bottom = m_optionsBox.bottom;
else
m_box.bottom -= m_bottomIncrements;
if (m_box.left - m_widthIncrements > m_optionsBox.left)
m_box.left = m_optionsBox.left;
else
m_box.left -= m_widthIncrements;
if (m_box.right + m_widthIncrements < m_optionsBox.right)
m_box.right = m_optionsBox.right;
else
m_box.right += m_widthIncrements;
}
}
// Increment or decrement current frame counter dependent on direction
if (forwards)
m_autoAnimating2++;
else
m_autoAnimating2--;
if (m_useDirtyRects) {
// Lock refresh time
t = GetMicroTimer() - t;
int32 r = REFRESH_LIMITER - t;
if (t < REFRESH_LIMITER) {
g_system->delayMillis(r / 1000);
}
}
return TRUE8;
}
bool8 OptionsManager::AnimateThoseBrackets(bool8 forwards) {
uint32 pitch;
uint8 *surface_address;
// Check if we still need to animate the brackets opening or closing
if (forwards) {
// Are we finished opening
if (m_autoAnimating > m_over_n_Frames)
return FALSE8;
} else {
// Are we finished closing
if (m_autoAnimating < 0)
return FALSE8;
}
// Time lock code
uint32 t = GetMicroTimer();
2020-09-21 04:59:09 +00:00
LRECT repairRect;
repairRect.left = m_optionsBox.left - 1;
repairRect.right = m_optionsBox.right + 2;
repairRect.top = m_optionsBox.top - 1;
repairRect.bottom = m_optionsBox.bottom + 2;
surface_manager->Blit_surface_to_surface(m_myScreenSurfaceID, working_buffer_id, &repairRect, &repairRect);
int32 nWidthIterations = (m_over_n_Frames / 3);
int32 nHeightIterations = m_over_n_Frames - nWidthIterations;
// The animating line portion of the animation occurs over a proportion of the total frames
if (m_autoAnimating < nWidthIterations) {
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
// Draw the horizontal line
Draw_horizontal_line(m_box.left, m_box.top, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.top - 1, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
// Calculate m_grower value dependent on frame
if (m_autoAnimating == 0) {
// To give slight pause when only a dot
m_grower = 1;
} else if (m_autoAnimating == nWidthIterations - 1) {
// To ensure we reach the desired width on the last iteration
m_grower = 15;
} else if (m_autoAnimating > nWidthIterations - 3) {
// To pause when we're near the end of the line
m_grower = 0;
} else {
// Regular growth
m_grower = ((SCREEN_WIDTH / 2) - m_optionsBox.left - 3) / (nWidthIterations - 4);
}
// Apply modifications with clipping
if (forwards) {
// Check we don't exceed our desired width limit
if (m_box.left - m_grower < m_optionsBox.left)
m_box.left = m_optionsBox.left;
else
m_box.left -= m_grower;
if (m_box.right + m_grower > m_optionsBox.right)
m_box.right = m_optionsBox.right;
else
m_box.right += m_grower;
} else {
// We are closing so shrink the width of our box by m_grower
m_box.left += m_grower;
m_box.right -= m_grower;
// Catch any overlap and cap it with limits
if (m_box.left > m_box.right) {
m_box.left = SCREEN_WIDTH / 2;
m_box.right = SCREEN_WIDTH / 2;
}
}
} else {
// Now grow or shrink the brackets
// Lock the directdraw surface (working buffer)
surface_address = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
// Vertical bracket lips are drawn from top to bottom
Draw_vertical_line(m_box.left, m_box.top, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.left - 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.top - 1, m_lipLength + 1, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.left, m_box.bottom - m_lipLength, m_lipLength, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.left - 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_vertical_line(m_box.right + 1, m_box.bottom - m_lipLength, m_lipLength + 2, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.top, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.top - 1, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.bottom, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(m_box.left, m_box.bottom + 1, m_box.right - m_box.left, &m_drawColour, surface_address, pitch);
// Unlock the working buffer
surface_manager->Unlock_surface(working_buffer_id);
// Calculate m_grower value dependent on frame
if (m_autoAnimating == nWidthIterations) {
// To give slight pause
m_grower = 1;
} else if (m_autoAnimating == m_over_n_Frames - 1) {
// To ensure we reach the desired width on the last iteration
m_grower = 15;
} else if (m_autoAnimating > m_over_n_Frames - 3) {
// To pause when we're near the end of the line
m_grower = 2;
} else {
// Regular growth
m_grower = ((SCREEN_DEPTH / 2) - m_optionsBox.top - 3) / (nHeightIterations - 4);
// Special spongey feel
if (m_autoAnimating % 2 == 0)
m_grower++;
}
// Apply modifications with clipping
if (forwards) {
// Check we don't exceed our desired height limit
if (m_box.top - m_grower < m_optionsBox.top)
m_box.top = m_optionsBox.top;
else
m_box.top -= m_grower;
if (m_box.bottom + m_grower > m_optionsBox.bottom)
m_box.bottom = m_optionsBox.bottom;
else
m_box.bottom += m_grower;
} else {
// We are closing so shrink the height of our box by m_grower
m_box.top += m_grower;
m_box.bottom -= m_grower;
// Catch any overlap and cap it with limits
if (m_box.top > m_box.bottom) {
m_box.top = m_box.bottom = m_optionsBox.top + ((m_optionsBox.bottom - m_optionsBox.top) / 2);
}
}
// Ensure the bracket lips are the correct size
if (m_box.bottom - m_box.top < 10)
m_lipLength = m_box.bottom - m_box.top;
else
m_lipLength = 10;
}
// Increment or decrement current frame counter dependent on direction
if (forwards)
m_autoAnimating++;
else
m_autoAnimating--;
// Lock refresh time
t = GetMicroTimer() - t;
if (t < REFRESH_LIMITER) {
int32 r = REFRESH_LIMITER - t;
g_system->delayMillis(r / 1000);
}
return TRUE8;
}
void OptionsManager::DarkenScreen() {
uint8 subtractive[8];
uint8 fadeBy = 0x50;
// Fade by table
subtractive[4] = subtractive[0] = fadeBy;
subtractive[5] = subtractive[1] = fadeBy;
subtractive[6] = subtractive[2] = fadeBy;
subtractive[7] = subtractive[3] = 0x00;
// Lock the directdraw surface (working buffer)
uint8 *pixels = (uint8 *)surface_manager->Lock_surface(m_myScreenSurfaceID);
uint32 pitch = surface_manager->Get_pitch(m_myScreenSurfaceID);
// Darken the screen
#if 1
for (uint32 lines = 0; lines < SCREEN_DEPTH; lines++) {
2021-03-07 08:56:34 +00:00
for (int32 xPos = 0; xPos < SCREEN_WIDTH; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Subtract from RGB components
2021-03-07 08:56:34 +00:00
for (int32 i = 0; i < 3; i++) {
pixel[i] = MAX(0, pixel[i] - subtractive[i]);
}
}
// Next line
pixels += pitch;
}
#else
_asm {
lea edi, subtractive ; // Load the address of the blend colour block
mov esi, pixels ; // Load the address of the pixels
movq MM0, [edi] ; // Put address of the blend colour block into MMX register
sub esi, 8
mov edx, SCREEN_DEPTH ; // Number of line to darken
mov eax, pitch;
mov ebx, (SCREEN_WIDTH/2); // Pixel Counter (2 pixels at a time mind)
om_subtractive_fade_row_loop:
mov ecx, ebx ; // Pixel Counter (2 pixels at a time mind)
om_subtractive_fade_loop:
movq MM1, [esi + ecx * 8] ; // Load 2 pixels
psubusb MM1, MM0 ; // Do the subtract
movq [esi + ecx * 8], MM1 ; // Store the result
dec ecx ; // Reduce counter
jne om_subtractive_fade_loop; // On to the next 2 pixels
add esi, eax; // On to the next row
dec edx;
jne om_subtractive_fade_row_loop;
EMMS ; // Clear/Set MMX/FPU flag
}
#endif
// Unlock the working buffer
surface_manager->Unlock_surface(m_myScreenSurfaceID);
}
void OptionsManager::BloodScreen() {
uint8 subtractive[8];
uint8 fadeBy = 0x32;
// Fade by table
subtractive[4] = subtractive[0] = 0xFF; // No blue
subtractive[5] = subtractive[1] = 0xFF; // No green
subtractive[6] = subtractive[2] = fadeBy; // Fade red component
subtractive[7] = subtractive[3] = 0x00; // Don't care about alpha
// Lock the directdraw surface (working buffer)
uint8 *pixels = (uint8 *)surface_manager->Lock_surface(m_myScreenSurfaceID);
uint32 pitch = surface_manager->Get_pitch(m_myScreenSurfaceID);
// Darken the screen
#if 1
for (uint32 lines = 0; lines < SCREEN_DEPTH; lines++) {
2021-03-07 08:56:34 +00:00
for (int32 xPos = 0; xPos < SCREEN_WIDTH; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Subtract from RGB components
2021-03-07 08:56:34 +00:00
for (int32 i = 0; i < 3; i++) {
pixel[i] = MAX(0, pixel[i] - subtractive[i]);
}
}
// Next line
pixels += pitch;
}
#else
_asm {
lea edi, subtractive ; // Load the address of the blend colour block
mov esi, pixels ; // Load the address of the pixels
movq MM0, [edi] ; // Put address of the blend colour block into MMX register
sub esi, 8
mov edx, SCREEN_DEPTH ; // Number of line to darken
mov eax, pitch;
mov ebx, (SCREEN_WIDTH/2); // Pixel Counter (2 pixels at a time mind)
om_blood_fade_row_loop:
mov ecx, ebx ; // Pixel Counter (2 pixels at a time mind)
om_blood_fade_loop:
movq MM1, [esi + ecx * 8] ; // Load 2 pixels
psubusb MM1, MM0 ; // Do the subtract
movq [esi + ecx * 8], MM1 ; // Store the result
dec ecx ; // Reduce counter
jne om_blood_fade_loop; // On to the next 2 pixels
add esi, eax; // On to the next row
dec edx;
jne om_blood_fade_row_loop;
EMMS ; // Clear/Set MMX/FPU flag
}
#endif
// Unlock the working buffer
surface_manager->Unlock_surface(m_myScreenSurfaceID);
}
void OptionsManager::SetDrawColour(uint32 def) {
switch (def) {
case BASE:
m_drawColour.red = g_drawColour.red;
m_drawColour.blue = g_drawColour.blue;
m_drawColour.green = g_drawColour.green;
break;
case BASE_DARK:
m_drawColour.red = g_drawColourDark.red;
m_drawColour.blue = g_drawColourDark.blue;
m_drawColour.green = g_drawColourDark.green;
break;
case SELECTED:
m_drawColour.red = g_drawSelected.red;
m_drawColour.blue = g_drawSelected.blue;
m_drawColour.green = g_drawSelected.green;
break;
case SELECTED_DARK:
m_drawColour.red = g_drawSelectedDark.red;
m_drawColour.blue = g_drawSelectedDark.blue;
m_drawColour.green = g_drawSelectedDark.green;
break;
}
}
void OptionsManager::DrawRectangle(bool8 selected, uint32 x, uint32 y, uint32 width, uint32 height, uint8 *surface_address, uint32 pitch) {
if (selected) {
SetDrawColour(SELECTED);
} else {
SetDrawColour(BASE);
}
Draw_vertical_line(x - 1, y - 1, height + 2, &m_drawColour, surface_address, pitch);
Draw_vertical_line(x + width, y - 1, height + 2, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(x, y - 1, width, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(x, y + height, width, &m_drawColour, surface_address, pitch);
if (selected) {
SetDrawColour(SELECTED_DARK);
} else {
SetDrawColour(BASE_DARK);
}
Draw_vertical_line(x, y, height, &m_drawColour, surface_address, pitch);
Draw_vertical_line(x + width + 1, y - 1, height + 3, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(x, y, width, &m_drawColour, surface_address, pitch);
Draw_horizontal_line(x - 1, y + height + 1, width + 2, &m_drawColour, surface_address, pitch);
}
const char *OptionsManager::GetTextFromReference(uint32 hashRef) {
char *textLine = NULL;
// Get the text via a label
if (m_global_text)
textLine = (char *)m_global_text->Try_fetch_item_by_hash(hashRef);
if (!textLine) {
// Try again with reloaded text file
LoadGlobalTextFile();
textLine = (char *)m_global_text->Try_fetch_item_by_hash(hashRef);
if (!textLine)
return "Missing text!";
}
// All options text is non-spoken so we can assume it begins with the & flag character
textLine++;
// To be a line number, there must be an open brace as the first string character.
if (textLine[0] == TS_LINENO_OPEN) {
2021-03-07 08:56:34 +00:00
int32 nLineLength = strlen((const char *)textLine);
// Okay, we appear to have a legal line number. Find the close brace for it.
2021-03-07 08:56:34 +00:00
int32 nCloseBracePos = 1;
while ((nCloseBracePos < nLineLength) && (textLine[nCloseBracePos] != TS_LINENO_CLOSE))
++nCloseBracePos;
// If we didn't find one then this is an error.
if (nCloseBracePos == nLineLength)
Fatal_error("Failed to find the end of the line number in [%s]", textLine);
// Right we appear to have a present-and-correct line number. To display it we don't have
// to do anything special. If the displaying of line numbers is turned off then we must skip
// past the line number.
2021-03-14 07:59:48 +00:00
if (!g_px->speechLineNumbers) {
// Skip to first non-space after the line number.
const char *pcTextLine = (const char *)(&textLine[nCloseBracePos + 1]);
while ((*pcTextLine != '\0') && (*pcTextLine == ' '))
++pcTextLine;
// If we got to the end of the string then we have a line number with no text following it.
if (*pcTextLine == '\0')
Fatal_error("Found line number [%s] with no text", textLine);
// Write the modified pointer back into the text block
2020-09-20 17:44:03 +00:00
textLine = const_cast<char *>(pcTextLine);
}
}
return ((const char *)textLine);
}
void OptionsManager::LoadBitmapFont() {
sprintf(m_fontName, FONT_PATH, OPTIONS_FONT_NAME);
uint32 hashedname = NULL_HASH;
2021-03-14 12:29:25 +00:00
pxString font_cluster = FONT_CLUSTER_PATH;
m_font_file = (_pxBitmap *)rs_font->Res_open(m_fontName, hashedname, font_cluster, font_cluster_hash);
if (m_font_file->schema != PC_BITMAP_SCHEMA)
Fatal_error("Incorrect versions loading [%s] (engine has %d, data has %d", m_fontName, PC_BITMAP_SCHEMA, m_font_file->schema);
m_fontPalette = (uint32 *)m_font_file->Fetch_palette_pointer();
}
void OptionsManager::LoadGlobalTextFile() {
// Set this up for resman
char globalClusterFile[MAXLEN_CLUSTER_URL];
sprintf(globalClusterFile, GLOBAL_CLUSTER_PATH);
uint32 globalClusterHash = NULL_HASH;
char textFileName[100];
uint32 buf_hash = NULL_HASH;
// Has a language been specified
sprintf(textFileName, GLOBAL_TEXT_FILE);
// Special text loading code so the translators can test their stuff
if (tt) {
// Ok, translators mode has been activated
// Only load the global text if it hasn't been loaded already
if (m_global_text == NULL)
m_global_text = LoadTranslatedFile("global", "global\\global\\");
} else
m_global_text = (_linked_data_file *)rs1->Res_open(textFileName, buf_hash, globalClusterFile, globalClusterHash);
m_global_text = (_linked_data_file *)rs1->Res_open(textFileName, buf_hash, globalClusterFile, globalClusterHash);
}
bool8 OptionsManager::SetCharacterSprite(char c) {
2021-03-07 08:56:34 +00:00
int32 index = (int32)c - 32;
if (index < 0)
index += 256;
// Catch ernoeous characters and make them apostrophes
if ((uint)index >= m_font_file->Fetch_number_of_items())
index = 7;
m_currentSprite = m_font_file->Fetch_item_by_number(index);
if (!m_currentSprite)
return FALSE8;
return TRUE8;
}
uint32 OptionsManager::CalculateStringWidth(const char *str) {
if (!str)
Fatal_error("Cannot calculate width of a NULL or empty string");
2021-03-07 08:56:34 +00:00
uint32 noChars = strlen(str);
int32 sentenceWidth = 0;
// Loop through all characters to get sentence length
2021-03-07 08:56:34 +00:00
for (uint32 i = 0; i < noChars; i++) {
// Select current sprite
SetCharacterSprite(str[i]);
// Keep track of sentence width
sentenceWidth += (m_currentSprite->width + 1);
}
return sentenceWidth;
}
void OptionsManager::DisplayText(uint8 *ad, uint32 pitch, const char *str, int32 x, int32 y, uint32 col, bool8 centredHorizontally, bool8 boxed) {
static char errorText[] = "MISSING TEXT !";
if (!str)
str = errorText;
// How many characters do we draw
2021-03-07 08:56:34 +00:00
uint32 noChars = strlen(str);
int32 sentenceWidth;
int32 initialX;
// Ensure font is loaded as this function can be used outside this module
if (m_font_file == NULL) {
LoadBitmapFont();
}
// Do we need to modify x pos before we start to draw
if (centredHorizontally) {
sentenceWidth = CalculateStringWidth(str);
// Override x value with this new one thus centring horizontally
initialX = (SCREEN_WIDTH / 2) - (sentenceWidth / 2);
x = initialX;
}
2021-03-07 08:56:34 +00:00
for (uint32 i = 0; i < noChars; i++) {
// Select current sprite
SetCharacterSprite(str[i]);
RenderCharacter(ad, pitch, x, y, col);
x += (m_currentSprite->width + 1);
}
if (boxed) {
2020-09-21 04:59:09 +00:00
LRECT r;
GetCentredRectFotText(str, r, y);
DrawRectangle(FALSE8, r.left, r.top, r.right - r.left, r.bottom - r.top, ad, pitch);
}
}
2020-09-21 04:59:09 +00:00
void OptionsManager::GetCentredRectFotText(const char *str, LRECT &r, int32 height) {
int32 sentenceWidth = CalculateStringWidth(str);
r.left = (SCREEN_WIDTH / 2) - (sentenceWidth / 2) - 50;
r.right = r.left + (sentenceWidth + 100);
r.top = height - 3;
r.bottom = height + m_fontHeight + 3;
}
// Macros to get colour values from 32-bit palette pointer
#define GET_R(X) (((X) >> 16) & 0xFF)
#define GET_G(X) (((X) >> 8) & 0xFF)
#define GET_B(X) ((X)&0xFF)
void OptionsManager::RenderCharacter(uint8 *ad, uint32 pitch, int32 nX, int32 nY, uint32 col) {
uint8 *src;
uint8 *write;
uint32 x, y;
uint32 width, height;
src = m_currentSprite->data;
height = m_currentSprite->height;
width = m_currentSprite->width;
// Modify the palette for our anti-aliased coloured font
switch (col) {
case SELECTEDFONT:
m_fontPalette[3] = 0xCA0000;
m_fontPalette[4] = 0x960000;
m_fontPalette[5] = 0x640000;
break;
case PALEFONT:
m_fontPalette[3] = 0x808080;
m_fontPalette[4] = 0x6E6E6E;
m_fontPalette[5] = 0x505050;
break;
// Main Font
default:
m_fontPalette[3] = 0xFEFEFE;
m_fontPalette[4] = 0xC1C1C1;
m_fontPalette[5] = 0x646464;
break;
// Drawing blue 46, 87, 156 0x2E579C
// 29, 56, 102 0x1D3866
// 22, 42, 76 0x162A4C
// Darker blue 22, 42, 76 0x162A4C
// 14, 28, 51 0x0E1C33
// 7, 14, 25 0x070E19
// Gray 128, 128, 128 0x808080
// 110, 110, 110 0x6E6E6E
// 80, 80, 80 0x505050
// White 254, 254, 254 0xFEFEFE
// 193, 193, 193 0xC1C1C1
// 100, 100, 100 0x646464
// Red 202, 0, 0 0xCA0000
// 150, 0, 0 0x960000
// 100, 0, 0 0x640000
}
// Move to first byte to begin drawing
ad += (nY * pitch);
ad += (nX * 4);
for (y = 0; y < height; y++) {
write = ad;
for (x = 0; x < width; x++) {
// Don't draw pixels with palette index 0 (transparency)
if (*src) {
*write++ = (uint8)GET_B(m_fontPalette[*src]); // b
*write++ = (uint8)GET_G(m_fontPalette[*src]); // g
*write++ = (uint8)GET_R(m_fontPalette[*src]); // r
write++; // a
} else {
write += 4;
}
src++;
}
ad += pitch;
}
}
void OptionsManager::UnlockMovies() {
for (uint32 m = 0; m < TOTAL_NUMBER_OF_MOVIES; m++)
g_movieLibrary[m].visible = TRUE8;
}
// On complete game
void DoSomeMagicStuff() {
g_theOptionsManager->UnlockMovies();
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
g_theOptionsManager->DisplayText(ad, pitch, "Movies visible", 0, SCREEN_DEPTH - 30, SELECTEDFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
surface_manager->Flip();
g_system->delayMillis(1000);
}
// Magic input sequences
uint8 magic_unlockmovies[13] = {Common::KEYCODE_t, Common::KEYCODE_e, Common::KEYCODE_s, Common::KEYCODE_t, Common::KEYCODE_b, Common::KEYCODE_r, Common::KEYCODE_e,
Common::KEYCODE_a, Common::KEYCODE_k, Common::KEYCODE_d, Common::KEYCODE_o, Common::KEYCODE_w, Common::KEYCODE_n};
uint8 magic_fastmovies[6] = {Common::KEYCODE_s, Common::KEYCODE_p, Common::KEYCODE_e, Common::KEYCODE_e, Common::KEYCODE_d, Common::KEYCODE_y};
uint8 magic_slideshowextras[12] = {Common::KEYCODE_h, Common::KEYCODE_o, Common::KEYCODE_l, Common::KEYCODE_i, Common::KEYCODE_d, Common::KEYCODE_a,
Common::KEYCODE_y, Common::KEYCODE_s, Common::KEYCODE_n, Common::KEYCODE_a, Common::KEYCODE_p, Common::KEYCODE_s};
uint8 magic_avcontrol[7] = {Common::KEYCODE_r, Common::KEYCODE_a, Common::KEYCODE_b, Common::KEYCODE_v, Common::KEYCODE_i, Common::KEYCODE_e, Common::KEYCODE_w};
uint8 magic_completeme[6] = {Common::KEYCODE_c, Common::KEYCODE_a, Common::KEYCODE_5, Common::KEYCODE_2, Common::KEYCODE_4, Common::KEYCODE_8};
uint8 magic_videocontrol[7] = {Common::KEYCODE_a, Common::KEYCODE_p, Common::KEYCODE_r, Common::KEYCODE_i, Common::KEYCODE_c, Common::KEYCODE_o, Common::KEYCODE_t};
uint32 magico = 0;
void OptionsManager::PollInput() {
static uint32 counter = 0;
if (!m_editing) {
if (!m_slideshowActive) {
// Escape acts as generic back button for the majority of screens
if (Read_DI_once_keys(Common::KEYCODE_ESCAPE))
OnEscapeKey();
}
// Selection (up down input)
2021-02-13 21:44:45 +00:00
if (Read_DI_keys(Common::KEYCODE_DOWN) || Read_DI_keys(down_key)) {
MoveSelected(TRUE8);
2021-02-13 21:44:45 +00:00
} else if (Read_DI_keys(Common::KEYCODE_UP) || Read_DI_keys(up_key)) {
MoveSelected(FALSE8);
} else {
m_moveLimiter = FALSE8;
}
// Choose command
2021-02-13 21:44:45 +00:00
if (Read_DI_keys(Common::KEYCODE_RETURN) || Read_DI_keys(fire_key) || Read_DI_keys(interact_key)) {
DoChoice();
} else {
m_choiceLimiter = FALSE8;
}
// Alter current selection (left right input)
2021-02-13 21:44:45 +00:00
if (Read_DI_keys(Common::KEYCODE_LEFT) || Read_DI_keys(left_key)) {
AlterSelected(FALSE8);
2021-02-13 21:44:45 +00:00
} else if (Read_DI_keys(Common::KEYCODE_RIGHT) || Read_DI_keys(right_key)) {
AlterSelected(TRUE8);
} else {
m_alterLimiter = FALSE8;
}
// Just magic_unlockmovies
if (m_activeMenu == MAIN_TOP) {
if (magico >= 13) {
magico = 0;
DoSomeMagicStuff();
DrawWidescreenBorders();
}
if (Read_DI_keys(magic_unlockmovies[magico])) {
magico++;
counter = 0;
}
} else if (m_activeMenu == MAIN_MOVIES) {
if (magico >= 6) {
magico = 0;
g_theSequenceManager->setRate();
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
g_theOptionsManager->DisplayText(ad, pitch, "Zoom!", 0, SCREEN_DEPTH - 30, SELECTEDFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
surface_manager->Flip();
g_system->delayMillis(1000);
DrawWidescreenBorders();
}
if (Read_DI_keys(magic_fastmovies[magico])) {
magico++;
counter = 0;
}
} else if (m_activeMenu == MAIN_EXTRAS) {
if (magico >= 6) {
magico = 0;
// Complete game cheat
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
g_theOptionsManager->DisplayText(ad, pitch, "Extras unlocked", 0, SCREEN_DEPTH - 30, SELECTEDFONT, TRUE8);
2021-03-14 07:59:48 +00:00
g_px->game_completed = TRUE8;
surface_manager->Unlock_surface(working_buffer_id);
surface_manager->Flip();
g_system->delayMillis(1000);
DrawWidescreenBorders();
}
if (Read_DI_keys(magic_completeme[magico])) {
magico++;
counter = 0;
}
} else if (m_activeMenu == MAIN_PROFILES) {
if (magico >= 7) {
magico = 0;
g_av_userControlled = TRUE8;
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
g_theOptionsManager->DisplayText(ad, pitch, "Rabview enabled", 0, SCREEN_DEPTH - 30, SELECTEDFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
surface_manager->Flip();
g_system->delayMillis(1000);
DrawWidescreenBorders();
}
if (Read_DI_keys(magic_avcontrol[magico])) {
magico++;
counter = 0;
}
} else if (m_activeMenu == MAIN_VIDEO) {
if (magico >= 7) {
magico = 0;
// Toggle extra video options available
if (g_videoOptionsCheat == FALSE8)
g_videoOptionsCheat = TRUE8;
else
g_videoOptionsCheat = FALSE8;
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
g_theOptionsManager->DisplayText(ad, pitch, "OK", 0, SCREEN_DEPTH - 30, SELECTEDFONT, TRUE8);
surface_manager->Unlock_surface(working_buffer_id);
surface_manager->Flip();
g_system->delayMillis(1000);
DrawWidescreenBorders();
}
if (Read_DI_keys(magic_videocontrol[magico])) {
magico++;
counter = 0;
}
if (g_videoOptionsCheat == FALSE8) {
// Illegal selections without cheat
if (m_VIDEO_selected == SHADOWS)
2021-03-06 12:02:40 +00:00
m_VIDEO_selected = LEAVE;
if (m_VIDEO_selected == FRAMELIMITER)
m_VIDEO_selected = LEAVE;
}
}
}
counter++;
// Cycling
if (counter == 20) {
magico = 0;
counter = 0;
}
}
void OptionsManager::DoCredits() {
if (m_creditControl == FALSE8) {
// Make credits file name
char textFileName[128];
char movieFileName[128];
sprintf(textFileName, "%s.crd", gamelanguage);
sprintf(movieFileName, "gmovies\\title.bik");
// Free the sequence manager
UnloadTitleScreenMovie();
m_crediter.Initialise(textFileName, movieFileName, TRUE8, TRUE8, 0);
m_creditControl = TRUE8;
} else {
if (m_crediter.DoScreen() == 0) {
// Reinstate the title screen movie
LoadTitleScreenMovie();
m_creditControl = FALSE8;
2021-03-13 16:34:02 +00:00
g_stub->Pop_stub_mode();
}
}
}
2021-03-07 08:56:34 +00:00
void OptionsManager::InitialiseScrollingText(const char *textFileName, const char *movieFileName, int32 frameStart) {
// Free the sequence manager
UnloadTitleScreenMovie();
// If this is a credits file then we need to attach the bink logo at the end
bool8 appendLogo = FALSE8;
if (strcmp(textFileName, "english.crd") == 0)
appendLogo = TRUE8;
#ifdef PC_DEMO
m_crediter.Initialise(textFileName, movieFileName, TRUE8, appendLogo, frameStart);
#else
m_crediter.Initialise(textFileName, movieFileName, FALSE8, appendLogo, frameStart);
#endif
m_creditControl = TRUE8;
}
void OptionsManager::DoScrollingText() {
if (m_crediter.DoScreen() == 0) {
m_creditControl = FALSE8;
2021-03-13 16:34:02 +00:00
g_stub->Pop_stub_mode();
}
}
bool8 IsAValidSlide(uint32 num, char *slideFile) {
// Make the correct filename for this pic when clustered up
if (num < 10)
sprintf(slideFile, "images\\pc\\slide_0%d.bink", num);
else
sprintf(slideFile, "images\\pc\\slide_%d.bink", num);
uint32 fo, fs;
// Now see if it exists in the cluster
if (!DoesClusterContainFile(pxVString("a\\2dart"), HashString(slideFile), fo, fs))
return FALSE8;
return TRUE8;
}
#define MAX_SLIDES 30
void OptionsManager::InitialiseSlideShow() {
// Set to full screen
m_pageOn_from.left = m_pageOn_from.top = 0;
m_pageOn_from.right = SCREEN_WIDTH;
m_pageOn_from.bottom = SCREEN_DEPTH;
m_pageOff_from.left = m_pageOff_from.top = 0;
m_pageOff_from.right = SCREEN_WIDTH;
m_pageOff_from.bottom = SCREEN_DEPTH;
m_pageOff_dest.left = 0;
m_pageOff_dest.right = 0;
m_pageOff_dest.top = 0;
m_pageOff_dest.bottom = SCREEN_DEPTH;
surface_manager->Clear_surface(m_mySlotSurface1ID);
m_currentSlide = 0;
m_slideshowActive = TRUE8;
m_slideWadger = 0;
m_slideFillColour = 0;
}
#define WADGE_INCREMENTS 30
void OptionsManager::DrawSlideShow() {
char slideFile[128];
// Quit
2021-02-13 21:44:45 +00:00
if (Read_DI_once_keys(Common::KEYCODE_ESCAPE)) {
m_slideshowActive = FALSE8;
DrawWidescreenBorders();
return;
}
// The swap slides routine
if (m_slideWadger != 0) {
// Decrementing slide
if (m_slideWadger < 0) {
// Are we done
if (m_slideWadger == -1) {
// Move to the next valid slide backwards
if (m_currentSlide == 0)
m_currentSlide = MAX_SLIDES;
else
m_currentSlide--;
while (!IsAValidSlide(m_currentSlide, slideFile)) {
m_currentSlide--;
}
} else {
m_pageOn_from.right = SCREEN_WIDTH;
m_pageOn_from.left = (WADGE_INCREMENTS + m_slideWadger) * 20;
// Stretchy blit
surface_manager->Blit_surface_to_surface(m_mySlotSurface1ID, working_buffer_id, &m_pageOn_from, NULL, 0);
}
m_slideWadger++;
} else
// Incrementing slide
if (m_slideWadger > 0) {
// Are we done
if (m_slideWadger == 1) {
// Do we have any more slides
if (IsAValidSlide(m_currentSlide + 1, slideFile))
m_currentSlide++;
else
m_currentSlide = 0;
// Safety check
if (m_currentSlide > MAX_SLIDES)
Fatal_error("Slideshow all confused - hit AndyB");
} else {
m_pageOn_from.left = 0;
m_pageOn_from.right = SCREEN_WIDTH - ((WADGE_INCREMENTS - m_slideWadger) * 20);
// Stretchy blit
surface_manager->Blit_surface_to_surface(m_mySlotSurface1ID, working_buffer_id, &m_pageOn_from, NULL, 0);
}
m_slideWadger--;
}
// Could do an effect on the working buffer here
} else {
// Alter current visible slide (on left right input)
2021-02-13 21:44:45 +00:00
if (Read_DI_keys(Common::KEYCODE_LEFT) || Read_DI_keys(left_key)) {
if (!m_slideLimiter) {
m_slideLimiter = TRUE8;
// Caught by the swap slide routine above
m_slideWadger = -WADGE_INCREMENTS;
}
2021-02-13 21:44:45 +00:00
} else if (Read_DI_keys(Common::KEYCODE_RIGHT) || Read_DI_keys(right_key)) {
if (!m_slideLimiter) {
m_slideLimiter = TRUE8;
// Caught by the swap slide routine above
m_slideWadger = WADGE_INCREMENTS;
}
} else {
m_slideLimiter = FALSE8;
}
// Clean the screen first off
surface_manager->Fill_surface(m_mySlotSurface1ID, m_slideFillColour);
uint32 slideFileHash = NULL_HASH;
char art2DCluster[MAXLEN_CLUSTER_URL];
uint32 art2DClusterHash = NULL_HASH;
if (!IsAValidSlide(m_currentSlide, slideFile))
Fatal_error("Trying to display a non-existant slide image!");
// Set this up for resman and open the thb file
sprintf(art2DCluster, ICON_CLUSTER_PATH);
uint8 *slideptr = rs1->Res_open(slideFile, slideFileHash, art2DCluster, art2DClusterHash);
uint32 slideLen = rs_bg->Fetch_size(slideFile, slideFileHash, art2DCluster, art2DClusterHash);
// This slide is bink compressed
Video::BinkDecoder *binkDecoder = new Video::BinkDecoder();
binkDecoder->setDefaultHighColorFormat(Graphics::PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 24));
Common::MemoryReadStream *stream = new Common::MemoryReadStream((byte *)slideptr, slideLen);
if (!stream) {
Fatal_error("Failed open bink file");
}
if (!binkDecoder->loadStream(stream)) {
Fatal_error("Failed open bink file");
}
// Verify image dimensions
if (binkDecoder->getWidth() > SCREEN_WIDTH || binkDecoder->getHeight() > SCREEN_DEPTH)
Fatal_error("Slide image is too large to fit screen!");
// Let bink do it stuff
const Graphics::Surface *surfaceBink = binkDecoder->decodeNextFrame();
if (!surfaceBink)
Fatal_error("Filaed get slide image!");
// Lock the buffers now so bink has somewhere ot put it's data
uint8 *surface = (uint8 *)surface_manager->Lock_surface(m_mySlotSurface1ID);
int16 pitch = surface_manager->Get_pitch(m_mySlotSurface1ID);
uint32 height = surface_manager->Get_height(m_mySlotSurface1ID);
// Screen coordinates
uint32 m_x = 0;
uint32 m_y = 0;
// Centre of the screen please
if (binkDecoder->getWidth() != SCREEN_WIDTH) {
m_x = (SCREEN_WIDTH / 2) - (binkDecoder->getWidth() / 2);
}
if (binkDecoder->getHeight() != SCREEN_DEPTH) {
m_y = (SCREEN_DEPTH / 2) - (binkDecoder->getHeight() / 2);
}
2021-03-07 08:56:34 +00:00
for (int32 i = 0; i < surfaceBink->h; i++) {
if (i + m_y >= height) {
break;
}
memcpy(surface + (m_x * 4) + (i + m_y) * pitch, surfaceBink->getBasePtr(0, i), MIN(surfaceBink->pitch, pitch));
}
// Get the first pixel colour
m_slideFillColour = *((int32 *)surface + m_x + (m_y * pitch));
surface_manager->Unlock_surface(m_mySlotSurface1ID);
binkDecoder->close();
delete binkDecoder;
// Update the screen
surface_manager->Blit_surface_to_surface(m_mySlotSurface1ID, working_buffer_id, NULL, NULL, 0);
// Now ensure the slide surroundings are the correct colour at this cycle
// If the slide has width less than SCREEN_WIDTH
if (m_x != 0) {
Fill_rect(0, 0, m_x, SCREEN_DEPTH, m_slideFillColour);
Fill_rect(SCREEN_WIDTH - m_x, 0, SCREEN_WIDTH, SCREEN_DEPTH, m_slideFillColour);
}
// If the slide has height less than SCREEN_DEPTH
if (m_y != 0) {
Fill_rect(m_x, 0, SCREEN_WIDTH - m_x, m_y, m_slideFillColour);
Fill_rect(m_x, SCREEN_DEPTH - m_y, SCREEN_WIDTH - m_x, SCREEN_DEPTH, m_slideFillColour);
}
}
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
// Print a helpful message
const char *msg = GetTextFromReference(HashString("opt_slideshowmessage"));
DisplayText(ad, pitch, msg, 10, SCREEN_DEPTH - 10 - m_fontHeight, PALEFONT, FALSE8);
surface_manager->Unlock_surface(working_buffer_id);
}
// Generic symbols in the text file
#define TITLE_LINE_SYMBOL '*'
#define CENTRE_LINE_SYMBOL '!'
// Version specific symbols in the text file
#define IGNORE_LINE_SYMBOL '+'
#define OKAY_LINE_SYMBOL '-'
// Defines
#define CREDIT_LINE_SPACING 20
#define CREDITS_PER_SCREEN ((480 / CREDIT_LINE_SPACING) + 1)
// Line types
#define IGNORE_LINE 0x00
#define CENTRED_LINE 0x01
#define TITLE_LINE 0x02
#define PERSON_LINE 0x03
#define PC_LINE 0x04
#define EMPTY_LINE 0x05
void LoadLogo(uint32 to_surface_id) {
char thbFile[128];
uint32 thbFileHash = NULL_HASH;
char art2DCluster[MAXLEN_CLUSTER_URL];
uint32 art2DClusterHash = NULL_HASH;
// Make the correct filename for this pic when clustered up
sprintf(thbFile, "images\\pc\\binklogo.thb");
// Set this up for resman and open the thb file
sprintf(art2DCluster, ICON_CLUSTER_PATH);
uint8 *data = (uint8 *)rs1->Res_open(thbFile, thbFileHash, art2DCluster, art2DClusterHash);
// First off, check the thumb surface is valid
if (!to_surface_id)
Fatal_error("LoadLogo() cannot read to a null surface");
uint8 *surface_address = surface_manager->Lock_surface(to_surface_id);
uint32 pitch = surface_manager->Get_pitch(to_surface_id);
// Now we need to read the 60 by 60 image data into the surface
for (uint32 i = 0; i < 60; i++) {
for (uint32 j = 0; j < 60; j++) {
*surface_address++ = *data++;
*surface_address++ = *data++;
*surface_address++ = *data++;
*surface_address++ = *data++;
}
surface_address += (pitch - 240);
}
// Release it now
surface_manager->Unlock_surface(to_surface_id);
}
uint32 ExamineCharacter(char c) {
if (c == TITLE_LINE_SYMBOL)
return TITLE_LINE;
if (c == CENTRE_LINE_SYMBOL)
return CENTRED_LINE;
if (c == IGNORE_LINE_SYMBOL)
return IGNORE_LINE;
if (c == OKAY_LINE_SYMBOL)
return PC_LINE;
if (c == 0)
return EMPTY_LINE;
// Must be a regular name line
return PERSON_LINE;
}
uint32 GetFileSz(const char *path) {
Common::File file;
if (!file.open(path)) {
return 0;
}
return (uint32)file.size();
}
Crediter::Crediter()
: m_creditsFile(NULL), m_numberOfBytes(0), m_endOfCredits(0), m_currentHeight(0), m_cursor(0), m_scrollOffset(0), m_logoSurfaceID(0), m_logoDraw(0), m_logoAttached(0),
m_movieSurfaceID(0), m_movieBackdrop(FALSE8), m_loopingMovie(FALSE8), m_frameStart(0), m_totalMovieFrames(0) {
memset(m_theData, 0, MAX_BYTESIZE_OF_CREDITS_FILE);
m_movieRect.left = m_movieRect.right = m_movieRect.bottom = m_movieRect.top = 0;
}
2021-03-07 08:56:34 +00:00
void Crediter::Initialise(const char *textFileName, const char *movieFileName, bool8 loopingMovie, bool8 attachLogo, int32 frameStart) {
// Zero out our memory
memset(m_theData, 0, MAX_BYTESIZE_OF_CREDITS_FILE);
// Set this up for resman and open the file
char globalClusterFile[MAXLEN_CLUSTER_URL];
sprintf(globalClusterFile, GLOBAL_CLUSTER_PATH);
uint32 globalClusterHash = NULL_HASH;
uint32 buf_hash = NULL_HASH;
m_loopingMovie = loopingMovie;
m_frameStart = frameStart;
uint8 *data;
data = (uint8 *)rs1->Res_open(textFileName, buf_hash, globalClusterFile, globalClusterHash, 0, &m_numberOfBytes);
// Check the size is ok
if (m_numberOfBytes > MAX_BYTESIZE_OF_CREDITS_FILE)
Fatal_error(pxVString("Credits file exceeds budget! (%d > %d)", m_numberOfBytes, MAX_BYTESIZE_OF_CREDITS_FILE));
// Read the file into our private memory
memcpy(m_theData, data, m_numberOfBytes);
// Setup pointer to the file data
m_creditsFile = (char *)m_theData;
// Process the file first
2021-03-07 08:56:34 +00:00
int32 i = 0;
while (m_creditsFile[i]) {
// New line encountered (NB: two bytes, carriage return and line feed)
if (m_creditsFile[i] == 0x0d) {
// Overwrite with terminators
m_creditsFile[i] = 0;
m_creditsFile[i + 1] = 0;
i += 2;
} else
i++;
}
m_endOfCredits = -500;
if (m_frameStart == 0)
m_scrollOffset = 0;
else
m_scrollOffset = -1 * (SCREEN_DEPTH / 2) + 65;
m_logoAttached = attachLogo;
if (m_logoAttached) {
// Load the bink logo
m_logoSurfaceID = surface_manager->Create_new_surface("Bink logo", 60, 60, SYSTEM);
LoadLogo(m_logoSurfaceID);
m_logoDraw = -1;
}
// Now sort out the movie to play as the backdrop
if (movieFileName == NULL) {
m_movieBackdrop = FALSE8;
} else {
// Safety check
if (g_personalSequenceManager->busy() == TRUE8)
Fatal_error("Crediter() class: Can't use this sequence manager (g_personalSequenceManager) as it's busy");
m_movieBackdrop = TRUE8;
// Initialise background movie (looping)
if (!g_personalSequenceManager->registerMovie(movieFileName, 0, m_loopingMovie)) {
Fatal_error(pxVString("Couldn't register the movie: %s", movieFileName));
}
// Calculate movie blitting rectangle
uint32 movieWidth = g_personalSequenceManager->getMovieWidth();
uint32 movieHeight = g_personalSequenceManager->getMovieHeight();
m_totalMovieFrames = g_personalSequenceManager->getMovieFrames();
if (m_frameStart >= m_totalMovieFrames)
Fatal_error("Crediter() class: Can't start scrolling text at frame %d when movie only has %d frames", m_frameStart, m_totalMovieFrames);
m_movieRect.left = 0;
m_movieRect.top = 0;
if (movieWidth != SCREEN_WIDTH) {
m_movieRect.left = (SCREEN_WIDTH / 2) - (movieWidth / 2);
}
if (movieHeight != SCREEN_DEPTH) {
m_movieRect.top = (SCREEN_DEPTH / 2) - (movieHeight / 2);
}
m_movieRect.right = m_movieRect.left + movieWidth;
m_movieRect.bottom = m_movieRect.top + movieHeight;
// Get a new surface for the movie
m_movieSurfaceID = surface_manager->Create_new_surface("Crediter Movie", SCREEN_WIDTH, SCREEN_DEPTH, SYSTEM);
}
}
int32 Crediter::DoScreen() {
uint32 halfScreenW = SCREEN_WIDTH / 2;
uint32 halfScreenH = SCREEN_DEPTH / 2;
2020-09-21 04:59:09 +00:00
LRECT logo_rect;
bool8 doText = FALSE8;
bool8 onlastMovieFrame = FALSE8;
// Are we done
2021-02-13 21:44:45 +00:00
if (m_endOfCredits == 0 || Read_DI_keys(Common::KEYCODE_ESCAPE)) {
if (m_logoAttached)
surface_manager->Kill_surface(m_logoSurfaceID);
if (m_movieBackdrop)
surface_manager->Kill_surface(m_movieSurfaceID);
g_personalSequenceManager->kill();
return 0;
}
// Need to hold on last frame if not looping the movie and it ends
if (m_loopingMovie == FALSE8 && m_totalMovieFrames == g_personalSequenceManager->getFrameNumber()) {
onlastMovieFrame = TRUE8;
}
if (m_movieBackdrop == FALSE8) {
// Clear screen
surface_manager->Fill_surface(working_buffer_id, 0x001010);
} else {
// Control what frame we start to display the scrolling text
if (m_frameStart <= g_personalSequenceManager->getFrameNumber())
doText = TRUE8;
// Draw a frame of the movie
if (onlastMovieFrame == FALSE8)
g_personalSequenceManager->drawFrame(m_movieSurfaceID);
// Now blit the movie frame without transparency to the working buffer
surface_manager->Blit_surface_to_surface(m_movieSurfaceID, working_buffer_id, &m_movieRect, &m_movieRect, 0);
}
// Only draw the scrolling text after a certain frame number
if (doText == FALSE8)
return 1;
// Reset
m_currentHeight = halfScreenH;
m_cursor = 0;
// Scroll up please
m_currentHeight -= m_scrollOffset;
linesDone:
// Do that funky chicken
if (m_currentHeight < 0) {
uint32 r = ExamineCharacter(m_creditsFile[m_cursor]);
if (r == TITLE_LINE) {
// Need to skip an extra line if we find a title character
m_cursor += strlen(&m_creditsFile[m_cursor]) + 2;
// Check next line as well
uint32 r2 = ExamineCharacter(m_creditsFile[m_cursor]);
// Special case if next line is to be ignored
if (r2 == IGNORE_LINE)
goto linesDone;
} else if (r == IGNORE_LINE) {
// Don't go to new line if we're to ignore this line
m_cursor += strlen(&m_creditsFile[m_cursor]) + 2;
goto linesDone;
} else if (r == PC_LINE) {
m_cursor++;
goto linesDone;
}
m_cursor += strlen(&m_creditsFile[m_cursor]) + 2;
m_currentHeight += CREDIT_LINE_SPACING;
goto linesDone;
}
uint8 *ad = surface_manager->Lock_surface(working_buffer_id);
uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
// Draw the credit lines all nicely formatted
for (uint32 count = 0; TRUE8; count++) {
// Is this the end of the file
if (m_cursor >= (uint32)m_numberOfBytes) {
if (m_logoAttached) {
// Trigger the bink logo drawing
if (m_logoDraw == -1)
m_logoDraw = 470;
}
m_endOfCredits++;
break;
}
// Now look at a line
uint32 ret = ExamineCharacter(m_creditsFile[m_cursor]);
// An empty line
if (ret == EMPTY_LINE) {
// Skip the terminator
m_cursor += 2;
// Increment line height
m_currentHeight += CREDIT_LINE_SPACING;
// Dont draw this line
} else if (ret == IGNORE_LINE) {
// Skip the special character
m_cursor++;
// Skip this line
m_cursor += strlen(&m_creditsFile[m_cursor]) + 2;
continue;
} else if (ret == CENTRED_LINE) {
// Skip the special character
m_cursor++;
// Draw line centred
g_theOptionsManager->DisplayText(ad, pitch, &m_creditsFile[m_cursor], 0, m_currentHeight, NORMALFONT, TRUE8);
// Move pointer to next line
m_cursor += strlen(&m_creditsFile[m_cursor]) + 2;
// Increment line height
m_currentHeight += CREDIT_LINE_SPACING;
} else if (ret == TITLE_LINE) {
// Skip the special character
m_cursor++;
// Draw line on the left
g_theOptionsManager->DisplayText(ad, pitch, &m_creditsFile[m_cursor],
halfScreenW - g_theOptionsManager->CalculateStringWidth(&m_creditsFile[m_cursor]) - 10, m_currentHeight, PALEFONT, FALSE8);
// Move pointer to next line
m_cursor += strlen(&m_creditsFile[m_cursor]) + 2;
// Don't increment height for title lines unless it's a double title line
if (ExamineCharacter(m_creditsFile[m_cursor]) == TITLE_LINE)
m_currentHeight += CREDIT_LINE_SPACING;
} else if (ret == PERSON_LINE) {
// Draw line on the right
g_theOptionsManager->DisplayText(ad, pitch, &m_creditsFile[m_cursor], halfScreenW + 10, m_currentHeight, NORMALFONT, FALSE8);
// Move pointer to next line
m_cursor += strlen(&m_creditsFile[m_cursor]) + 2;
// Increment line height
m_currentHeight += CREDIT_LINE_SPACING;
} else {
// Line must be a forced pc line so take move over this character and do it again
m_cursor++;
continue;
}
// This is what will usually halt this loop (ie running out of space on the screen)
if (m_currentHeight > SCREEN_DEPTH - CREDIT_LINE_SPACING)
break;
}
surface_manager->Unlock_surface(working_buffer_id);
// Scrolling speed
if (m_endOfCredits < 0) {
// Increment scrolling offset
m_scrollOffset++;
if (m_logoDraw != -1)
m_logoDraw--;
}
// The bink logo at the end of the file
if (m_logoDraw != -1 && m_logoAttached == TRUE8) {
logo_rect.top = m_logoDraw;
2021-03-07 08:56:34 +00:00
int32 remainder = SCREEN_DEPTH - m_logoDraw;
if (remainder < 60) {
logo_rect.bottom = SCREEN_DEPTH - 1;
} else {
logo_rect.bottom = logo_rect.top + 60;
}
logo_rect.left = halfScreenW - 30;
logo_rect.right = logo_rect.left + 60;
surface_manager->Blit_surface_to_surface(m_logoSurfaceID, working_buffer_id, NULL, &logo_rect, 0);
}
// Draw border rects
Fill_rect(0, 0, SCREEN_WIDTH, 67, 0);
Fill_rect(0, 413, SCREEN_WIDTH, SCREEN_DEPTH, 0);
return 1;
}
} // End of namespace ICB