1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 
15 #define USE_CLIB
16 #include <stdio.h>
17 #include "ac/global_game.h"
18 #include "ac/common.h"
19 #include "ac/view.h"
20 #include "ac/character.h"
21 #include "ac/draw.h"
22 #include "ac/dynamicsprite.h"
23 #include "ac/event.h"
24 #include "ac/game.h"
25 #include "ac/gamesetup.h"
26 #include "ac/gamesetupstruct.h"
27 #include "ac/gamestate.h"
28 #include "ac/global_character.h"
29 #include "ac/global_gui.h"
30 #include "ac/global_hotspot.h"
31 #include "ac/global_inventoryitem.h"
32 #include "ac/global_translation.h"
33 #include "ac/gui.h"
34 #include "ac/hotspot.h"
35 #include "ac/keycode.h"
36 #include "ac/mouse.h"
37 #include "ac/object.h"
38 #include "ac/path_helper.h"
39 #include "ac/record.h"
40 #include "ac/room.h"
41 #include "ac/roomstatus.h"
42 #include "ac/roomstruct.h"
43 #include "ac/string.h"
44 #include "ac/system.h"
45 #include "debug/debugger.h"
46 #include "debug/debug_log.h"
47 #include "gui/guidialog.h"
48 #include "main/engine.h"
49 #include "main/game_start.h"
50 #include "main/game_run.h"
51 #include "main/graphics_mode.h"
52 #include "media/audio/audio.h"
53 #include "script/script.h"
54 #include "script/script_runtime.h"
55 #include "ac/spritecache.h"
56 #include "gfx/graphicsdriver.h"
57 #include "core/assetmanager.h"
58 #include "main/game_file.h"
59 #include "util/string_utils.h"
60 
61 using namespace AGS::Common;
62 
63 #define ALLEGRO_KEYBOARD_HANDLER
64 
65 extern GameState play;
66 extern ExecutingScript*curscript;
67 extern int displayed_room;
68 extern int game_paused;
69 extern int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
70 extern SpriteCache spriteset;
71 extern int frames_per_second;
72 extern int time_between_timers;
73 extern char gamefilenamebuf[200];
74 extern GameSetup usetup;
75 extern unsigned int load_new_game;
76 extern int load_new_game_restore;
77 extern GameSetupStruct game;
78 extern ViewStruct*views;
79 extern RoomStatus*croom;
80 extern int gui_disabled_style;
81 extern roomstruct thisroom;
82 extern int getloctype_index;
83 extern int offsetx, offsety;
84 extern char saveGameDirectory[260];
85 extern IGraphicsDriver *gfxDriver;
86 extern color palette[256];
87 
88 #if defined(IOS_VERSION) || defined(ANDROID_VERSION)
89 extern int psp_gfx_renderer;
90 #endif
91 
GiveScore(int amnt)92 void GiveScore(int amnt)
93 {
94     guis_need_update = 1;
95     play.score += amnt;
96 
97     if ((amnt > 0) && (play.score_sound >= 0))
98         play_audio_clip_by_index(play.score_sound);
99 
100     run_on_event (GE_GOT_SCORE, RuntimeScriptValue().SetInt32(amnt));
101 }
102 
restart_game()103 void restart_game() {
104     can_run_delayed_command();
105     if (inside_script) {
106         curscript->queue_action(ePSARestartGame, 0, "RestartGame");
107         return;
108     }
109     try_restore_save(RESTART_POINT_SAVE_GAME_NUMBER);
110 }
111 
RestoreGameSlot(int slnum)112 void RestoreGameSlot(int slnum) {
113     if (displayed_room < 0)
114         quit("!RestoreGameSlot: a game cannot be restored from within game_start");
115 
116     can_run_delayed_command();
117     if (inside_script) {
118         curscript->queue_action(ePSARestoreGame, slnum, "RestoreGameSlot");
119         return;
120     }
121     try_restore_save(slnum);
122 }
123 
DeleteSaveSlot(int slnum)124 void DeleteSaveSlot (int slnum) {
125     String nametouse;
126     nametouse = get_save_game_path(slnum);
127     unlink (nametouse);
128     if ((slnum >= 1) && (slnum <= MAXSAVEGAMES)) {
129         String thisname;
130         for (int i = MAXSAVEGAMES; i > slnum; i--) {
131             thisname = get_save_game_path(i);
132             if (Common::File::TestReadFile(thisname)) {
133                 // Rename the highest save game to fill in the gap
134                 rename (thisname, nametouse);
135                 break;
136             }
137         }
138 
139     }
140 }
141 
PauseGame()142 void PauseGame() {
143     game_paused++;
144     debug_script_log("Game paused");
145 }
UnPauseGame()146 void UnPauseGame() {
147     if (game_paused > 0)
148         game_paused--;
149     debug_script_log("Game UnPaused, pause level now %d", game_paused);
150 }
151 
152 
IsGamePaused()153 int IsGamePaused() {
154     if (game_paused>0) return 1;
155     return 0;
156 }
157 
GetSaveSlotDescription(int slnum,char * desbuf)158 int GetSaveSlotDescription(int slnum,char*desbuf) {
159     VALIDATE_STRING(desbuf);
160     String description;
161     if (read_savedgame_description(get_save_game_path(slnum), description))
162     {
163         strcpy(desbuf, description);
164         return 1;
165     }
166     sprintf(desbuf,"INVALID SLOT %d", slnum);
167     return 0;
168 }
169 
LoadSaveSlotScreenshot(int slnum,int width,int height)170 int LoadSaveSlotScreenshot(int slnum, int width, int height) {
171     int gotSlot;
172     multiply_up_coordinates(&width, &height);
173 
174     if (!read_savedgame_screenshot(get_save_game_path(slnum), gotSlot))
175         return 0;
176 
177     if (gotSlot == 0)
178         return 0;
179 
180     if ((spritewidth[gotSlot] == width) && (spriteheight[gotSlot] == height))
181         return gotSlot;
182 
183     // resize the sprite to the requested size
184     Bitmap *newPic = BitmapHelper::CreateBitmap(width, height, spriteset[gotSlot]->GetColorDepth());
185     newPic->StretchBlt(spriteset[gotSlot],
186         RectWH(0, 0, spritewidth[gotSlot], spriteheight[gotSlot]),
187         RectWH(0, 0, width, height));
188 
189     update_polled_stuff_if_runtime();
190 
191     // replace the bitmap in the sprite set
192     free_dynamic_sprite(gotSlot);
193     add_dynamic_sprite(gotSlot, newPic);
194 
195     return gotSlot;
196 }
197 
SetGlobalInt(int index,int valu)198 void SetGlobalInt(int index,int valu) {
199     if ((index<0) | (index>=MAXGSVALUES))
200         quit("!SetGlobalInt: invalid index");
201 
202     if (play.globalscriptvars[index] != valu) {
203         debug_script_log("GlobalInt %d set to %d", index, valu);
204     }
205 
206     play.globalscriptvars[index]=valu;
207 }
208 
209 
GetGlobalInt(int index)210 int GetGlobalInt(int index) {
211     if ((index<0) | (index>=MAXGSVALUES))
212         quit("!GetGlobalInt: invalid index");
213     return play.globalscriptvars[index];
214 }
215 
SetGlobalString(int index,const char * newval)216 void SetGlobalString (int index, const char *newval) {
217     if ((index<0) | (index >= MAXGLOBALSTRINGS))
218         quit("!SetGlobalString: invalid index");
219     debug_script_log("GlobalString %d set to '%s'", index, newval);
220     strncpy(play.globalstrings[index], newval, MAX_MAXSTRLEN);
221     // truncate it to 200 chars, to be sure
222     play.globalstrings[index][MAX_MAXSTRLEN - 1] = 0;
223 }
224 
GetGlobalString(int index,char * strval)225 void GetGlobalString (int index, char *strval) {
226     if ((index<0) | (index >= MAXGLOBALSTRINGS))
227         quit("!GetGlobalString: invalid index");
228     strcpy (strval, play.globalstrings[index]);
229 }
230 
RunAGSGame(const char * newgame,unsigned int mode,int data)231 int RunAGSGame (const char *newgame, unsigned int mode, int data) {
232 
233     can_run_delayed_command();
234 
235     int AllowedModes = RAGMODE_PRESERVEGLOBALINT | RAGMODE_LOADNOW;
236 
237     if ((mode & (~AllowedModes)) != 0)
238         quit("!RunAGSGame: mode value unknown");
239 
240     if (editor_debugging_enabled)
241     {
242         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.");
243     }
244 
245     if ((mode & RAGMODE_LOADNOW) == 0) {
246         // need to copy, since the script gets destroyed
247         get_install_dir_path(gamefilenamebuf, newgame);
248         game_file_name = gamefilenamebuf;
249         usetup.main_data_filename = game_file_name;
250         play.takeover_data = data;
251         load_new_game_restore = -1;
252 
253         if (inside_script) {
254             curscript->queue_action(ePSARunAGSGame, mode | RAGMODE_LOADNOW, "RunAGSGame");
255             ccInstance::GetCurrentInstance()->Abort();
256         }
257         else
258             load_new_game = mode | RAGMODE_LOADNOW;
259 
260         return 0;
261     }
262 
263     int ee;
264 
265     unload_old_room();
266     displayed_room = -10;
267 
268     unload_game_file();
269 
270     if (Common::AssetManager::SetDataFile(game_file_name) != Common::kAssetNoError)
271         quitprintf("!RunAGSGame: unable to load new game file '%s'", game_file_name.GetCStr());
272 
273     Bitmap *ds = GetVirtualScreen();
274     ds->Fill(0);
275     show_preload();
276 
277     String err_str;
278     if (!load_game_file(err_str))
279         quitprintf("!RunAGSGame: error loading new game file:\n%s", err_str.GetCStr());
280 
281     spriteset.reset();
282     if (spriteset.initFile ("acsprset.spr"))
283         quit("!RunAGSGame: error loading new sprites");
284 
285     if ((mode & RAGMODE_PRESERVEGLOBALINT) == 0) {
286         // reset GlobalInts
287         for (ee = 0; ee < MAXGSVALUES; ee++)
288             play.globalscriptvars[ee] = 0;
289     }
290 
291     engine_init_game_settings();
292     play.screen_is_faded_out = 1;
293 
294     if (load_new_game_restore >= 0) {
295         try_restore_save(load_new_game_restore);
296         load_new_game_restore = -1;
297     }
298     else
299         start_game();
300 
301     return 0;
302 }
303 
GetGameParameter(int parm,int data1,int data2,int data3)304 int GetGameParameter (int parm, int data1, int data2, int data3) {
305     switch (parm) {
306    case GP_SPRITEWIDTH:
307        return Game_GetSpriteWidth(data1);
308    case GP_SPRITEHEIGHT:
309        return Game_GetSpriteHeight(data1);
310    case GP_NUMLOOPS:
311        return Game_GetLoopCountForView(data1);
312    case GP_NUMFRAMES:
313        return Game_GetFrameCountForLoop(data1, data2);
314    case GP_FRAMESPEED:
315    case GP_FRAMEIMAGE:
316    case GP_FRAMESOUND:
317    case GP_ISFRAMEFLIPPED:
318        {
319            if ((data1 < 1) || (data1 > game.numviews)) {
320                quitprintf("!GetGameParameter: invalid view specified (v: %d, l: %d, f: %d)", data1, data2, data3);
321            }
322            if ((data2 < 0) || (data2 >= views[data1 - 1].numLoops)) {
323                quitprintf("!GetGameParameter: invalid loop specified (v: %d, l: %d, f: %d)", data1, data2, data3);
324            }
325            if ((data3 < 0) || (data3 >= views[data1 - 1].loops[data2].numFrames)) {
326                quitprintf("!GetGameParameter: invalid frame specified (v: %d, l: %d, f: %d)", data1, data2, data3);
327            }
328 
329            ViewFrame *pvf = &views[data1 - 1].loops[data2].frames[data3];
330 
331            if (parm == GP_FRAMESPEED)
332                return pvf->speed;
333            else if (parm == GP_FRAMEIMAGE)
334                return pvf->pic;
335            else if (parm == GP_FRAMESOUND)
336                return get_old_style_number_for_sound(pvf->sound);
337            else if (parm == GP_ISFRAMEFLIPPED)
338                return (pvf->flags & VFLG_FLIPSPRITE) ? 1 : 0;
339            else
340                quit("GetGameParameter internal error");
341        }
342    case GP_ISRUNNEXTLOOP:
343        return Game_GetRunNextSettingForLoop(data1, data2);
344    case GP_NUMGUIS:
345        return game.numgui;
346    case GP_NUMOBJECTS:
347        return croom->numobj;
348    case GP_NUMCHARACTERS:
349        return game.numcharacters;
350    case GP_NUMINVITEMS:
351        return game.numinvitems;
352    default:
353        quit("!GetGameParameter: unknown parameter specified");
354     }
355     return 0;
356 }
357 
QuitGame(int dialog)358 void QuitGame(int dialog) {
359     if (dialog) {
360         int rcode;
361         setup_for_dialog();
362         rcode=quitdialog();
363         restore_after_dialog();
364         if (rcode==0) return;
365     }
366     quit("|You have exited.");
367 }
368 
369 
370 
371 
SetRestartPoint()372 void SetRestartPoint() {
373     save_game(RESTART_POINT_SAVE_GAME_NUMBER, "Restart Game Auto-Save");
374 }
375 
376 
377 
SetGameSpeed(int newspd)378 void SetGameSpeed(int newspd) {
379     // if Ctrl+E has been used to max out frame rate, lock it there
380     if ((frames_per_second == 1000) && (display_fps == 2))
381         return;
382 
383     newspd += play.game_speed_modifier;
384     if (newspd>1000) newspd=1000;
385     if (newspd<10) newspd=10;
386     set_game_speed(newspd);
387     debug_script_log("Game speed set to %d", newspd);
388 }
389 
GetGameSpeed()390 int GetGameSpeed() {
391     return frames_per_second - play.game_speed_modifier;
392 }
393 
SetGameOption(int opt,int setting)394 int SetGameOption (int opt, int setting) {
395     if (((opt < 1) || (opt > OPT_HIGHESTOPTION)) && (opt != OPT_LIPSYNCTEXT))
396         quit("!SetGameOption: invalid option specified");
397 
398     if (opt == OPT_ANTIGLIDE)
399     {
400         for (int i = 0; i < game.numcharacters; i++)
401         {
402             if (setting)
403                 game.chars[i].flags |= CHF_ANTIGLIDE;
404             else
405                 game.chars[i].flags &= ~CHF_ANTIGLIDE;
406         }
407     }
408 
409     if ((opt == OPT_CROSSFADEMUSIC) && (game.audioClipTypeCount > AUDIOTYPE_LEGACY_MUSIC))
410     {
411         // legacy compatibility -- changing crossfade speed here also
412         // updates the new audio clip type style
413         game.audioClipTypes[AUDIOTYPE_LEGACY_MUSIC].crossfadeSpeed = setting;
414     }
415 
416     int oldval = game.options[opt];
417     game.options[opt] = setting;
418 
419     if (opt == OPT_DUPLICATEINV)
420         update_invorder();
421     else if (opt == OPT_DISABLEOFF)
422         gui_disabled_style = convert_gui_disabled_style(game.options[OPT_DISABLEOFF]);
423     else if (opt == OPT_PORTRAITSIDE) {
424         if (setting == 0)  // set back to Left
425             play.swap_portrait_side = 0;
426     }
427 
428     return oldval;
429 }
430 
GetGameOption(int opt)431 int GetGameOption (int opt) {
432     if (((opt < 1) || (opt > OPT_HIGHESTOPTION)) && (opt != OPT_LIPSYNCTEXT))
433         quit("!GetGameOption: invalid option specified");
434 
435     return game.options[opt];
436 }
437 
SkipUntilCharacterStops(int cc)438 void SkipUntilCharacterStops(int cc) {
439     if (!is_valid_character(cc))
440         quit("!SkipUntilCharacterStops: invalid character specified");
441     if (game.chars[cc].room!=displayed_room)
442         quit("!SkipUntilCharacterStops: specified character not in current room");
443 
444     // if they are not currently moving, do nothing
445     if (!game.chars[cc].walking)
446         return;
447 
448     if (play.in_cutscene)
449         quit("!SkipUntilCharacterStops: cannot be used within a cutscene");
450 
451     initialize_skippable_cutscene();
452     play.fast_forward = 2;
453     play.skip_until_char_stops = cc;
454 }
455 
EndSkippingUntilCharStops()456 void EndSkippingUntilCharStops() {
457     // not currently skipping, so ignore
458     if (play.skip_until_char_stops < 0)
459         return;
460 
461     stop_fast_forwarding();
462     play.skip_until_char_stops = -1;
463 }
464 
465 // skipwith decides how it can be skipped:
466 // 1 = ESC only
467 // 2 = any key
468 // 3 = mouse button
469 // 4 = mouse button or any key
470 // 5 = right click or ESC only
StartCutscene(int skipwith)471 void StartCutscene (int skipwith) {
472     static ScriptPosition last_cutscene_script_pos;
473 
474     if (play.in_cutscene) {
475         quitprintf("!StartCutscene: already in a cutscene; previous started in \"%s\", line %d",
476             last_cutscene_script_pos.Section.GetCStr(), last_cutscene_script_pos.Line);
477     }
478 
479     if ((skipwith < 1) || (skipwith > 5))
480         quit("!StartCutscene: invalid argument, must be 1 to 5.");
481 
482     get_script_position(last_cutscene_script_pos);
483 
484     // make sure they can't be skipping and cutsceneing at the same time
485     EndSkippingUntilCharStops();
486 
487     play.in_cutscene = skipwith;
488     initialize_skippable_cutscene();
489 }
490 
EndCutscene()491 int EndCutscene () {
492     if (play.in_cutscene == 0)
493         quit("!EndCutscene: not in a cutscene");
494 
495     int retval = play.fast_forward;
496     play.in_cutscene = 0;
497     // Stop it fast-forwarding
498     stop_fast_forwarding();
499 
500     // make sure that the screen redraws
501     invalidate_screen();
502 
503     // Return whether the player skipped it
504     return retval;
505 }
506 
sc_inputbox(const char * msg,char * bufr)507 void sc_inputbox(const char*msg,char*bufr) {
508     VALIDATE_STRING(bufr);
509     setup_for_dialog();
510     enterstringwindow(get_translation(msg),bufr);
511     restore_after_dialog();
512 }
513 
514 // GetLocationType exported function - just call through
515 // to the main function with default 0
GetLocationType(int xxx,int yyy)516 int GetLocationType(int xxx,int yyy) {
517     return __GetLocationType(xxx, yyy, 0);
518 }
519 
SaveCursorForLocationChange()520 void SaveCursorForLocationChange() {
521     // update the current location name
522     char tempo[100];
523     GetLocationName(divide_down_coordinate(mousex), divide_down_coordinate(mousey), tempo);
524 
525     if (play.get_loc_name_save_cursor != play.get_loc_name_last_time) {
526         play.get_loc_name_save_cursor = play.get_loc_name_last_time;
527         play.restore_cursor_mode_to = GetCursorMode();
528         play.restore_cursor_image_to = GetMouseCursor();
529         debug_script_log("Saving mouse: mode %d cursor %d", play.restore_cursor_mode_to, play.restore_cursor_image_to);
530     }
531 }
532 
GetLocationName(int xxx,int yyy,char * tempo)533 void GetLocationName(int xxx,int yyy,char*tempo) {
534     if (displayed_room < 0)
535         quit("!GetLocationName: no room has been loaded");
536 
537     VALIDATE_STRING(tempo);
538 
539     if (GetGUIAt(xxx, yyy) >= 0) {
540         tempo[0]=0;
541         int mover = GetInvAt (xxx, yyy);
542         if (mover > 0) {
543             if (play.get_loc_name_last_time != 1000 + mover)
544                 guis_need_update = 1;
545             play.get_loc_name_last_time = 1000 + mover;
546             strcpy(tempo,get_translation(game.invinfo[mover].name));
547         }
548         else if ((play.get_loc_name_last_time > 1000) && (play.get_loc_name_last_time < 1000 + MAX_INV)) {
549             // no longer selecting an item
550             guis_need_update = 1;
551             play.get_loc_name_last_time = -1;
552         }
553         return;
554     }
555     int loctype = GetLocationType (xxx, yyy);
556     xxx += divide_down_coordinate(offsetx);
557     yyy += divide_down_coordinate(offsety);
558     tempo[0]=0;
559     if ((xxx>=thisroom.width) | (xxx<0) | (yyy<0) | (yyy>=thisroom.height))
560         return;
561 
562     int onhs,aa;
563     if (loctype == 0) {
564         if (play.get_loc_name_last_time != 0) {
565             play.get_loc_name_last_time = 0;
566             guis_need_update = 1;
567         }
568         return;
569     }
570 
571     // on character
572     if (loctype == LOCTYPE_CHAR) {
573         onhs = getloctype_index;
574         strcpy(tempo,get_translation(game.chars[onhs].name));
575         if (play.get_loc_name_last_time != 2000+onhs)
576             guis_need_update = 1;
577         play.get_loc_name_last_time = 2000+onhs;
578         return;
579     }
580     // on object
581     if (loctype == LOCTYPE_OBJ) {
582         aa = getloctype_index;
583         strcpy(tempo,get_translation(thisroom.objectnames[aa]));
584         // Compatibility: < 3.1.1 games returned space for nameless object
585         // (presumably was a bug, but fixing it affected certain games behavior)
586         if (loaded_game_file_version < kGameVersion_311 && tempo[0] == 0) {
587             tempo[0] = ' ';
588             tempo[1] = 0;
589         }
590         if (play.get_loc_name_last_time != 3000+aa)
591             guis_need_update = 1;
592         play.get_loc_name_last_time = 3000+aa;
593         return;
594     }
595     onhs = getloctype_index;
596     if (onhs>0) strcpy(tempo,get_translation(thisroom.hotspotnames[onhs]));
597     if (play.get_loc_name_last_time != onhs)
598         guis_need_update = 1;
599     play.get_loc_name_last_time = onhs;
600 }
601 
IsKeyPressed(int keycode)602 int IsKeyPressed (int keycode) {
603 #ifdef ALLEGRO_KEYBOARD_HANDLER
604     if (keyboard_needs_poll())
605         poll_keyboard();
606     if (keycode >= AGS_EXT_KEY_SHIFT) {
607         // function keys are 12 lower in allegro 4
608         if ((keycode>=359) & (keycode<=368)) keycode-=12;
609         // F11-F12
610         else if ((keycode==433) || (keycode==434)) keycode-=76;
611         // left arrow
612         else if (keycode==375) keycode=382;
613         // right arrow
614         else if (keycode==377) keycode=383;
615         // up arrow
616         else if (keycode==372) keycode=384;
617         // down arrow
618         else if (keycode==380) keycode=385;
619         // numeric keypad
620         else if (keycode==379) keycode=338;
621         else if (keycode==380) keycode=339;
622         else if (keycode==381) keycode=340;
623         else if (keycode==375) keycode=341;
624         else if (keycode==376) keycode=342;
625         else if (keycode==377) keycode=343;
626         else if (keycode==371) keycode=344;
627         else if (keycode==372) keycode=345;
628         else if (keycode==373) keycode=346;
629         // insert
630         else if (keycode == AGS_KEYCODE_INSERT) keycode = KEY_INSERT + AGS_EXT_KEY_SHIFT;
631         // delete
632         else if (keycode == AGS_KEYCODE_DELETE) keycode = KEY_DEL + AGS_EXT_KEY_SHIFT;
633 
634         // deal with shift/ctrl/alt
635         if (keycode == 403) keycode = KEY_LSHIFT;
636         else if (keycode == 404) keycode = KEY_RSHIFT;
637         else if (keycode == 405) keycode = KEY_LCONTROL;
638         else if (keycode == 406) keycode = KEY_RCONTROL;
639         else if (keycode == 407) keycode = KEY_ALT;
640         else keycode -= AGS_EXT_KEY_SHIFT;
641 
642         if (rec_iskeypressed(keycode))
643             return 1;
644         // deal with numeric pad keys having different codes to arrow keys
645         if ((keycode == KEY_LEFT) && (rec_iskeypressed(KEY_4_PAD) != 0))
646             return 1;
647         if ((keycode == KEY_RIGHT) && (rec_iskeypressed(KEY_6_PAD) != 0))
648             return 1;
649         if ((keycode == KEY_UP) && (rec_iskeypressed(KEY_8_PAD) != 0))
650             return 1;
651         if ((keycode == KEY_DOWN) && (rec_iskeypressed(KEY_2_PAD) != 0))
652             return 1;
653         // PgDn/PgUp are equivalent to 3 and 9 on numeric pad
654         if ((keycode == KEY_9_PAD) && (rec_iskeypressed(KEY_PGUP) != 0))
655             return 1;
656         if ((keycode == KEY_3_PAD) && (rec_iskeypressed(KEY_PGDN) != 0))
657             return 1;
658         // Home/End are equivalent to 7 and 1
659         if ((keycode == KEY_7_PAD) && (rec_iskeypressed(KEY_HOME) != 0))
660             return 1;
661         if ((keycode == KEY_1_PAD) && (rec_iskeypressed(KEY_END) != 0))
662             return 1;
663         // insert/delete have numpad equivalents
664         if ((keycode == KEY_INSERT) && (rec_iskeypressed(KEY_0_PAD) != 0))
665             return 1;
666         if ((keycode == KEY_DEL) && (rec_iskeypressed(KEY_DEL_PAD) != 0))
667             return 1;
668 
669         return 0;
670     }
671     // convert ascii to scancode
672     else if ((keycode >= 'A') && (keycode <= 'Z'))
673     {
674         keycode = platform->ConvertKeycodeToScanCode(keycode);
675     }
676     else if ((keycode >= '0') && (keycode <= '9'))
677         keycode -= ('0' - KEY_0);
678     else if (keycode == 8)
679         keycode = KEY_BACKSPACE;
680     else if (keycode == 9)
681         keycode = KEY_TAB;
682     else if (keycode == 13) {
683         // check both the main return key and the numeric pad enter
684         if (rec_iskeypressed(KEY_ENTER))
685             return 1;
686         keycode = KEY_ENTER_PAD;
687     }
688     else if (keycode == ' ')
689         keycode = KEY_SPACE;
690     else if (keycode == 27)
691         keycode = KEY_ESC;
692     else if (keycode == '-') {
693         // check both the main - key and the numeric pad
694         if (rec_iskeypressed(KEY_MINUS))
695             return 1;
696         keycode = KEY_MINUS_PAD;
697     }
698     else if (keycode == '+') {
699         // check both the main + key and the numeric pad
700         if (rec_iskeypressed(KEY_EQUALS))
701             return 1;
702         keycode = KEY_PLUS_PAD;
703     }
704     else if (keycode == '/') {
705         // check both the main / key and the numeric pad
706         if (rec_iskeypressed(KEY_SLASH))
707             return 1;
708         keycode = KEY_SLASH_PAD;
709     }
710     else if (keycode == '=')
711         keycode = KEY_EQUALS;
712     else if (keycode == '[')
713         keycode = KEY_OPENBRACE;
714     else if (keycode == ']')
715         keycode = KEY_CLOSEBRACE;
716     else if (keycode == '\\')
717         keycode = KEY_BACKSLASH;
718     else if (keycode == ';')
719         keycode = KEY_SEMICOLON;
720     else if (keycode == '\'')
721         keycode = KEY_QUOTE;
722     else if (keycode == ',')
723         keycode = KEY_COMMA;
724     else if (keycode == '.')
725         keycode = KEY_STOP;
726     else {
727         debug_script_log("IsKeyPressed: unsupported keycode %d", keycode);
728         return 0;
729     }
730 
731     if (rec_iskeypressed(keycode))
732         return 1;
733     return 0;
734 #else
735     // old allegro version
736     quit("allegro keyboard handler not in use??");
737 #endif
738 }
739 
SaveScreenShot(const char * namm)740 int SaveScreenShot(const char*namm) {
741     char fileName[MAX_PATH];
742 
743     if (strchr(namm,'.') == NULL)
744         sprintf(fileName, "%s%s.bmp", saveGameDirectory, namm);
745     else
746         sprintf(fileName, "%s%s", saveGameDirectory, namm);
747 
748     Bitmap *buffer = CopyScreenIntoBitmap(play.viewport.GetWidth(), play.viewport.GetHeight());
749     if (!buffer->SaveToFile(fileName, palette) != 0)
750     {
751         delete buffer;
752         return 0;
753     }
754     delete buffer;
755     return 1;  // successful
756 }
757 
SetMultitasking(int mode)758 void SetMultitasking (int mode) {
759     if ((mode < 0) | (mode > 1))
760         quit("!SetMultitasking: invalid mode parameter");
761 
762     if (usetup.override_multitasking >= 0)
763     {
764         mode = usetup.override_multitasking;
765     }
766 
767     // Don't allow background running if full screen
768     if ((mode == 1) && (!scsystem.windowed))
769         mode = 0;
770 
771     if (mode == 0) {
772         if (set_display_switch_mode(SWITCH_PAUSE) == -1)
773             set_display_switch_mode(SWITCH_AMNESIA);
774         // install callbacks to stop the sound when switching away
775         set_display_switch_callback(SWITCH_IN, display_switch_in_resume);
776         set_display_switch_callback(SWITCH_OUT, display_switch_out_suspend);
777     }
778     else {
779         if (set_display_switch_mode (SWITCH_BACKGROUND) == -1)
780             set_display_switch_mode(SWITCH_BACKAMNESIA);
781         set_display_switch_callback(SWITCH_IN, display_switch_in);
782         set_display_switch_callback(SWITCH_OUT, display_switch_out);
783     }
784 }
785 
786 extern int getloctype_throughgui, getloctype_index;
787 
ProcessClick(int xx,int yy,int mood)788 void ProcessClick(int xx,int yy,int mood) {
789     getloctype_throughgui = 1;
790     int loctype = GetLocationType (xx, yy);
791     xx += divide_down_coordinate(offsetx);
792     yy += divide_down_coordinate(offsety);
793 
794     if ((mood==MODE_WALK) && (game.options[OPT_NOWALKMODE]==0)) {
795         int hsnum=get_hotspot_at(xx,yy);
796         if (hsnum<1) ;
797         else if (thisroom.hswalkto[hsnum].x<1) ;
798         else if (play.auto_use_walkto_points == 0) ;
799         else {
800             xx=thisroom.hswalkto[hsnum].x;
801             yy=thisroom.hswalkto[hsnum].y;
802             debug_script_log("Move to walk-to point hotspot %d", hsnum);
803         }
804         walk_character(game.playercharacter,xx,yy,0, true);
805         return;
806     }
807     play.usedmode=mood;
808 
809     if (loctype == 0) {
810         // click on nothing -> hotspot 0
811         getloctype_index = 0;
812         loctype = LOCTYPE_HOTSPOT;
813     }
814 
815     if (loctype == LOCTYPE_CHAR) {
816         if (check_click_on_character(xx,yy,mood)) return;
817     }
818     else if (loctype == LOCTYPE_OBJ) {
819         if (check_click_on_object(xx,yy,mood)) return;
820     }
821     else if (loctype == LOCTYPE_HOTSPOT)
822         RunHotspotInteraction (getloctype_index, mood);
823 }
824 
IsInteractionAvailable(int xx,int yy,int mood)825 int IsInteractionAvailable (int xx,int yy,int mood) {
826     getloctype_throughgui = 1;
827     int loctype = GetLocationType (xx, yy);
828     xx += divide_down_coordinate(offsetx);
829     yy += divide_down_coordinate(offsety);
830 
831     // You can always walk places
832     if ((mood==MODE_WALK) && (game.options[OPT_NOWALKMODE]==0))
833         return 1;
834 
835     play.check_interaction_only = 1;
836 
837     if (loctype == 0) {
838         // click on nothing -> hotspot 0
839         getloctype_index = 0;
840         loctype = LOCTYPE_HOTSPOT;
841     }
842 
843     if (loctype == LOCTYPE_CHAR) {
844         check_click_on_character(xx,yy,mood);
845     }
846     else if (loctype == LOCTYPE_OBJ) {
847         check_click_on_object(xx,yy,mood);
848     }
849     else if (loctype == LOCTYPE_HOTSPOT)
850         RunHotspotInteraction (getloctype_index, mood);
851 
852     int ciwas = play.check_interaction_only;
853     play.check_interaction_only = 0;
854 
855     if (ciwas == 2)
856         return 1;
857 
858     return 0;
859 }
860 
GetMessageText(int msg,char * buffer)861 void GetMessageText (int msg, char *buffer) {
862     VALIDATE_STRING(buffer);
863     get_message_text (msg, buffer, 0);
864 }
865 
SetSpeechFont(int fontnum)866 void SetSpeechFont (int fontnum) {
867     if ((fontnum < 0) || (fontnum >= game.numfonts))
868         quit("!SetSpeechFont: invalid font number.");
869     play.speech_font = fontnum;
870 }
871 
SetNormalFont(int fontnum)872 void SetNormalFont (int fontnum) {
873     if ((fontnum < 0) || (fontnum >= game.numfonts))
874         quit("!SetNormalFont: invalid font number.");
875     play.normal_font = fontnum;
876 }
877 
_sc_AbortGame(const char * text)878 void _sc_AbortGame(const char* text) {
879     char displbuf[STD_BUFFER_SIZE] = "!?";
880     snprintf(&displbuf[2], STD_BUFFER_SIZE - 3, "%s", text);
881     quit(displbuf);
882 }
883 
GetGraphicalVariable(const char * varName)884 int GetGraphicalVariable (const char *varName) {
885     InteractionVariable *theVar = FindGraphicalVariable(varName);
886     if (theVar == NULL) {
887         char quitmessage[120];
888         sprintf (quitmessage, "!GetGraphicalVariable: interaction variable '%s' not found", varName);
889         quit(quitmessage);
890         return 0;
891     }
892     return theVar->Value;
893 }
894 
SetGraphicalVariable(const char * varName,int p_value)895 void SetGraphicalVariable (const char *varName, int p_value) {
896     InteractionVariable *theVar = FindGraphicalVariable(varName);
897     if (theVar == NULL) {
898         char quitmessage[120];
899         sprintf (quitmessage, "!SetGraphicalVariable: interaction variable '%s' not found", varName);
900         quit(quitmessage);
901     }
902     else
903         theVar->Value = p_value;
904 }
905 
scrWait(int nloops)906 void scrWait(int nloops) {
907     if ((nloops < 1) && (loaded_game_file_version >= kGameVersion_262)) // 2.62+
908         quit("!Wait: must wait at least 1 loop");
909 
910     play.wait_counter = nloops;
911     play.key_skip_wait = 0;
912     GameLoopUntilEvent(UNTIL_MOVEEND,(long)&play.wait_counter);
913 }
914 
WaitKey(int nloops)915 int WaitKey(int nloops) {
916     if ((nloops < 1) && (loaded_game_file_version >= kGameVersion_262)) // 2.62+
917         quit("!WaitKey: must wait at least 1 loop");
918 
919     play.wait_counter = nloops;
920     play.key_skip_wait = 1;
921     GameLoopUntilEvent(UNTIL_MOVEEND,(long)&play.wait_counter);
922     if (play.wait_counter < 0)
923         return 1;
924     return 0;
925 }
926 
WaitMouseKey(int nloops)927 int WaitMouseKey(int nloops) {
928     if ((nloops < 1) && (loaded_game_file_version >= kGameVersion_262)) // 2.62+
929         quit("!WaitMouseKey: must wait at least 1 loop");
930 
931     play.wait_counter = nloops;
932     play.key_skip_wait = 3;
933     GameLoopUntilEvent(UNTIL_MOVEEND,(long)&play.wait_counter);
934     if (play.wait_counter < 0)
935         return 1;
936     return 0;
937 }
938