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 #include <string.h>
16 #include "script/script.h"
17 #include "ac/common.h"
18 #include "ac/roomstruct.h"
19 #include "ac/character.h"
20 #include "ac/dialog.h"
21 #include "ac/event.h"
22 #include "ac/game.h"
23 #include "ac/gamesetupstruct.h"
24 #include "ac/gamestate.h"
25 #include "ac/global_audio.h"
26 #include "ac/global_character.h"
27 #include "ac/global_dialog.h"
28 #include "ac/global_display.h"
29 #include "ac/global_game.h"
30 #include "ac/global_gui.h"
31 #include "ac/global_hotspot.h"
32 #include "ac/global_object.h"
33 #include "ac/global_room.h"
34 #include "ac/invwindow.h"
35 #include "ac/mouse.h"
36 #include "ac/room.h"
37 #include "ac/roomobject.h"
38 #include "script/cc_error.h"
39 #include "script/cc_options.h"
40 #include "debug/debug_log.h"
41 #include "main/game_run.h"
42 #include "media/audio/audio.h"
43 #include "media/audio/soundclip.h"
44 #include "media/video/video.h"
45 #include "script/script_runtime.h"
46 #include "util/string_utils.h"
47
48 extern GameSetupStruct game;
49 extern GameState play;
50 extern roomstruct thisroom;
51 extern int gameHasBeenRestored, displayed_room;
52 extern unsigned int load_new_game;
53 extern RoomObject*objs;
54 extern int our_eip;
55 extern CharacterInfo*playerchar;
56
57 ExecutingScript scripts[MAX_SCRIPT_AT_ONCE];
58 ExecutingScript*curscript = NULL;
59
60 PScript gamescript;
61 PScript dialogScriptsScript;
62 ccInstance *gameinst = NULL, *roominst = NULL;
63 ccInstance *dialogScriptsInst = NULL;
64 ccInstance *gameinstFork = NULL, *roominstFork = NULL;
65
66 int num_scripts=0;
67 int post_script_cleanup_stack = 0;
68
69 int inside_script=0,in_graph_script=0;
70 int no_blocking_functions = 0; // set to 1 while in rep_Exec_always
71
72 NonBlockingScriptFunction repExecAlways(REP_EXEC_ALWAYS_NAME, 0);
73 NonBlockingScriptFunction lateRepExecAlways(LATE_REP_EXEC_ALWAYS_NAME, 0);
74 NonBlockingScriptFunction getDialogOptionsDimensionsFunc("dialog_options_get_dimensions", 1);
75 NonBlockingScriptFunction renderDialogOptionsFunc("dialog_options_render", 1);
76 NonBlockingScriptFunction getDialogOptionUnderCursorFunc("dialog_options_get_active", 1);
77 NonBlockingScriptFunction runDialogOptionMouseClickHandlerFunc("dialog_options_mouse_click", 2);
78 NonBlockingScriptFunction runDialogOptionKeyPressHandlerFunc("dialog_options_key_press", 2);
79 NonBlockingScriptFunction runDialogOptionRepExecFunc("dialog_options_repexec", 1);
80
81 ScriptSystem scsystem;
82
83 std::vector<PScript> scriptModules;
84 std::vector<ccInstance *> moduleInst;
85 std::vector<ccInstance *> moduleInstFork;
86 std::vector<RuntimeScriptValue> moduleRepExecAddr;
87 int numScriptModules = 0;
88
89 std::vector<String> characterScriptObjNames;
90 String objectScriptObjNames[MAX_INIT_SPR];
91 std::vector<String> guiScriptObjNames;
92
93
run_dialog_request(int parmtr)94 int run_dialog_request (int parmtr) {
95 play.stop_dialog_at_end = DIALOG_RUNNING;
96 gameinst->RunTextScriptIParam("dialog_request", RuntimeScriptValue().SetInt32(parmtr));
97
98 if (play.stop_dialog_at_end == DIALOG_STOP) {
99 play.stop_dialog_at_end = DIALOG_NONE;
100 return -2;
101 }
102 if (play.stop_dialog_at_end >= DIALOG_NEWTOPIC) {
103 int tval = play.stop_dialog_at_end - DIALOG_NEWTOPIC;
104 play.stop_dialog_at_end = DIALOG_NONE;
105 return tval;
106 }
107 if (play.stop_dialog_at_end >= DIALOG_NEWROOM) {
108 int roomnum = play.stop_dialog_at_end - DIALOG_NEWROOM;
109 play.stop_dialog_at_end = DIALOG_NONE;
110 NewRoom(roomnum);
111 return -2;
112 }
113 play.stop_dialog_at_end = DIALOG_NONE;
114 return -1;
115 }
116
run_function_on_non_blocking_thread(NonBlockingScriptFunction * funcToRun)117 void run_function_on_non_blocking_thread(NonBlockingScriptFunction* funcToRun) {
118
119 update_script_mouse_coords();
120
121 int room_changes_was = play.room_changes;
122 funcToRun->atLeastOneImplementationExists = false;
123
124 // run modules
125 // modules need a forkedinst for this to work
126 for (int kk = 0; kk < numScriptModules; kk++) {
127 funcToRun->moduleHasFunction[kk] = moduleInstFork[kk]->DoRunScriptFuncCantBlock(funcToRun, funcToRun->moduleHasFunction[kk]);
128
129 if (room_changes_was != play.room_changes)
130 return;
131 }
132
133 funcToRun->globalScriptHasFunction = gameinstFork->DoRunScriptFuncCantBlock(funcToRun, funcToRun->globalScriptHasFunction);
134
135 if (room_changes_was != play.room_changes)
136 return;
137
138 funcToRun->roomHasFunction = roominstFork->DoRunScriptFuncCantBlock(funcToRun, funcToRun->roomHasFunction);
139 }
140
141 //-----------------------------------------------------------
142 // [IKM] 2012-06-22
143 //
144 // run_interaction_event() and run_interaction_script()
145 // are *almost* identical, except for the first parameter
146 // type.
147 // May these types be made children of the same base?
148 //-----------------------------------------------------------
149
150
151 // Returns 0 normally, or -1 to indicate that the NewInteraction has
152 // become invalid and don't run another interaction on it
153 // (eg. a room change occured)
run_interaction_event(Interaction * nint,int evnt,int chkAny,int isInv)154 int run_interaction_event (Interaction *nint, int evnt, int chkAny, int isInv) {
155
156 if (evnt < 0 || (size_t)evnt >= nint->Events.size() ||
157 (nint->Events[evnt].Response.get() == NULL) || (nint->Events[evnt].Response->Cmds.size() == 0)) {
158 // no response defined for this event
159 // If there is a response for "Any Click", then abort now so as to
160 // run that instead
161 if (chkAny < 0) ;
162 else if ((size_t)chkAny < nint->Events.size() &&
163 (nint->Events[chkAny].Response.get() != NULL) && (nint->Events[chkAny].Response->Cmds.size() > 0))
164 return 0;
165
166 // Otherwise, run unhandled_event
167 run_unhandled_event(evnt);
168
169 return 0;
170 }
171
172 if (play.check_interaction_only) {
173 play.check_interaction_only = 2;
174 return -1;
175 }
176
177 int cmdsrun = 0, retval = 0;
178 // Right, so there were some commands defined in response to the event.
179 retval = run_interaction_commandlist (nint->Events[evnt].Response.get(), &nint->Events[evnt].TimesRun, &cmdsrun);
180
181 // An inventory interaction, but the wrong item was used
182 if ((isInv) && (cmdsrun == 0))
183 run_unhandled_event (evnt);
184
185 return retval;
186 }
187
188 // Returns 0 normally, or -1 to indicate that the NewInteraction has
189 // become invalid and don't run another interaction on it
190 // (eg. a room change occured)
run_interaction_script(InteractionScripts * nint,int evnt,int chkAny,int isInv)191 int run_interaction_script(InteractionScripts *nint, int evnt, int chkAny, int isInv) {
192
193 if ((nint->ScriptFuncNames[evnt] == NULL) || (nint->ScriptFuncNames[evnt][0u] == 0)) {
194 // no response defined for this event
195 // If there is a response for "Any Click", then abort now so as to
196 // run that instead
197 if (chkAny < 0) ;
198 else if ((nint->ScriptFuncNames[chkAny] != NULL) && (nint->ScriptFuncNames[chkAny][0u] != 0))
199 return 0;
200
201 // Otherwise, run unhandled_event
202 run_unhandled_event(evnt);
203
204 return 0;
205 }
206
207 if (play.check_interaction_only) {
208 play.check_interaction_only = 2;
209 return -1;
210 }
211
212 int room_was = play.room_changes;
213
214 RuntimeScriptValue rval_null;
215
216 update_mp3();
217 if ((strstr(evblockbasename,"character")!=0) || (strstr(evblockbasename,"inventory")!=0)) {
218 // Character or Inventory (global script)
219 QueueScriptFunction(kScInstGame, nint->ScriptFuncNames[evnt]);
220 }
221 else {
222 // Other (room script)
223 QueueScriptFunction(kScInstRoom, nint->ScriptFuncNames[evnt]);
224 }
225 update_mp3();
226
227 int retval = 0;
228 // if the room changed within the action
229 if (room_was != play.room_changes)
230 retval = -1;
231
232 return retval;
233 }
234
create_global_script()235 int create_global_script() {
236 ccSetOption(SCOPT_AUTOIMPORT, 1);
237 for (int kk = 0; kk < numScriptModules; kk++) {
238 moduleInst[kk] = ccInstance::CreateFromScript(scriptModules[kk]);
239 if (moduleInst[kk] == NULL)
240 return -3;
241 // create a forked instance for rep_exec_always
242 moduleInstFork[kk] = moduleInst[kk]->Fork();
243 if (moduleInstFork[kk] == NULL)
244 return -3;
245
246 moduleRepExecAddr[kk] = moduleInst[kk]->GetSymbolAddress(REP_EXEC_NAME);
247 }
248 gameinst = ccInstance::CreateFromScript(gamescript);
249 if (gameinst == NULL)
250 return -3;
251 // create a forked instance for rep_exec_always
252 gameinstFork = gameinst->Fork();
253 if (gameinstFork == NULL)
254 return -3;
255
256 if (dialogScriptsScript != NULL)
257 {
258 dialogScriptsInst = ccInstance::CreateFromScript(dialogScriptsScript);
259 if (dialogScriptsInst == NULL)
260 return -3;
261 }
262
263 ccSetOption(SCOPT_AUTOIMPORT, 0);
264 return 0;
265 }
266
cancel_all_scripts()267 void cancel_all_scripts() {
268 int aa;
269
270 for (aa = 0; aa < num_scripts; aa++) {
271 if (scripts[aa].forked)
272 scripts[aa].inst->AbortAndDestroy();
273 else
274 scripts[aa].inst->Abort();
275 scripts[aa].numanother = 0;
276 }
277 num_scripts = 0;
278 /* if (gameinst!=NULL) ->Abort(gameinst);
279 if (roominst!=NULL) ->Abort(roominst);*/
280 }
281
GetScriptInstanceByType(ScriptInstType sc_inst)282 ccInstance *GetScriptInstanceByType(ScriptInstType sc_inst)
283 {
284 if (sc_inst == kScInstGame)
285 return gameinst;
286 else if (sc_inst == kScInstRoom)
287 return roominst;
288 return NULL;
289 }
290
QueueScriptFunction(ScriptInstType sc_inst,const char * fn_name,size_t param_count,const RuntimeScriptValue & p1,const RuntimeScriptValue & p2)291 void QueueScriptFunction(ScriptInstType sc_inst, const char *fn_name, size_t param_count, const RuntimeScriptValue &p1, const RuntimeScriptValue &p2)
292 {
293 if (inside_script)
294 // queue the script for the run after current script is finished
295 curscript->run_another (fn_name, sc_inst, param_count, p1, p2);
296 else
297 // if no script is currently running, run the requested script right away
298 RunScriptFunction(sc_inst, fn_name, param_count, p1, p2);
299 }
300
RunScriptFunction(ScriptInstType sc_inst,const char * fn_name,size_t param_count,const RuntimeScriptValue & p1,const RuntimeScriptValue & p2)301 void RunScriptFunction(ScriptInstType sc_inst, const char *fn_name, size_t param_count, const RuntimeScriptValue &p1, const RuntimeScriptValue &p2)
302 {
303 ccInstance *inst = GetScriptInstanceByType(sc_inst);
304 if (inst)
305 {
306 if (param_count == 2)
307 inst->RunTextScript2IParam(fn_name, p1, p2);
308 else if (param_count == 1)
309 inst->RunTextScriptIParam(fn_name, p1);
310 else if (param_count == 0)
311 inst->RunTextScript(fn_name);
312 }
313 }
314
315 //=============================================================================
316
317
318 char bname[MAX_FUNCTION_NAME_LEN+1],bne[MAX_FUNCTION_NAME_LEN+1];
make_ts_func_name(char * base,int iii,int subd)319 char* make_ts_func_name(char*base,int iii,int subd) {
320 snprintf(bname,MAX_FUNCTION_NAME_LEN,base,iii);
321 snprintf(bne,MAX_FUNCTION_NAME_LEN,"%s_%c",bname,subd+'a');
322 return &bne[0];
323 }
324
post_script_cleanup()325 void post_script_cleanup() {
326 // should do any post-script stuff here, like go to new room
327 if (ccError) quit(ccErrorString);
328 ExecutingScript copyof = scripts[num_scripts-1];
329 if (scripts[num_scripts-1].forked)
330 delete scripts[num_scripts-1].inst;
331 num_scripts--;
332 inside_script--;
333
334 if (num_scripts > 0)
335 curscript = &scripts[num_scripts-1];
336 else {
337 curscript = NULL;
338 }
339 // if (abort_executor) user_disabled_data2=aborted_ip;
340
341 int old_room_number = displayed_room;
342
343 // run the queued post-script actions
344 for (int ii = 0; ii < copyof.numPostScriptActions; ii++) {
345 int thisData = copyof.postScriptActionData[ii];
346
347 switch (copyof.postScriptActions[ii]) {
348 case ePSANewRoom:
349 // only change rooms when all scripts are done
350 if (num_scripts == 0) {
351 new_room(thisData, playerchar);
352 // don't allow any pending room scripts from the old room
353 // in run_another to be executed
354 return;
355 }
356 else
357 curscript->queue_action(ePSANewRoom, thisData, "NewRoom");
358 break;
359 case ePSAInvScreen:
360 invscreen();
361 break;
362 case ePSARestoreGame:
363 cancel_all_scripts();
364 try_restore_save(thisData);
365 return;
366 case ePSARestoreGameDialog:
367 restore_game_dialog();
368 return;
369 case ePSARunAGSGame:
370 cancel_all_scripts();
371 load_new_game = thisData;
372 return;
373 case ePSARunDialog:
374 do_conversation(thisData);
375 break;
376 case ePSARestartGame:
377 cancel_all_scripts();
378 restart_game();
379 return;
380 case ePSASaveGame:
381 save_game(thisData, copyof.postScriptSaveSlotDescription[ii]);
382 break;
383 case ePSASaveGameDialog:
384 save_game_dialog();
385 break;
386 default:
387 quitprintf("undefined post script action found: %d", copyof.postScriptActions[ii]);
388 }
389 // if the room changed in a conversation, for example, abort
390 if (old_room_number != displayed_room) {
391 return;
392 }
393 }
394
395
396 int jj;
397 for (jj = 0; jj < copyof.numanother; jj++) {
398 old_room_number = displayed_room;
399 QueuedScript &script = copyof.ScFnQueue[jj];
400 RunScriptFunction(script.Instance, script.FnName, script.ParamCount, script.Param1, script.Param2);
401 if (script.Instance == kScInstRoom && script.ParamCount == 1)
402 {
403 // some bogus hack for "on_call" event handler
404 play.roomscript_finished = 1;
405 }
406
407 // if they've changed rooms, cancel any further pending scripts
408 if ((displayed_room != old_room_number) || (load_new_game))
409 break;
410 }
411 copyof.numanother = 0;
412
413 }
414
quit_with_script_error(const char * functionName)415 void quit_with_script_error(const char *functionName)
416 {
417 quitprintf("%sError running function '%s':\n%s", (ccErrorIsUserError ? "!" : ""), functionName, ccErrorString);
418 }
419
get_nivalue(InteractionCommandList * nic,int idx,int parm)420 int get_nivalue (InteractionCommandList *nic, int idx, int parm) {
421 if (nic->Cmds[idx].Data[parm].Type == AGS::Common::kInterValVariable) {
422 // return the value of the variable
423 return get_interaction_variable(nic->Cmds[idx].Data[parm].Value)->Value;
424 }
425 return nic->Cmds[idx].Data[parm].Value;
426 }
427
get_interaction_variable(int varindx)428 InteractionVariable *get_interaction_variable (int varindx) {
429
430 if ((varindx >= LOCAL_VARIABLE_OFFSET) && (varindx < LOCAL_VARIABLE_OFFSET + thisroom.numLocalVars))
431 return &thisroom.localvars[varindx - LOCAL_VARIABLE_OFFSET];
432
433 if ((varindx < 0) || (varindx >= numGlobalVars))
434 quit("!invalid interaction variable specified");
435
436 return &globalvars[varindx];
437 }
438
FindGraphicalVariable(const char * varName)439 InteractionVariable *FindGraphicalVariable(const char *varName) {
440 int ii;
441 for (ii = 0; ii < numGlobalVars; ii++) {
442 if (stricmp (globalvars[ii].Name, varName) == 0)
443 return &globalvars[ii];
444 }
445 for (ii = 0; ii < thisroom.numLocalVars; ii++) {
446 if (stricmp (thisroom.localvars[ii].Name, varName) == 0)
447 return &thisroom.localvars[ii];
448 }
449 return NULL;
450 }
451
452 #define IPARAM1 get_nivalue(nicl, i, 0)
453 #define IPARAM2 get_nivalue(nicl, i, 1)
454 #define IPARAM3 get_nivalue(nicl, i, 2)
455 #define IPARAM4 get_nivalue(nicl, i, 3)
456 #define IPARAM5 get_nivalue(nicl, i, 4)
457
458 struct TempEip {
459 int oldval;
TempEipTempEip460 TempEip (int newval) {
461 oldval = our_eip;
462 our_eip = newval;
463 }
~TempEipTempEip464 ~TempEip () { our_eip = oldval; }
465 };
466
467 // the 'cmdsrun' parameter counts how many commands are run.
468 // if a 'Inv Item Was Used' check does not pass, it doesn't count
469 // so cmdsrun remains 0 if no inventory items matched
run_interaction_commandlist(InteractionCommandList * nicl,int * timesrun,int * cmdsrun)470 int run_interaction_commandlist (InteractionCommandList *nicl, int *timesrun, int*cmdsrun) {
471 size_t i;
472
473 if (nicl == NULL)
474 return -1;
475
476 for (i = 0; i < nicl->Cmds.size(); i++) {
477 cmdsrun[0] ++;
478 int room_was = play.room_changes;
479
480 switch (nicl->Cmds[i].Type) {
481 case 0: // Do nothing
482 break;
483 case 1: // Run script
484 {
485 TempEip tempip(4001);
486 RuntimeScriptValue rval_null;
487 update_mp3();
488 if ((strstr(evblockbasename,"character")!=0) || (strstr(evblockbasename,"inventory")!=0)) {
489 // Character or Inventory (global script)
490 const char *torun = make_ts_func_name(evblockbasename,evblocknum,nicl->Cmds[i].Data[0].Value);
491 // we are already inside the mouseclick event of the script, can't nest calls
492 QueueScriptFunction(kScInstGame, torun);
493 }
494 else {
495 // Other (room script)
496 const char *torun = make_ts_func_name(evblockbasename,evblocknum,nicl->Cmds[i].Data[0].Value);
497 QueueScriptFunction(kScInstRoom, torun);
498 }
499 update_mp3();
500 break;
501 }
502 case 2: // Add score (first time)
503 if (timesrun[0] > 0)
504 break;
505 timesrun[0] ++;
506 case 3: // Add score
507 GiveScore (IPARAM1);
508 break;
509 case 4: // Display Message
510 /* if (comprdata<0)
511 display_message_aschar=evb->data[ss];*/
512 DisplayMessage(IPARAM1);
513 break;
514 case 5: // Play Music
515 PlayMusicResetQueue(IPARAM1);
516 break;
517 case 6: // Stop Music
518 stopmusic ();
519 break;
520 case 7: // Play Sound
521 play_sound (IPARAM1);
522 break;
523 case 8: // Play Flic
524 play_flc_file(IPARAM1, IPARAM2);
525 break;
526 case 9: // Run Dialog
527 { int room_was = play.room_changes;
528 RunDialog(IPARAM1);
529 // if they changed room within the dialog script,
530 // the interaction command list is no longer valid
531 if (room_was != play.room_changes)
532 return -1;
533 }
534 break;
535 case 10: // Enable Dialog Option
536 SetDialogOption (IPARAM1, IPARAM2, 1);
537 break;
538 case 11: // Disable Dialog Option
539 SetDialogOption (IPARAM1, IPARAM2, 0);
540 break;
541 case 12: // Go To Screen
542 Character_ChangeRoomAutoPosition(playerchar, IPARAM1, IPARAM2);
543 return -1;
544 case 13: // Add Inventory
545 add_inventory (IPARAM1);
546 break;
547 case 14: // Move Object
548 MoveObject (IPARAM1, IPARAM2, IPARAM3, IPARAM4);
549 // if they want to wait until finished, do so
550 if (IPARAM5)
551 GameLoopUntilEvent(UNTIL_MOVEEND,(long)&objs[IPARAM1].moving);
552 break;
553 case 15: // Object Off
554 ObjectOff (IPARAM1);
555 break;
556 case 16: // Object On
557 ObjectOn (IPARAM1);
558 break;
559 case 17: // Set Object View
560 SetObjectView (IPARAM1, IPARAM2);
561 break;
562 case 18: // Animate Object
563 AnimateObject (IPARAM1, IPARAM2, IPARAM3, IPARAM4);
564 break;
565 case 19: // Move Character
566 if (IPARAM4)
567 MoveCharacterBlocking (IPARAM1, IPARAM2, IPARAM3, 0);
568 else
569 MoveCharacter (IPARAM1, IPARAM2, IPARAM3);
570 break;
571 case 20: // If Inventory Item was used
572 if (play.usedinv == IPARAM1) {
573 if (game.options[OPT_NOLOSEINV] == 0)
574 lose_inventory (play.usedinv);
575 if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
576 return -1;
577 }
578 else
579 cmdsrun[0] --;
580 break;
581 case 21: // if player has inventory item
582 if (playerchar->inv[IPARAM1] > 0)
583 if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
584 return -1;
585 break;
586 case 22: // if a character is moving
587 if (game.chars[IPARAM1].walking)
588 if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
589 return -1;
590 break;
591 case 23: // if two variables are equal
592 if (IPARAM1 == IPARAM2)
593 if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
594 return -1;
595 break;
596 case 24: // Stop character walking
597 StopMoving (IPARAM1);
598 break;
599 case 25: // Go to screen at specific co-ordinates
600 NewRoomEx (IPARAM1, IPARAM2, IPARAM3);
601 return -1;
602 case 26: // Move NPC to different room
603 if (!is_valid_character(IPARAM1))
604 quit("!Move NPC to different room: invalid character specified");
605 game.chars[IPARAM1].room = IPARAM2;
606 break;
607 case 27: // Set character view
608 SetCharacterView (IPARAM1, IPARAM2);
609 break;
610 case 28: // Release character view
611 ReleaseCharacterView (IPARAM1);
612 break;
613 case 29: // Follow character
614 FollowCharacter (IPARAM1, IPARAM2);
615 break;
616 case 30: // Stop following
617 FollowCharacter (IPARAM1, -1);
618 break;
619 case 31: // Disable hotspot
620 DisableHotspot (IPARAM1);
621 break;
622 case 32: // Enable hotspot
623 EnableHotspot (IPARAM1);
624 break;
625 case 33: // Set variable value
626 get_interaction_variable(nicl->Cmds[i].Data[0].Value)->Value = IPARAM2;
627 break;
628 case 34: // Run animation
629 scAnimateCharacter(IPARAM1, IPARAM2, IPARAM3, 0);
630 GameLoopUntilEvent(UNTIL_SHORTIS0,(long)&game.chars[IPARAM1].animating);
631 break;
632 case 35: // Quick animation
633 SetCharacterView (IPARAM1, IPARAM2);
634 scAnimateCharacter(IPARAM1, IPARAM3, IPARAM4, 0);
635 GameLoopUntilEvent(UNTIL_SHORTIS0,(long)&game.chars[IPARAM1].animating);
636 ReleaseCharacterView (IPARAM1);
637 break;
638 case 36: // Set idle animation
639 SetCharacterIdle (IPARAM1, IPARAM2, IPARAM3);
640 break;
641 case 37: // Disable idle animation
642 SetCharacterIdle (IPARAM1, -1, -1);
643 break;
644 case 38: // Lose inventory item
645 lose_inventory (IPARAM1);
646 break;
647 case 39: // Show GUI
648 InterfaceOn (IPARAM1);
649 break;
650 case 40: // Hide GUI
651 InterfaceOff (IPARAM1);
652 break;
653 case 41: // Stop running more commands
654 return -1;
655 case 42: // Face location
656 FaceLocation (IPARAM1, IPARAM2, IPARAM3);
657 break;
658 case 43: // Pause command processor
659 scrWait (IPARAM1);
660 break;
661 case 44: // Change character view
662 ChangeCharacterView (IPARAM1, IPARAM2);
663 break;
664 case 45: // If player character is
665 if (GetPlayerCharacter() == IPARAM1)
666 if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
667 return -1;
668 break;
669 case 46: // if cursor mode is
670 if (GetCursorMode() == IPARAM1)
671 if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
672 return -1;
673 break;
674 case 47: // if player has been to room
675 if (HasBeenToRoom(IPARAM1))
676 if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
677 return -1;
678 break;
679 default:
680 quit("unknown new interaction command");
681 break;
682 }
683
684 // if the room changed within the action, nicl is no longer valid
685 if (room_was != play.room_changes)
686 return -1;
687 }
688 return 0;
689
690 }
691
692 // check and abort game if the script is currently
693 // inside the rep_exec_always function
can_run_delayed_command()694 void can_run_delayed_command() {
695 if (no_blocking_functions)
696 quit("!This command cannot be used within non-blocking events such as " REP_EXEC_ALWAYS_NAME);
697 }
698
run_unhandled_event(int evnt)699 void run_unhandled_event (int evnt) {
700
701 if (play.check_interaction_only)
702 return;
703
704 int evtype=0;
705 if (strnicmp(evblockbasename,"hotspot",7)==0) evtype=1;
706 else if (strnicmp(evblockbasename,"object",6)==0) evtype=2;
707 else if (strnicmp(evblockbasename,"character",9)==0) evtype=3;
708 else if (strnicmp(evblockbasename,"inventory",9)==0) evtype=5;
709 else if (strnicmp(evblockbasename,"region",6)==0)
710 return; // no unhandled_events for regions
711
712 // clicked Hotspot 0, so change the type code
713 if ((evtype == 1) & (evblocknum == 0) & (evnt != 0) & (evnt != 5) & (evnt != 6))
714 evtype = 4;
715 if ((evtype==1) & ((evnt==0) | (evnt==5) | (evnt==6)))
716 ; // character stands on hotspot, mouse moves over hotspot, any click
717 else if ((evtype==2) & (evnt==4)) ; // any click on object
718 else if ((evtype==3) & (evnt==4)) ; // any click on character
719 else if (evtype > 0) {
720 can_run_delayed_command();
721
722 QueueScriptFunction(kScInstGame, "unhandled_event", 2, RuntimeScriptValue().SetInt32(evtype), RuntimeScriptValue().SetInt32(evnt));
723 }
724 }
725