scummvm/engines/ags/engine/ac/global_game.cpp
Thierry Crozat 61e3a92963 AGS: fixed few more warnings related to typecasts
From upstream 7946be9e75a0edda6fc9d955ea771f60f9b8e968
2022-10-09 18:49:17 +01:00

842 lines
25 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/savefile.h"
#include "ags/lib/std/math.h"
#include "ags/shared/core/platform.h"
#include "ags/shared/ac/audio_clip_type.h"
#include "ags/engine/ac/global_game.h"
#include "ags/shared/ac/common.h"
#include "ags/shared/ac/view.h"
#include "ags/engine/ac/character.h"
#include "ags/engine/ac/draw.h"
#include "ags/engine/ac/dynamic_sprite.h"
#include "ags/engine/ac/event.h"
#include "ags/engine/ac/game.h"
#include "ags/engine/ac/game_setup.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_character.h"
#include "ags/engine/ac/global_gui.h"
#include "ags/engine/ac/global_hotspot.h"
#include "ags/engine/ac/global_inventory_item.h"
#include "ags/engine/ac/global_translation.h"
#include "ags/engine/ac/gui.h"
#include "ags/engine/ac/hotspot.h"
#include "ags/shared/ac/keycode.h"
#include "ags/engine/ac/mouse.h"
#include "ags/engine/ac/object.h"
#include "ags/engine/ac/path_helper.h"
#include "ags/engine/ac/sys_events.h"
#include "ags/engine/ac/room.h"
#include "ags/engine/ac/room_status.h"
#include "ags/engine/ac/string.h"
#include "ags/engine/ac/system.h"
#include "ags/engine/debugging/debugger.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/shared/font/fonts.h"
#include "ags/engine/gui/gui_dialog.h"
#include "ags/engine/main/engine.h"
#include "ags/engine/main/game_run.h"
#include "ags/engine/main/graphics_mode.h"
#include "ags/engine/main/game_start.h"
#include "ags/engine/script/script.h"
#include "ags/engine/script/script_runtime.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/engine/gfx/graphics_driver.h"
#include "ags/shared/core/asset_manager.h"
#include "ags/engine/main/config.h"
#include "ags/engine/main/game_file.h"
#include "ags/shared/util/path.h"
#include "ags/shared/util/string_utils.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/engine/platform/base/sys_main.h"
#include "ags/ags.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
void AbortGame() {
// make sure scripts stop at the next step
cancel_all_scripts();
}
void GiveScore(int amnt) {
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_AllScore);
_GP(play).score += amnt;
if ((amnt > 0) && (_GP(play).score_sound >= 0))
play_audio_clip_by_index(_GP(play).score_sound);
run_on_event(GE_GOT_SCORE, RuntimeScriptValue().SetInt32(amnt));
}
void restart_game() {
can_run_delayed_command();
if (_G(inside_script)) {
_G(curscript)->queue_action(ePSARestartGame, 0, "RestartGame");
return;
}
try_restore_save(RESTART_POINT_SAVE_GAME_NUMBER);
}
void RestoreGameSlot(int slnum) {
if (_G(displayed_room) < 0)
quit("!RestoreGameSlot: a game cannot be restored from within game_start");
can_run_delayed_command();
if (_G(inside_script)) {
_G(curscript)->queue_action(ePSARestoreGame, slnum, "RestoreGameSlot");
return;
}
try_restore_save(slnum);
}
void DeleteSaveSlot(int slnum) {
String nametouse;
nametouse = get_save_game_path(slnum);
Shared::File::DeleteFile(nametouse);
}
void PauseGame() {
_G(game_paused)++;
debug_script_log("Game paused");
}
void UnPauseGame() {
if (_G(game_paused) > 0)
_G(game_paused)--;
debug_script_log("Game UnPaused, pause level now %d", _G(game_paused));
}
int IsGamePaused() {
if (_G(game_paused) > 0) return 1;
return 0;
}
bool GetSaveSlotDescription(int slnum, String &description) {
if (read_savedgame_description(get_save_game_path(slnum), description))
return true;
description.Format("INVALID SLOT %d", slnum);
return false;
}
int GetSaveSlotDescription(int slnum, char *desbuf) {
VALIDATE_STRING(desbuf);
String description;
bool res = GetSaveSlotDescription(slnum, description);
snprintf(desbuf, MAX_MAXSTRLEN, "%s", description.GetCStr());
return res ? 1 : 0;
}
int LoadSaveSlotScreenshot(int slnum, int width, int height) {
int gotSlot;
data_to_game_coords(&width, &height);
if (!read_savedgame_screenshot(get_save_game_path(slnum), gotSlot))
return 0;
if (gotSlot == 0)
return 0;
if ((_GP(game).SpriteInfos[gotSlot].Width == width) && (_GP(game).SpriteInfos[gotSlot].Height == height))
return gotSlot;
// resize the sprite to the requested size
Bitmap *newPic = BitmapHelper::CreateBitmap(width, height, _GP(spriteset)[gotSlot]->GetColorDepth());
newPic->StretchBlt(_GP(spriteset)[gotSlot],
RectWH(0, 0, _GP(game).SpriteInfos[gotSlot].Width, _GP(game).SpriteInfos[gotSlot].Height),
RectWH(0, 0, width, height));
update_polled_stuff_if_runtime();
// replace the bitmap in the sprite set
free_dynamic_sprite(gotSlot);
add_dynamic_sprite(gotSlot, newPic);
return gotSlot;
}
void FillSaveList(std::vector<SaveListItem> &saves, size_t max_count) {
if (max_count == 0)
return; // duh
String svg_dir = get_save_game_directory();
String svg_suff = get_save_game_suffix();
String searchPath = Path::ConcatPaths(svg_dir, String::FromFormat("agssave.???%s", svg_suff.GetCStr()));
time_t time = 0;
SaveStateList saveList = ::AGS::g_vm->listSaves();
for (uint idx = 0; idx < saveList.size(); ++idx) {
int saveGameSlot = saveList[idx].getSaveSlot();
// only list games .000 to .xxx (to allow higher slots for other perposes)
if (saveGameSlot < 0 || saveGameSlot > TOP_LISTEDSAVESLOT)
continue;
String description;
GetSaveSlotDescription(saveGameSlot, description);
saves.push_back(SaveListItem(saveGameSlot, description, time));
if (saves.size() >= max_count)
break;
}
}
void SetGlobalInt(int index, int valu) {
if ((index < 0) | (index >= MAXGSVALUES))
quitprintf("!SetGlobalInt: invalid index %d, supported range is %d - %d", index, 0, MAXGSVALUES - 1);
if (_GP(play).globalscriptvars[index] != valu) {
debug_script_log("GlobalInt %d set to %d", index, valu);
}
_GP(play).globalscriptvars[index] = valu;
}
int GetGlobalInt(int index) {
if ((index < 0) | (index >= MAXGSVALUES))
quitprintf("!GetGlobalInt: invalid index %d, supported range is %d - %d", index, 0, MAXGSVALUES - 1);
return _GP(play).globalscriptvars[index];
}
void SetGlobalString(int index, const char *newval) {
if ((index < 0) | (index >= MAXGLOBALSTRINGS))
quitprintf("!SetGlobalString: invalid index %d, supported range is %d - %d", index, 0, MAXGLOBALSTRINGS - 1);
debug_script_log("GlobalString %d set to '%s'", index, newval);
snprintf(_GP(play).globalstrings[index], MAX_MAXSTRLEN, "%s", newval);
}
void GetGlobalString(int index, char *strval) {
if ((index < 0) | (index >= MAXGLOBALSTRINGS))
quitprintf("!GetGlobalString: invalid index %d, supported range is %d - %d", index, 0, MAXGLOBALSTRINGS - 1);
snprintf(strval, MAX_MAXSTRLEN, "%s", _GP(play).globalstrings[index]);
}
// TODO: refactor this method, and use same shared procedure at both normal stop/startup and in RunAGSGame
int RunAGSGame(const String &newgame, unsigned int mode, int data) {
can_run_delayed_command();
int AllowedModes = RAGMODE_PRESERVEGLOBALINT | RAGMODE_LOADNOW;
if ((mode & (~AllowedModes)) != 0)
quit("!RunAGSGame: mode value unknown");
if (_G(editor_debugging_enabled)) {
quit("!RunAGSGame cannot be used while running the game from within the AGS Editor. You must build the game EXE and run it from there to use this function.");
}
if ((mode & RAGMODE_LOADNOW) == 0) {
_GP(ResPaths).GamePak.Path = PathFromInstallDir(newgame);
_GP(ResPaths).GamePak.Name = newgame;
_GP(play).takeover_data = data;
_G(load_new_game_restore) = -1;
if (_G(inside_script)) {
_G(curscript)->queue_action(ePSARunAGSGame, mode | RAGMODE_LOADNOW, "RunAGSGame");
ccInstance::GetCurrentInstance()->Abort();
} else
_G(load_new_game) = mode | RAGMODE_LOADNOW;
return 0;
}
int ee;
unload_old_room();
_G(displayed_room) = -10;
#if defined (AGS_AUTO_WRITE_USER_CONFIG)
save_config_file(); // save current user config in case engine fails to run new game
#endif // AGS_AUTO_WRITE_USER_CONFIG
unload_game_file();
// Adjust config (NOTE: normally, RunAGSGame would need a redesign to allow separate config etc per each game)
_GP(usetup).translation = ""; // reset to default, prevent from trying translation file of game A in game B
_GP(AssetMgr)->RemoveAllLibraries();
// TODO: refactor and share same code with the startup!
if (_GP(AssetMgr)->AddLibrary(_GP(ResPaths).GamePak.Path) != Shared::kAssetNoError)
quitprintf("!RunAGSGame: unable to load new game file '%s'", _GP(ResPaths).GamePak.Path.GetCStr());
engine_assign_assetpaths();
show_preload();
HError err = load_game_file();
if (!err)
quitprintf("!RunAGSGame: error loading new game file:\n%s", err->FullMessage().GetCStr());
_GP(spriteset).Reset();
err = _GP(spriteset).InitFile(SpriteFile::DefaultSpriteFileName, SpriteFile::DefaultSpriteIndexName);
if (!err)
quitprintf("!RunAGSGame: error loading new sprites:\n%s", err->FullMessage().GetCStr());
if ((mode & RAGMODE_PRESERVEGLOBALINT) == 0) {
// reset GlobalInts
for (ee = 0; ee < MAXGSVALUES; ee++)
_GP(play).globalscriptvars[ee] = 0;
}
engine_init_game_settings();
_GP(play).screen_is_faded_out = 1;
if (_G(load_new_game_restore) >= 0) {
try_restore_save(_G(load_new_game_restore));
_G(load_new_game_restore) = -1;
} else
start_game();
return 0;
}
int GetGameParameter(int parm, int data1, int data2, int data3) {
switch (parm) {
case GP_SPRITEWIDTH:
return Game_GetSpriteWidth(data1);
case GP_SPRITEHEIGHT:
return Game_GetSpriteHeight(data1);
case GP_NUMLOOPS:
return Game_GetLoopCountForView(data1);
case GP_NUMFRAMES:
return Game_GetFrameCountForLoop(data1, data2);
case GP_FRAMESPEED:
case GP_FRAMEIMAGE:
case GP_FRAMESOUND:
case GP_ISFRAMEFLIPPED: {
if ((data1 < 1) || (data1 > _GP(game).numviews)) {
quitprintf("!GetGameParameter: invalid view specified (v: %d, l: %d, f: %d)", data1, data2, data3);
}
if ((data2 < 0) || (data2 >= _GP(views)[data1 - 1].numLoops)) {
quitprintf("!GetGameParameter: invalid loop specified (v: %d, l: %d, f: %d)", data1, data2, data3);
}
if ((data3 < 0) || (data3 >= _GP(views)[data1 - 1].loops[data2].numFrames)) {
quitprintf("!GetGameParameter: invalid frame specified (v: %d, l: %d, f: %d)", data1, data2, data3);
}
ViewFrame *pvf = &_GP(views)[data1 - 1].loops[data2].frames[data3];
if (parm == GP_FRAMESPEED)
return pvf->speed;
else if (parm == GP_FRAMEIMAGE)
return pvf->pic;
else if (parm == GP_FRAMESOUND)
return get_old_style_number_for_sound(pvf->sound);
else if (parm == GP_ISFRAMEFLIPPED)
return (pvf->flags & VFLG_FLIPSPRITE) ? 1 : 0;
else
quit("GetGameParameter internal error");
break;
}
case GP_ISRUNNEXTLOOP:
return Game_GetRunNextSettingForLoop(data1, data2);
case GP_NUMGUIS:
return _GP(game).numgui;
case GP_NUMOBJECTS:
return _G(croom)->numobj;
case GP_NUMCHARACTERS:
return _GP(game).numcharacters;
case GP_NUMINVITEMS:
return _GP(game).numinvitems;
default:
quit("!GetGameParameter: unknown parameter specified");
}
return 0;
}
void QuitGame(int dialog) {
if (dialog) {
int rcode;
setup_for_dialog();
rcode = quitdialog();
restore_after_dialog();
if (rcode == 0) return;
}
quit("|You have exited.");
}
void SetRestartPoint() {
save_game(RESTART_POINT_SAVE_GAME_NUMBER, "Restart Game Auto-Save");
}
void SetGameSpeed(int newspd) {
newspd += _GP(play).game_speed_modifier;
if (newspd > 1000) newspd = 1000;
if (newspd < 10) newspd = 10;
set_game_speed(newspd);
debug_script_log("Game speed set to %d", newspd);
}
int GetGameSpeed() {
return ::lround(get_current_fps()) - _GP(play).game_speed_modifier;
}
int SetGameOption(int opt, int setting) {
if (((opt < 1) || (opt > OPT_HIGHESTOPTION)) && (opt != OPT_LIPSYNCTEXT))
quit("!SetGameOption: invalid option specified");
if (opt == OPT_ANTIGLIDE) {
for (int i = 0; i < _GP(game).numcharacters; i++) {
if (setting)
_GP(game).chars[i].flags |= CHF_ANTIGLIDE;
else
_GP(game).chars[i].flags &= ~CHF_ANTIGLIDE;
}
}
if ((opt == OPT_CROSSFADEMUSIC) && (_GP(game).audioClipTypes.size() > AUDIOTYPE_LEGACY_MUSIC)) {
// legacy compatibility -- changing crossfade speed here also
// updates the new audio clip type style
_GP(game).audioClipTypes[AUDIOTYPE_LEGACY_MUSIC].crossfadeSpeed = setting;
}
int oldval = _GP(game).options[opt];
_GP(game).options[opt] = setting;
if (opt == OPT_DUPLICATEINV)
update_invorder();
else if (opt == OPT_DISABLEOFF) {
GUI::Options.DisabledStyle = static_cast<GuiDisableStyle>(_GP(game).options[OPT_DISABLEOFF]);
// If GUI was disabled at this time then also update it, as visual style could've changed
if (_GP(play).disabled_user_interface > 0) {
GUI::MarkAllGUIForUpdate();
}
} else if (opt == OPT_PORTRAITSIDE) {
if (setting == 0) // set back to Left
_GP(play).swap_portrait_side = 0;
} else if (opt == OPT_ANTIALIASFONTS) {
adjust_fonts_for_render_mode(setting != 0);
}
return oldval;
}
int GetGameOption(int opt) {
if (((opt < 1) || (opt > OPT_HIGHESTOPTION)) && (opt != OPT_LIPSYNCTEXT))
quit("!GetGameOption: invalid option specified");
return _GP(game).options[opt];
}
void SkipUntilCharacterStops(int cc) {
if (!is_valid_character(cc))
quit("!SkipUntilCharacterStops: invalid character specified");
if (_GP(game).chars[cc].room != _G(displayed_room))
quit("!SkipUntilCharacterStops: specified character not in current room");
// if they are not currently moving, do nothing
if (!_GP(game).chars[cc].walking)
return;
if (is_in_cutscene())
quit("!SkipUntilCharacterStops: cannot be used within a cutscene");
initialize_skippable_cutscene();
_GP(play).fast_forward = 2;
_GP(play).skip_until_char_stops = cc;
}
void EndSkippingUntilCharStops() {
// not currently skipping, so ignore
if (_GP(play).skip_until_char_stops < 0)
return;
stop_fast_forwarding();
_GP(play).skip_until_char_stops = -1;
}
void StartCutscene(int skipwith) {
static ScriptPosition last_cutscene_script_pos;
if (is_in_cutscene()) {
quitprintf("!StartCutscene: already in a cutscene; previous started in \"%s\", line %d",
last_cutscene_script_pos.Section.GetCStr(), last_cutscene_script_pos.Line);
}
if ((skipwith < 1) || (skipwith > 6))
quit("!StartCutscene: invalid argument, must be 1 to 5.");
get_script_position(last_cutscene_script_pos);
// make sure they can't be skipping and cutsceneing at the same time
EndSkippingUntilCharStops();
_GP(play).in_cutscene = skipwith;
initialize_skippable_cutscene();
}
void SkipCutscene() {
if (is_in_cutscene())
start_skipping_cutscene();
}
int EndCutscene() {
if (!is_in_cutscene())
quit("!EndCutscene: not in a cutscene");
int retval = _GP(play).fast_forward;
_GP(play).in_cutscene = 0;
// Stop it fast-forwarding
stop_fast_forwarding();
// make sure that the screen redraws
invalidate_screen();
// Return whether the player skipped it
return retval;
}
void sc_inputbox(const char *msg, char *bufr) {
VALIDATE_STRING(bufr);
setup_for_dialog();
enterstringwindow(get_translation(msg), bufr);
restore_after_dialog();
}
// GetLocationType exported function - just call through
// to the main function with default 0
int GetLocationType(int xxx, int yyy) {
return __GetLocationType(xxx, yyy, 0);
}
void SaveCursorForLocationChange() {
// update the current location name
char tempo[100];
GetLocationName(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey)), tempo);
if (_GP(play).get_loc_name_save_cursor != _GP(play).get_loc_name_last_time) {
_GP(play).get_loc_name_save_cursor = _GP(play).get_loc_name_last_time;
_GP(play).restore_cursor_mode_to = GetCursorMode();
_GP(play).restore_cursor_image_to = GetMouseCursor();
debug_script_log("Saving mouse: mode %d cursor %d", _GP(play).restore_cursor_mode_to, _GP(play).restore_cursor_image_to);
}
}
void GetLocationName(int xxx, int yyy, char *tempo) {
if (_G(displayed_room) < 0)
quit("!GetLocationName: no room has been loaded");
VALIDATE_STRING(tempo);
tempo[0] = 0;
if (GetGUIAt(xxx, yyy) >= 0) {
int mover = GetInvAt(xxx, yyy);
if (mover > 0) {
if (_GP(play).get_loc_name_last_time != 1000 + mover)
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
_GP(play).get_loc_name_last_time = 1000 + mover;
snprintf(tempo, MAX_MAXSTRLEN, "%s", get_translation(_GP(game).invinfo[mover].name));
} else if ((_GP(play).get_loc_name_last_time > 1000) && (_GP(play).get_loc_name_last_time < 1000 + MAX_INV)) {
// no longer selecting an item
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
_GP(play).get_loc_name_last_time = -1;
}
return;
}
int loctype = GetLocationType(xxx, yyy); // GetLocationType takes screen coords
VpPoint vpt = _GP(play).ScreenToRoomDivDown(xxx, yyy);
if (vpt.second < 0)
return;
xxx = vpt.first.X;
yyy = vpt.first.Y;
if ((xxx >= _GP(thisroom).Width) | (xxx < 0) | (yyy < 0) | (yyy >= _GP(thisroom).Height))
return;
int onhs, aa;
if (loctype == 0) {
if (_GP(play).get_loc_name_last_time != 0) {
_GP(play).get_loc_name_last_time = 0;
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
}
return;
}
// on character
if (loctype == LOCTYPE_CHAR) {
onhs = _G(getloctype_index);
snprintf(tempo, MAX_MAXSTRLEN, "%s", get_translation(_GP(game).chars[onhs].name));
if (_GP(play).get_loc_name_last_time != 2000 + onhs)
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
_GP(play).get_loc_name_last_time = 2000 + onhs;
return;
}
// on object
if (loctype == LOCTYPE_OBJ) {
aa = _G(getloctype_index);
snprintf(tempo, MAX_MAXSTRLEN, "%s", get_translation(_G(croom)->obj[aa].name.GetCStr()));
// Compatibility: < 3.1.1 games returned space for nameless object
// (presumably was a bug, but fixing it affected certain games behavior)
if (_G(loaded_game_file_version) < kGameVersion_311 && tempo[0] == 0) {
tempo[0] = ' ';
tempo[1] = 0;
}
if (_GP(play).get_loc_name_last_time != 3000 + aa)
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
_GP(play).get_loc_name_last_time = 3000 + aa;
return;
}
onhs = _G(getloctype_index);
if (onhs > 0)
snprintf(tempo, MAX_MAXSTRLEN, "%s", get_translation(_G(croom)->hotspot[onhs].Name.GetCStr()));
if (_GP(play).get_loc_name_last_time != onhs)
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
_GP(play).get_loc_name_last_time = onhs;
}
int IsKeyPressed(int keycode) {
auto status = ags_iskeydown(static_cast<eAGSKeyCode>(keycode));
if (status < 0) {
debug_script_log("IsKeyPressed: unsupported keycode %d", keycode);
return 0;
}
return status;
}
int SaveScreenShot(const char *namm) {
String fileName;
String svg_dir = get_save_game_directory();
if (strchr(namm, '.') == nullptr)
fileName = Path::MakePath(svg_dir, namm, "bmp");
else
fileName = Path::ConcatPaths(svg_dir, namm);
Bitmap *buffer = CopyScreenIntoBitmap(_GP(play).GetMainViewport().GetWidth(), _GP(play).GetMainViewport().GetHeight());
if (!buffer->SaveToFile(fileName, _G(palette)) != 0) {
delete buffer;
return 0;
}
delete buffer;
return 1; // successful
}
void SetMultitasking(int mode) {
if ((mode < 0) | (mode > 1))
quit("!SetMultitasking: invalid mode parameter");
// Save requested setting
_GP(usetup).multitasking = mode != 0;
// Account for the override config option (must be checked first!)
if ((_GP(usetup).override_multitasking >= 0) && (mode != _GP(usetup).override_multitasking)) {
Debug::Printf("SetMultitasking: overridden by user config: %d -> %d", mode, _GP(usetup).override_multitasking);
mode = _GP(usetup).override_multitasking;
}
// Must run on background if debugger is connected
if ((mode == 0) && (_G(editor_debugging_initialized) != 0)) {
Debug::Printf("SetMultitasking: overridden by the external debugger: %d -> 1", mode);
mode = 1;
}
// Regardless, don't allow background running if exclusive full screen
if ((mode == 1) && _G(gfxDriver)->GetDisplayMode().IsRealFullscreen()) {
Debug::Printf("SetMultitasking: overridden by fullscreen: %d -> 0", mode);
mode = 0;
}
// Install engine callbacks for switching in and out the window
Debug::Printf(kDbgMsg_Info, "Multitasking mode set: %d", mode);
if (mode == 0) {
sys_set_background_mode(false);
sys_evt_set_focus_callbacks(display_switch_in_resume, display_switch_out_suspend);
} else {
sys_set_background_mode(true);
sys_evt_set_focus_callbacks(display_switch_in, display_switch_out);
}
}
void RoomProcessClick(int xx, int yy, int mood) {
_G(getloctype_throughgui) = 1;
int loctype = GetLocationType(xx, yy);
VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
if (vpt.second < 0)
return;
xx = vpt.first.X;
yy = vpt.first.Y;
if ((mood == MODE_WALK) && (_GP(game).options[OPT_NOWALKMODE] == 0)) {
int hsnum = get_hotspot_at(xx, yy);
if (hsnum < 1);
else if (_GP(thisroom).Hotspots[hsnum].WalkTo.X < 1);
else if (_GP(play).auto_use_walkto_points == 0);
else {
xx = _GP(thisroom).Hotspots[hsnum].WalkTo.X;
yy = _GP(thisroom).Hotspots[hsnum].WalkTo.Y;
debug_script_log("Move to walk-to point hotspot %d", hsnum);
}
walk_character(_GP(game).playercharacter, xx, yy, 0, true);
return;
}
_GP(play).usedmode = mood;
if (loctype == 0) {
// click on nothing -> hotspot 0
_G(getloctype_index) = 0;
loctype = LOCTYPE_HOTSPOT;
}
if (loctype == LOCTYPE_CHAR) {
if (check_click_on_character(xx, yy, mood)) return;
} else if (loctype == LOCTYPE_OBJ) {
if (check_click_on_object(xx, yy, mood)) return;
} else if (loctype == LOCTYPE_HOTSPOT)
RunHotspotInteraction(_G(getloctype_index), mood);
}
int IsInteractionAvailable(int xx, int yy, int mood) {
_G(getloctype_throughgui) = 1;
int loctype = GetLocationType(xx, yy);
VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
if (vpt.second < 0)
return 0;
xx = vpt.first.X;
yy = vpt.first.Y;
// You can always walk places
if ((mood == MODE_WALK) && (_GP(game).options[OPT_NOWALKMODE] == 0))
return 1;
_GP(play).check_interaction_only = 1;
if (loctype == 0) {
// click on nothing -> hotspot 0
_G(getloctype_index) = 0;
loctype = LOCTYPE_HOTSPOT;
}
if (loctype == LOCTYPE_CHAR) {
check_click_on_character(xx, yy, mood);
} else if (loctype == LOCTYPE_OBJ) {
check_click_on_object(xx, yy, mood);
} else if (loctype == LOCTYPE_HOTSPOT)
RunHotspotInteraction(_G(getloctype_index), mood);
int ciwas = _GP(play).check_interaction_only;
_GP(play).check_interaction_only = 0;
if (ciwas == 2)
return 1;
return 0;
}
void GetMessageText(int msg, char *buffer) {
VALIDATE_STRING(buffer);
get_message_text(msg, buffer, 0);
}
void SetSpeechFont(int fontnum) {
if ((fontnum < 0) || (fontnum >= _GP(game).numfonts))
quit("!SetSpeechFont: invalid font number.");
_GP(play).speech_font = fontnum;
}
void SetNormalFont(int fontnum) {
if ((fontnum < 0) || (fontnum >= _GP(game).numfonts))
quit("!SetNormalFont: invalid font number.");
_GP(play).normal_font = fontnum;
}
void _sc_AbortGame(const char *text) {
char displbuf[STD_BUFFER_SIZE] = "!?";
snprintf(&displbuf[2], STD_BUFFER_SIZE - 3, "%s", text);
quit(displbuf);
}
int GetGraphicalVariable(const char *varName) {
InteractionVariable *theVar = FindGraphicalVariable(varName);
if (theVar == nullptr) {
quitprintf("!GetGraphicalVariable: interaction variable '%s' not found", varName);
return 0;
}
return theVar->Value;
}
void SetGraphicalVariable(const char *varName, int p_value) {
InteractionVariable *theVar = FindGraphicalVariable(varName);
if (theVar == nullptr) {
quitprintf("!SetGraphicalVariable: interaction variable '%s' not found", varName);
} else
theVar->Value = p_value;
}
int WaitImpl(int skip_type, int nloops) {
// if skipping cutscene and expecting user input: don't wait at all
if (_GP(play).fast_forward && ((skip_type & ~SKIP_AUTOTIMER) != 0))
return 0;
_GP(play).wait_counter = nloops;
_GP(play).wait_skipped_by = SKIP_NONE;
_GP(play).wait_skipped_by = SKIP_AUTOTIMER; // we set timer flag by default to simplify that case
_GP(play).wait_skipped_by_data = 0;
_GP(play).key_skip_wait = skip_type;
GameLoopUntilValueIsZero(&_GP(play).wait_counter);
if (_GP(game).options[OPT_BASESCRIPTAPI] < kScriptAPI_v360) {
// < 3.6.0 return 1 is skipped by user input, otherwise 0
return ((_GP(play).wait_skipped_by & (SKIP_KEYPRESS | SKIP_MOUSECLICK)) != 0) ? 1 : 0;
}
// >= 3.6.0 return skip (input) type flags with keycode
return _GP(play).GetWaitSkipResult();
}
void scrWait(int nloops) {
WaitImpl(SKIP_AUTOTIMER, nloops);
}
int WaitKey(int nloops) {
return WaitImpl(SKIP_KEYPRESS | SKIP_AUTOTIMER, nloops);
}
int WaitMouse(int nloops) {
return WaitImpl(SKIP_MOUSECLICK | SKIP_AUTOTIMER, nloops);
}
int WaitMouseKey(int nloops) {
return WaitImpl(SKIP_KEYPRESS | SKIP_MOUSECLICK | SKIP_AUTOTIMER, nloops);
}
int WaitInput(int input_flags, int nloops) {
return WaitImpl(input_flags >> 16 | SKIP_AUTOTIMER, nloops);
}
void SkipWait() {
_GP(play).wait_counter = 0;
}
void scStartRecording(int /*keyToStop*/) {
debug_script_warn("StartRecording: not supported");
}
} // namespace AGS3