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/savefile.h"
24 #include "ags/lib/std/math.h"
25 #include "ags/shared/core/platform.h"
26 #include "ags/shared/ac/audio_clip_type.h"
27 #include "ags/engine/ac/global_game.h"
28 #include "ags/shared/ac/common.h"
29 #include "ags/shared/ac/view.h"
30 #include "ags/engine/ac/character.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.h"
35 #include "ags/engine/ac/game_setup.h"
36 #include "ags/shared/ac/game_setup_struct.h"
37 #include "ags/engine/ac/game_state.h"
38 #include "ags/engine/ac/global_character.h"
39 #include "ags/engine/ac/global_gui.h"
40 #include "ags/engine/ac/global_hotspot.h"
41 #include "ags/engine/ac/global_inventory_item.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/mouse.h"
47 #include "ags/engine/ac/object.h"
48 #include "ags/engine/ac/path_helper.h"
49 #include "ags/engine/ac/sys_events.h"
50 #include "ags/engine/ac/room.h"
51 #include "ags/engine/ac/room_status.h"
52 #include "ags/engine/ac/string.h"
53 #include "ags/engine/ac/system.h"
54 #include "ags/engine/debugging/debugger.h"
55 #include "ags/engine/debugging/debug_log.h"
56 #include "ags/engine/gui/gui_dialog.h"
57 #include "ags/engine/main/engine.h"
58 #include "ags/engine/main/game_run.h"
59 #include "ags/engine/main/graphics_mode.h"
60 #include "ags/engine/main/game_start.h"
61 #include "ags/engine/script/script.h"
62 #include "ags/engine/script/script_runtime.h"
63 #include "ags/shared/ac/sprite_cache.h"
64 #include "ags/shared/gfx/bitmap.h"
65 #include "ags/engine/gfx/graphics_driver.h"
66 #include "ags/shared/core/asset_manager.h"
67 #include "ags/engine/main/config.h"
68 #include "ags/engine/main/game_file.h"
69 #include "ags/shared/util/path.h"
70 #include "ags/shared/util/string_utils.h"
71 #include "ags/engine/media/audio/audio_system.h"
72 #include "ags/engine/platform/base/sys_main.h"
73 #include "ags/ags.h"
74 #include "ags/globals.h"
75
76 namespace AGS3 {
77
78 using namespace AGS::Shared;
79
GiveScore(int amnt)80 void GiveScore(int amnt) {
81 GUI::MarkSpecialLabelsForUpdate(kLabelMacro_AllScore);
82 _GP(play).score += amnt;
83
84 if ((amnt > 0) && (_GP(play).score_sound >= 0))
85 play_audio_clip_by_index(_GP(play).score_sound);
86
87 run_on_event(GE_GOT_SCORE, RuntimeScriptValue().SetInt32(amnt));
88 }
89
restart_game()90 void restart_game() {
91 can_run_delayed_command();
92 if (_G(inside_script)) {
93 _G(curscript)->queue_action(ePSARestartGame, 0, "RestartGame");
94 return;
95 }
96 try_restore_save(RESTART_POINT_SAVE_GAME_NUMBER);
97 }
98
RestoreGameSlot(int slnum)99 void RestoreGameSlot(int slnum) {
100 if (_G(displayed_room) < 0)
101 quit("!RestoreGameSlot: a game cannot be restored from within game_start");
102
103 can_run_delayed_command();
104 if (_G(inside_script)) {
105 _G(curscript)->queue_action(ePSARestoreGame, slnum, "RestoreGameSlot");
106 return;
107 }
108 try_restore_save(slnum);
109 }
110
DeleteSaveSlot(int slnum)111 void DeleteSaveSlot(int slnum) {
112 String nametouse;
113 nametouse = get_save_game_path(slnum);
114 Shared::File::DeleteFile(nametouse);
115 }
116
PauseGame()117 void PauseGame() {
118 _G(game_paused)++;
119 debug_script_log("Game paused");
120 }
UnPauseGame()121 void UnPauseGame() {
122 if (_G(game_paused) > 0)
123 _G(game_paused)--;
124 debug_script_log("Game UnPaused, pause level now %d", _G(game_paused));
125 }
126
127
IsGamePaused()128 int IsGamePaused() {
129 if (_G(game_paused) > 0) return 1;
130 return 0;
131 }
132
GetSaveSlotDescription(int slnum,String & description)133 bool GetSaveSlotDescription(int slnum, String &description) {
134 if (read_savedgame_description(get_save_game_path(slnum), description))
135 return true;
136 description.Format("INVALID SLOT %d", slnum);
137 return false;
138 }
139
GetSaveSlotDescription(int slnum,char * desbuf)140 int GetSaveSlotDescription(int slnum, char *desbuf) {
141 VALIDATE_STRING(desbuf);
142 String description;
143 bool res = GetSaveSlotDescription(slnum, description);
144 snprintf(desbuf, MAX_MAXSTRLEN, "%s", description.GetCStr());
145 return res ? 1 : 0;
146 }
147
LoadSaveSlotScreenshot(int slnum,int width,int height)148 int LoadSaveSlotScreenshot(int slnum, int width, int height) {
149 int gotSlot;
150 data_to_game_coords(&width, &height);
151
152 if (!read_savedgame_screenshot(get_save_game_path(slnum), gotSlot))
153 return 0;
154
155 if (gotSlot == 0)
156 return 0;
157
158 if ((_GP(game).SpriteInfos[gotSlot].Width == width) && (_GP(game).SpriteInfos[gotSlot].Height == height))
159 return gotSlot;
160
161 // resize the sprite to the requested size
162 Bitmap *newPic = BitmapHelper::CreateBitmap(width, height, _GP(spriteset)[gotSlot]->GetColorDepth());
163 newPic->StretchBlt(_GP(spriteset)[gotSlot],
164 RectWH(0, 0, _GP(game).SpriteInfos[gotSlot].Width, _GP(game).SpriteInfos[gotSlot].Height),
165 RectWH(0, 0, width, height));
166
167 update_polled_stuff_if_runtime();
168
169 // replace the bitmap in the sprite set
170 free_dynamic_sprite(gotSlot);
171 add_dynamic_sprite(gotSlot, newPic);
172
173 return gotSlot;
174 }
175
FillSaveList(std::vector<SaveListItem> & saves,size_t max_count)176 void FillSaveList(std::vector<SaveListItem> &saves, size_t max_count) {
177 if (max_count == 0)
178 return; // duh
179
180 String svg_dir = get_save_game_directory();
181 String svg_suff = get_save_game_suffix();
182 String searchPath = Path::ConcatPaths(svg_dir, String::FromFormat("agssave.???%s", svg_suff.GetCStr()));
183 time_t time = 0;
184
185 SaveStateList saveList = ::AGS::g_vm->listSaves();
186 for (uint idx = 0; idx < saveList.size(); ++idx) {
187 int saveGameSlot = saveList[idx].getSaveSlot();
188
189 // only list games .000 to .099 (to allow higher slots for other perposes)
190 if (saveGameSlot > 99)
191 continue;
192
193 String description;
194 GetSaveSlotDescription(saveGameSlot, description);
195 saves.push_back(SaveListItem(saveGameSlot, description, time));
196 if (saves.size() >= max_count)
197 break;
198 }
199 }
200
SetGlobalInt(int index,int valu)201 void SetGlobalInt(int index, int valu) {
202 if ((index < 0) | (index >= MAXGSVALUES))
203 quit("!SetGlobalInt: invalid index");
204
205 if (_GP(play).globalscriptvars[index] != valu) {
206 debug_script_log("GlobalInt %d set to %d", index, valu);
207 }
208
209 _GP(play).globalscriptvars[index] = valu;
210 }
211
212
GetGlobalInt(int index)213 int GetGlobalInt(int index) {
214 if ((index < 0) | (index >= MAXGSVALUES))
215 quit("!GetGlobalInt: invalid index");
216 return _GP(play).globalscriptvars[index];
217 }
218
SetGlobalString(int index,const char * newval)219 void SetGlobalString(int index, const char *newval) {
220 if ((index < 0) | (index >= MAXGLOBALSTRINGS))
221 quit("!SetGlobalString: invalid index");
222 debug_script_log("GlobalString %d set to '%s'", index, newval);
223 strncpy(_GP(play).globalstrings[index], newval, MAX_MAXSTRLEN);
224 // truncate it to 200 chars, to be sure
225 _GP(play).globalstrings[index][MAX_MAXSTRLEN - 1] = 0;
226 }
227
GetGlobalString(int index,char * strval)228 void GetGlobalString(int index, char *strval) {
229 if ((index < 0) | (index >= MAXGLOBALSTRINGS))
230 quit("!GetGlobalString: invalid index");
231 strcpy(strval, _GP(play).globalstrings[index]);
232 }
233
234 // TODO: refactor this method, and use same shared procedure at both normal stop/startup and in RunAGSGame
RunAGSGame(const String & newgame,unsigned int mode,int data)235 int RunAGSGame(const String &newgame, unsigned int mode, int data) {
236
237 can_run_delayed_command();
238
239 int AllowedModes = RAGMODE_PRESERVEGLOBALINT | RAGMODE_LOADNOW;
240
241 if ((mode & (~AllowedModes)) != 0)
242 quit("!RunAGSGame: mode value unknown");
243
244 if (_G(editor_debugging_enabled)) {
245 quit("!RunAGSGame cannot be used while running the game from within the AGS Editor. You must build the game EXE and run it from there to use this function.");
246 }
247
248 if ((mode & RAGMODE_LOADNOW) == 0) {
249 _GP(ResPaths).GamePak.Path = PathFromInstallDir(newgame);
250 _GP(ResPaths).GamePak.Name = newgame;
251 _GP(play).takeover_data = data;
252 _G(load_new_game_restore) = -1;
253
254 if (_G(inside_script)) {
255 _G(curscript)->queue_action(ePSARunAGSGame, mode | RAGMODE_LOADNOW, "RunAGSGame");
256 ccInstance::GetCurrentInstance()->Abort();
257 } else
258 _G(load_new_game) = mode | RAGMODE_LOADNOW;
259
260 return 0;
261 }
262
263 int ee;
264
265 unload_old_room();
266 _G(displayed_room) = -10;
267
268 #if defined (AGS_AUTO_WRITE_USER_CONFIG)
269 save_config_file(); // save current user config in case engine fails to run new game
270 #endif // AGS_AUTO_WRITE_USER_CONFIG
271 unload_game_file();
272
273 // Adjust config (NOTE: normally, RunAGSGame would need a redesign to allow separate config etc per each game)
274 _GP(usetup).translation = ""; // reset to default, prevent from trying translation file of game A in game B
275
276 _GP(AssetMgr)->RemoveAllLibraries();
277
278 // TODO: refactor and share same code with the startup!
279 if (_GP(AssetMgr)->AddLibrary(_GP(ResPaths).GamePak.Path) != Shared::kAssetNoError)
280 quitprintf("!RunAGSGame: unable to load new game file '%s'", _GP(ResPaths).GamePak.Path.GetCStr());
281 engine_assign_assetpaths();
282
283 show_preload();
284
285 HError err = load_game_file();
286 if (!err)
287 quitprintf("!RunAGSGame: error loading new game file:\n%s", err->FullMessage().GetCStr());
288
289 _GP(spriteset).Reset();
290 err = _GP(spriteset).InitFile(SpriteFile::DefaultSpriteFileName, SpriteFile::DefaultSpriteIndexName);
291 if (!err)
292 quitprintf("!RunAGSGame: error loading new sprites:\n%s", err->FullMessage().GetCStr());
293
294 if ((mode & RAGMODE_PRESERVEGLOBALINT) == 0) {
295 // reset GlobalInts
296 for (ee = 0; ee < MAXGSVALUES; ee++)
297 _GP(play).globalscriptvars[ee] = 0;
298 }
299
300 engine_init_game_settings();
301 _GP(play).screen_is_faded_out = 1;
302
303 if (_G(load_new_game_restore) >= 0) {
304 try_restore_save(_G(load_new_game_restore));
305 _G(load_new_game_restore) = -1;
306 } else
307 start_game();
308
309 return 0;
310 }
311
GetGameParameter(int parm,int data1,int data2,int data3)312 int GetGameParameter(int parm, int data1, int data2, int data3) {
313 switch (parm) {
314 case GP_SPRITEWIDTH:
315 return Game_GetSpriteWidth(data1);
316 case GP_SPRITEHEIGHT:
317 return Game_GetSpriteHeight(data1);
318 case GP_NUMLOOPS:
319 return Game_GetLoopCountForView(data1);
320 case GP_NUMFRAMES:
321 return Game_GetFrameCountForLoop(data1, data2);
322 case GP_FRAMESPEED:
323 case GP_FRAMEIMAGE:
324 case GP_FRAMESOUND:
325 case GP_ISFRAMEFLIPPED: {
326 if ((data1 < 1) || (data1 > _GP(game).numviews)) {
327 quitprintf("!GetGameParameter: invalid view specified (v: %d, l: %d, f: %d)", data1, data2, data3);
328 }
329 if ((data2 < 0) || (data2 >= _G(views)[data1 - 1].numLoops)) {
330 quitprintf("!GetGameParameter: invalid loop specified (v: %d, l: %d, f: %d)", data1, data2, data3);
331 }
332 if ((data3 < 0) || (data3 >= _G(views)[data1 - 1].loops[data2].numFrames)) {
333 quitprintf("!GetGameParameter: invalid frame specified (v: %d, l: %d, f: %d)", data1, data2, data3);
334 }
335
336 ViewFrame *pvf = &_G(views)[data1 - 1].loops[data2].frames[data3];
337
338 if (parm == GP_FRAMESPEED)
339 return pvf->speed;
340 else if (parm == GP_FRAMEIMAGE)
341 return pvf->pic;
342 else if (parm == GP_FRAMESOUND)
343 return get_old_style_number_for_sound(pvf->sound);
344 else if (parm == GP_ISFRAMEFLIPPED)
345 return (pvf->flags & VFLG_FLIPSPRITE) ? 1 : 0;
346 else
347 quit("GetGameParameter internal error");
348 break;
349 }
350 case GP_ISRUNNEXTLOOP:
351 return Game_GetRunNextSettingForLoop(data1, data2);
352 case GP_NUMGUIS:
353 return _GP(game).numgui;
354 case GP_NUMOBJECTS:
355 return _G(croom)->numobj;
356 case GP_NUMCHARACTERS:
357 return _GP(game).numcharacters;
358 case GP_NUMINVITEMS:
359 return _GP(game).numinvitems;
360 default:
361 quit("!GetGameParameter: unknown parameter specified");
362 }
363 return 0;
364 }
365
QuitGame(int dialog)366 void QuitGame(int dialog) {
367 if (dialog) {
368 int rcode;
369 setup_for_dialog();
370 rcode = quitdialog();
371 restore_after_dialog();
372 if (rcode == 0) return;
373 }
374 quit("|You have exited.");
375 }
376
377
378
379
SetRestartPoint()380 void SetRestartPoint() {
381 save_game(RESTART_POINT_SAVE_GAME_NUMBER, "Restart Game Auto-Save");
382 }
383
384
385
SetGameSpeed(int newspd)386 void SetGameSpeed(int newspd) {
387 newspd += _GP(play).game_speed_modifier;
388 if (newspd > 1000) newspd = 1000;
389 if (newspd < 10) newspd = 10;
390 set_game_speed(newspd);
391 debug_script_log("Game speed set to %d", newspd);
392 }
393
GetGameSpeed()394 int GetGameSpeed() {
395 return ::lround(get_current_fps()) - _GP(play).game_speed_modifier;
396 }
397
SetGameOption(int opt,int setting)398 int SetGameOption(int opt, int setting) {
399 if (((opt < 1) || (opt > OPT_HIGHESTOPTION)) && (opt != OPT_LIPSYNCTEXT))
400 quit("!SetGameOption: invalid option specified");
401
402 if (opt == OPT_ANTIGLIDE) {
403 for (int i = 0; i < _GP(game).numcharacters; i++) {
404 if (setting)
405 _GP(game).chars[i].flags |= CHF_ANTIGLIDE;
406 else
407 _GP(game).chars[i].flags &= ~CHF_ANTIGLIDE;
408 }
409 }
410
411 if ((opt == OPT_CROSSFADEMUSIC) && (_GP(game).audioClipTypes.size() > AUDIOTYPE_LEGACY_MUSIC)) {
412 // legacy compatibility -- changing crossfade speed here also
413 // updates the new audio clip type style
414 _GP(game).audioClipTypes[AUDIOTYPE_LEGACY_MUSIC].crossfadeSpeed = setting;
415 }
416
417 int oldval = _GP(game).options[opt];
418 _GP(game).options[opt] = setting;
419
420 if (opt == OPT_DUPLICATEINV)
421 update_invorder();
422 else if (opt == OPT_DISABLEOFF) {
423 _G(gui_disabled_style) = convert_gui_disabled_style(_GP(game).options[OPT_DISABLEOFF]);
424 // If GUI was disabled at this time then also update it, as visual style could've changed
425 if (_GP(play).disabled_user_interface > 0) {
426 GUI::MarkAllGUIForUpdate();
427 }
428 } else if (opt == OPT_PORTRAITSIDE) {
429 if (setting == 0) // set back to Left
430 _GP(play).swap_portrait_side = 0;
431 }
432
433 return oldval;
434 }
435
GetGameOption(int opt)436 int GetGameOption(int opt) {
437 if (((opt < 1) || (opt > OPT_HIGHESTOPTION)) && (opt != OPT_LIPSYNCTEXT))
438 quit("!GetGameOption: invalid option specified");
439
440 return _GP(game).options[opt];
441 }
442
SkipUntilCharacterStops(int cc)443 void SkipUntilCharacterStops(int cc) {
444 if (!is_valid_character(cc))
445 quit("!SkipUntilCharacterStops: invalid character specified");
446 if (_GP(game).chars[cc].room != _G(displayed_room))
447 quit("!SkipUntilCharacterStops: specified character not in current room");
448
449 // if they are not currently moving, do nothing
450 if (!_GP(game).chars[cc].walking)
451 return;
452
453 if (is_in_cutscene())
454 quit("!SkipUntilCharacterStops: cannot be used within a cutscene");
455
456 initialize_skippable_cutscene();
457 _GP(play).fast_forward = 2;
458 _GP(play).skip_until_char_stops = cc;
459 }
460
EndSkippingUntilCharStops()461 void EndSkippingUntilCharStops() {
462 // not currently skipping, so ignore
463 if (_GP(play).skip_until_char_stops < 0)
464 return;
465
466 stop_fast_forwarding();
467 _GP(play).skip_until_char_stops = -1;
468 }
469
StartCutscene(int skipwith)470 void StartCutscene(int skipwith) {
471 static ScriptPosition last_cutscene_script_pos;
472
473 if (is_in_cutscene()) {
474 quitprintf("!StartCutscene: already in a cutscene; previous started in \"%s\", line %d",
475 last_cutscene_script_pos.Section.GetCStr(), last_cutscene_script_pos.Line);
476 }
477
478 if ((skipwith < 1) || (skipwith > 6))
479 quit("!StartCutscene: invalid argument, must be 1 to 5.");
480
481 get_script_position(last_cutscene_script_pos);
482
483 // make sure they can't be skipping and cutsceneing at the same time
484 EndSkippingUntilCharStops();
485
486 _GP(play).in_cutscene = skipwith;
487 initialize_skippable_cutscene();
488 }
489
SkipCutscene()490 void SkipCutscene() {
491 if (is_in_cutscene())
492 start_skipping_cutscene();
493 }
494
EndCutscene()495 int EndCutscene() {
496 if (!is_in_cutscene())
497 quit("!EndCutscene: not in a cutscene");
498
499 int retval = _GP(play).fast_forward;
500 _GP(play).in_cutscene = 0;
501 // Stop it fast-forwarding
502 stop_fast_forwarding();
503
504 // make sure that the screen redraws
505 invalidate_screen();
506
507 // Return whether the player skipped it
508 return retval;
509 }
510
sc_inputbox(const char * msg,char * bufr)511 void sc_inputbox(const char *msg, char *bufr) {
512 VALIDATE_STRING(bufr);
513 setup_for_dialog();
514 enterstringwindow(get_translation(msg), bufr);
515 restore_after_dialog();
516 }
517
518 // GetLocationType exported function - just call through
519 // to the main function with default 0
GetLocationType(int xxx,int yyy)520 int GetLocationType(int xxx, int yyy) {
521 return __GetLocationType(xxx, yyy, 0);
522 }
523
SaveCursorForLocationChange()524 void SaveCursorForLocationChange() {
525 // update the current location name
526 char tempo[100];
527 GetLocationName(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey)), tempo);
528
529 if (_GP(play).get_loc_name_save_cursor != _GP(play).get_loc_name_last_time) {
530 _GP(play).get_loc_name_save_cursor = _GP(play).get_loc_name_last_time;
531 _GP(play).restore_cursor_mode_to = GetCursorMode();
532 _GP(play).restore_cursor_image_to = GetMouseCursor();
533 debug_script_log("Saving mouse: mode %d cursor %d", _GP(play).restore_cursor_mode_to, _GP(play).restore_cursor_image_to);
534 }
535 }
536
GetLocationName(int xxx,int yyy,char * tempo)537 void GetLocationName(int xxx, int yyy, char *tempo) {
538 if (_G(displayed_room) < 0)
539 quit("!GetLocationName: no room has been loaded");
540
541 VALIDATE_STRING(tempo);
542
543 tempo[0] = 0;
544
545 if (GetGUIAt(xxx, yyy) >= 0) {
546 int mover = GetInvAt(xxx, yyy);
547 if (mover > 0) {
548 if (_GP(play).get_loc_name_last_time != 1000 + mover)
549 GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
550 _GP(play).get_loc_name_last_time = 1000 + mover;
551 strcpy(tempo, get_translation(_GP(game).invinfo[mover].name));
552 } else if ((_GP(play).get_loc_name_last_time > 1000) && (_GP(play).get_loc_name_last_time < 1000 + MAX_INV)) {
553 // no longer selecting an item
554 GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
555 _GP(play).get_loc_name_last_time = -1;
556 }
557 return;
558 }
559
560 int loctype = GetLocationType(xxx, yyy); // GetLocationType takes screen coords
561 VpPoint vpt = _GP(play).ScreenToRoomDivDown(xxx, yyy);
562 if (vpt.second < 0)
563 return;
564 xxx = vpt.first.X;
565 yyy = vpt.first.Y;
566 if ((xxx >= _GP(thisroom).Width) | (xxx < 0) | (yyy < 0) | (yyy >= _GP(thisroom).Height))
567 return;
568
569 int onhs, aa;
570 if (loctype == 0) {
571 if (_GP(play).get_loc_name_last_time != 0) {
572 _GP(play).get_loc_name_last_time = 0;
573 GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
574 }
575 return;
576 }
577
578 // on character
579 if (loctype == LOCTYPE_CHAR) {
580 onhs = _G(getloctype_index);
581 strcpy(tempo, get_translation(_GP(game).chars[onhs].name));
582 if (_GP(play).get_loc_name_last_time != 2000 + onhs)
583 GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
584 _GP(play).get_loc_name_last_time = 2000 + onhs;
585 return;
586 }
587 // on object
588 if (loctype == LOCTYPE_OBJ) {
589 aa = _G(getloctype_index);
590 strcpy(tempo, get_translation(_GP(thisroom).Objects[aa].Name.GetCStr()));
591 // Compatibility: < 3.1.1 games returned space for nameless object
592 // (presumably was a bug, but fixing it affected certain games behavior)
593 if (_G(loaded_game_file_version) < kGameVersion_311 && tempo[0] == 0) {
594 tempo[0] = ' ';
595 tempo[1] = 0;
596 }
597 if (_GP(play).get_loc_name_last_time != 3000 + aa)
598 GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
599 _GP(play).get_loc_name_last_time = 3000 + aa;
600 return;
601 }
602 onhs = _G(getloctype_index);
603 if (onhs > 0) strcpy(tempo, get_translation(_GP(thisroom).Hotspots[onhs].Name.GetCStr()));
604 if (_GP(play).get_loc_name_last_time != onhs)
605 GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
606 _GP(play).get_loc_name_last_time = onhs;
607 }
608
IsKeyPressed(int keycode)609 int IsKeyPressed(int keycode) {
610 auto status = ags_iskeydown(static_cast<eAGSKeyCode>(keycode));
611 if (status < 0) {
612 debug_script_log("IsKeyPressed: unsupported keycode %d", keycode);
613 return 0;
614 }
615 return status;
616 }
617
SaveScreenShot(const char * namm)618 int SaveScreenShot(const char *namm) {
619 String fileName;
620 String svg_dir = get_save_game_directory();
621
622 if (strchr(namm, '.') == nullptr)
623 fileName = Path::MakePath(svg_dir, namm, "bmp");
624 else
625 fileName = Path::ConcatPaths(svg_dir, namm);
626
627 Bitmap *buffer = CopyScreenIntoBitmap(_GP(play).GetMainViewport().GetWidth(), _GP(play).GetMainViewport().GetHeight());
628 if (!buffer->SaveToFile(fileName, _G(palette)) != 0) {
629 delete buffer;
630 return 0;
631 }
632 delete buffer;
633 return 1; // successful
634 }
635
SetMultitasking(int mode)636 void SetMultitasking(int mode) {
637 if ((mode < 0) | (mode > 1))
638 quit("!SetMultitasking: invalid mode parameter");
639
640 if (_GP(usetup).override_multitasking >= 0) {
641 mode = _GP(usetup).override_multitasking;
642 }
643
644 // Don't allow background running if full screen
645 if ((mode == 1) && (!_GP(scsystem).windowed))
646 mode = 0;
647
648 // Install engine callbacks for switching in and out the window
649 if (mode == 0) {
650 sys_set_background_mode(false);
651 sys_evt_set_focus_callbacks(display_switch_in_resume, display_switch_out_suspend);
652 } else {
653 sys_set_background_mode(true);
654 sys_evt_set_focus_callbacks(display_switch_in, display_switch_out);
655 }
656 }
657
RoomProcessClick(int xx,int yy,int mood)658 void RoomProcessClick(int xx, int yy, int mood) {
659 _G(getloctype_throughgui) = 1;
660 int loctype = GetLocationType(xx, yy);
661 VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
662 if (vpt.second < 0)
663 return;
664 xx = vpt.first.X;
665 yy = vpt.first.Y;
666
667 if ((mood == MODE_WALK) && (_GP(game).options[OPT_NOWALKMODE] == 0)) {
668 int hsnum = get_hotspot_at(xx, yy);
669 if (hsnum < 1);
670 else if (_GP(thisroom).Hotspots[hsnum].WalkTo.X < 1);
671 else if (_GP(play).auto_use_walkto_points == 0);
672 else {
673 xx = _GP(thisroom).Hotspots[hsnum].WalkTo.X;
674 yy = _GP(thisroom).Hotspots[hsnum].WalkTo.Y;
675 debug_script_log("Move to walk-to point hotspot %d", hsnum);
676 }
677 walk_character(_GP(game).playercharacter, xx, yy, 0, true);
678 return;
679 }
680 _GP(play).usedmode = mood;
681
682 if (loctype == 0) {
683 // click on nothing -> hotspot 0
684 _G(getloctype_index) = 0;
685 loctype = LOCTYPE_HOTSPOT;
686 }
687
688 if (loctype == LOCTYPE_CHAR) {
689 if (check_click_on_character(xx, yy, mood)) return;
690 } else if (loctype == LOCTYPE_OBJ) {
691 if (check_click_on_object(xx, yy, mood)) return;
692 } else if (loctype == LOCTYPE_HOTSPOT)
693 RunHotspotInteraction(_G(getloctype_index), mood);
694 }
695
IsInteractionAvailable(int xx,int yy,int mood)696 int IsInteractionAvailable(int xx, int yy, int mood) {
697 _G(getloctype_throughgui) = 1;
698 int loctype = GetLocationType(xx, yy);
699 VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
700 if (vpt.second < 0)
701 return 0;
702 xx = vpt.first.X;
703 yy = vpt.first.Y;
704
705 // You can always walk places
706 if ((mood == MODE_WALK) && (_GP(game).options[OPT_NOWALKMODE] == 0))
707 return 1;
708
709 _GP(play).check_interaction_only = 1;
710
711 if (loctype == 0) {
712 // click on nothing -> hotspot 0
713 _G(getloctype_index) = 0;
714 loctype = LOCTYPE_HOTSPOT;
715 }
716
717 if (loctype == LOCTYPE_CHAR) {
718 check_click_on_character(xx, yy, mood);
719 } else if (loctype == LOCTYPE_OBJ) {
720 check_click_on_object(xx, yy, mood);
721 } else if (loctype == LOCTYPE_HOTSPOT)
722 RunHotspotInteraction(_G(getloctype_index), mood);
723
724 int ciwas = _GP(play).check_interaction_only;
725 _GP(play).check_interaction_only = 0;
726
727 if (ciwas == 2)
728 return 1;
729
730 return 0;
731 }
732
GetMessageText(int msg,char * buffer)733 void GetMessageText(int msg, char *buffer) {
734 VALIDATE_STRING(buffer);
735 get_message_text(msg, buffer, 0);
736 }
737
SetSpeechFont(int fontnum)738 void SetSpeechFont(int fontnum) {
739 if ((fontnum < 0) || (fontnum >= _GP(game).numfonts))
740 quit("!SetSpeechFont: invalid font number.");
741 _GP(play).speech_font = fontnum;
742 }
743
SetNormalFont(int fontnum)744 void SetNormalFont(int fontnum) {
745 if ((fontnum < 0) || (fontnum >= _GP(game).numfonts))
746 quit("!SetNormalFont: invalid font number.");
747 _GP(play).normal_font = fontnum;
748 }
749
_sc_AbortGame(const char * text)750 void _sc_AbortGame(const char *text) {
751 char displbuf[STD_BUFFER_SIZE] = "!?";
752 snprintf(&displbuf[2], STD_BUFFER_SIZE - 3, "%s", text);
753 quit(displbuf);
754 }
755
GetGraphicalVariable(const char * varName)756 int GetGraphicalVariable(const char *varName) {
757 InteractionVariable *theVar = FindGraphicalVariable(varName);
758 if (theVar == nullptr) {
759 quitprintf("!GetGraphicalVariable: interaction variable '%s' not found", varName);
760 return 0;
761 }
762 return theVar->Value;
763 }
764
SetGraphicalVariable(const char * varName,int p_value)765 void SetGraphicalVariable(const char *varName, int p_value) {
766 InteractionVariable *theVar = FindGraphicalVariable(varName);
767 if (theVar == nullptr) {
768 quitprintf("!SetGraphicalVariable: interaction variable '%s' not found", varName);
769 } else
770 theVar->Value = p_value;
771 }
772
WaitImpl(int skip_type,int nloops)773 int WaitImpl(int skip_type, int nloops) {
774 _GP(play).wait_counter = nloops;
775 _GP(play).wait_skipped_by = SKIP_NONE;
776 _GP(play).wait_skipped_by = SKIP_AUTOTIMER; // we set timer flag by default to simplify that case
777 _GP(play).wait_skipped_by_data = 0;
778 _GP(play).key_skip_wait = skip_type;
779
780 GameLoopUntilValueIsZero(&_GP(play).wait_counter);
781
782 if (_GP(game).options[OPT_BASESCRIPTAPI] < kScriptAPI_v360) {
783 // < 3.6.0 return 1 is skipped by user input, otherwise 0
784 return (_GP(play).wait_skipped_by & (SKIP_KEYPRESS | SKIP_MOUSECLICK)) != 0 ? 1 : 0;
785 }
786 // >= 3.6.0 return positive keycode, negative mouse button code, or 0 as time-out
787 return _GP(play).GetWaitSkipResult();
788 }
789
scrWait(int nloops)790 void scrWait(int nloops) {
791 WaitImpl(SKIP_AUTOTIMER, nloops);
792 }
793
WaitKey(int nloops)794 int WaitKey(int nloops) {
795 return WaitImpl(SKIP_KEYPRESS | SKIP_AUTOTIMER, nloops);
796 }
797
WaitMouse(int nloops)798 int WaitMouse(int nloops) {
799 return WaitImpl(SKIP_MOUSECLICK | SKIP_AUTOTIMER, nloops);
800 }
801
WaitMouseKey(int nloops)802 int WaitMouseKey(int nloops) {
803 return WaitImpl(SKIP_KEYPRESS | SKIP_MOUSECLICK | SKIP_AUTOTIMER, nloops);
804 }
805
SkipWait()806 void SkipWait() {
807 _GP(play).wait_counter = 0;
808 }
809
810 } // namespace AGS3
811