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