mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-13 07:14:59 +00:00
![Thierry Crozat](/assets/img/avatar_default.png)
This may result in object texture not updating if the old sprite was deleted but a new dynamic sprite was created right after having same ID. Was broken by upstream 9dffb04 in 3.5.1, and also some later changes in 3.6.0. From upstream 7ed51861898d864902b0f61b0cb7d0acadef1fe3
1786 lines
58 KiB
C++
1786 lines
58 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/memstream.h"
|
|
#include "ags/engine/ac/game.h"
|
|
#include "ags/shared/ac/common.h"
|
|
#include "ags/shared/ac/view.h"
|
|
#include "ags/engine/ac/audio_channel.h"
|
|
#include "ags/engine/ac/button.h"
|
|
#include "ags/engine/ac/character.h"
|
|
#include "ags/engine/ac/dialog.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_setup.h"
|
|
#include "ags/shared/ac/game_setup_struct.h"
|
|
#include "ags/engine/ac/game_state.h"
|
|
#include "ags/engine/ac/global_audio.h"
|
|
#include "ags/engine/ac/global_display.h"
|
|
#include "ags/engine/ac/global_game.h"
|
|
#include "ags/engine/ac/global_gui.h"
|
|
#include "ags/engine/ac/global_object.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/lip_sync.h"
|
|
#include "ags/engine/ac/mouse.h"
|
|
#include "ags/engine/ac/move_list.h"
|
|
#include "ags/engine/ac/overlay.h"
|
|
#include "ags/engine/ac/path_helper.h"
|
|
#include "ags/engine/ac/sys_events.h"
|
|
#include "ags/engine/ac/rich_game_media.h"
|
|
#include "ags/engine/ac/room_status.h"
|
|
#include "ags/shared/ac/sprite_cache.h"
|
|
#include "ags/engine/ac/string.h"
|
|
#include "ags/engine/ac/translation.h"
|
|
#include "ags/engine/ac/dynobj/all_dynamic_classes.h"
|
|
#include "ags/engine/ac/dynobj/all_script_classes.h"
|
|
#include "ags/engine/ac/dynobj/script_camera.h"
|
|
#include "ags/engine/debugging/debug_log.h"
|
|
#include "ags/engine/debugging/debugger.h"
|
|
#include "ags/shared/debugging/out.h"
|
|
#include "ags/engine/device/mouse_w32.h"
|
|
#include "ags/shared/font/fonts.h"
|
|
#include "ags/engine/game/savegame.h"
|
|
#include "ags/shared/gfx/bitmap.h"
|
|
#include "ags/engine/gfx/graphics_driver.h"
|
|
#include "ags/shared/gui/gui_button.h"
|
|
#include "ags/shared/gui/gui_slider.h"
|
|
#include "ags/engine/gui/gui_dialog.h"
|
|
#include "ags/engine/main/engine.h"
|
|
#include "ags/engine/media/audio/audio_system.h"
|
|
#include "ags/engine/media/video/video.h"
|
|
#include "ags/engine/platform/base/ags_platform_driver.h"
|
|
#include "ags/engine/platform/base/sys_main.h"
|
|
#include "ags/plugins/plugin_engine.h"
|
|
#include "ags/engine/script/script.h"
|
|
#include "ags/engine/script/script_runtime.h"
|
|
#include "ags/shared/util/directory.h"
|
|
#include "ags/shared/util/file.h"
|
|
#include "ags/shared/util/path.h"
|
|
|
|
#include "ags/shared/debugging/out.h"
|
|
#include "ags/engine/script/script_api.h"
|
|
#include "ags/engine/script/script_runtime.h"
|
|
#include "ags/ags.h"
|
|
#include "ags/globals.h"
|
|
|
|
namespace AGS3 {
|
|
|
|
using namespace AGS::Shared;
|
|
using namespace AGS::Engine;
|
|
|
|
//=============================================================================
|
|
// Audio
|
|
//=============================================================================
|
|
|
|
void Game_StopAudio(int audioType) {
|
|
if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
|
|
quitprintf("!Game.StopAudio: invalid audio type %d", audioType);
|
|
|
|
for (int aa = 0; aa < _GP(game).numGameChannels; aa++) {
|
|
if (audioType == SCR_NO_VALUE) {
|
|
stop_or_fade_out_channel(aa);
|
|
} else {
|
|
ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
|
|
if ((clip != nullptr) && (clip->type == audioType))
|
|
stop_or_fade_out_channel(aa);
|
|
}
|
|
}
|
|
|
|
remove_clips_of_type_from_queue(audioType);
|
|
}
|
|
|
|
int Game_IsAudioPlaying(int audioType) {
|
|
if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
|
|
quitprintf("!Game.IsAudioPlaying: invalid audio type %d", audioType);
|
|
|
|
if (_GP(play).fast_forward)
|
|
return 0;
|
|
|
|
for (int aa = 0; aa < _GP(game).numGameChannels; aa++) {
|
|
ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
|
|
if (clip != nullptr) {
|
|
if ((clip->type == audioType) || (audioType == SCR_NO_VALUE)) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Game_SetAudioTypeSpeechVolumeDrop(int audioType, int volumeDrop) {
|
|
if ((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size()))
|
|
quitprintf("!Game.SetAudioTypeVolume: invalid audio type: %d", audioType);
|
|
|
|
Debug::Printf("Game.SetAudioTypeSpeechVolumeDrop: type: %d, drop: %d", audioType, volumeDrop);
|
|
_GP(game).audioClipTypes[audioType].volume_reduction_while_speech_playing = volumeDrop;
|
|
update_volume_drop_if_voiceover();
|
|
}
|
|
|
|
void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
|
|
if ((volume < 0) || (volume > 100))
|
|
quitprintf("!Game.SetAudioTypeVolume: volume %d is not between 0..100", volume);
|
|
if ((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size()))
|
|
quitprintf("!Game.SetAudioTypeVolume: invalid audio type: %d", audioType);
|
|
|
|
Debug::Printf("Game.SetAudioTypeVolume: type: %d, volume: %d, change: %d", audioType, volume, changeType);
|
|
if ((changeType == VOL_CHANGEEXISTING) ||
|
|
(changeType == VOL_BOTH)) {
|
|
for (int aa = 0; aa < _GP(game).numGameChannels; aa++) {
|
|
ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
|
|
if ((clip != nullptr) && (clip->type == audioType)) {
|
|
auto *ch = AudioChans::GetChannel(aa);
|
|
if (ch)
|
|
ch->set_volume100(volume);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((changeType == VOL_SETFUTUREDEFAULT) ||
|
|
(changeType == VOL_BOTH)) {
|
|
_GP(play).default_audio_type_volumes[audioType] = volume;
|
|
|
|
// update queued clip volumes
|
|
update_queued_clips_volume(audioType, volume);
|
|
}
|
|
|
|
}
|
|
|
|
int Game_GetMODPattern() {
|
|
if (_G(current_music_type) != MUS_MOD)
|
|
return -1;
|
|
auto *music_ch = AudioChans::GetChannelIfPlaying(SCHAN_MUSIC);
|
|
return music_ch ? music_ch->get_pos() : -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
// ---
|
|
//=============================================================================
|
|
|
|
int Game_GetDialogCount() {
|
|
return _GP(game).numdialog;
|
|
}
|
|
|
|
void set_debug_mode(bool on) {
|
|
_GP(play).debug_mode = on ? 1 : 0;
|
|
debug_set_console(on);
|
|
}
|
|
|
|
void set_game_speed(int new_fps) {
|
|
_G(frames_per_second) = new_fps;
|
|
if (!isTimerFpsMaxed()) // if in maxed mode, don't update timer for now
|
|
setTimerFps(new_fps);
|
|
}
|
|
|
|
void setup_for_dialog() {
|
|
_G(cbuttfont) = _GP(play).normal_font;
|
|
_G(acdialog_font) = _GP(play).normal_font;
|
|
_G(oldmouse) = _G(cur_cursor);
|
|
set_mouse_cursor(CURS_ARROW);
|
|
}
|
|
void restore_after_dialog() {
|
|
set_mouse_cursor(_G(oldmouse));
|
|
invalidate_screen();
|
|
}
|
|
|
|
String get_save_game_directory() {
|
|
return _G(saveGameDirectory);
|
|
}
|
|
|
|
String get_save_game_suffix() {
|
|
return _G(saveGameSuffix);
|
|
}
|
|
|
|
void set_save_game_suffix(const String &suffix) {
|
|
_G(saveGameSuffix) = suffix;
|
|
}
|
|
|
|
String get_save_game_filename(int slotNum) {
|
|
return String(g_engine->getSaveStateName(slotNum).c_str());
|
|
}
|
|
|
|
String get_save_game_path(int slotNum) {
|
|
#if AGS_PLATFORM_SCUMMVM
|
|
return Common::String::format("%s%s", SAVE_FOLDER_PREFIX,
|
|
::AGS::g_vm->getSaveStateName(slotNum).c_str());
|
|
#else
|
|
return Path::ConcatPaths(_G(saveGameDirectory), get_save_game_filename(slotNum));
|
|
#endif
|
|
}
|
|
|
|
#if !AGS_PLATFORM_SCUMMVM
|
|
// Convert a path possibly containing path tags into acceptable save path
|
|
// NOTE that the game script may issue an order to change the save directory to
|
|
// a dir of a new name. While we let this work, we also try to keep these
|
|
// inside same parent location, would that be a common system directory,
|
|
// or a custom one set by a player in config.
|
|
static bool MakeSaveGameDir(const String &new_dir, FSLocation &fsdir) {
|
|
fsdir = FSLocation();
|
|
// don't allow absolute paths
|
|
if (!is_relative_filename(new_dir))
|
|
return false;
|
|
|
|
String fixed_newdir = FixSlashAfterToken(new_dir);
|
|
if (fixed_newdir.CompareLeft(UserSavedgamesRootToken, UserSavedgamesRootToken.GetLength()) == 0) {
|
|
fixed_newdir.ClipLeft(UserSavedgamesRootToken.GetLength());
|
|
} else if (game.options[OPT_SAFEFILEPATHS] > 0) { // For games made in the safe-path-aware versions of AGS, report a warning
|
|
debug_script_warn("Attempt to explicitly set savegame location relative to the game installation directory ('%s') denied;\nPath will be remapped to the user documents directory: '%s'",
|
|
fixed_newdir.GetCStr(), fsdir.FullDir.GetCStr());
|
|
}
|
|
|
|
// Resolve the new dir relative to the user data parent dir
|
|
fsdir = GetGameUserDataDir().Concat(fixed_newdir);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// Tries to assign a new save directory, and copies the restart point if available
|
|
static bool SetSaveGameDirectory(const FSLocation &fsdir) {
|
|
if (!Directory::CreateAllDirectories(fsdir.BaseDir, fsdir.FullDir)) {
|
|
debug_script_warn("SetSaveGameDirectory: failed to create all subdirectories: %s", fsdir.FullDir.GetCStr());
|
|
return false;
|
|
}
|
|
String newSaveGameDir = fsdir.FullDir;
|
|
|
|
if (!File::TestCreateFile(Path::ConcatPaths(newSaveGameDir, "agstmp.tmp")))
|
|
return false;
|
|
|
|
// copy the Restart Game file, if applicable
|
|
String restartGamePath = Path::ConcatPaths(_G(saveGameDirectory), get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
|
|
Stream *restartGameFile = File::OpenFileRead(restartGamePath);
|
|
if (restartGameFile != nullptr) {
|
|
long fileSize = restartGameFile->GetLength();
|
|
char *mbuffer = (char *)malloc(fileSize);
|
|
restartGameFile->Read(mbuffer, fileSize);
|
|
delete restartGameFile;
|
|
|
|
restartGamePath = Path::ConcatPaths(newSaveGameDir, get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
|
|
restartGameFile = File::CreateFile(restartGamePath);
|
|
restartGameFile->Write(mbuffer, fileSize);
|
|
delete restartGameFile;
|
|
free(mbuffer);
|
|
}
|
|
|
|
_G(saveGameDirectory) = newSaveGameDir;
|
|
return true;
|
|
}
|
|
|
|
void SetDefaultSaveDirectory() {
|
|
// Request a default save location, and assign it as a save dir
|
|
FSLocation fsdir = GetGameUserDataDir();
|
|
SetSaveGameDirectory(fsdir);
|
|
}
|
|
|
|
int Game_SetSaveGameDirectory(const char *newFolder) {
|
|
#if AGS_PLATFORM_SCUMMVM
|
|
return 1;
|
|
#else
|
|
// First resolve the script path (it may contain tokens)
|
|
FSLocation fsdir;
|
|
if (!MakeSaveGameDir(newFolder, fsdir))
|
|
return 0;
|
|
// If resolved successfully, try to assign the new dir
|
|
return SetSaveGameDirectory(fsdir) ? 1 : 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
const char *Game_GetSaveSlotDescription(int slnum) {
|
|
String description;
|
|
if (read_savedgame_description(get_save_game_path(slnum), description)) {
|
|
return CreateNewScriptString(description.GetCStr());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
void restore_game_dialog() {
|
|
can_run_delayed_command();
|
|
if (_GP(thisroom).Options.SaveLoadDisabled == 1) {
|
|
DisplayMessage(983);
|
|
return;
|
|
}
|
|
if (_G(inside_script)) {
|
|
_G(curscript)->queue_action(ePSARestoreGameDialog, 0, "RestoreGameDialog");
|
|
return;
|
|
}
|
|
do_restore_game_dialog();
|
|
}
|
|
|
|
bool do_restore_game_dialog() {
|
|
setup_for_dialog();
|
|
int toload = loadgamedialog();
|
|
restore_after_dialog();
|
|
if (toload >= 0)
|
|
try_restore_save(toload);
|
|
return toload >= 0;
|
|
}
|
|
|
|
void save_game_dialog() {
|
|
if (_GP(thisroom).Options.SaveLoadDisabled == 1) {
|
|
DisplayMessage(983);
|
|
return;
|
|
}
|
|
if (_G(inside_script)) {
|
|
_G(curscript)->queue_action(ePSASaveGameDialog, 0, "SaveGameDialog");
|
|
return;
|
|
}
|
|
do_save_game_dialog();
|
|
}
|
|
|
|
bool do_save_game_dialog() {
|
|
setup_for_dialog();
|
|
int toload = savegamedialog();
|
|
restore_after_dialog();
|
|
if (toload >= 0)
|
|
save_game(toload, get_gui_dialog_buffer());
|
|
return toload >= 0;
|
|
}
|
|
|
|
void free_do_once_tokens() {
|
|
_GP(play).do_once_tokens.resize(0);
|
|
}
|
|
|
|
|
|
// Free all the memory associated with the game
|
|
void unload_game_file() {
|
|
close_translation();
|
|
|
|
_GP(play).FreeViewportsAndCameras();
|
|
|
|
_GP(charextra).clear();
|
|
_GP(mls).clear();
|
|
|
|
dispose_game_drawdata();
|
|
|
|
delete _G(gameinstFork);
|
|
delete _G(gameinst);
|
|
_G(gameinstFork) = nullptr;
|
|
_G(gameinst) = nullptr;
|
|
_GP(gamescript).reset();
|
|
|
|
delete _G(dialogScriptsInst);
|
|
_G(dialogScriptsInst) = nullptr;
|
|
_GP(dialogScriptsScript).reset();
|
|
|
|
for (size_t i = 0; i < _G(numScriptModules); ++i) {
|
|
delete _GP(moduleInstFork)[i];
|
|
delete _GP(moduleInst)[i];
|
|
_GP(scriptModules)[i].reset();
|
|
}
|
|
|
|
_GP(moduleInstFork).resize(0);
|
|
_GP(moduleInst).resize(0);
|
|
_GP(scriptModules).resize(0);
|
|
_GP(repExecAlways).moduleHasFunction.resize(0);
|
|
_GP(lateRepExecAlways).moduleHasFunction.resize(0);
|
|
_GP(getDialogOptionsDimensionsFunc).moduleHasFunction.resize(0);
|
|
_GP(renderDialogOptionsFunc).moduleHasFunction.resize(0);
|
|
_GP(getDialogOptionUnderCursorFunc).moduleHasFunction.resize(0);
|
|
_GP(runDialogOptionMouseClickHandlerFunc).moduleHasFunction.resize(0);
|
|
_GP(runDialogOptionKeyPressHandlerFunc).moduleHasFunction.resize(0);
|
|
_GP(runDialogOptionTextInputHandlerFunc).moduleHasFunction.resize(0);
|
|
_GP(runDialogOptionRepExecFunc).moduleHasFunction.resize(0);
|
|
_GP(runDialogOptionCloseFunc).moduleHasFunction.resize(0);
|
|
_G(numScriptModules) = 0;
|
|
|
|
_GP(views).clear();
|
|
|
|
if (_G(splipsync) != nullptr) {
|
|
for (int i = 0; i < _G(numLipLines); ++i) {
|
|
free(_G(splipsync)[i].endtimeoffs);
|
|
free(_G(splipsync)[i].frame);
|
|
}
|
|
free(_G(splipsync));
|
|
_G(splipsync) = nullptr;
|
|
_G(numLipLines) = 0;
|
|
_G(curLipLine) = -1;
|
|
}
|
|
|
|
for (auto &dlg : _G(dialog)) {
|
|
if (dlg.optionscripts != nullptr)
|
|
free(dlg.optionscripts);
|
|
}
|
|
_G(dialog).clear();
|
|
delete[] _G(scrDialog);
|
|
_G(scrDialog) = nullptr;
|
|
|
|
_GP(guis).clear();
|
|
free(_G(scrGui));
|
|
|
|
free_all_fonts();
|
|
|
|
ccRemoveAllSymbols();
|
|
ccUnregisterAllObjects();
|
|
pl_stop_plugins();
|
|
|
|
free_do_once_tokens();
|
|
_GP(play).gui_draw_order.clear();
|
|
|
|
resetRoomStatuses();
|
|
|
|
// free game struct last because it contains object counts
|
|
_GP(game).Free();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *Game_GetGlobalStrings(int index) {
|
|
if ((index < 0) || (index >= MAXGLOBALSTRINGS))
|
|
quit("!Game.GlobalStrings: invalid index");
|
|
|
|
return CreateNewScriptString(_GP(play).globalstrings[index]);
|
|
}
|
|
|
|
|
|
// ** GetGameParameter replacement functions
|
|
|
|
int Game_GetInventoryItemCount() {
|
|
// because of the dummy item 0, this is always one higher than it should be
|
|
return _GP(game).numinvitems - 1;
|
|
}
|
|
|
|
int Game_GetFontCount() {
|
|
return _GP(game).numfonts;
|
|
}
|
|
|
|
int Game_GetMouseCursorCount() {
|
|
return _GP(game).numcursors;
|
|
}
|
|
|
|
int Game_GetCharacterCount() {
|
|
return _GP(game).numcharacters;
|
|
}
|
|
|
|
int Game_GetGUICount() {
|
|
return _GP(game).numgui;
|
|
}
|
|
|
|
int Game_GetViewCount() {
|
|
return _GP(game).numviews;
|
|
}
|
|
|
|
int Game_GetUseNativeCoordinates() {
|
|
return _GP(game).IsDataInNativeCoordinates() ? 1 : 0;
|
|
}
|
|
|
|
int Game_GetSpriteWidth(int spriteNum) {
|
|
if (spriteNum < 0)
|
|
return 0;
|
|
|
|
if (!_GP(spriteset).DoesSpriteExist(spriteNum))
|
|
return 0;
|
|
|
|
return game_to_data_coord(_GP(game).SpriteInfos[spriteNum].Width);
|
|
}
|
|
|
|
int Game_GetSpriteHeight(int spriteNum) {
|
|
if (spriteNum < 0)
|
|
return 0;
|
|
|
|
if (!_GP(spriteset).DoesSpriteExist(spriteNum))
|
|
return 0;
|
|
|
|
return game_to_data_coord(_GP(game).SpriteInfos[spriteNum].Height);
|
|
}
|
|
|
|
int Game_GetLoopCountForView(int viewNumber) {
|
|
if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
|
|
quit("!GetGameParameter: invalid view specified");
|
|
|
|
return _GP(views)[viewNumber - 1].numLoops;
|
|
}
|
|
|
|
int Game_GetRunNextSettingForLoop(int viewNumber, int loopNumber) {
|
|
if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
|
|
quit("!GetGameParameter: invalid view specified");
|
|
if ((loopNumber < 0) || (loopNumber >= _GP(views)[viewNumber - 1].numLoops))
|
|
quit("!GetGameParameter: invalid loop specified");
|
|
|
|
return (_GP(views)[viewNumber - 1].loops[loopNumber].RunNextLoop()) ? 1 : 0;
|
|
}
|
|
|
|
int Game_GetFrameCountForLoop(int viewNumber, int loopNumber) {
|
|
if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
|
|
quit("!GetGameParameter: invalid view specified");
|
|
if ((loopNumber < 0) || (loopNumber >= _GP(views)[viewNumber - 1].numLoops))
|
|
quit("!GetGameParameter: invalid loop specified");
|
|
|
|
return _GP(views)[viewNumber - 1].loops[loopNumber].numFrames;
|
|
}
|
|
|
|
ScriptViewFrame *Game_GetViewFrame(int viewNumber, int loopNumber, int frame) {
|
|
if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
|
|
quit("!GetGameParameter: invalid view specified");
|
|
if ((loopNumber < 0) || (loopNumber >= _GP(views)[viewNumber - 1].numLoops))
|
|
quit("!GetGameParameter: invalid loop specified");
|
|
if ((frame < 0) || (frame >= _GP(views)[viewNumber - 1].loops[loopNumber].numFrames))
|
|
quit("!GetGameParameter: invalid frame specified");
|
|
|
|
ScriptViewFrame *sdt = new ScriptViewFrame(viewNumber - 1, loopNumber, frame);
|
|
ccRegisterManagedObject(sdt, sdt);
|
|
return sdt;
|
|
}
|
|
|
|
int Game_DoOnceOnly(const char *token) {
|
|
for (int i = 0; i < (int)_GP(play).do_once_tokens.size(); i++) {
|
|
if (_GP(play).do_once_tokens[i] == token) {
|
|
return 0;
|
|
}
|
|
}
|
|
_GP(play).do_once_tokens.push_back(token);
|
|
return 1;
|
|
}
|
|
|
|
int Game_GetTextReadingSpeed() {
|
|
return _GP(play).text_speed;
|
|
}
|
|
|
|
void Game_SetTextReadingSpeed(int newTextSpeed) {
|
|
if (newTextSpeed < 1)
|
|
quitprintf("!Game.TextReadingSpeed: %d is an invalid speed", newTextSpeed);
|
|
|
|
_GP(play).text_speed = newTextSpeed;
|
|
}
|
|
|
|
int Game_GetMinimumTextDisplayTimeMs() {
|
|
return _GP(play).text_min_display_time_ms;
|
|
}
|
|
|
|
void Game_SetMinimumTextDisplayTimeMs(int newTextMinTime) {
|
|
_GP(play).text_min_display_time_ms = newTextMinTime;
|
|
}
|
|
|
|
int Game_GetIgnoreUserInputAfterTextTimeoutMs() {
|
|
return _GP(play).ignore_user_input_after_text_timeout_ms;
|
|
}
|
|
|
|
void Game_SetIgnoreUserInputAfterTextTimeoutMs(int newValueMs) {
|
|
_GP(play).ignore_user_input_after_text_timeout_ms = newValueMs;
|
|
}
|
|
|
|
const char *Game_GetFileName() {
|
|
return CreateNewScriptString(_GP(ResPaths).GamePak.Name.GetCStr());
|
|
}
|
|
|
|
const char *Game_GetName() {
|
|
return CreateNewScriptString(_GP(play).game_name);
|
|
}
|
|
|
|
void Game_SetName(const char *newName) {
|
|
strncpy(_GP(play).game_name, newName, 99);
|
|
_GP(play).game_name[99] = 0;
|
|
sys_window_set_title(_GP(play).game_name);
|
|
}
|
|
|
|
int Game_GetSkippingCutscene() {
|
|
if (_GP(play).fast_forward) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Game_GetInSkippableCutscene() {
|
|
if (_GP(play).in_cutscene) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Game_GetColorFromRGB(int red, int grn, int blu) {
|
|
if ((red < 0) || (red > 255) || (grn < 0) || (grn > 255) ||
|
|
(blu < 0) || (blu > 255))
|
|
quit("!GetColorFromRGB: colour values must be 0-255");
|
|
|
|
if (_GP(game).color_depth == 1) {
|
|
return makecol8(red, grn, blu);
|
|
}
|
|
|
|
int agscolor = ((blu >> 3) & 0x1f);
|
|
agscolor += ((grn >> 2) & 0x3f) << 5;
|
|
agscolor += ((red >> 3) & 0x1f) << 11;
|
|
return agscolor;
|
|
}
|
|
|
|
const char *Game_InputBox(const char *msg) {
|
|
char buffer[STD_BUFFER_SIZE];
|
|
sc_inputbox(msg, buffer);
|
|
return CreateNewScriptString(buffer);
|
|
}
|
|
|
|
const char *Game_GetLocationName(int x, int y) {
|
|
char buffer[STD_BUFFER_SIZE];
|
|
GetLocationName(x, y, buffer);
|
|
return CreateNewScriptString(buffer);
|
|
}
|
|
|
|
const char *Game_GetGlobalMessages(int index) {
|
|
if ((index < 500) || (index >= MAXGLOBALMES + 500)) {
|
|
return nullptr;
|
|
}
|
|
char buffer[STD_BUFFER_SIZE];
|
|
buffer[0] = 0;
|
|
replace_tokens(get_translation(get_global_message(index)), buffer, STD_BUFFER_SIZE);
|
|
return CreateNewScriptString(buffer);
|
|
}
|
|
|
|
int Game_GetSpeechFont() {
|
|
return _GP(play).speech_font;
|
|
}
|
|
int Game_GetNormalFont() {
|
|
return _GP(play).normal_font;
|
|
}
|
|
|
|
const char *Game_GetTranslationFilename() {
|
|
char buffer[STD_BUFFER_SIZE];
|
|
GetTranslationName(buffer);
|
|
return CreateNewScriptString(buffer);
|
|
}
|
|
|
|
int Game_ChangeTranslation(const char *newFilename) {
|
|
if ((newFilename == nullptr) || (newFilename[0] == 0)) { // switch back to default translation
|
|
close_translation();
|
|
_GP(usetup).translation = "";
|
|
GUI::MarkForTranslationUpdate();
|
|
return 1;
|
|
}
|
|
|
|
String oldTransFileName = get_translation_name();
|
|
if (!init_translation(newFilename, oldTransFileName))
|
|
return 0; // failed, kept previous translation
|
|
|
|
_GP(usetup).translation = newFilename;
|
|
GUI::MarkForTranslationUpdate();
|
|
return 1;
|
|
}
|
|
|
|
const char *Game_GetSpeechVoxFilename() {
|
|
return CreateNewScriptString(get_voicepak_name().GetCStr());
|
|
}
|
|
|
|
bool Game_ChangeSpeechVox(const char *newFilename) {
|
|
if (!init_voicepak(newFilename)) {
|
|
// if failed (and was not default)- fallback to default
|
|
if (strlen(newFilename) > 0)
|
|
init_voicepak();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ScriptAudioClip *Game_GetAudioClip(int index) {
|
|
if (index < 0 || (size_t)index >= _GP(game).audioClips.size())
|
|
return nullptr;
|
|
return &_GP(game).audioClips[index];
|
|
}
|
|
|
|
ScriptCamera *Game_GetCamera() {
|
|
return _GP(play).GetScriptCamera(0);
|
|
}
|
|
|
|
int Game_GetCameraCount() {
|
|
return _GP(play).GetRoomCameraCount();
|
|
}
|
|
|
|
ScriptCamera *Game_GetAnyCamera(int index) {
|
|
return _GP(play).GetScriptCamera(index);
|
|
}
|
|
|
|
void Game_SimulateKeyPress(int key) {
|
|
ags_simulate_keypress(static_cast<eAGSKeyCode>(key));
|
|
}
|
|
|
|
int Game_BlockingWaitSkipped() {
|
|
return _GP(play).GetWaitSkipResult();
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
// save game functions
|
|
|
|
|
|
|
|
void serialize_bitmap(const Shared::Bitmap *thispic, Stream *out) {
|
|
if (thispic != nullptr) {
|
|
out->WriteInt32(thispic->GetWidth());
|
|
out->WriteInt32(thispic->GetHeight());
|
|
out->WriteInt32(thispic->GetColorDepth());
|
|
for (int cc = 0; cc < thispic->GetHeight(); cc++) {
|
|
switch (thispic->GetColorDepth()) {
|
|
case 8:
|
|
// CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8;
|
|
// therefore 15-bit bitmaps are saved only partially? is this a bug? or?
|
|
case 15:
|
|
out->WriteArray(&thispic->GetScanLine(cc)[0], thispic->GetWidth(), 1);
|
|
break;
|
|
case 16:
|
|
out->WriteArrayOfInt16((const int16_t *)&thispic->GetScanLine(cc)[0], thispic->GetWidth());
|
|
break;
|
|
case 32:
|
|
out->WriteArrayOfInt32((const int32_t *)&thispic->GetScanLine(cc)[0], thispic->GetWidth());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// On Windows we could just use IIDFromString but this is _G(platform)-independent
|
|
void convert_guid_from_text_to_binary(const char *guidText, unsigned char *buffer) {
|
|
guidText++; // skip {
|
|
for (int bytesDone = 0; bytesDone < 16; bytesDone++) {
|
|
if (*guidText == '-')
|
|
guidText++;
|
|
|
|
char tempString[3];
|
|
tempString[0] = guidText[0];
|
|
tempString[1] = guidText[1];
|
|
tempString[2] = 0;
|
|
uint thisByte = 0;
|
|
sscanf(tempString, "%X", &thisByte);
|
|
|
|
buffer[bytesDone] = thisByte;
|
|
guidText += 2;
|
|
}
|
|
|
|
// Swap bytes to give correct GUID order
|
|
unsigned char temp;
|
|
temp = buffer[0];
|
|
buffer[0] = buffer[3];
|
|
buffer[3] = temp;
|
|
temp = buffer[1];
|
|
buffer[1] = buffer[2];
|
|
buffer[2] = temp;
|
|
temp = buffer[4];
|
|
buffer[4] = buffer[5];
|
|
buffer[5] = temp;
|
|
temp = buffer[6];
|
|
buffer[6] = buffer[7];
|
|
buffer[7] = temp;
|
|
}
|
|
|
|
Bitmap *read_serialized_bitmap(Stream *in) {
|
|
Bitmap *thispic;
|
|
int picwid = in->ReadInt32();
|
|
int pichit = in->ReadInt32();
|
|
int piccoldep = in->ReadInt32();
|
|
thispic = BitmapHelper::CreateBitmap(picwid, pichit, piccoldep);
|
|
if (thispic == nullptr)
|
|
return nullptr;
|
|
for (int vv = 0; vv < pichit; vv++) {
|
|
switch (piccoldep) {
|
|
case 8:
|
|
// CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8
|
|
case 15:
|
|
in->ReadArray(thispic->GetScanLineForWriting(vv), picwid, 1);
|
|
break;
|
|
case 16:
|
|
in->ReadArrayOfInt16((int16_t *)thispic->GetScanLineForWriting(vv), picwid);
|
|
break;
|
|
case 32:
|
|
in->ReadArrayOfInt32((int32_t *)thispic->GetScanLineForWriting(vv), picwid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return thispic;
|
|
}
|
|
|
|
void skip_serialized_bitmap(Stream *in) {
|
|
int picwid = in->ReadInt32();
|
|
int pichit = in->ReadInt32();
|
|
int piccoldep = in->ReadInt32();
|
|
// CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8
|
|
int bpp = piccoldep / 8;
|
|
in->Seek(picwid * pichit * bpp);
|
|
}
|
|
|
|
long write_screen_shot_for_vista(Stream *out, Bitmap *screenshot) {
|
|
// Save the screenshot to a memory stream so we can access the raw data
|
|
Common::MemoryWriteStreamDynamic bitmap(DisposeAfterUse::YES);
|
|
screenshot->SaveToFile(bitmap, _G(palette));
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
// Write the bitmap to the output stream
|
|
out->Write(bitmap.getData(), bitmap.size());
|
|
|
|
return bitmap.size();
|
|
}
|
|
|
|
Bitmap *create_savegame_screenshot() {
|
|
// Render the view without any UI elements
|
|
int old_flags = _G(debug_flags);
|
|
_G(debug_flags) |= DBG_NOIFACE;
|
|
construct_game_scene(true);
|
|
render_to_screen();
|
|
_G(debug_flags) = old_flags;
|
|
|
|
int usewid = data_to_game_coord(_GP(play).screenshot_width);
|
|
int usehit = data_to_game_coord(_GP(play).screenshot_height);
|
|
const Rect &viewport = _GP(play).GetMainViewport();
|
|
if (usewid > viewport.GetWidth())
|
|
usewid = viewport.GetWidth();
|
|
if (usehit > viewport.GetHeight())
|
|
usehit = viewport.GetHeight();
|
|
|
|
if ((_GP(play).screenshot_width < 16) || (_GP(play).screenshot_height < 16))
|
|
quit("!Invalid game.screenshot_width/height, must be from 16x16 to screen res");
|
|
|
|
Bitmap *screenshot = CopyScreenIntoBitmap(usewid, usehit);
|
|
screenshot->GetAllegroBitmap()->makeOpaque();
|
|
|
|
// Restore original screen
|
|
construct_game_scene(true);
|
|
render_to_screen();
|
|
|
|
return screenshot;
|
|
}
|
|
|
|
void save_game(int slotn, const char *descript) {
|
|
// dont allow save in rep_exec_always, because we dont save
|
|
// the state of blocked scripts
|
|
can_run_delayed_command();
|
|
|
|
if (_G(inside_script)) {
|
|
snprintf(_G(curscript)->postScriptSaveSlotDescription[_G(curscript)->queue_action(ePSASaveGame, slotn, "SaveGameSlot")],
|
|
MAX_QUEUED_ACTION_DESC, "%s", descript);
|
|
return;
|
|
}
|
|
|
|
if (_G(platform)->GetDiskFreeSpaceMB() < 2) {
|
|
Display("ERROR: There is not enough disk space free to save the game. Clear some disk space and try again.");
|
|
return;
|
|
}
|
|
|
|
VALIDATE_STRING(descript);
|
|
String nametouse = get_save_game_path(slotn);
|
|
std::unique_ptr<Bitmap> screenShot;
|
|
if (_GP(game).options[OPT_SAVESCREENSHOT] != 0)
|
|
screenShot.reset(create_savegame_screenshot());
|
|
|
|
std::unique_ptr<Stream> out(StartSavegame(nametouse, descript, screenShot.get()));
|
|
if (out == nullptr) {
|
|
Display("ERROR: Unable to open savegame file for writing!");
|
|
return;
|
|
}
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
// Actual dynamic game data is saved here
|
|
SaveGameState(out.get());
|
|
|
|
if (screenShot != nullptr) {
|
|
int screenShotOffset = out->GetPosition() - sizeof(RICH_GAME_MEDIA_HEADER);
|
|
int screenShotSize = write_screen_shot_for_vista(out.get(), screenShot.get());
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
out->Seek(12, kSeekBegin);
|
|
out->WriteInt32(screenShotOffset);
|
|
out->Seek(4);
|
|
out->WriteInt32(screenShotSize);
|
|
}
|
|
}
|
|
|
|
bool read_savedgame_description(const String &savedgame, String &description) {
|
|
SavegameDescription desc;
|
|
HSaveError err = OpenSavegame(savedgame, desc, kSvgDesc_UserText);
|
|
if (!err) {
|
|
Debug::Printf(kDbgMsg_Error, "Unable to read save's description.\n%s", err->FullMessage().GetCStr());
|
|
return false;
|
|
}
|
|
description = desc.UserText;
|
|
return true;
|
|
}
|
|
|
|
bool read_savedgame_screenshot(const String &savedgame, int &want_shot) {
|
|
want_shot = 0;
|
|
|
|
SavegameDescription desc;
|
|
HSaveError err = OpenSavegame(savedgame, desc, kSvgDesc_UserImage);
|
|
if (!err) {
|
|
Debug::Printf(kDbgMsg_Error, "Unable to read save's screenshot.\n%s", err->FullMessage().GetCStr());
|
|
return false;
|
|
}
|
|
|
|
if (desc.UserImage.get()) {
|
|
int slot = _GP(spriteset).GetFreeIndex();
|
|
if (slot > 0) {
|
|
// add it into the sprite set
|
|
add_dynamic_sprite(slot, PrepareSpriteForUse(desc.UserImage.release(), false));
|
|
want_shot = slot;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// Test if the game file contains expected GUID / legacy id
|
|
bool test_game_guid(const String &filepath, const String &guid, int legacy_id) {
|
|
std::unique_ptr<AssetManager> amgr(new AssetManager());
|
|
if (amgr->AddLibrary(filepath) != kAssetNoError)
|
|
return false;
|
|
MainGameSource src;
|
|
if (!OpenMainGameFileFromDefaultAsset(src, amgr.get()))
|
|
return false;
|
|
GameSetupStruct g;
|
|
PreReadGameData(g, src.InputStream.get(), src.DataVersion);
|
|
if (!guid.IsEmpty())
|
|
return guid.CompareNoCase(g.guid) == 0;
|
|
return legacy_id == g.uniqueid;
|
|
}
|
|
|
|
static const SavegameDescription *loadDesc;
|
|
static bool TestGame(const String &filepath) {
|
|
return test_game_guid(filepath, loadDesc->GameGuid, loadDesc->LegacyID);
|
|
}
|
|
|
|
HSaveError load_game(const String &path, int slotNumber, bool &data_overwritten) {
|
|
data_overwritten = false;
|
|
_G(gameHasBeenRestored)++;
|
|
|
|
_G(oldeip) = _G(our_eip);
|
|
_G(our_eip) = 2050;
|
|
|
|
HSaveError err;
|
|
SavegameSource src;
|
|
SavegameDescription desc;
|
|
err = OpenSavegame(path, src, desc, kSvgDesc_EnvInfo);
|
|
|
|
// saved in incompatible enviroment
|
|
if (!err)
|
|
return err;
|
|
// CHECKME: is this color depth test still essential? if yes, is there possible workaround?
|
|
else if (desc.ColorDepth != _GP(game).GetColorDepth())
|
|
return new SavegameError(kSvgErr_DifferentColorDepth, String::FromFormat("Running: %d-bit, saved in: %d-bit.", _GP(game).GetColorDepth(), desc.ColorDepth));
|
|
|
|
// saved with different game file
|
|
// if savegame is modern enough then test game GUIDs
|
|
if (!desc.GameGuid.IsEmpty() || desc.LegacyID != 0) {
|
|
if (desc.GameGuid.Compare(_GP(game).guid) != 0 && desc.LegacyID != _GP(game).uniqueid) {
|
|
// Try to find wanted game's data using game id
|
|
loadDesc = &desc;
|
|
String gamefile = FindGameData(_GP(ResPaths).DataDir, TestGame);
|
|
|
|
if (Shared::File::TestReadFile(gamefile)) {
|
|
RunAGSGame(gamefile.GetCStr(), 0, 0);
|
|
_G(load_new_game_restore) = slotNumber;
|
|
return HSaveError::None();
|
|
}
|
|
return new SavegameError(kSvgErr_GameGuidMismatch);
|
|
}
|
|
}
|
|
// if it's old then do the stupid old-style filename test
|
|
// TODO: remove filename test after deprecating old saves
|
|
else if (desc.MainDataFilename.Compare(_GP(ResPaths).GamePak.Name)) {
|
|
String gamefile = Path::ConcatPaths(_GP(ResPaths).DataDir, desc.MainDataFilename);
|
|
if (Shared::File::TestReadFile(gamefile)) {
|
|
RunAGSGame(desc.MainDataFilename, 0, 0);
|
|
_G(load_new_game_restore) = slotNumber;
|
|
return HSaveError::None();
|
|
}
|
|
// if it does not exist, continue loading savedgame in current game, and pray for the best
|
|
Shared::Debug::Printf(kDbgMsg_Warn, "WARNING: the saved game '%s' references game file '%s' (title: '%s'), but it cannot be found in the current directory. Trying to restore in the running game instead.",
|
|
path.GetCStr(), desc.MainDataFilename.GetCStr(), desc.GameTitle.GetCStr());
|
|
}
|
|
|
|
// do the actual restore
|
|
err = RestoreGameState(src.InputStream.get(), src.Version);
|
|
data_overwritten = true;
|
|
if (!err)
|
|
return err;
|
|
src.InputStream.reset();
|
|
_G(our_eip) = _G(oldeip);
|
|
|
|
// ensure input state is reset
|
|
ags_clear_input_state();
|
|
// call "After Restore" event callback
|
|
run_on_event(GE_RESTORE_GAME, RuntimeScriptValue().SetInt32(slotNumber));
|
|
return HSaveError::None();
|
|
}
|
|
|
|
bool try_restore_save(int slot) {
|
|
return try_restore_save(get_save_game_path(slot), slot);
|
|
}
|
|
|
|
bool try_restore_save(const Shared::String &path, int slot) {
|
|
bool data_overwritten;
|
|
HSaveError err = load_game(path, slot, data_overwritten);
|
|
if (!err) {
|
|
String error = String::FromFormat("Unable to restore the saved game.\n%s",
|
|
err->FullMessage().GetCStr());
|
|
Debug::Printf(kDbgMsg_Error, "%s", error.GetCStr());
|
|
// currently AGS cannot properly revert to stable state if some of the
|
|
// game data was released or overwritten by the data from save file,
|
|
// this is why we tell engine to shutdown if that happened.
|
|
if (data_overwritten)
|
|
quitprintf("%s", error.GetCStr());
|
|
else
|
|
Display(error.GetCStr());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool is_in_cutscene() {
|
|
return _GP(play).in_cutscene > 0;
|
|
}
|
|
|
|
CutsceneSkipStyle get_cutscene_skipstyle() {
|
|
return static_cast<CutsceneSkipStyle>(_GP(play).in_cutscene);
|
|
}
|
|
|
|
void start_skipping_cutscene() {
|
|
_GP(play).fast_forward = 1;
|
|
// if a drop-down icon bar is up, remove it as it will pause the game
|
|
if (_G(ifacepopped) >= 0)
|
|
remove_popup_interface(_G(ifacepopped));
|
|
|
|
// if a text message is currently displayed, remove it
|
|
if (_GP(play).text_overlay_on > 0) {
|
|
remove_screen_overlay(_GP(play).text_overlay_on);
|
|
_GP(play).SetWaitSkipResult(SKIP_AUTOTIMER);
|
|
}
|
|
}
|
|
|
|
bool check_skip_cutscene_keypress(int kgn) {
|
|
|
|
CutsceneSkipStyle skip = get_cutscene_skipstyle();
|
|
if (skip == eSkipSceneAnyKey || skip == eSkipSceneKeyMouse ||
|
|
(kgn == eAGSKeyCodeEscape && (skip == eSkipSceneEscOnly || skip == eSkipSceneEscOrRMB))) {
|
|
start_skipping_cutscene();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool check_skip_cutscene_mclick(int mbut) {
|
|
CutsceneSkipStyle skip = get_cutscene_skipstyle();
|
|
if (skip == eSkipSceneMouse || skip == eSkipSceneKeyMouse ||
|
|
(mbut == kMouseRight && skip == eSkipSceneEscOrRMB)) {
|
|
start_skipping_cutscene();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Helper functions used by StartCutscene/EndCutscene, but also
|
|
// by SkipUntilCharacterStops
|
|
void initialize_skippable_cutscene() {
|
|
_GP(play).end_cutscene_music = -1;
|
|
}
|
|
|
|
void stop_fast_forwarding() {
|
|
// when the skipping of a cutscene comes to an end, update things
|
|
_GP(play).fast_forward = 0;
|
|
setpal();
|
|
if (_GP(play).end_cutscene_music >= 0)
|
|
newmusic(_GP(play).end_cutscene_music);
|
|
|
|
// Restore actual volume of sounds
|
|
for (int aa = 0; aa < TOTAL_AUDIO_CHANNELS; aa++) {
|
|
auto *ch = AudioChans::GetChannelIfPlaying(aa);
|
|
if (ch) {
|
|
ch->set_mute(false);
|
|
}
|
|
}
|
|
|
|
update_music_volume();
|
|
}
|
|
|
|
// allowHotspot0 defines whether Hotspot 0 returns LOCTYPE_HOTSPOT
|
|
// or whether it returns 0
|
|
int __GetLocationType(int xxx, int yyy, int allowHotspot0) {
|
|
_G(getloctype_index) = 0;
|
|
// If it's not in ProcessClick, then return 0 when over a GUI
|
|
if ((GetGUIAt(xxx, yyy) >= 0) && (_G(getloctype_throughgui) == 0))
|
|
return 0;
|
|
|
|
_G(getloctype_throughgui) = 0;
|
|
|
|
const int scrx = xxx;
|
|
const int scry = yyy;
|
|
VpPoint vpt = _GP(play).ScreenToRoomDivDown(xxx, yyy);
|
|
if (vpt.second < 0)
|
|
return 0;
|
|
xxx = vpt.first.X;
|
|
yyy = vpt.first.Y;
|
|
if ((xxx >= _GP(thisroom).Width) | (xxx < 0) | (yyy < 0) | (yyy >= _GP(thisroom).Height))
|
|
return 0;
|
|
|
|
// check characters, objects and walkbehinds, work out which is
|
|
// foremost visible to the player
|
|
int charat = is_pos_on_character(xxx, yyy);
|
|
int hsat = get_hotspot_at(xxx, yyy);
|
|
int objat = GetObjectIDAtScreen(scrx, scry);
|
|
|
|
data_to_game_coords(&xxx, &yyy);
|
|
|
|
int wbat = _GP(thisroom).WalkBehindMask->GetPixel(xxx, yyy);
|
|
|
|
if (wbat <= 0) wbat = 0;
|
|
else wbat = _G(croom)->walkbehind_base[wbat];
|
|
|
|
int winner = 0;
|
|
// if it's an Ignore Walkbehinds object, then ignore the walkbehind
|
|
if ((objat >= 0) && ((_G(objs)[objat].flags & OBJF_NOWALKBEHINDS) != 0))
|
|
wbat = 0;
|
|
if ((charat >= 0) && ((_GP(game).chars[charat].flags & CHF_NOWALKBEHINDS) != 0))
|
|
wbat = 0;
|
|
|
|
if ((charat >= 0) && (objat >= 0)) {
|
|
if ((wbat > _G(obj_lowest_yp)) && (wbat > _G(char_lowest_yp)))
|
|
winner = LOCTYPE_HOTSPOT;
|
|
else if (_G(obj_lowest_yp) > _G(char_lowest_yp))
|
|
winner = LOCTYPE_OBJ;
|
|
else
|
|
winner = LOCTYPE_CHAR;
|
|
} else if (charat >= 0) {
|
|
if (wbat > _G(char_lowest_yp))
|
|
winner = LOCTYPE_HOTSPOT;
|
|
else
|
|
winner = LOCTYPE_CHAR;
|
|
} else if (objat >= 0) {
|
|
if (wbat > _G(obj_lowest_yp))
|
|
winner = LOCTYPE_HOTSPOT;
|
|
else
|
|
winner = LOCTYPE_OBJ;
|
|
}
|
|
|
|
if (winner == 0) {
|
|
if (hsat >= 0)
|
|
winner = LOCTYPE_HOTSPOT;
|
|
}
|
|
|
|
if ((winner == LOCTYPE_HOTSPOT) && (!allowHotspot0) && (hsat == 0))
|
|
winner = 0;
|
|
|
|
if (winner == LOCTYPE_HOTSPOT)
|
|
_G(getloctype_index) = hsat;
|
|
else if (winner == LOCTYPE_CHAR)
|
|
_G(getloctype_index) = charat;
|
|
else if (winner == LOCTYPE_OBJ)
|
|
_G(getloctype_index) = objat;
|
|
|
|
return winner;
|
|
}
|
|
|
|
// Called whenever game looses input focus
|
|
void display_switch_out() {
|
|
Debug::Printf("Switching out from the game");
|
|
_G(switched_away) = true;
|
|
ags_clear_input_state();
|
|
// Always unlock mouse when switching out from the game
|
|
_GP(mouse).UnlockFromWindow();
|
|
}
|
|
|
|
// Called when game looses input focus and must pause until focus is returned
|
|
void display_switch_out_suspend() {
|
|
Debug::Printf("Suspending the game on switch out");
|
|
_G(switching_away_from_game)++;
|
|
_G(game_update_suspend)++;
|
|
display_switch_out();
|
|
|
|
_G(platform)->PauseApplication();
|
|
|
|
// TODO: find out if anything has to be done here for SDL backend
|
|
|
|
video_pause();
|
|
|
|
// Pause all the sounds
|
|
for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
|
|
auto *ch = AudioChans::GetChannelIfPlaying(i);
|
|
if (ch) {
|
|
ch->pause();
|
|
}
|
|
}
|
|
|
|
_G(switching_away_from_game)--;
|
|
}
|
|
|
|
// Called whenever game gets input focus
|
|
void display_switch_in() {
|
|
Debug::Printf("Switching back into the game");
|
|
ags_clear_input_state();
|
|
// If auto lock option is set, lock mouse to the game window
|
|
if (_GP(usetup).mouse_auto_lock && _GP(scsystem).windowed)
|
|
_GP(mouse).TryLockToWindow();
|
|
_G(switched_away) = false;
|
|
}
|
|
|
|
// Called when game gets input focus and must resume after pause
|
|
void display_switch_in_resume() {
|
|
Debug::Printf("Resuming the game on switch in");
|
|
display_switch_in();
|
|
|
|
// Resume all the sounds
|
|
for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
|
|
auto *ch = AudioChans::GetChannelIfPlaying(i);
|
|
if (ch) {
|
|
ch->resume();
|
|
}
|
|
}
|
|
video_resume();
|
|
|
|
// clear the screen if necessary
|
|
if (_G(gfxDriver) && _G(gfxDriver)->UsesMemoryBackBuffer())
|
|
_G(gfxDriver)->ClearRectangle(0, 0, _GP(game).GetGameRes().Width - 1, _GP(game).GetGameRes().Height - 1, nullptr);
|
|
|
|
// TODO: find out if anything has to be done here for SDL backend
|
|
|
|
_G(platform)->ResumeApplication();
|
|
_G(game_update_suspend)--;
|
|
}
|
|
|
|
void replace_tokens(const char *srcmes, char *destm, int maxlen) {
|
|
int indxdest = 0, indxsrc = 0;
|
|
const char *srcp;
|
|
char *destp;
|
|
while (srcmes[indxsrc] != 0) {
|
|
srcp = &srcmes[indxsrc];
|
|
destp = &destm[indxdest];
|
|
if ((strncmp(srcp, "@IN", 3) == 0) | (strncmp(srcp, "@GI", 3) == 0)) {
|
|
int tokentype = 0;
|
|
if (srcp[1] == 'I') tokentype = 1;
|
|
else tokentype = 2;
|
|
int inx = atoi(&srcp[3]);
|
|
srcp++;
|
|
indxsrc += 2;
|
|
while (srcp[0] != '@') {
|
|
if (srcp[0] == 0) quit("!Display: special token not terminated");
|
|
srcp++;
|
|
indxsrc++;
|
|
}
|
|
char tval[10];
|
|
if (tokentype == 1) {
|
|
if ((inx < 1) | (inx >= _GP(game).numinvitems))
|
|
quit("!Display: invalid inv item specified in @IN@");
|
|
snprintf(tval, sizeof(tval), "%d", _G(playerchar)->inv[inx]);
|
|
} else {
|
|
if ((inx < 0) | (inx >= MAXGSVALUES))
|
|
quit("!Display: invalid global int index speicifed in @GI@");
|
|
snprintf(tval, sizeof(tval), "%d", GetGlobalInt(inx));
|
|
}
|
|
snprintf(destp, maxlen, "%s", tval);
|
|
indxdest += strlen(tval);
|
|
} else {
|
|
destp[0] = srcp[0];
|
|
indxdest++;
|
|
indxsrc++;
|
|
}
|
|
if (indxdest >= maxlen - 3)
|
|
break;
|
|
}
|
|
destm[indxdest] = 0;
|
|
}
|
|
|
|
const char *get_global_message(int msnum) {
|
|
if (_GP(game).messages[msnum - 500] == nullptr)
|
|
return "";
|
|
return get_translation(_GP(game).messages[msnum - 500]);
|
|
}
|
|
|
|
void get_message_text(int msnum, char *buffer, char giveErr) {
|
|
int maxlen = 9999;
|
|
if (!giveErr)
|
|
maxlen = MAX_MAXSTRLEN;
|
|
|
|
if (msnum >= 500) {
|
|
|
|
if ((msnum >= MAXGLOBALMES + 500) || (_GP(game).messages[msnum - 500] == nullptr)) {
|
|
if (giveErr)
|
|
quit("!DisplayGlobalMessage: message does not exist");
|
|
buffer[0] = 0;
|
|
return;
|
|
}
|
|
buffer[0] = 0;
|
|
replace_tokens(get_translation(_GP(game).messages[msnum - 500]), buffer, maxlen);
|
|
return;
|
|
} else if (msnum < 0 || (size_t)msnum >= _GP(thisroom).MessageCount) {
|
|
if (giveErr)
|
|
quit("!DisplayMessage: Invalid message number to display");
|
|
buffer[0] = 0;
|
|
return;
|
|
}
|
|
|
|
buffer[0] = 0;
|
|
replace_tokens(get_translation(_GP(thisroom).Messages[msnum].GetCStr()), buffer, maxlen);
|
|
}
|
|
|
|
bool unserialize_audio_script_object(int index, const char *objectType, Stream *in, size_t data_sz) {
|
|
if (strcmp(objectType, "AudioChannel") == 0) {
|
|
_GP(ccDynamicAudio).Unserialize(index, in, data_sz);
|
|
} else if (strcmp(objectType, "AudioClip") == 0) {
|
|
_GP(ccDynamicAudioClip).Unserialize(index, in, data_sz);
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void game_sprite_updated(int sprnum) {
|
|
// update the shared texture (if exists)
|
|
_G(gfxDriver)->UpdateSharedDDB(sprnum, _GP(spriteset)[sprnum], (_GP(game).SpriteInfos[sprnum].Flags & SPF_ALPHACHANNEL) != 0, false);
|
|
|
|
// character and object draw caches
|
|
reset_objcache_for_sprite(sprnum, false);
|
|
|
|
// gui backgrounds
|
|
for (auto &gui : _GP(guis)) {
|
|
if (gui.BgImage == sprnum) {
|
|
gui.MarkChanged();
|
|
}
|
|
}
|
|
// gui buttons
|
|
for (auto &but : _GP(guibuts)) {
|
|
if (but.CurrentImage == sprnum) {
|
|
but.MarkChanged();
|
|
}
|
|
}
|
|
// gui sliders
|
|
for (auto &slider : _GP(guislider)) {
|
|
if ((slider.BgImage == sprnum) || (slider.HandleImage == sprnum)) {
|
|
slider.MarkChanged();
|
|
}
|
|
}
|
|
// overlays
|
|
for (auto &over : _GP(screenover)) {
|
|
if (over.GetSpriteNum() == sprnum)
|
|
over.MarkChanged();
|
|
}
|
|
}
|
|
|
|
void game_sprite_deleted(int sprnum) {
|
|
// clear from texture cache
|
|
_G(gfxDriver)->ClearSharedDDB(sprnum);
|
|
// character and object draw caches
|
|
reset_objcache_for_sprite(sprnum, true);
|
|
// room object graphics
|
|
if (_G(croom) != nullptr) {
|
|
for (size_t i = 0; i < (size_t)_G(croom)->numobj; ++i) {
|
|
if (_G(objs)[i].num == sprnum)
|
|
_G(objs)[i].num = 0;
|
|
}
|
|
}
|
|
// gui backgrounds
|
|
for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
|
|
if (_GP(guis)[i].BgImage == sprnum) {
|
|
_GP(guis)[i].BgImage = 0;
|
|
_GP(guis)[i].MarkChanged();
|
|
}
|
|
}
|
|
// gui buttons
|
|
for (auto &but : _GP(guibuts)) {
|
|
if (but.Image == sprnum)
|
|
but.Image = 0;
|
|
if (but.MouseOverImage == sprnum)
|
|
but.MouseOverImage = 0;
|
|
if (but.PushedImage == sprnum)
|
|
but.PushedImage = 0;
|
|
|
|
if (but.CurrentImage == sprnum) {
|
|
but.CurrentImage = 0;
|
|
but.MarkChanged();
|
|
}
|
|
}
|
|
// gui sliders
|
|
for (auto &slider : _GP(guislider)) {
|
|
if ((slider.BgImage == sprnum) || (slider.HandleImage == sprnum))
|
|
slider.MarkChanged();
|
|
if (slider.BgImage == sprnum)
|
|
slider.BgImage = 0;
|
|
if (slider.HandleImage == sprnum)
|
|
slider.HandleImage = 0;
|
|
}
|
|
// views
|
|
for (size_t v = 0; v < (size_t)_GP(game).numviews; ++v) {
|
|
for (size_t l = 0; l < (size_t)_GP(views)[v].numLoops; ++l) {
|
|
for (size_t f = 0; f < (size_t)_GP(views)[v].loops[l].numFrames; ++f) {
|
|
if (_GP(views)[v].loops[l].frames[f].pic == sprnum)
|
|
_GP(views)[v].loops[l].frames[f].pic = 0;
|
|
}
|
|
}
|
|
}
|
|
// overlays
|
|
for (auto &over : _GP(screenover)) {
|
|
if (over.GetSpriteNum() == sprnum)
|
|
over.SetSpriteNum(0);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Script API Functions
|
|
//
|
|
//=============================================================================
|
|
|
|
// int (int audioType);
|
|
RuntimeScriptValue Sc_Game_IsAudioPlaying(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_PINT(Game_IsAudioPlaying);
|
|
}
|
|
|
|
// void (int audioType, int volumeDrop)
|
|
RuntimeScriptValue Sc_Game_SetAudioTypeSpeechVolumeDrop(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT2(Game_SetAudioTypeSpeechVolumeDrop);
|
|
}
|
|
|
|
// void (int audioType, int volume, int changeType)
|
|
RuntimeScriptValue Sc_Game_SetAudioTypeVolume(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT3(Game_SetAudioTypeVolume);
|
|
}
|
|
|
|
// void (int audioType)
|
|
RuntimeScriptValue Sc_Game_StopAudio(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(Game_StopAudio);
|
|
}
|
|
|
|
// int (const char *newFilename)
|
|
RuntimeScriptValue Sc_Game_ChangeTranslation(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_POBJ(Game_ChangeTranslation, const char);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_ChangeSpeechVox(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_BOOL_POBJ(Game_ChangeSpeechVox, const char);
|
|
}
|
|
|
|
// int (const char *token)
|
|
RuntimeScriptValue Sc_Game_DoOnceOnly(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_POBJ(Game_DoOnceOnly, const char);
|
|
}
|
|
|
|
// int (int red, int grn, int blu)
|
|
RuntimeScriptValue Sc_Game_GetColorFromRGB(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_PINT3(Game_GetColorFromRGB);
|
|
}
|
|
|
|
// int (int viewNumber, int loopNumber)
|
|
RuntimeScriptValue Sc_Game_GetFrameCountForLoop(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_PINT2(Game_GetFrameCountForLoop);
|
|
}
|
|
|
|
// const char* (int x, int y)
|
|
RuntimeScriptValue Sc_Game_GetLocationName(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ_PINT2(const char, _GP(myScriptStringImpl), Game_GetLocationName);
|
|
}
|
|
|
|
// int (int viewNumber)
|
|
RuntimeScriptValue Sc_Game_GetLoopCountForView(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_PINT(Game_GetLoopCountForView);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetMODPattern(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetMODPattern);
|
|
}
|
|
|
|
// int (int viewNumber, int loopNumber)
|
|
RuntimeScriptValue Sc_Game_GetRunNextSettingForLoop(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_PINT2(Game_GetRunNextSettingForLoop);
|
|
}
|
|
|
|
// const char* (int slnum)
|
|
RuntimeScriptValue Sc_Game_GetSaveSlotDescription(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Game_GetSaveSlotDescription);
|
|
}
|
|
|
|
// ScriptViewFrame* (int viewNumber, int loopNumber, int frame)
|
|
RuntimeScriptValue Sc_Game_GetViewFrame(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJAUTO_PINT3(ScriptViewFrame, Game_GetViewFrame);
|
|
}
|
|
|
|
// const char* (const char *msg)
|
|
RuntimeScriptValue Sc_Game_InputBox(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ_POBJ(const char, _GP(myScriptStringImpl), Game_InputBox, const char);
|
|
}
|
|
|
|
// int (const char *newFolder)
|
|
RuntimeScriptValue Sc_Game_SetSaveGameDirectory(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_POBJ(Game_SetSaveGameDirectory, const char);
|
|
}
|
|
|
|
// void (int evenAmbient);
|
|
RuntimeScriptValue Sc_StopAllSounds(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(StopAllSounds);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetCharacterCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetCharacterCount);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetDialogCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetDialogCount);
|
|
}
|
|
|
|
// const char *()
|
|
RuntimeScriptValue Sc_Game_GetFileName(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ(const char, _GP(myScriptStringImpl), Game_GetFileName);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetFontCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetFontCount);
|
|
}
|
|
|
|
// const char* (int index)
|
|
RuntimeScriptValue Sc_Game_GetGlobalMessages(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Game_GetGlobalMessages);
|
|
}
|
|
|
|
// const char* (int index)
|
|
RuntimeScriptValue Sc_Game_GetGlobalStrings(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Game_GetGlobalStrings);
|
|
}
|
|
|
|
// void (int index, char *newval);
|
|
RuntimeScriptValue Sc_SetGlobalString(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT_POBJ(SetGlobalString, const char);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetGUICount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetGUICount);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetIgnoreUserInputAfterTextTimeoutMs);
|
|
}
|
|
|
|
// void (int newValueMs)
|
|
RuntimeScriptValue Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(Game_SetIgnoreUserInputAfterTextTimeoutMs);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetInSkippableCutscene(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetInSkippableCutscene);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetInventoryItemCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetInventoryItemCount);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetMinimumTextDisplayTimeMs(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetMinimumTextDisplayTimeMs);
|
|
}
|
|
|
|
// void (int newTextMinTime)
|
|
RuntimeScriptValue Sc_Game_SetMinimumTextDisplayTimeMs(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(Game_SetMinimumTextDisplayTimeMs);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetMouseCursorCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetMouseCursorCount);
|
|
}
|
|
|
|
// const char *()
|
|
RuntimeScriptValue Sc_Game_GetName(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ(const char, _GP(myScriptStringImpl), Game_GetName);
|
|
}
|
|
|
|
// void (const char *newName)
|
|
RuntimeScriptValue Sc_Game_SetName(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_POBJ(Game_SetName, const char);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetNormalFont(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetNormalFont);
|
|
}
|
|
|
|
// void (int fontnum);
|
|
RuntimeScriptValue Sc_SetNormalFont(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(SetNormalFont);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetSkippingCutscene(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetSkippingCutscene);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetSpeechFont(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetSpeechFont);
|
|
}
|
|
|
|
// void (int fontnum);
|
|
RuntimeScriptValue Sc_SetSpeechFont(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(SetSpeechFont);
|
|
}
|
|
|
|
// int (int spriteNum)
|
|
RuntimeScriptValue Sc_Game_GetSpriteWidth(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_PINT(Game_GetSpriteWidth);
|
|
}
|
|
|
|
// int (int spriteNum)
|
|
RuntimeScriptValue Sc_Game_GetSpriteHeight(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_PINT(Game_GetSpriteHeight);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetTextReadingSpeed(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetTextReadingSpeed);
|
|
}
|
|
|
|
// void (int newTextSpeed)
|
|
RuntimeScriptValue Sc_Game_SetTextReadingSpeed(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(Game_SetTextReadingSpeed);
|
|
}
|
|
|
|
// const char* ()
|
|
RuntimeScriptValue Sc_Game_GetTranslationFilename(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ(const char, _GP(myScriptStringImpl), Game_GetTranslationFilename);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_GetSpeechVoxFilename(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ(const char, _GP(myScriptStringImpl), Game_GetSpeechVoxFilename);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetUseNativeCoordinates(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetUseNativeCoordinates);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Game_GetViewCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetViewCount);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_GetAudioClipCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_VARGET_INT(_GP(game).audioClips.size());
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_GetAudioClip(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJ_PINT(ScriptAudioClip, _GP(ccDynamicAudioClip), Game_GetAudioClip);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_IsPluginLoaded(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_BOOL_POBJ(pl_is_plugin_loaded, const char);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_PlayVoiceClip(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJ_POBJ_PINT_PBOOL(ScriptAudioChannel, _GP(ccDynamicAudio), PlayVoiceClip, CharacterInfo);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_GetCamera(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJAUTO(ScriptCamera, Game_GetCamera);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_GetCameraCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_GetCameraCount);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_GetAnyCamera(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJAUTO_PINT(ScriptCamera, Game_GetAnyCamera);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_SimulateKeyPress(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT(Game_SimulateKeyPress);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Game_BlockingWaitSkipped(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Game_BlockingWaitSkipped);
|
|
}
|
|
|
|
void RegisterGameAPI() {
|
|
ccAddExternalStaticFunction("Game::IsAudioPlaying^1", Sc_Game_IsAudioPlaying);
|
|
ccAddExternalStaticFunction("Game::SetAudioTypeSpeechVolumeDrop^2", Sc_Game_SetAudioTypeSpeechVolumeDrop);
|
|
ccAddExternalStaticFunction("Game::SetAudioTypeVolume^3", Sc_Game_SetAudioTypeVolume);
|
|
ccAddExternalStaticFunction("Game::StopAudio^1", Sc_Game_StopAudio);
|
|
ccAddExternalStaticFunction("Game::ChangeTranslation^1", Sc_Game_ChangeTranslation);
|
|
ccAddExternalStaticFunction("Game::DoOnceOnly^1", Sc_Game_DoOnceOnly);
|
|
ccAddExternalStaticFunction("Game::GetColorFromRGB^3", Sc_Game_GetColorFromRGB);
|
|
ccAddExternalStaticFunction("Game::GetFrameCountForLoop^2", Sc_Game_GetFrameCountForLoop);
|
|
ccAddExternalStaticFunction("Game::GetLocationName^2", Sc_Game_GetLocationName);
|
|
ccAddExternalStaticFunction("Game::GetLoopCountForView^1", Sc_Game_GetLoopCountForView);
|
|
ccAddExternalStaticFunction("Game::GetMODPattern^0", Sc_Game_GetMODPattern);
|
|
ccAddExternalStaticFunction("Game::GetRunNextSettingForLoop^2", Sc_Game_GetRunNextSettingForLoop);
|
|
ccAddExternalStaticFunction("Game::GetSaveSlotDescription^1", Sc_Game_GetSaveSlotDescription);
|
|
ccAddExternalStaticFunction("Game::GetViewFrame^3", Sc_Game_GetViewFrame);
|
|
ccAddExternalStaticFunction("Game::InputBox^1", Sc_Game_InputBox);
|
|
ccAddExternalStaticFunction("Game::SetSaveGameDirectory^1", Sc_Game_SetSaveGameDirectory);
|
|
ccAddExternalStaticFunction("Game::StopSound^1", Sc_StopAllSounds);
|
|
ccAddExternalStaticFunction("Game::get_CharacterCount", Sc_Game_GetCharacterCount);
|
|
ccAddExternalStaticFunction("Game::get_DialogCount", Sc_Game_GetDialogCount);
|
|
ccAddExternalStaticFunction("Game::get_FileName", Sc_Game_GetFileName);
|
|
ccAddExternalStaticFunction("Game::get_FontCount", Sc_Game_GetFontCount);
|
|
ccAddExternalStaticFunction("Game::geti_GlobalMessages", Sc_Game_GetGlobalMessages);
|
|
ccAddExternalStaticFunction("Game::geti_GlobalStrings", Sc_Game_GetGlobalStrings);
|
|
ccAddExternalStaticFunction("Game::seti_GlobalStrings", Sc_SetGlobalString);
|
|
ccAddExternalStaticFunction("Game::get_GUICount", Sc_Game_GetGUICount);
|
|
ccAddExternalStaticFunction("Game::get_IgnoreUserInputAfterTextTimeoutMs", Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs);
|
|
ccAddExternalStaticFunction("Game::set_IgnoreUserInputAfterTextTimeoutMs", Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs);
|
|
ccAddExternalStaticFunction("Game::get_InSkippableCutscene", Sc_Game_GetInSkippableCutscene);
|
|
ccAddExternalStaticFunction("Game::get_InventoryItemCount", Sc_Game_GetInventoryItemCount);
|
|
ccAddExternalStaticFunction("Game::get_MinimumTextDisplayTimeMs", Sc_Game_GetMinimumTextDisplayTimeMs);
|
|
ccAddExternalStaticFunction("Game::set_MinimumTextDisplayTimeMs", Sc_Game_SetMinimumTextDisplayTimeMs);
|
|
ccAddExternalStaticFunction("Game::get_MouseCursorCount", Sc_Game_GetMouseCursorCount);
|
|
ccAddExternalStaticFunction("Game::get_Name", Sc_Game_GetName);
|
|
ccAddExternalStaticFunction("Game::set_Name", Sc_Game_SetName);
|
|
ccAddExternalStaticFunction("Game::get_NormalFont", Sc_Game_GetNormalFont);
|
|
ccAddExternalStaticFunction("Game::set_NormalFont", Sc_SetNormalFont);
|
|
ccAddExternalStaticFunction("Game::get_SkippingCutscene", Sc_Game_GetSkippingCutscene);
|
|
ccAddExternalStaticFunction("Game::get_SpeechFont", Sc_Game_GetSpeechFont);
|
|
ccAddExternalStaticFunction("Game::set_SpeechFont", Sc_SetSpeechFont);
|
|
ccAddExternalStaticFunction("Game::geti_SpriteWidth", Sc_Game_GetSpriteWidth);
|
|
ccAddExternalStaticFunction("Game::geti_SpriteHeight", Sc_Game_GetSpriteHeight);
|
|
ccAddExternalStaticFunction("Game::get_TextReadingSpeed", Sc_Game_GetTextReadingSpeed);
|
|
ccAddExternalStaticFunction("Game::set_TextReadingSpeed", Sc_Game_SetTextReadingSpeed);
|
|
ccAddExternalStaticFunction("Game::get_TranslationFilename", Sc_Game_GetTranslationFilename);
|
|
ccAddExternalStaticFunction("Game::get_UseNativeCoordinates", Sc_Game_GetUseNativeCoordinates);
|
|
ccAddExternalStaticFunction("Game::get_ViewCount", Sc_Game_GetViewCount);
|
|
ccAddExternalStaticFunction("Game::get_AudioClipCount", Sc_Game_GetAudioClipCount);
|
|
ccAddExternalStaticFunction("Game::geti_AudioClips", Sc_Game_GetAudioClip);
|
|
ccAddExternalStaticFunction("Game::IsPluginLoaded", Sc_Game_IsPluginLoaded);
|
|
ccAddExternalStaticFunction("Game::ChangeSpeechVox", Sc_Game_ChangeSpeechVox);
|
|
ccAddExternalStaticFunction("Game::PlayVoiceClip", Sc_Game_PlayVoiceClip);
|
|
ccAddExternalStaticFunction("Game::SimulateKeyPress", Sc_Game_SimulateKeyPress);
|
|
ccAddExternalStaticFunction("Game::get_BlockingWaitSkipped", Sc_Game_BlockingWaitSkipped);
|
|
ccAddExternalStaticFunction("Game::get_SpeechVoxFilename", Sc_Game_GetSpeechVoxFilename);
|
|
|
|
ccAddExternalStaticFunction("Game::get_Camera", Sc_Game_GetCamera);
|
|
ccAddExternalStaticFunction("Game::get_CameraCount", Sc_Game_GetCameraCount);
|
|
ccAddExternalStaticFunction("Game::geti_Cameras", Sc_Game_GetAnyCamera);
|
|
}
|
|
|
|
void RegisterStaticObjects() {
|
|
ccAddExternalStaticObject("game", &_GP(play), &_GP(GameStaticManager));
|
|
ccAddExternalStaticObject("gs_globals", &_GP(play).globalvars[0], &_GP(GlobalStaticManager));
|
|
ccAddExternalStaticObject("mouse", &_GP(scmouse), &_GP(GlobalStaticManager));
|
|
ccAddExternalStaticObject("palette", &_G(palette)[0], &_GP(GlobalStaticManager));
|
|
ccAddExternalStaticObject("system", &_GP(scsystem), &_GP(GlobalStaticManager));
|
|
ccAddExternalStaticObject("savegameindex", &_GP(play).filenumbers[0], &_GP(GlobalStaticManager));
|
|
}
|
|
|
|
} // namespace AGS3
|