1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/memstream.h"
24 #include "ags/engine/ac/game.h"
25 #include "ags/shared/ac/common.h"
26 #include "ags/shared/ac/view.h"
27 #include "ags/engine/ac/audio_channel.h"
28 #include "ags/engine/ac/character.h"
29 #include "ags/engine/ac/character_cache.h"
30 #include "ags/shared/ac/dialog_topic.h"
31 #include "ags/engine/ac/draw.h"
32 #include "ags/engine/ac/dynamic_sprite.h"
33 #include "ags/engine/ac/event.h"
34 #include "ags/engine/ac/game_setup.h"
35 #include "ags/shared/ac/game_setup_struct.h"
36 #include "ags/engine/ac/game_state.h"
37 #include "ags/engine/ac/global_audio.h"
38 #include "ags/engine/ac/global_display.h"
39 #include "ags/engine/ac/global_game.h"
40 #include "ags/engine/ac/global_gui.h"
41 #include "ags/engine/ac/global_object.h"
42 #include "ags/engine/ac/global_translation.h"
43 #include "ags/engine/ac/gui.h"
44 #include "ags/engine/ac/hotspot.h"
45 #include "ags/shared/ac/keycode.h"
46 #include "ags/engine/ac/lip_sync.h"
47 #include "ags/engine/ac/mouse.h"
48 #include "ags/engine/ac/object_cache.h"
49 #include "ags/engine/ac/overlay.h"
50 #include "ags/engine/ac/path_helper.h"
51 #include "ags/engine/ac/sys_events.h"
52 #include "ags/engine/ac/rich_game_media.h"
53 #include "ags/engine/ac/room_status.h"
54 #include "ags/shared/ac/sprite_cache.h"
55 #include "ags/engine/ac/string.h"
56 #include "ags/engine/ac/translation.h"
57 #include "ags/engine/ac/dynobj/all_dynamic_classes.h"
58 #include "ags/engine/ac/dynobj/all_script_classes.h"
59 #include "ags/engine/ac/dynobj/script_camera.h"
60 #include "ags/engine/debugging/debug_log.h"
61 #include "ags/engine/debugging/debugger.h"
62 #include "ags/shared/debugging/out.h"
63 #include "ags/engine/device/mouse_w32.h"
64 #include "ags/shared/font/fonts.h"
65 #include "ags/engine/game/savegame.h"
66 #include "ags/shared/gfx/bitmap.h"
67 #include "ags/engine/gfx/graphics_driver.h"
68 #include "ags/shared/gui/gui_button.h"
69 #include "ags/engine/gui/gui_dialog.h"
70 #include "ags/engine/main/engine.h"
71 #include "ags/engine/media/audio/audio_system.h"
72 #include "ags/engine/platform/base/ags_platform_driver.h"
73 #include "ags/engine/platform/base/sys_main.h"
74 #include "ags/plugins/plugin_engine.h"
75 #include "ags/engine/script/script.h"
76 #include "ags/engine/script/script_runtime.h"
77 #include "ags/shared/util/directory.h"
78 #include "ags/shared/util/file.h"
79 #include "ags/shared/util/path.h"
80
81 #include "ags/shared/debugging/out.h"
82 #include "ags/engine/script/script_api.h"
83 #include "ags/engine/script/script_runtime.h"
84 #include "ags/ags.h"
85 #include "ags/globals.h"
86
87 namespace AGS3 {
88
89 using namespace AGS::Shared;
90 using namespace AGS::Engine;
91
92 //=============================================================================
93 // Audio
94 //=============================================================================
95
Game_StopAudio(int audioType)96 void Game_StopAudio(int audioType) {
97 if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
98 quitprintf("!Game.StopAudio: invalid audio type %d", audioType);
99 int aa;
100
101 for (aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
102 if (audioType == SCR_NO_VALUE) {
103 stop_or_fade_out_channel(aa);
104 } else {
105 ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
106 if ((clip != nullptr) && (clip->type == audioType))
107 stop_or_fade_out_channel(aa);
108 }
109 }
110
111 remove_clips_of_type_from_queue(audioType);
112 }
113
Game_IsAudioPlaying(int audioType)114 int Game_IsAudioPlaying(int audioType) {
115 if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
116 quitprintf("!Game.IsAudioPlaying: invalid audio type %d", audioType);
117
118 if (_GP(play).fast_forward)
119 return 0;
120
121 for (int aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
122 ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
123 if (clip != nullptr) {
124 if ((clip->type == audioType) || (audioType == SCR_NO_VALUE)) {
125 return 1;
126 }
127 }
128 }
129 return 0;
130 }
131
Game_SetAudioTypeSpeechVolumeDrop(int audioType,int volumeDrop)132 void Game_SetAudioTypeSpeechVolumeDrop(int audioType, int volumeDrop) {
133 if ((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size()))
134 quitprintf("!Game.SetAudioTypeVolume: invalid audio type: %d", audioType);
135
136 Debug::Printf("Game.SetAudioTypeSpeechVolumeDrop: type: %d, drop: %d", audioType, volumeDrop);
137 _GP(game).audioClipTypes[audioType].volume_reduction_while_speech_playing = volumeDrop;
138 update_volume_drop_if_voiceover();
139 }
140
Game_SetAudioTypeVolume(int audioType,int volume,int changeType)141 void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
142 if ((volume < 0) || (volume > 100))
143 quitprintf("!Game.SetAudioTypeVolume: volume %d is not between 0..100", volume);
144 if ((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size()))
145 quitprintf("!Game.SetAudioTypeVolume: invalid audio type: %d", audioType);
146
147 Debug::Printf("Game.SetAudioTypeVolume: type: %d, volume: %d, change: %d", audioType, volume, changeType);
148 if ((changeType == VOL_CHANGEEXISTING) ||
149 (changeType == VOL_BOTH)) {
150 AudioChannelsLock lock;
151 for (int aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
152 ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
153 if ((clip != nullptr) && (clip->type == audioType)) {
154 auto *ch = lock.GetChannel(aa);
155 if (ch)
156 ch->set_volume_percent(volume);
157 }
158 }
159 }
160
161 if ((changeType == VOL_SETFUTUREDEFAULT) ||
162 (changeType == VOL_BOTH)) {
163 _GP(play).default_audio_type_volumes[audioType] = volume;
164
165 // update queued clip volumes
166 update_queued_clips_volume(audioType, volume);
167 }
168
169 }
170
Game_GetMODPattern()171 int Game_GetMODPattern() {
172 if (_G(current_music_type) != MUS_MOD)
173 return -1;
174 AudioChannelsLock lock;
175 auto *music_ch = lock.GetChannelIfPlaying(SCHAN_MUSIC);
176 return music_ch ? music_ch->get_pos() : -1;
177 }
178
179 //=============================================================================
180 // ---
181 //=============================================================================
182
Game_GetDialogCount()183 int Game_GetDialogCount() {
184 return _GP(game).numdialog;
185 }
186
set_debug_mode(bool on)187 void set_debug_mode(bool on) {
188 _GP(play).debug_mode = on ? 1 : 0;
189 debug_set_console(on);
190 }
191
set_game_speed(int new_fps)192 void set_game_speed(int new_fps) {
193 _G(frames_per_second) = new_fps;
194 if (!isTimerFpsMaxed()) // if in maxed mode, don't update timer for now
195 setTimerFps(new_fps);
196 }
197
setup_for_dialog()198 void setup_for_dialog() {
199 _G(cbuttfont) = _GP(play).normal_font;
200 _G(acdialog_font) = _GP(play).normal_font;
201 if (!_GP(play).mouse_cursor_hidden)
202 ags_domouse(DOMOUSE_ENABLE);
203 _G(oldmouse) = _G(cur_cursor);
204 set_mouse_cursor(CURS_ARROW);
205 }
restore_after_dialog()206 void restore_after_dialog() {
207 set_mouse_cursor(_G(oldmouse));
208 if (!_GP(play).mouse_cursor_hidden)
209 ags_domouse(DOMOUSE_DISABLE);
210 invalidate_screen();
211 }
212
213
214
get_save_game_directory()215 String get_save_game_directory() {
216 return _G(saveGameDirectory);
217 }
218
get_save_game_suffix()219 String get_save_game_suffix() {
220 return _G(saveGameSuffix);
221 }
222
set_save_game_suffix(const String & suffix)223 void set_save_game_suffix(const String &suffix) {
224 _G(saveGameSuffix) = suffix;
225 }
226
227 #if !AGS_PLATFORM_SCUMMVM
get_save_game_filename(int slotNum)228 String get_save_game_filename(int slotNum) {
229 return String::FromFormat("agssave.%03d%s", slotNum, _G(saveGameSuffix).GetCStr());
230 }
231 #endif
232
get_save_game_path(int slotNum)233 String get_save_game_path(int slotNum) {
234 #if AGS_PLATFORM_SCUMMVM
235 return Common::String::format("%s%s", SAVE_FOLDER_PREFIX,
236 ::AGS::g_vm->getSaveStateName(slotNum).c_str());
237 #else
238 return Path::ConcatPaths(_G(saveGameDirectory), get_save_game_filename(slotNum));
239 #endif
240 }
241
242 #if !AGS_PLATFORM_SCUMMVM
243 // Convert a path possibly containing path tags into acceptable save path
MakeSaveGameDir(const String & newFolder,FSLocation & fsloc)244 bool MakeSaveGameDir(const String &newFolder, FSLocation &fsloc) {
245 fsloc = FSLocation();
246 // don't allow absolute paths
247 if (!Path::IsRelativePath(newFolder))
248 return false;
249
250 String base_dir;
251 String sub_dir;
252
253 if (newFolder.CompareLeft(UserSavedgamesRootToken) == 0) {
254 // IMPORTANT: for compatibility reasons we support both cases:
255 // when token is followed by the path separator and when it is not, in which case it's assumed.
256 if (saveGameParent.IsEmpty()) {
257 base_dir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
258 sub_dir = newFolder.Mid(UserSavedgamesRootToken.GetLength());
259 } else {
260 // If there is a custom save parent directory, then replace
261 // not only root token, but also first subdirectory after the token
262 base_dir = saveGameParent;
263 sub_dir = Path::ConcatPaths(".", newFolder.Mid(UserSavedgamesRootToken.GetLength()));
264 sub_dir.ClipSection('/', 0, 1); // TODO: Path helper function for this?
265 }
266 fsloc = FSLocation(base_dir, sub_dir);
267 } else {
268 // Convert the path relative to installation folder into path relative to the
269 // safe save path with default name
270 if (saveGameParent.IsEmpty()) {
271 base_dir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
272 sub_dir = Path::ConcatPaths(game.saveGameFolderName, newFolder);
273 } else {
274 base_dir = saveGameParent;
275 sub_dir = newFolder;
276 }
277 fsloc = FSLocation(base_dir, sub_dir);
278 // For games made in the safe-path-aware versions of AGS, report a warning
279 if (game.options[OPT_SAFEFILEPATHS]) {
280 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'",
281 newFolder.GetCStr(), fsloc.FullDir.GetCStr());
282 }
283 }
284 return true;
285 }
286 #endif
287
SetCustomSaveParent(const String & path)288 bool SetCustomSaveParent(const String &path) {
289 if (SetSaveGameDirectoryPath(path, true)) {
290 _G(saveGameParent) = path;
291 return true;
292 }
293 return false;
294 }
295
SetSaveGameDirectoryPath(const String & newFolder,bool explicit_path)296 bool SetSaveGameDirectoryPath(const String &newFolder, bool explicit_path) {
297 #if AGS_PLATFORM_SCUMMVM
298 return false;
299 #else
300 String newFolder = new_dir.IsEmpty() ? "." : new_dir;
301 String newSaveGameDir;
302 if (explicit_path) {
303 newSaveGameDir = PathFromInstallDir(newFolder);
304 if (!Directory::CreateDirectory(newSaveGameDir))
305 return false;
306 } else {
307 FSLocation fsloc;
308 if (!MakeSaveGameDir(newFolder, fsloc))
309 return false;
310 if (!Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.SubDir)) {
311 debug_script_warn("SetSaveGameDirectory: failed to create all subdirectories: %s", fsloc.FullDir.GetCStr());
312 return false;
313 }
314 newSaveGameDir = fsloc.FullDir;
315 }
316
317 String newFolderTempFile = Path::ConcatPaths(newSaveGameDir, "agstmp.tmp");
318 if (!File::TestCreateFile(newFolderTempFile))
319 return false;
320
321 // copy the Restart Game file, if applicable
322 String restartGamePath = Path::ConcatPaths(saveGameDirectory, get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
323 Stream *restartGameFile = File::OpenFileRead(restartGamePath);
324 if (restartGameFile != nullptr) {
325 long fileSize = restartGameFile->GetLength();
326 char *mbuffer = (char *)malloc(fileSize);
327 restartGameFile->Read(mbuffer, fileSize);
328 delete restartGameFile;
329
330 restartGamePath = Path::ConcatPaths(newSaveGameDir, get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
331 restartGameFile = File::CreateFile(restartGamePath);
332 restartGameFile->Write(mbuffer, fileSize);
333 delete restartGameFile;
334 free(mbuffer);
335 }
336
337 saveGameDirectory = newSaveGameDir;
338 return true;
339 #endif
340 }
341
Game_SetSaveGameDirectory(const String & newFolder)342 int Game_SetSaveGameDirectory(const String &newFolder) {
343 return SetSaveGameDirectoryPath(newFolder, false) ? 1 : 0;
344 }
345
Game_GetSaveSlotDescription(int slnum)346 const char *Game_GetSaveSlotDescription(int slnum) {
347 String description;
348 if (read_savedgame_description(get_save_game_path(slnum), description)) {
349 return CreateNewScriptString(description.GetCStr());
350 }
351 return nullptr;
352 }
353
354
restore_game_dialog()355 void restore_game_dialog() {
356 can_run_delayed_command();
357 if (_GP(thisroom).Options.SaveLoadDisabled == 1) {
358 DisplayMessage(983);
359 return;
360 }
361 if (_G(inside_script)) {
362 _G(curscript)->queue_action(ePSARestoreGameDialog, 0, "RestoreGameDialog");
363 return;
364 }
365 setup_for_dialog();
366 int toload = loadgamedialog();
367 restore_after_dialog();
368 if (toload >= 0) {
369 try_restore_save(toload);
370 }
371 }
372
save_game_dialog()373 void save_game_dialog() {
374 if (_GP(thisroom).Options.SaveLoadDisabled == 1) {
375 DisplayMessage(983);
376 return;
377 }
378 if (_G(inside_script)) {
379 _G(curscript)->queue_action(ePSASaveGameDialog, 0, "SaveGameDialog");
380 return;
381 }
382 setup_for_dialog();
383 int toload = savegamedialog();
384 restore_after_dialog();
385 if (toload >= 0)
386 save_game(toload, get_gui_dialog_buffer());
387 }
388
free_do_once_tokens()389 void free_do_once_tokens() {
390 _GP(play).do_once_tokens.resize(0);
391 }
392
393
394 // Free all the memory associated with the game
395 // TODO: call this when exiting the game (currently only called in RunAGSGame)
unload_game_file()396 void unload_game_file() {
397 close_translation();
398
399 _GP(play).FreeViewportsAndCameras();
400
401 _GP(characterScriptObjNames).clear();
402 free(_G(charextra));
403 free(_G(mls));
404 free(_G(actsps));
405 free(_G(actspsbmp));
406 free(_G(actspswb));
407 free(_G(actspswbbmp));
408 free(_G(actspswbcache));
409
410 if ((_G(gameinst) != nullptr) && (_G(gameinst)->pc != 0)) {
411 quit("Error: unload_game called while script still running");
412 } else {
413 delete _G(gameinstFork);
414 delete _G(gameinst);
415 _G(gameinstFork) = nullptr;
416 _G(gameinst) = nullptr;
417 }
418
419 _GP(gamescript).reset();
420
421 if ((_G(dialogScriptsInst) != nullptr) && (_G(dialogScriptsInst)->pc != 0)) {
422 quit("Error: unload_game called while dialog script still running");
423 } else if (_G(dialogScriptsInst) != nullptr) {
424 delete _G(dialogScriptsInst);
425 _G(dialogScriptsInst) = nullptr;
426 }
427
428 _GP(dialogScriptsScript).reset();
429
430 for (int i = 0; i < _G(numScriptModules); ++i) {
431 delete _GP(moduleInstFork)[i];
432 delete _GP(moduleInst)[i];
433 _GP(scriptModules)[i].reset();
434 }
435
436 _GP(moduleInstFork).resize(0);
437 _GP(moduleInst).resize(0);
438 _GP(scriptModules).resize(0);
439 _GP(repExecAlways).moduleHasFunction.resize(0);
440 _GP(lateRepExecAlways).moduleHasFunction.resize(0);
441 _GP(getDialogOptionsDimensionsFunc).moduleHasFunction.resize(0);
442 _GP(renderDialogOptionsFunc).moduleHasFunction.resize(0);
443 _GP(getDialogOptionUnderCursorFunc).moduleHasFunction.resize(0);
444 _GP(runDialogOptionMouseClickHandlerFunc).moduleHasFunction.resize(0);
445 _GP(runDialogOptionKeyPressHandlerFunc).moduleHasFunction.resize(0);
446 _GP(runDialogOptionRepExecFunc).moduleHasFunction.resize(0);
447 _G(numScriptModules) = 0;
448
449 free(_G(views));
450 _G(views) = nullptr;
451
452 free(_G(charcache));
453 _G(charcache) = nullptr;
454
455 if (_G(splipsync) != nullptr) {
456 for (int i = 0; i < _G(numLipLines); ++i) {
457 free(_G(splipsync)[i].endtimeoffs);
458 free(_G(splipsync)[i].frame);
459 }
460 free(_G(splipsync));
461 _G(splipsync) = nullptr;
462 _G(numLipLines) = 0;
463 _G(curLipLine) = -1;
464 }
465
466 for (int i = 0; i < _GP(game).numdialog; ++i) {
467 if (_G(dialog)[i].optionscripts != nullptr)
468 free(_G(dialog)[i].optionscripts);
469 _G(dialog)[i].optionscripts = nullptr;
470 }
471 free(_G(dialog));
472 _G(dialog) = nullptr;
473 delete[] _G(scrDialog);
474 _G(scrDialog) = nullptr;
475
476 for (int i = 0; i < _GP(game).numgui; ++i) {
477 free(_G(guibg)[i]);
478 _G(guibg)[i] = nullptr;
479 }
480
481 _GP(guiScriptObjNames).clear();
482 free(_G(guibg));
483 _GP(guis).clear();
484 free(_G(scrGui));
485
486 free_all_fonts();
487
488 pl_stop_plugins();
489 ccRemoveAllSymbols();
490 ccUnregisterAllObjects();
491
492 free_do_once_tokens();
493 free(_GP(play).gui_draw_order);
494
495 resetRoomStatuses();
496
497 // free game struct last because it contains object counts
498 _GP(game).Free();
499 }
500
501
502
503
504
505
Game_GetGlobalStrings(int index)506 const char *Game_GetGlobalStrings(int index) {
507 if ((index < 0) || (index >= MAXGLOBALSTRINGS))
508 quit("!Game.GlobalStrings: invalid index");
509
510 return CreateNewScriptString(_GP(play).globalstrings[index]);
511 }
512
513
514 // ** GetGameParameter replacement functions
515
Game_GetInventoryItemCount()516 int Game_GetInventoryItemCount() {
517 // because of the dummy item 0, this is always one higher than it should be
518 return _GP(game).numinvitems - 1;
519 }
520
Game_GetFontCount()521 int Game_GetFontCount() {
522 return _GP(game).numfonts;
523 }
524
Game_GetMouseCursorCount()525 int Game_GetMouseCursorCount() {
526 return _GP(game).numcursors;
527 }
528
Game_GetCharacterCount()529 int Game_GetCharacterCount() {
530 return _GP(game).numcharacters;
531 }
532
Game_GetGUICount()533 int Game_GetGUICount() {
534 return _GP(game).numgui;
535 }
536
Game_GetViewCount()537 int Game_GetViewCount() {
538 return _GP(game).numviews;
539 }
540
Game_GetUseNativeCoordinates()541 int Game_GetUseNativeCoordinates() {
542 return _GP(game).IsDataInNativeCoordinates() ? 1 : 0;
543 }
544
Game_GetSpriteWidth(int spriteNum)545 int Game_GetSpriteWidth(int spriteNum) {
546 if (spriteNum < 0)
547 return 0;
548
549 if (!_GP(spriteset).DoesSpriteExist(spriteNum))
550 return 0;
551
552 return game_to_data_coord(_GP(game).SpriteInfos[spriteNum].Width);
553 }
554
Game_GetSpriteHeight(int spriteNum)555 int Game_GetSpriteHeight(int spriteNum) {
556 if (spriteNum < 0)
557 return 0;
558
559 if (!_GP(spriteset).DoesSpriteExist(spriteNum))
560 return 0;
561
562 return game_to_data_coord(_GP(game).SpriteInfos[spriteNum].Height);
563 }
564
Game_GetLoopCountForView(int viewNumber)565 int Game_GetLoopCountForView(int viewNumber) {
566 if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
567 quit("!GetGameParameter: invalid view specified");
568
569 return _G(views)[viewNumber - 1].numLoops;
570 }
571
Game_GetRunNextSettingForLoop(int viewNumber,int loopNumber)572 int Game_GetRunNextSettingForLoop(int viewNumber, int loopNumber) {
573 if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
574 quit("!GetGameParameter: invalid view specified");
575 if ((loopNumber < 0) || (loopNumber >= _G(views)[viewNumber - 1].numLoops))
576 quit("!GetGameParameter: invalid loop specified");
577
578 return (_G(views)[viewNumber - 1].loops[loopNumber].RunNextLoop()) ? 1 : 0;
579 }
580
Game_GetFrameCountForLoop(int viewNumber,int loopNumber)581 int Game_GetFrameCountForLoop(int viewNumber, int loopNumber) {
582 if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
583 quit("!GetGameParameter: invalid view specified");
584 if ((loopNumber < 0) || (loopNumber >= _G(views)[viewNumber - 1].numLoops))
585 quit("!GetGameParameter: invalid loop specified");
586
587 return _G(views)[viewNumber - 1].loops[loopNumber].numFrames;
588 }
589
Game_GetViewFrame(int viewNumber,int loopNumber,int frame)590 ScriptViewFrame *Game_GetViewFrame(int viewNumber, int loopNumber, int frame) {
591 if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
592 quit("!GetGameParameter: invalid view specified");
593 if ((loopNumber < 0) || (loopNumber >= _G(views)[viewNumber - 1].numLoops))
594 quit("!GetGameParameter: invalid loop specified");
595 if ((frame < 0) || (frame >= _G(views)[viewNumber - 1].loops[loopNumber].numFrames))
596 quit("!GetGameParameter: invalid frame specified");
597
598 ScriptViewFrame *sdt = new ScriptViewFrame(viewNumber - 1, loopNumber, frame);
599 ccRegisterManagedObject(sdt, sdt);
600 return sdt;
601 }
602
Game_DoOnceOnly(const char * token)603 int Game_DoOnceOnly(const char *token) {
604 for (int i = 0; i < (int)_GP(play).do_once_tokens.size(); i++) {
605 if (_GP(play).do_once_tokens[i] == token) {
606 return 0;
607 }
608 }
609 _GP(play).do_once_tokens.push_back(token);
610 return 1;
611 }
612
Game_GetTextReadingSpeed()613 int Game_GetTextReadingSpeed() {
614 return _GP(play).text_speed;
615 }
616
Game_SetTextReadingSpeed(int newTextSpeed)617 void Game_SetTextReadingSpeed(int newTextSpeed) {
618 if (newTextSpeed < 1)
619 quitprintf("!Game.TextReadingSpeed: %d is an invalid speed", newTextSpeed);
620
621 _GP(play).text_speed = newTextSpeed;
622 }
623
Game_GetMinimumTextDisplayTimeMs()624 int Game_GetMinimumTextDisplayTimeMs() {
625 return _GP(play).text_min_display_time_ms;
626 }
627
Game_SetMinimumTextDisplayTimeMs(int newTextMinTime)628 void Game_SetMinimumTextDisplayTimeMs(int newTextMinTime) {
629 _GP(play).text_min_display_time_ms = newTextMinTime;
630 }
631
Game_GetIgnoreUserInputAfterTextTimeoutMs()632 int Game_GetIgnoreUserInputAfterTextTimeoutMs() {
633 return _GP(play).ignore_user_input_after_text_timeout_ms;
634 }
635
Game_SetIgnoreUserInputAfterTextTimeoutMs(int newValueMs)636 void Game_SetIgnoreUserInputAfterTextTimeoutMs(int newValueMs) {
637 _GP(play).ignore_user_input_after_text_timeout_ms = newValueMs;
638 }
639
Game_GetFileName()640 const char *Game_GetFileName() {
641 return CreateNewScriptString(_GP(ResPaths).GamePak.Name.GetCStr());
642 }
643
Game_GetName()644 const char *Game_GetName() {
645 return CreateNewScriptString(_GP(play).game_name);
646 }
647
Game_SetName(const char * newName)648 void Game_SetName(const char *newName) {
649 strncpy(_GP(play).game_name, newName, 99);
650 _GP(play).game_name[99] = 0;
651 sys_window_set_title(_GP(play).game_name);
652 }
653
Game_GetSkippingCutscene()654 int Game_GetSkippingCutscene() {
655 if (_GP(play).fast_forward) {
656 return 1;
657 }
658 return 0;
659 }
660
Game_GetInSkippableCutscene()661 int Game_GetInSkippableCutscene() {
662 if (_GP(play).in_cutscene) {
663 return 1;
664 }
665 return 0;
666 }
667
Game_GetColorFromRGB(int red,int grn,int blu)668 int Game_GetColorFromRGB(int red, int grn, int blu) {
669 if ((red < 0) || (red > 255) || (grn < 0) || (grn > 255) ||
670 (blu < 0) || (blu > 255))
671 quit("!GetColorFromRGB: colour values must be 0-255");
672
673 if (_GP(game).color_depth == 1) {
674 return makecol8(red, grn, blu);
675 }
676
677 int agscolor = ((blu >> 3) & 0x1f);
678 agscolor += ((grn >> 2) & 0x3f) << 5;
679 agscolor += ((red >> 3) & 0x1f) << 11;
680 return agscolor;
681 }
682
Game_InputBox(const char * msg)683 const char *Game_InputBox(const char *msg) {
684 char buffer[STD_BUFFER_SIZE];
685 sc_inputbox(msg, buffer);
686 return CreateNewScriptString(buffer);
687 }
688
Game_GetLocationName(int x,int y)689 const char *Game_GetLocationName(int x, int y) {
690 char buffer[STD_BUFFER_SIZE];
691 GetLocationName(x, y, buffer);
692 return CreateNewScriptString(buffer);
693 }
694
Game_GetGlobalMessages(int index)695 const char *Game_GetGlobalMessages(int index) {
696 if ((index < 500) || (index >= MAXGLOBALMES + 500)) {
697 return nullptr;
698 }
699 char buffer[STD_BUFFER_SIZE];
700 buffer[0] = 0;
701 replace_tokens(get_translation(get_global_message(index)), buffer, STD_BUFFER_SIZE);
702 return CreateNewScriptString(buffer);
703 }
704
Game_GetSpeechFont()705 int Game_GetSpeechFont() {
706 return _GP(play).speech_font;
707 }
Game_GetNormalFont()708 int Game_GetNormalFont() {
709 return _GP(play).normal_font;
710 }
711
Game_GetTranslationFilename()712 const char *Game_GetTranslationFilename() {
713 char buffer[STD_BUFFER_SIZE];
714 GetTranslationName(buffer);
715 return CreateNewScriptString(buffer);
716 }
717
Game_ChangeTranslation(const char * newFilename)718 int Game_ChangeTranslation(const char *newFilename) {
719 if ((newFilename == nullptr) || (newFilename[0] == 0)) {
720 close_translation();
721 _GP(usetup).translation = "";
722 return 1;
723 }
724
725 String oldTransFileName = get_translation_name();
726 if (init_translation(newFilename, oldTransFileName, false)) {
727 _GP(usetup).translation = newFilename;
728 return 1;
729 }
730 return 0;
731 }
732
Game_GetAudioClip(int index)733 ScriptAudioClip *Game_GetAudioClip(int index) {
734 if (index < 0 || (size_t)index >= _GP(game).audioClips.size())
735 return nullptr;
736 return &_GP(game).audioClips[index];
737 }
738
Game_GetCamera()739 ScriptCamera *Game_GetCamera() {
740 return _GP(play).GetScriptCamera(0);
741 }
742
Game_GetCameraCount()743 int Game_GetCameraCount() {
744 return _GP(play).GetRoomCameraCount();
745 }
746
Game_GetAnyCamera(int index)747 ScriptCamera *Game_GetAnyCamera(int index) {
748 return _GP(play).GetScriptCamera(index);
749 }
750
Game_SimulateKeyPress(int key)751 void Game_SimulateKeyPress(int key) {
752 ags_simulate_keypress(static_cast<eAGSKeyCode>(key));
753 }
754
Game_BlockingWaitSkipped()755 int Game_BlockingWaitSkipped() {
756 return _GP(play).GetWaitSkipResult();
757 }
758
759 //=============================================================================
760
761 // save game functions
762
763
764
serialize_bitmap(const Shared::Bitmap * thispic,Stream * out)765 void serialize_bitmap(const Shared::Bitmap *thispic, Stream *out) {
766 if (thispic != nullptr) {
767 out->WriteInt32(thispic->GetWidth());
768 out->WriteInt32(thispic->GetHeight());
769 out->WriteInt32(thispic->GetColorDepth());
770 for (int cc = 0; cc < thispic->GetHeight(); cc++) {
771 switch (thispic->GetColorDepth()) {
772 case 8:
773 // CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8;
774 // therefore 15-bit bitmaps are saved only partially? is this a bug? or?
775 case 15:
776 out->WriteArray(&thispic->GetScanLine(cc)[0], thispic->GetWidth(), 1);
777 break;
778 case 16:
779 out->WriteArrayOfInt16((const int16_t *)&thispic->GetScanLine(cc)[0], thispic->GetWidth());
780 break;
781 case 32:
782 out->WriteArrayOfInt32((const int32_t *)&thispic->GetScanLine(cc)[0], thispic->GetWidth());
783 break;
784 }
785 }
786 }
787 }
788
789 // On Windows we could just use IIDFromString but this is _G(platform)-independant
convert_guid_from_text_to_binary(const char * guidText,unsigned char * buffer)790 void convert_guid_from_text_to_binary(const char *guidText, unsigned char *buffer) {
791 guidText++; // skip {
792 for (int bytesDone = 0; bytesDone < 16; bytesDone++) {
793 if (*guidText == '-')
794 guidText++;
795
796 char tempString[3];
797 tempString[0] = guidText[0];
798 tempString[1] = guidText[1];
799 tempString[2] = 0;
800 uint thisByte = 0;
801 sscanf(tempString, "%X", &thisByte);
802
803 buffer[bytesDone] = thisByte;
804 guidText += 2;
805 }
806
807 // Swap bytes to give correct GUID order
808 unsigned char temp;
809 temp = buffer[0];
810 buffer[0] = buffer[3];
811 buffer[3] = temp;
812 temp = buffer[1];
813 buffer[1] = buffer[2];
814 buffer[2] = temp;
815 temp = buffer[4];
816 buffer[4] = buffer[5];
817 buffer[5] = temp;
818 temp = buffer[6];
819 buffer[6] = buffer[7];
820 buffer[7] = temp;
821 }
822
read_serialized_bitmap(Stream * in)823 Bitmap *read_serialized_bitmap(Stream *in) {
824 Bitmap *thispic;
825 int picwid = in->ReadInt32();
826 int pichit = in->ReadInt32();
827 int piccoldep = in->ReadInt32();
828 thispic = BitmapHelper::CreateBitmap(picwid, pichit, piccoldep);
829 if (thispic == nullptr)
830 return nullptr;
831 for (int vv = 0; vv < pichit; vv++) {
832 switch (piccoldep) {
833 case 8:
834 // CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8
835 case 15:
836 in->ReadArray(thispic->GetScanLineForWriting(vv), picwid, 1);
837 break;
838 case 16:
839 in->ReadArrayOfInt16((int16_t *)thispic->GetScanLineForWriting(vv), picwid);
840 break;
841 case 32:
842 in->ReadArrayOfInt32((int32_t *)thispic->GetScanLineForWriting(vv), picwid);
843 break;
844 }
845 }
846
847 return thispic;
848 }
849
skip_serialized_bitmap(Stream * in)850 void skip_serialized_bitmap(Stream *in) {
851 int picwid = in->ReadInt32();
852 int pichit = in->ReadInt32();
853 int piccoldep = in->ReadInt32();
854 // CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8
855 int bpp = piccoldep / 8;
856 in->Seek(picwid * pichit * bpp);
857 }
858
write_screen_shot_for_vista(Stream * out,Bitmap * screenshot)859 long write_screen_shot_for_vista(Stream *out, Bitmap *screenshot) {
860 // Save the screenshot to a memory stream so we can access the raw data
861 Common::MemoryWriteStreamDynamic bitmap(DisposeAfterUse::YES);
862 screenshot->SaveToFile(bitmap, _G(palette));
863
864 update_polled_stuff_if_runtime();
865
866 // Write the bitmap to the output stream
867 out->Write(bitmap.getData(), bitmap.size());
868
869 return bitmap.size();
870 }
871
create_savegame_screenshot()872 Bitmap *create_savegame_screenshot() {
873 // Render the view without any UI elements
874 int old_flags = _G(debug_flags);
875 _G(debug_flags) |= DBG_NOIFACE;
876 construct_game_scene(true);
877 render_to_screen();
878 _G(debug_flags) = old_flags;
879
880 int usewid = data_to_game_coord(_GP(play).screenshot_width);
881 int usehit = data_to_game_coord(_GP(play).screenshot_height);
882 const Rect &viewport = _GP(play).GetMainViewport();
883 if (usewid > viewport.GetWidth())
884 usewid = viewport.GetWidth();
885 if (usehit > viewport.GetHeight())
886 usehit = viewport.GetHeight();
887
888 if ((_GP(play).screenshot_width < 16) || (_GP(play).screenshot_height < 16))
889 quit("!Invalid game.screenshot_width/height, must be from 16x16 to screen res");
890
891 Bitmap *screenshot = CopyScreenIntoBitmap(usewid, usehit);
892 screenshot->GetAllegroBitmap()->makeOpaque();
893
894 // Restore original screen
895 construct_game_scene(true);
896 render_to_screen();
897
898 return screenshot;
899 }
900
save_game(int slotn,const char * descript)901 void save_game(int slotn, const char *descript) {
902 // dont allow save in rep_exec_always, because we dont save
903 // the state of blocked scripts
904 can_run_delayed_command();
905
906 if (_G(inside_script)) {
907 strcpy(_G(curscript)->postScriptSaveSlotDescription[_G(curscript)->queue_action(ePSASaveGame, slotn, "SaveGameSlot")], descript);
908 return;
909 }
910
911 if (_G(platform)->GetDiskFreeSpaceMB() < 2) {
912 Display("ERROR: There is not enough disk space free to save the game. Clear some disk space and try again.");
913 return;
914 }
915
916 VALIDATE_STRING(descript);
917 String nametouse = get_save_game_path(slotn);
918 UBitmap screenShot;
919
920 // WORKAROUND: AGS originally only creates savegames if the game flags
921 // that it supports it. But we want it all the time for ScummVM GMM
922 if (/*_GP(game).options[OPT_SAVESCREENSHOT] != 0*/ true)
923 screenShot.reset(create_savegame_screenshot());
924
925 Engine::UStream out(StartSavegame(nametouse, descript, screenShot.get()));
926 if (out == nullptr) {
927 Display("ERROR: Unable to open savegame file for writing!");
928 return;
929 }
930
931 update_polled_stuff_if_runtime();
932
933 // Actual dynamic game data is saved here
934 SaveGameState(out.get());
935
936 if (screenShot != nullptr) {
937 int screenShotOffset = out->GetPosition() - sizeof(RICH_GAME_MEDIA_HEADER);
938 int screenShotSize = write_screen_shot_for_vista(out.get(), screenShot.get());
939
940 update_polled_stuff_if_runtime();
941
942 out->Seek(12, kSeekBegin);
943 out->WriteInt32(screenShotOffset);
944 out->Seek(4);
945 out->WriteInt32(screenShotSize);
946 }
947 }
948
read_savedgame_description(const String & savedgame,String & description)949 bool read_savedgame_description(const String &savedgame, String &description) {
950 SavegameDescription desc;
951 HSaveError err = OpenSavegame(savedgame, desc, kSvgDesc_UserText);
952 if (!err) {
953 Debug::Printf(kDbgMsg_Error, "Unable to read save's description.\n%s", err->FullMessage().GetCStr());
954 return false;
955 }
956 description = desc.UserText;
957 return true;
958 }
959
read_savedgame_screenshot(const String & savedgame,int & want_shot)960 bool read_savedgame_screenshot(const String &savedgame, int &want_shot) {
961 want_shot = 0;
962
963 SavegameDescription desc;
964 HSaveError err = OpenSavegame(savedgame, desc, kSvgDesc_UserImage);
965 if (!err) {
966 Debug::Printf(kDbgMsg_Error, "Unable to read save's screenshot.\n%s", err->FullMessage().GetCStr());
967 return false;
968 }
969
970 if (desc.UserImage.get()) {
971 int slot = _GP(spriteset).GetFreeIndex();
972 if (slot > 0) {
973 // add it into the sprite set
974 add_dynamic_sprite(slot, ReplaceBitmapWithSupportedFormat(desc.UserImage.release()));
975 want_shot = slot;
976 }
977 }
978 return true;
979 }
980
981
982 // Test if the game file contains expected GUID / legacy id
test_game_guid(const String & filepath,const String & guid,int legacy_id)983 bool test_game_guid(const String &filepath, const String &guid, int legacy_id) {
984 MainGameSource src;
985 HGameFileError err = OpenMainGameFileFromDefaultAsset(src);
986 if (!err)
987 return false;
988 GameSetupStruct g;
989 PreReadGameData(g, src.InputStream.get(), src.DataVersion);
990 if (!guid.IsEmpty())
991 return guid.CompareNoCase(g.guid) == 0;
992 return legacy_id == g.uniqueid;
993 }
994
995 static const SavegameDescription *loadDesc;
TestGame(const String & filepath)996 static bool TestGame(const String &filepath) {
997 return test_game_guid(filepath, loadDesc->GameGuid, loadDesc->LegacyID);
998 }
999
load_game(const String & path,int slotNumber,bool & data_overwritten)1000 HSaveError load_game(const String &path, int slotNumber, bool &data_overwritten) {
1001 data_overwritten = false;
1002 _G(gameHasBeenRestored)++;
1003
1004 _G(oldeip) = _G(our_eip);
1005 _G(our_eip) = 2050;
1006
1007 HSaveError err;
1008 SavegameSource src;
1009 SavegameDescription desc;
1010 err = OpenSavegame(path, src, desc, kSvgDesc_EnvInfo);
1011
1012 // saved in incompatible enviroment
1013 if (!err)
1014 return err;
1015 // CHECKME: is this color depth test still essential? if yes, is there possible workaround?
1016 else if (desc.ColorDepth != _GP(game).GetColorDepth())
1017 return new SavegameError(kSvgErr_DifferentColorDepth, String::FromFormat("Running: %d-bit, saved in: %d-bit.", _GP(game).GetColorDepth(), desc.ColorDepth));
1018
1019 // saved with different game file
1020 // if savegame is modern enough then test game GUIDs
1021 if (!desc.GameGuid.IsEmpty() || desc.LegacyID != 0) {
1022 if (desc.GameGuid.Compare(_GP(game).guid) != 0 && desc.LegacyID != _GP(game).uniqueid) {
1023 // Try to find wanted game's data using game id
1024 loadDesc = &desc;
1025 String gamefile = FindGameData(_GP(ResPaths).DataDir, TestGame);
1026
1027 if (Shared::File::TestReadFile(gamefile)) {
1028 RunAGSGame(desc.MainDataFilename, 0, 0);
1029 _G(load_new_game_restore) = slotNumber;
1030 return HSaveError::None();
1031 }
1032 return new SavegameError(kSvgErr_GameGuidMismatch);
1033 }
1034 }
1035 // if it's old then do the stupid old-style filename test
1036 // TODO: remove filename test after deprecating old saves
1037 else if (desc.MainDataFilename.Compare(_GP(ResPaths).GamePak.Name)) {
1038 String gamefile = Path::ConcatPaths(_GP(ResPaths).DataDir, desc.MainDataFilename);
1039 if (Shared::File::TestReadFile(gamefile)) {
1040 RunAGSGame(desc.MainDataFilename, 0, 0);
1041 _G(load_new_game_restore) = slotNumber;
1042 return HSaveError::None();
1043 }
1044 // if it does not exist, continue loading savedgame in current game, and pray for the best
1045 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.",
1046 path.GetCStr(), desc.MainDataFilename.GetCStr(), desc.GameTitle.GetCStr());
1047 }
1048
1049 // do the actual restore
1050 err = RestoreGameState(src.InputStream.get(), src.Version);
1051 data_overwritten = true;
1052 if (!err)
1053 return err;
1054 src.InputStream.reset();
1055 _G(our_eip) = _G(oldeip);
1056
1057 // ensure input state is reset
1058 ags_clear_input_state();
1059 // call "After Restore" event callback
1060 run_on_event(GE_RESTORE_GAME, RuntimeScriptValue().SetInt32(slotNumber));
1061 return HSaveError::None();
1062 }
1063
try_restore_save(int slot)1064 bool try_restore_save(int slot) {
1065 return try_restore_save(get_save_game_path(slot), slot);
1066 }
1067
try_restore_save(const Shared::String & path,int slot)1068 bool try_restore_save(const Shared::String &path, int slot) {
1069 bool data_overwritten;
1070 HSaveError err = load_game(path, slot, data_overwritten);
1071 if (!err) {
1072 String error = String::FromFormat("Unable to restore the saved game.\n%s",
1073 err->FullMessage().GetCStr());
1074 Debug::Printf(kDbgMsg_Error, "%s", error.GetCStr());
1075 // currently AGS cannot properly revert to stable state if some of the
1076 // game data was released or overwritten by the data from save file,
1077 // this is why we tell engine to shutdown if that happened.
1078 if (data_overwritten)
1079 quitprintf("%s", error.GetCStr());
1080 else
1081 Display(error.GetCStr());
1082 return false;
1083 }
1084 return true;
1085 }
1086
is_in_cutscene()1087 bool is_in_cutscene() {
1088 return _GP(play).in_cutscene > 0;
1089 }
1090
get_cutscene_skipstyle()1091 CutsceneSkipStyle get_cutscene_skipstyle() {
1092 return static_cast<CutsceneSkipStyle>(_GP(play).in_cutscene);
1093 }
1094
start_skipping_cutscene()1095 void start_skipping_cutscene() {
1096 _GP(play).fast_forward = 1;
1097 // if a drop-down icon bar is up, remove it as it will pause the game
1098 if (_G(ifacepopped) >= 0)
1099 remove_popup_interface(_G(ifacepopped));
1100
1101 // if a text message is currently displayed, remove it
1102 if (_GP(play).text_overlay_on > 0) {
1103 remove_screen_overlay(_GP(play).text_overlay_on);
1104 _GP(play).SetWaitSkipResult(SKIP_AUTOTIMER);
1105 }
1106 }
1107
check_skip_cutscene_keypress(int kgn)1108 bool check_skip_cutscene_keypress(int kgn) {
1109
1110 CutsceneSkipStyle skip = get_cutscene_skipstyle();
1111 if (skip == eSkipSceneAnyKey || skip == eSkipSceneKeyMouse ||
1112 (kgn == eAGSKeyCodeEscape && (skip == eSkipSceneEscOnly || skip == eSkipSceneEscOrRMB))) {
1113 start_skipping_cutscene();
1114 return true;
1115 }
1116 return false;
1117 }
1118
check_skip_cutscene_mclick(int mbut)1119 bool check_skip_cutscene_mclick(int mbut) {
1120 CutsceneSkipStyle skip = get_cutscene_skipstyle();
1121 if (skip == eSkipSceneMouse || skip == eSkipSceneKeyMouse ||
1122 (mbut == MouseRight && skip == eSkipSceneEscOrRMB)) {
1123 start_skipping_cutscene();
1124 return true;
1125 }
1126 return false;
1127 }
1128
1129 // Helper functions used by StartCutscene/EndCutscene, but also
1130 // by SkipUntilCharacterStops
initialize_skippable_cutscene()1131 void initialize_skippable_cutscene() {
1132 _GP(play).end_cutscene_music = -1;
1133 }
1134
stop_fast_forwarding()1135 void stop_fast_forwarding() {
1136 // when the skipping of a cutscene comes to an end, update things
1137 _GP(play).fast_forward = 0;
1138 setpal();
1139 if (_GP(play).end_cutscene_music >= 0)
1140 newmusic(_GP(play).end_cutscene_music);
1141
1142 {
1143 AudioChannelsLock lock;
1144
1145 // Restore actual volume of sounds
1146 for (int aa = 0; aa <= MAX_SOUND_CHANNELS; aa++) {
1147 auto *ch = lock.GetChannelIfPlaying(aa);
1148 if (ch) {
1149 ch->set_mute(false);
1150 }
1151 }
1152 } // -- AudioChannelsLock
1153
1154 update_music_volume();
1155 }
1156
1157 // allowHotspot0 defines whether Hotspot 0 returns LOCTYPE_HOTSPOT
1158 // or whether it returns 0
__GetLocationType(int xxx,int yyy,int allowHotspot0)1159 int __GetLocationType(int xxx, int yyy, int allowHotspot0) {
1160 _G(getloctype_index) = 0;
1161 // If it's not in ProcessClick, then return 0 when over a GUI
1162 if ((GetGUIAt(xxx, yyy) >= 0) && (_G(getloctype_throughgui) == 0))
1163 return 0;
1164
1165 _G(getloctype_throughgui) = 0;
1166
1167 const int scrx = xxx;
1168 const int scry = yyy;
1169 VpPoint vpt = _GP(play).ScreenToRoomDivDown(xxx, yyy);
1170 if (vpt.second < 0)
1171 return 0;
1172 xxx = vpt.first.X;
1173 yyy = vpt.first.Y;
1174 if ((xxx >= _GP(thisroom).Width) | (xxx < 0) | (yyy < 0) | (yyy >= _GP(thisroom).Height))
1175 return 0;
1176
1177 // check characters, objects and walkbehinds, work out which is
1178 // foremost visible to the player
1179 int charat = is_pos_on_character(xxx, yyy);
1180 int hsat = get_hotspot_at(xxx, yyy);
1181 int objat = GetObjectIDAtScreen(scrx, scry);
1182
1183 data_to_game_coords(&xxx, &yyy);
1184
1185 int wbat = _GP(thisroom).WalkBehindMask->GetPixel(xxx, yyy);
1186
1187 if (wbat <= 0) wbat = 0;
1188 else wbat = _G(croom)->walkbehind_base[wbat];
1189
1190 int winner = 0;
1191 // if it's an Ignore Walkbehinds object, then ignore the walkbehind
1192 if ((objat >= 0) && ((_G(objs)[objat].flags & OBJF_NOWALKBEHINDS) != 0))
1193 wbat = 0;
1194 if ((charat >= 0) && ((_GP(game).chars[charat].flags & CHF_NOWALKBEHINDS) != 0))
1195 wbat = 0;
1196
1197 if ((charat >= 0) && (objat >= 0)) {
1198 if ((wbat > _G(obj_lowest_yp)) && (wbat > _G(char_lowest_yp)))
1199 winner = LOCTYPE_HOTSPOT;
1200 else if (_G(obj_lowest_yp) > _G(char_lowest_yp))
1201 winner = LOCTYPE_OBJ;
1202 else
1203 winner = LOCTYPE_CHAR;
1204 } else if (charat >= 0) {
1205 if (wbat > _G(char_lowest_yp))
1206 winner = LOCTYPE_HOTSPOT;
1207 else
1208 winner = LOCTYPE_CHAR;
1209 } else if (objat >= 0) {
1210 if (wbat > _G(obj_lowest_yp))
1211 winner = LOCTYPE_HOTSPOT;
1212 else
1213 winner = LOCTYPE_OBJ;
1214 }
1215
1216 if (winner == 0) {
1217 if (hsat >= 0)
1218 winner = LOCTYPE_HOTSPOT;
1219 }
1220
1221 if ((winner == LOCTYPE_HOTSPOT) && (!allowHotspot0) && (hsat == 0))
1222 winner = 0;
1223
1224 if (winner == LOCTYPE_HOTSPOT)
1225 _G(getloctype_index) = hsat;
1226 else if (winner == LOCTYPE_CHAR)
1227 _G(getloctype_index) = charat;
1228 else if (winner == LOCTYPE_OBJ)
1229 _G(getloctype_index) = objat;
1230
1231 return winner;
1232 }
1233
1234 // Called whenever game looses input focus
display_switch_out()1235 void display_switch_out() {
1236 _G(switched_away) = true;
1237 ags_clear_input_state();
1238 // Always unlock mouse when switching out from the game
1239 _GP(mouse).UnlockFromWindow();
1240 }
1241
1242 // Called when game looses input focus and must pause until focus is returned
display_switch_out_suspend()1243 void display_switch_out_suspend() {
1244 _G(switching_away_from_game)++;
1245 _G(game_update_suspend)++;
1246 display_switch_out();
1247
1248 _G(platform)->PauseApplication();
1249
1250 // TODO: find out if anything has to be done here for SDL backend
1251
1252 {
1253 // stop the sound stuttering
1254 AudioChannelsLock lock;
1255 for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
1256 auto *ch = lock.GetChannelIfPlaying(i);
1257 if (ch) {
1258 ch->pause();
1259 }
1260 }
1261 } // -- AudioChannelsLock
1262
1263 // restore the callbacks
1264 SetMultitasking(0);
1265
1266 _G(switching_away_from_game)--;
1267 }
1268
1269 // Called whenever game gets input focus
display_switch_in()1270 void display_switch_in() {
1271 ags_clear_input_state();
1272 // If auto lock option is set, lock mouse to the game window
1273 if (_GP(usetup).mouse_auto_lock && _GP(scsystem).windowed)
1274 _GP(mouse).TryLockToWindow();
1275 _G(switched_away) = false;
1276 }
1277
1278 // Called when game gets input focus and must resume after pause
display_switch_in_resume()1279 void display_switch_in_resume() {
1280 display_switch_in();
1281
1282 {
1283 AudioChannelsLock lock;
1284 for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
1285 auto *ch = lock.GetChannelIfPlaying(i);
1286 if (ch) {
1287 ch->resume();
1288 }
1289 }
1290 } // -- AudioChannelsLock
1291
1292 // clear the screen if necessary
1293 if (_G(gfxDriver) && _G(gfxDriver)->UsesMemoryBackBuffer())
1294 _G(gfxDriver)->ClearRectangle(0, 0, _GP(game).GetGameRes().Width - 1, _GP(game).GetGameRes().Height - 1, nullptr);
1295
1296 // TODO: find out if anything has to be done here for SDL backend
1297
1298 _G(platform)->ResumeApplication();
1299 _G(game_update_suspend)--;
1300 }
1301
replace_tokens(const char * srcmes,char * destm,int maxlen)1302 void replace_tokens(const char *srcmes, char *destm, int maxlen) {
1303 int indxdest = 0, indxsrc = 0;
1304 const char *srcp;
1305 char *destp;
1306 while (srcmes[indxsrc] != 0) {
1307 srcp = &srcmes[indxsrc];
1308 destp = &destm[indxdest];
1309 if ((strncmp(srcp, "@IN", 3) == 0) | (strncmp(srcp, "@GI", 3) == 0)) {
1310 int tokentype = 0;
1311 if (srcp[1] == 'I') tokentype = 1;
1312 else tokentype = 2;
1313 int inx = atoi(&srcp[3]);
1314 srcp++;
1315 indxsrc += 2;
1316 while (srcp[0] != '@') {
1317 if (srcp[0] == 0) quit("!Display: special token not terminated");
1318 srcp++;
1319 indxsrc++;
1320 }
1321 char tval[10];
1322 if (tokentype == 1) {
1323 if ((inx < 1) | (inx >= _GP(game).numinvitems))
1324 quit("!Display: invalid inv item specified in @IN@");
1325 snprintf(tval, sizeof(tval), "%d", _G(playerchar)->inv[inx]);
1326 } else {
1327 if ((inx < 0) | (inx >= MAXGSVALUES))
1328 quit("!Display: invalid global int index speicifed in @GI@");
1329 snprintf(tval, sizeof(tval), "%d", GetGlobalInt(inx));
1330 }
1331 strcpy(destp, tval);
1332 indxdest += strlen(tval);
1333 } else {
1334 destp[0] = srcp[0];
1335 indxdest++;
1336 indxsrc++;
1337 }
1338 if (indxdest >= maxlen - 3)
1339 break;
1340 }
1341 destm[indxdest] = 0;
1342 }
1343
get_global_message(int msnum)1344 const char *get_global_message(int msnum) {
1345 if (_GP(game).messages[msnum - 500] == nullptr)
1346 return "";
1347 return get_translation(_GP(game).messages[msnum - 500]);
1348 }
1349
get_message_text(int msnum,char * buffer,char giveErr)1350 void get_message_text(int msnum, char *buffer, char giveErr) {
1351 int maxlen = 9999;
1352 if (!giveErr)
1353 maxlen = MAX_MAXSTRLEN;
1354
1355 if (msnum >= 500) {
1356
1357 if ((msnum >= MAXGLOBALMES + 500) || (_GP(game).messages[msnum - 500] == nullptr)) {
1358 if (giveErr)
1359 quit("!DisplayGlobalMessage: message does not exist");
1360 buffer[0] = 0;
1361 return;
1362 }
1363 buffer[0] = 0;
1364 replace_tokens(get_translation(_GP(game).messages[msnum - 500]), buffer, maxlen);
1365 return;
1366 } else if (msnum < 0 || (size_t)msnum >= _GP(thisroom).MessageCount) {
1367 if (giveErr)
1368 quit("!DisplayMessage: Invalid message number to display");
1369 buffer[0] = 0;
1370 return;
1371 }
1372
1373 buffer[0] = 0;
1374 replace_tokens(get_translation(_GP(thisroom).Messages[msnum].GetCStr()), buffer, maxlen);
1375 }
1376
unserialize_audio_script_object(int index,const char * objectType,const char * serializedData,int dataSize)1377 bool unserialize_audio_script_object(int index, const char *objectType, const char *serializedData, int dataSize) {
1378 if (strcmp(objectType, "AudioChannel") == 0) {
1379 _GP(ccDynamicAudio).Unserialize(index, serializedData, dataSize);
1380 } else if (strcmp(objectType, "AudioClip") == 0) {
1381 _GP(ccDynamicAudioClip).Unserialize(index, serializedData, dataSize);
1382 } else {
1383 return false;
1384 }
1385 return true;
1386 }
1387
game_sprite_updated(int sprnum)1388 void game_sprite_updated(int sprnum) {
1389 // Check if this sprite is assigned to any game object, and update them if necessary
1390 // room objects cache
1391 if (_G(croom) != nullptr) {
1392 for (size_t i = 0; i < (size_t)_G(croom)->numobj; ++i) {
1393 if (_G(objs)[i].num == sprnum)
1394 _G(objcache)[i].sppic = -1;
1395 }
1396 }
1397 // character cache
1398 for (size_t i = 0; i < (size_t)_GP(game).numcharacters; ++i) {
1399 if (_G(charcache)[i].sppic == sprnum)
1400 _G(charcache)[i].sppic = -1;
1401 }
1402 // gui backgrounds
1403 for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
1404 if (_GP(guis)[i].BgImage == sprnum) {
1405 _GP(guis)[i].MarkChanged();
1406 }
1407 }
1408 // gui buttons
1409 for (size_t i = 0; i < (size_t)_G(numguibuts); ++i) {
1410 if (_GP(guibuts)[i].CurrentImage == sprnum) {
1411 _GP(guibuts)[i].NotifyParentChanged();
1412 }
1413 }
1414 }
1415
game_sprite_deleted(int sprnum)1416 void game_sprite_deleted(int sprnum) {
1417 // Check if this sprite is assigned to any game object, and update them if necessary
1418 // room objects and their cache
1419 if (_G(croom) != nullptr) {
1420 for (size_t i = 0; i < (size_t)_G(croom)->numobj; ++i) {
1421 if (_G(objs)[i].num == sprnum) {
1422 _G(objs)[i].num = 0;
1423 _G(objcache)[i].sppic = -1;
1424 }
1425 }
1426 }
1427 // character cache
1428 for (size_t i = 0; i < (size_t)_GP(game).numcharacters; ++i) {
1429 if (_G(charcache)[i].sppic == sprnum)
1430 _G(charcache)[i].sppic = -1;
1431 }
1432 // gui backgrounds
1433 for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
1434 if (_GP(guis)[i].BgImage == sprnum) {
1435 _GP(guis)[i].BgImage = 0;
1436 _GP(guis)[i].MarkChanged();
1437 }
1438 }
1439 // gui buttons
1440 for (size_t i = 0; i < (size_t)_G(numguibuts); ++i) {
1441 if (_GP(guibuts)[i].Image == sprnum)
1442 _GP(guibuts)[i].Image = 0;
1443 if (_GP(guibuts)[i].MouseOverImage == sprnum)
1444 _GP(guibuts)[i].MouseOverImage = 0;
1445 if (_GP(guibuts)[i].PushedImage == sprnum)
1446 _GP(guibuts)[i].PushedImage = 0;
1447
1448 if (_GP(guibuts)[i].CurrentImage == sprnum) {
1449 _GP(guibuts)[i].CurrentImage = 0;
1450 _GP(guibuts)[i].NotifyParentChanged();
1451 }
1452 }
1453 // views
1454 for (size_t v = 0; v < (size_t)_GP(game).numviews; ++v) {
1455 for (size_t l = 0; l < (size_t)_G(views)[v].numLoops; ++l) {
1456 for (size_t f = 0; f < (size_t)_G(views)[v].loops[l].numFrames; ++f) {
1457 if (_G(views)[v].loops[l].frames[f].pic == sprnum)
1458 _G(views)[v].loops[l].frames[f].pic = 0;
1459 }
1460 }
1461 }
1462 }
1463
1464 //=============================================================================
1465 //
1466 // Script API Functions
1467 //
1468 //=============================================================================
1469
1470 // int (int audioType);
Sc_Game_IsAudioPlaying(const RuntimeScriptValue * params,int32_t param_count)1471 RuntimeScriptValue Sc_Game_IsAudioPlaying(const RuntimeScriptValue *params, int32_t param_count) {
1472 API_SCALL_INT_PINT(Game_IsAudioPlaying);
1473 }
1474
1475 // void (int audioType, int volumeDrop)
Sc_Game_SetAudioTypeSpeechVolumeDrop(const RuntimeScriptValue * params,int32_t param_count)1476 RuntimeScriptValue Sc_Game_SetAudioTypeSpeechVolumeDrop(const RuntimeScriptValue *params, int32_t param_count) {
1477 API_SCALL_VOID_PINT2(Game_SetAudioTypeSpeechVolumeDrop);
1478 }
1479
1480 // void (int audioType, int volume, int changeType)
Sc_Game_SetAudioTypeVolume(const RuntimeScriptValue * params,int32_t param_count)1481 RuntimeScriptValue Sc_Game_SetAudioTypeVolume(const RuntimeScriptValue *params, int32_t param_count) {
1482 API_SCALL_VOID_PINT3(Game_SetAudioTypeVolume);
1483 }
1484
1485 // void (int audioType)
Sc_Game_StopAudio(const RuntimeScriptValue * params,int32_t param_count)1486 RuntimeScriptValue Sc_Game_StopAudio(const RuntimeScriptValue *params, int32_t param_count) {
1487 API_SCALL_VOID_PINT(Game_StopAudio);
1488 }
1489
1490 // int (const char *newFilename)
Sc_Game_ChangeTranslation(const RuntimeScriptValue * params,int32_t param_count)1491 RuntimeScriptValue Sc_Game_ChangeTranslation(const RuntimeScriptValue *params, int32_t param_count) {
1492 API_SCALL_INT_POBJ(Game_ChangeTranslation, const char);
1493 }
1494
1495 // int (const char *token)
Sc_Game_DoOnceOnly(const RuntimeScriptValue * params,int32_t param_count)1496 RuntimeScriptValue Sc_Game_DoOnceOnly(const RuntimeScriptValue *params, int32_t param_count) {
1497 API_SCALL_INT_POBJ(Game_DoOnceOnly, const char);
1498 }
1499
1500 // int (int red, int grn, int blu)
Sc_Game_GetColorFromRGB(const RuntimeScriptValue * params,int32_t param_count)1501 RuntimeScriptValue Sc_Game_GetColorFromRGB(const RuntimeScriptValue *params, int32_t param_count) {
1502 API_SCALL_INT_PINT3(Game_GetColorFromRGB);
1503 }
1504
1505 // int (int viewNumber, int loopNumber)
Sc_Game_GetFrameCountForLoop(const RuntimeScriptValue * params,int32_t param_count)1506 RuntimeScriptValue Sc_Game_GetFrameCountForLoop(const RuntimeScriptValue *params, int32_t param_count) {
1507 API_SCALL_INT_PINT2(Game_GetFrameCountForLoop);
1508 }
1509
1510 // const char* (int x, int y)
Sc_Game_GetLocationName(const RuntimeScriptValue * params,int32_t param_count)1511 RuntimeScriptValue Sc_Game_GetLocationName(const RuntimeScriptValue *params, int32_t param_count) {
1512 API_CONST_SCALL_OBJ_PINT2(const char, _GP(myScriptStringImpl), Game_GetLocationName);
1513 }
1514
1515 // int (int viewNumber)
Sc_Game_GetLoopCountForView(const RuntimeScriptValue * params,int32_t param_count)1516 RuntimeScriptValue Sc_Game_GetLoopCountForView(const RuntimeScriptValue *params, int32_t param_count) {
1517 API_SCALL_INT_PINT(Game_GetLoopCountForView);
1518 }
1519
1520 // int ()
Sc_Game_GetMODPattern(const RuntimeScriptValue * params,int32_t param_count)1521 RuntimeScriptValue Sc_Game_GetMODPattern(const RuntimeScriptValue *params, int32_t param_count) {
1522 API_SCALL_INT(Game_GetMODPattern);
1523 }
1524
1525 // int (int viewNumber, int loopNumber)
Sc_Game_GetRunNextSettingForLoop(const RuntimeScriptValue * params,int32_t param_count)1526 RuntimeScriptValue Sc_Game_GetRunNextSettingForLoop(const RuntimeScriptValue *params, int32_t param_count) {
1527 API_SCALL_INT_PINT2(Game_GetRunNextSettingForLoop);
1528 }
1529
1530 // const char* (int slnum)
Sc_Game_GetSaveSlotDescription(const RuntimeScriptValue * params,int32_t param_count)1531 RuntimeScriptValue Sc_Game_GetSaveSlotDescription(const RuntimeScriptValue *params, int32_t param_count) {
1532 API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Game_GetSaveSlotDescription);
1533 }
1534
1535 // ScriptViewFrame* (int viewNumber, int loopNumber, int frame)
Sc_Game_GetViewFrame(const RuntimeScriptValue * params,int32_t param_count)1536 RuntimeScriptValue Sc_Game_GetViewFrame(const RuntimeScriptValue *params, int32_t param_count) {
1537 API_SCALL_OBJAUTO_PINT3(ScriptViewFrame, Game_GetViewFrame);
1538 }
1539
1540 // const char* (const char *msg)
Sc_Game_InputBox(const RuntimeScriptValue * params,int32_t param_count)1541 RuntimeScriptValue Sc_Game_InputBox(const RuntimeScriptValue *params, int32_t param_count) {
1542 API_CONST_SCALL_OBJ_POBJ(const char, _GP(myScriptStringImpl), Game_InputBox, const char);
1543 }
1544
1545 // int (const char *newFolder)
Sc_Game_SetSaveGameDirectory(const RuntimeScriptValue * params,int32_t param_count)1546 RuntimeScriptValue Sc_Game_SetSaveGameDirectory(const RuntimeScriptValue *params, int32_t param_count) {
1547 API_SCALL_INT_POBJ(Game_SetSaveGameDirectory, const char);
1548 }
1549
1550 // void (int evenAmbient);
Sc_StopAllSounds(const RuntimeScriptValue * params,int32_t param_count)1551 RuntimeScriptValue Sc_StopAllSounds(const RuntimeScriptValue *params, int32_t param_count) {
1552 API_SCALL_VOID_PINT(StopAllSounds);
1553 }
1554
1555 // int ()
Sc_Game_GetCharacterCount(const RuntimeScriptValue * params,int32_t param_count)1556 RuntimeScriptValue Sc_Game_GetCharacterCount(const RuntimeScriptValue *params, int32_t param_count) {
1557 API_SCALL_INT(Game_GetCharacterCount);
1558 }
1559
1560 // int ()
Sc_Game_GetDialogCount(const RuntimeScriptValue * params,int32_t param_count)1561 RuntimeScriptValue Sc_Game_GetDialogCount(const RuntimeScriptValue *params, int32_t param_count) {
1562 API_SCALL_INT(Game_GetDialogCount);
1563 }
1564
1565 // const char *()
Sc_Game_GetFileName(const RuntimeScriptValue * params,int32_t param_count)1566 RuntimeScriptValue Sc_Game_GetFileName(const RuntimeScriptValue *params, int32_t param_count) {
1567 API_CONST_SCALL_OBJ(const char, _GP(myScriptStringImpl), Game_GetFileName);
1568 }
1569
1570 // int ()
Sc_Game_GetFontCount(const RuntimeScriptValue * params,int32_t param_count)1571 RuntimeScriptValue Sc_Game_GetFontCount(const RuntimeScriptValue *params, int32_t param_count) {
1572 API_SCALL_INT(Game_GetFontCount);
1573 }
1574
1575 // const char* (int index)
Sc_Game_GetGlobalMessages(const RuntimeScriptValue * params,int32_t param_count)1576 RuntimeScriptValue Sc_Game_GetGlobalMessages(const RuntimeScriptValue *params, int32_t param_count) {
1577 API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Game_GetGlobalMessages);
1578 }
1579
1580 // const char* (int index)
Sc_Game_GetGlobalStrings(const RuntimeScriptValue * params,int32_t param_count)1581 RuntimeScriptValue Sc_Game_GetGlobalStrings(const RuntimeScriptValue *params, int32_t param_count) {
1582 API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Game_GetGlobalStrings);
1583 }
1584
1585 // void (int index, char *newval);
Sc_SetGlobalString(const RuntimeScriptValue * params,int32_t param_count)1586 RuntimeScriptValue Sc_SetGlobalString(const RuntimeScriptValue *params, int32_t param_count) {
1587 API_SCALL_VOID_PINT_POBJ(SetGlobalString, const char);
1588 }
1589
1590 // int ()
Sc_Game_GetGUICount(const RuntimeScriptValue * params,int32_t param_count)1591 RuntimeScriptValue Sc_Game_GetGUICount(const RuntimeScriptValue *params, int32_t param_count) {
1592 API_SCALL_INT(Game_GetGUICount);
1593 }
1594
1595 // int ()
Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue * params,int32_t param_count)1596 RuntimeScriptValue Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue *params, int32_t param_count) {
1597 API_SCALL_INT(Game_GetIgnoreUserInputAfterTextTimeoutMs);
1598 }
1599
1600 // void (int newValueMs)
Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue * params,int32_t param_count)1601 RuntimeScriptValue Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue *params, int32_t param_count) {
1602 API_SCALL_VOID_PINT(Game_SetIgnoreUserInputAfterTextTimeoutMs);
1603 }
1604
1605 // int ()
Sc_Game_GetInSkippableCutscene(const RuntimeScriptValue * params,int32_t param_count)1606 RuntimeScriptValue Sc_Game_GetInSkippableCutscene(const RuntimeScriptValue *params, int32_t param_count) {
1607 API_SCALL_INT(Game_GetInSkippableCutscene);
1608 }
1609
1610 // int ()
Sc_Game_GetInventoryItemCount(const RuntimeScriptValue * params,int32_t param_count)1611 RuntimeScriptValue Sc_Game_GetInventoryItemCount(const RuntimeScriptValue *params, int32_t param_count) {
1612 API_SCALL_INT(Game_GetInventoryItemCount);
1613 }
1614
1615 // int ()
Sc_Game_GetMinimumTextDisplayTimeMs(const RuntimeScriptValue * params,int32_t param_count)1616 RuntimeScriptValue Sc_Game_GetMinimumTextDisplayTimeMs(const RuntimeScriptValue *params, int32_t param_count) {
1617 API_SCALL_INT(Game_GetMinimumTextDisplayTimeMs);
1618 }
1619
1620 // void (int newTextMinTime)
Sc_Game_SetMinimumTextDisplayTimeMs(const RuntimeScriptValue * params,int32_t param_count)1621 RuntimeScriptValue Sc_Game_SetMinimumTextDisplayTimeMs(const RuntimeScriptValue *params, int32_t param_count) {
1622 API_SCALL_VOID_PINT(Game_SetMinimumTextDisplayTimeMs);
1623 }
1624
1625 // int ()
Sc_Game_GetMouseCursorCount(const RuntimeScriptValue * params,int32_t param_count)1626 RuntimeScriptValue Sc_Game_GetMouseCursorCount(const RuntimeScriptValue *params, int32_t param_count) {
1627 API_SCALL_INT(Game_GetMouseCursorCount);
1628 }
1629
1630 // const char *()
Sc_Game_GetName(const RuntimeScriptValue * params,int32_t param_count)1631 RuntimeScriptValue Sc_Game_GetName(const RuntimeScriptValue *params, int32_t param_count) {
1632 API_CONST_SCALL_OBJ(const char, _GP(myScriptStringImpl), Game_GetName);
1633 }
1634
1635 // void (const char *newName)
Sc_Game_SetName(const RuntimeScriptValue * params,int32_t param_count)1636 RuntimeScriptValue Sc_Game_SetName(const RuntimeScriptValue *params, int32_t param_count) {
1637 API_SCALL_VOID_POBJ(Game_SetName, const char);
1638 }
1639
1640 // int ()
Sc_Game_GetNormalFont(const RuntimeScriptValue * params,int32_t param_count)1641 RuntimeScriptValue Sc_Game_GetNormalFont(const RuntimeScriptValue *params, int32_t param_count) {
1642 API_SCALL_INT(Game_GetNormalFont);
1643 }
1644
1645 // void (int fontnum);
Sc_SetNormalFont(const RuntimeScriptValue * params,int32_t param_count)1646 RuntimeScriptValue Sc_SetNormalFont(const RuntimeScriptValue *params, int32_t param_count) {
1647 API_SCALL_VOID_PINT(SetNormalFont);
1648 }
1649
1650 // int ()
Sc_Game_GetSkippingCutscene(const RuntimeScriptValue * params,int32_t param_count)1651 RuntimeScriptValue Sc_Game_GetSkippingCutscene(const RuntimeScriptValue *params, int32_t param_count) {
1652 API_SCALL_INT(Game_GetSkippingCutscene);
1653 }
1654
1655 // int ()
Sc_Game_GetSpeechFont(const RuntimeScriptValue * params,int32_t param_count)1656 RuntimeScriptValue Sc_Game_GetSpeechFont(const RuntimeScriptValue *params, int32_t param_count) {
1657 API_SCALL_INT(Game_GetSpeechFont);
1658 }
1659
1660 // void (int fontnum);
Sc_SetSpeechFont(const RuntimeScriptValue * params,int32_t param_count)1661 RuntimeScriptValue Sc_SetSpeechFont(const RuntimeScriptValue *params, int32_t param_count) {
1662 API_SCALL_VOID_PINT(SetSpeechFont);
1663 }
1664
1665 // int (int spriteNum)
Sc_Game_GetSpriteWidth(const RuntimeScriptValue * params,int32_t param_count)1666 RuntimeScriptValue Sc_Game_GetSpriteWidth(const RuntimeScriptValue *params, int32_t param_count) {
1667 API_SCALL_INT_PINT(Game_GetSpriteWidth);
1668 }
1669
1670 // int (int spriteNum)
Sc_Game_GetSpriteHeight(const RuntimeScriptValue * params,int32_t param_count)1671 RuntimeScriptValue Sc_Game_GetSpriteHeight(const RuntimeScriptValue *params, int32_t param_count) {
1672 API_SCALL_INT_PINT(Game_GetSpriteHeight);
1673 }
1674
1675 // int ()
Sc_Game_GetTextReadingSpeed(const RuntimeScriptValue * params,int32_t param_count)1676 RuntimeScriptValue Sc_Game_GetTextReadingSpeed(const RuntimeScriptValue *params, int32_t param_count) {
1677 API_SCALL_INT(Game_GetTextReadingSpeed);
1678 }
1679
1680 // void (int newTextSpeed)
Sc_Game_SetTextReadingSpeed(const RuntimeScriptValue * params,int32_t param_count)1681 RuntimeScriptValue Sc_Game_SetTextReadingSpeed(const RuntimeScriptValue *params, int32_t param_count) {
1682 API_SCALL_VOID_PINT(Game_SetTextReadingSpeed);
1683 }
1684
1685 // const char* ()
Sc_Game_GetTranslationFilename(const RuntimeScriptValue * params,int32_t param_count)1686 RuntimeScriptValue Sc_Game_GetTranslationFilename(const RuntimeScriptValue *params, int32_t param_count) {
1687 API_CONST_SCALL_OBJ(const char, _GP(myScriptStringImpl), Game_GetTranslationFilename);
1688 }
1689
1690 // int ()
Sc_Game_GetUseNativeCoordinates(const RuntimeScriptValue * params,int32_t param_count)1691 RuntimeScriptValue Sc_Game_GetUseNativeCoordinates(const RuntimeScriptValue *params, int32_t param_count) {
1692 API_SCALL_INT(Game_GetUseNativeCoordinates);
1693 }
1694
1695 // int ()
Sc_Game_GetViewCount(const RuntimeScriptValue * params,int32_t param_count)1696 RuntimeScriptValue Sc_Game_GetViewCount(const RuntimeScriptValue *params, int32_t param_count) {
1697 API_SCALL_INT(Game_GetViewCount);
1698 }
1699
Sc_Game_GetAudioClipCount(const RuntimeScriptValue * params,int32_t param_count)1700 RuntimeScriptValue Sc_Game_GetAudioClipCount(const RuntimeScriptValue *params, int32_t param_count) {
1701 API_VARGET_INT(_GP(game).audioClips.size());
1702 }
1703
Sc_Game_GetAudioClip(const RuntimeScriptValue * params,int32_t param_count)1704 RuntimeScriptValue Sc_Game_GetAudioClip(const RuntimeScriptValue *params, int32_t param_count) {
1705 API_SCALL_OBJ_PINT(ScriptAudioClip, _GP(ccDynamicAudioClip), Game_GetAudioClip);
1706 }
1707
Sc_Game_IsPluginLoaded(const RuntimeScriptValue * params,int32_t param_count)1708 RuntimeScriptValue Sc_Game_IsPluginLoaded(const RuntimeScriptValue *params, int32_t param_count) {
1709 API_SCALL_BOOL_OBJ(pl_is_plugin_loaded, const char);
1710 }
1711
Sc_Game_PlayVoiceClip(const RuntimeScriptValue * params,int32_t param_count)1712 RuntimeScriptValue Sc_Game_PlayVoiceClip(const RuntimeScriptValue *params, int32_t param_count) {
1713 API_SCALL_OBJ_POBJ_PINT_PBOOL(ScriptAudioChannel, _GP(ccDynamicAudio), PlayVoiceClip, CharacterInfo);
1714 }
1715
Sc_Game_GetCamera(const RuntimeScriptValue * params,int32_t param_count)1716 RuntimeScriptValue Sc_Game_GetCamera(const RuntimeScriptValue *params, int32_t param_count) {
1717 API_SCALL_OBJAUTO(ScriptCamera, Game_GetCamera);
1718 }
1719
Sc_Game_GetCameraCount(const RuntimeScriptValue * params,int32_t param_count)1720 RuntimeScriptValue Sc_Game_GetCameraCount(const RuntimeScriptValue *params, int32_t param_count) {
1721 API_SCALL_INT(Game_GetCameraCount);
1722 }
1723
Sc_Game_GetAnyCamera(const RuntimeScriptValue * params,int32_t param_count)1724 RuntimeScriptValue Sc_Game_GetAnyCamera(const RuntimeScriptValue *params, int32_t param_count) {
1725 API_SCALL_OBJAUTO_PINT(ScriptCamera, Game_GetAnyCamera);
1726 }
1727
Sc_Game_SimulateKeyPress(const RuntimeScriptValue * params,int32_t param_count)1728 RuntimeScriptValue Sc_Game_SimulateKeyPress(const RuntimeScriptValue *params, int32_t param_count) {
1729 API_SCALL_VOID_PINT(Game_SimulateKeyPress);
1730 }
1731
Sc_Game_BlockingWaitSkipped(const RuntimeScriptValue * params,int32_t param_count)1732 RuntimeScriptValue Sc_Game_BlockingWaitSkipped(const RuntimeScriptValue *params, int32_t param_count) {
1733 API_SCALL_INT(Game_BlockingWaitSkipped);
1734 }
1735
RegisterGameAPI()1736 void RegisterGameAPI() {
1737 ccAddExternalStaticFunction("Game::IsAudioPlaying^1", Sc_Game_IsAudioPlaying);
1738 ccAddExternalStaticFunction("Game::SetAudioTypeSpeechVolumeDrop^2", Sc_Game_SetAudioTypeSpeechVolumeDrop);
1739 ccAddExternalStaticFunction("Game::SetAudioTypeVolume^3", Sc_Game_SetAudioTypeVolume);
1740 ccAddExternalStaticFunction("Game::StopAudio^1", Sc_Game_StopAudio);
1741 ccAddExternalStaticFunction("Game::ChangeTranslation^1", Sc_Game_ChangeTranslation);
1742 ccAddExternalStaticFunction("Game::DoOnceOnly^1", Sc_Game_DoOnceOnly);
1743 ccAddExternalStaticFunction("Game::GetColorFromRGB^3", Sc_Game_GetColorFromRGB);
1744 ccAddExternalStaticFunction("Game::GetFrameCountForLoop^2", Sc_Game_GetFrameCountForLoop);
1745 ccAddExternalStaticFunction("Game::GetLocationName^2", Sc_Game_GetLocationName);
1746 ccAddExternalStaticFunction("Game::GetLoopCountForView^1", Sc_Game_GetLoopCountForView);
1747 ccAddExternalStaticFunction("Game::GetMODPattern^0", Sc_Game_GetMODPattern);
1748 ccAddExternalStaticFunction("Game::GetRunNextSettingForLoop^2", Sc_Game_GetRunNextSettingForLoop);
1749 ccAddExternalStaticFunction("Game::GetSaveSlotDescription^1", Sc_Game_GetSaveSlotDescription);
1750 ccAddExternalStaticFunction("Game::GetViewFrame^3", Sc_Game_GetViewFrame);
1751 ccAddExternalStaticFunction("Game::InputBox^1", Sc_Game_InputBox);
1752 ccAddExternalStaticFunction("Game::SetSaveGameDirectory^1", Sc_Game_SetSaveGameDirectory);
1753 ccAddExternalStaticFunction("Game::StopSound^1", Sc_StopAllSounds);
1754 ccAddExternalStaticFunction("Game::get_CharacterCount", Sc_Game_GetCharacterCount);
1755 ccAddExternalStaticFunction("Game::get_DialogCount", Sc_Game_GetDialogCount);
1756 ccAddExternalStaticFunction("Game::get_FileName", Sc_Game_GetFileName);
1757 ccAddExternalStaticFunction("Game::get_FontCount", Sc_Game_GetFontCount);
1758 ccAddExternalStaticFunction("Game::geti_GlobalMessages", Sc_Game_GetGlobalMessages);
1759 ccAddExternalStaticFunction("Game::geti_GlobalStrings", Sc_Game_GetGlobalStrings);
1760 ccAddExternalStaticFunction("Game::seti_GlobalStrings", Sc_SetGlobalString);
1761 ccAddExternalStaticFunction("Game::get_GUICount", Sc_Game_GetGUICount);
1762 ccAddExternalStaticFunction("Game::get_IgnoreUserInputAfterTextTimeoutMs", Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs);
1763 ccAddExternalStaticFunction("Game::set_IgnoreUserInputAfterTextTimeoutMs", Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs);
1764 ccAddExternalStaticFunction("Game::get_InSkippableCutscene", Sc_Game_GetInSkippableCutscene);
1765 ccAddExternalStaticFunction("Game::get_InventoryItemCount", Sc_Game_GetInventoryItemCount);
1766 ccAddExternalStaticFunction("Game::get_MinimumTextDisplayTimeMs", Sc_Game_GetMinimumTextDisplayTimeMs);
1767 ccAddExternalStaticFunction("Game::set_MinimumTextDisplayTimeMs", Sc_Game_SetMinimumTextDisplayTimeMs);
1768 ccAddExternalStaticFunction("Game::get_MouseCursorCount", Sc_Game_GetMouseCursorCount);
1769 ccAddExternalStaticFunction("Game::get_Name", Sc_Game_GetName);
1770 ccAddExternalStaticFunction("Game::set_Name", Sc_Game_SetName);
1771 ccAddExternalStaticFunction("Game::get_NormalFont", Sc_Game_GetNormalFont);
1772 ccAddExternalStaticFunction("Game::set_NormalFont", Sc_SetNormalFont);
1773 ccAddExternalStaticFunction("Game::get_SkippingCutscene", Sc_Game_GetSkippingCutscene);
1774 ccAddExternalStaticFunction("Game::get_SpeechFont", Sc_Game_GetSpeechFont);
1775 ccAddExternalStaticFunction("Game::set_SpeechFont", Sc_SetSpeechFont);
1776 ccAddExternalStaticFunction("Game::geti_SpriteWidth", Sc_Game_GetSpriteWidth);
1777 ccAddExternalStaticFunction("Game::geti_SpriteHeight", Sc_Game_GetSpriteHeight);
1778 ccAddExternalStaticFunction("Game::get_TextReadingSpeed", Sc_Game_GetTextReadingSpeed);
1779 ccAddExternalStaticFunction("Game::set_TextReadingSpeed", Sc_Game_SetTextReadingSpeed);
1780 ccAddExternalStaticFunction("Game::get_TranslationFilename", Sc_Game_GetTranslationFilename);
1781 ccAddExternalStaticFunction("Game::get_UseNativeCoordinates", Sc_Game_GetUseNativeCoordinates);
1782 ccAddExternalStaticFunction("Game::get_ViewCount", Sc_Game_GetViewCount);
1783 ccAddExternalStaticFunction("Game::get_AudioClipCount", Sc_Game_GetAudioClipCount);
1784 ccAddExternalStaticFunction("Game::geti_AudioClips", Sc_Game_GetAudioClip);
1785 ccAddExternalStaticFunction("Game::IsPluginLoaded", Sc_Game_IsPluginLoaded);
1786 ccAddExternalStaticFunction("Game::PlayVoiceClip", Sc_Game_PlayVoiceClip);
1787 ccAddExternalStaticFunction("Game::SimulateKeyPress", Sc_Game_SimulateKeyPress);
1788 ccAddExternalStaticFunction("Game::get_BlockingWaitSkipped", Sc_Game_BlockingWaitSkipped);
1789
1790 ccAddExternalStaticFunction("Game::get_Camera", Sc_Game_GetCamera);
1791 ccAddExternalStaticFunction("Game::get_CameraCount", Sc_Game_GetCameraCount);
1792 ccAddExternalStaticFunction("Game::geti_Cameras", Sc_Game_GetAnyCamera);
1793 }
1794
RegisterStaticObjects()1795 void RegisterStaticObjects() {
1796 ccAddExternalStaticObject("game", &_GP(play), &_GP(GameStaticManager));
1797 ccAddExternalStaticObject("gs_globals", &_GP(play).globalvars[0], &_GP(GlobalStaticManager));
1798 ccAddExternalStaticObject("mouse", &_GP(scmouse), &_GP(GlobalStaticManager));
1799 ccAddExternalStaticObject("palette", &_G(palette)[0], &_GP(GlobalStaticManager));
1800 ccAddExternalStaticObject("system", &_GP(scsystem), &_GP(GlobalStaticManager));
1801 ccAddExternalStaticObject("savegameindex", &_GP(play).filenumbers[0], &_GP(GlobalStaticManager));
1802 }
1803
1804 } // namespace AGS3
1805