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