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 // Scripting module script function component
24
25 #include "saga/saga.h"
26
27 #include "saga/gfx.h"
28 #include "saga/actor.h"
29 #include "saga/animation.h"
30 #include "saga/console.h"
31 #include "saga/events.h"
32 #include "saga/font.h"
33 #include "saga/interface.h"
34 #include "saga/music.h"
35 #include "saga/itedata.h"
36 #include "saga/puzzle.h"
37 #include "saga/render.h"
38 #include "saga/sound.h"
39 #include "saga/sndres.h"
40 #include "saga/resource.h"
41
42 #include "saga/script.h"
43 #include "saga/objectmap.h"
44
45 #include "saga/scene.h"
46 #include "saga/isomap.h"
47
48 #include "common/config-manager.h"
49
50 namespace Saga {
51
setupITEScriptFuncList()52 void Script::setupITEScriptFuncList() {
53 static const ScriptFunctionDescription ITEScriptFunctionsList[ITE_SCRIPT_FUNCTION_MAX] = {
54 OPCODE(sfPutString),
55 OPCODE(sfWait),
56 OPCODE(sfTakeObject),
57 OPCODE(sfIsCarried),
58 OPCODE(sfStatusBar),
59 OPCODE(sfMainMode),
60 OPCODE(sfScriptWalkTo),
61 OPCODE(sfScriptDoAction),
62 OPCODE(sfSetActorFacing),
63 OPCODE(sfStartBgdAnim),
64 OPCODE(sfStopBgdAnim),
65 OPCODE(sfLockUser),
66 OPCODE(sfPreDialog),
67 OPCODE(sfKillActorThreads),
68 OPCODE(sfFaceTowards),
69 OPCODE(sfSetFollower),
70 OPCODE(sfScriptGotoScene),
71 OPCODE(sfSetObjImage),
72 OPCODE(sfSetObjName),
73 OPCODE(sfGetObjImage),
74 OPCODE(sfGetNumber),
75 OPCODE(sfScriptOpenDoor),
76 OPCODE(sfScriptCloseDoor),
77 OPCODE(sfSetBgdAnimSpeed),
78 OPCODE(sfCycleColors),
79 OPCODE(sfDoCenterActor),
80 OPCODE(sfStartBgdAnimSpeed),
81 OPCODE(sfScriptWalkToAsync),
82 OPCODE(sfEnableZone),
83 OPCODE(sfSetActorState),
84 OPCODE(sfScriptMoveTo),
85 OPCODE(sfSceneEq),
86 OPCODE(sfDropObject),
87 OPCODE(sfFinishBgdAnim),
88 OPCODE(sfSwapActors),
89 OPCODE(sfSimulSpeech),
90 OPCODE(sfScriptWalk),
91 OPCODE(sfCycleFrames),
92 OPCODE(sfSetFrame),
93 OPCODE(sfSetPortrait),
94 OPCODE(sfSetProtagPortrait),
95 OPCODE(sfChainBgdAnim),
96 OPCODE(sfScriptSpecialWalk),
97 OPCODE(sfPlaceActor),
98 OPCODE(sfCheckUserInterrupt),
99 OPCODE(sfScriptWalkRelative),
100 OPCODE(sfScriptMoveRelative),
101 OPCODE(sfSimulSpeech2),
102 OPCODE(sfPlacard),
103 OPCODE(sfPlacardOff),
104 OPCODE(sfSetProtagState),
105 OPCODE(sfResumeBgdAnim),
106 OPCODE(sfThrowActor),
107 OPCODE(sfWaitWalk),
108 OPCODE(sfScriptSceneID),
109 OPCODE(sfChangeActorScene),
110 OPCODE(sfScriptClimb),
111 OPCODE(sfSetDoorState),
112 OPCODE(sfSetActorZ),
113 OPCODE(sfScriptText),
114 OPCODE(sfGetActorX),
115 OPCODE(sfGetActorY),
116 OPCODE(sfEraseDelta),
117 OPCODE(sfPlayMusic),
118 OPCODE(sfPickClimbOutPos),
119 OPCODE(sfTossRif),
120 OPCODE(sfShowControls),
121 OPCODE(sfShowMap),
122 OPCODE(sfPuzzleWon),
123 OPCODE(sfEnableEscape),
124 OPCODE(sfPlaySound),
125 OPCODE(sfPlayLoopedSound),
126 OPCODE(sfGetDeltaFrame),
127 OPCODE(sfShowProtect),
128 OPCODE(sfProtectResult),
129 OPCODE(sfRand),
130 OPCODE(sfFadeMusic),
131 OPCODE(sfPlayVoice)
132 };
133
134 _scriptFunctionsList = ITEScriptFunctionsList;
135 }
136
137 // Script function #0 (0x00)
138 // Print a debugging message
sfPutString(SCRIPTFUNC_PARAMS)139 void Script::sfPutString(SCRIPTFUNC_PARAMS) {
140 const char *str = thread->_strings->getString(thread->pop());
141
142 _vm->_console->debugPrintf("sfPutString: %s\n",str);
143 debug(0, "sfPutString: %s", str);
144 }
145
146 // Script function #1 (0x01) blocking
147 // Param1: time in ticks
sfWait(SCRIPTFUNC_PARAMS)148 void Script::sfWait(SCRIPTFUNC_PARAMS) {
149 int16 time = thread->pop();
150
151 if (!_skipSpeeches) {
152 thread->waitDelay(_vm->ticksToMSec(time)); // put thread to sleep
153 }
154 }
155
156 // Script function #2 (0x02)
sfTakeObject(SCRIPTFUNC_PARAMS)157 void Script::sfTakeObject(SCRIPTFUNC_PARAMS) {
158 uint16 objectId = thread->pop();
159 ObjectData *obj = _vm->_actor->getObj(objectId);
160
161 if (obj->_sceneNumber != ITE_SCENE_INV) {
162 obj->_sceneNumber = ITE_SCENE_INV;
163
164 // Normally, when objects are picked up, they should always have the same
165 // _spriteListResourceId as their _index value. Some don't in IHNM, so
166 // we fix their sprite here
167 // Fixes bugs #2057200 - "IHNM: Invisible inventory objects",
168 // #1861126 - "IHNM: Crash when Gorrister cuts sheet in the mooring ring"
169 // and some incorrect objects in the IHNM demo
170 if (_vm->getGameId() == GID_IHNM)
171 obj->_spriteListResourceId = obj->_index;
172
173 _vm->_interface->addToInventory(objectId);
174 }
175 }
176
177 // Script function #3 (0x03)
178 // Check if an object is carried.
sfIsCarried(SCRIPTFUNC_PARAMS)179 void Script::sfIsCarried(SCRIPTFUNC_PARAMS) {
180 uint16 objectId = thread->pop();
181 CommonObjectData *object;
182
183 if (_vm->_actor->validObjId(objectId)) {
184 object = _vm->_actor->getObj(objectId);
185 thread->_returnValue = (object->_sceneNumber == ITE_SCENE_INV) ? 1 : 0;
186 } else {
187 thread->_returnValue = 0;
188 }
189 }
190
191 // Script function #4 (0x04) nonblocking
192 // Set the command display to the specified text string
193 // Param1: dialogue index of string
sfStatusBar(SCRIPTFUNC_PARAMS)194 void Script::sfStatusBar(SCRIPTFUNC_PARAMS) {
195 _vm->_interface->setStatusText(thread->_strings->getString(thread->pop()));
196 }
197
198 // Script function #5 (0x05)
sfMainMode(SCRIPTFUNC_PARAMS)199 void Script::sfMainMode(SCRIPTFUNC_PARAMS) {
200 _vm->_actor->_centerActor = _vm->_actor->_protagonist;
201
202 showVerb();
203 _vm->_interface->activate();
204 _vm->_interface->setMode(kPanelMain);
205 // Sometimes, the active cutaway is cleared after this opcode is called,
206 // resulting in an incorrect mode being set. An example is Ellen's chapter
207 // in IHNM, when using the computer with the chaos trebler CD. Make sure
208 // that the saved mode is kPanelMain, so that it won't get overwritten
209 // by an incorrect stored mode
210 _vm->_interface->rememberMode();
211
212 if (_vm->getGameId() == GID_ITE)
213 setPointerVerb();
214
215 // The early Windows and Mac demos of ITE were non-interactive. In those demos,
216 // the intro is shown and then when the first scene is shown, there's a dialog
217 // thanking the user for playing the demo and asking him to buy the full game,
218 // without allowing him to continue any further. The game data itself for these
219 // demos does not contain any scripts for the first scene (i.e. there's no text
220 // in the game data to look at Rif's silver medallion). Also, there are no more
221 // scenes apart from the Grand Tournament scene. This opcode is called in those
222 // demos, and I assume that its use there is to just show the popup window and
223 // exit the game. Therefore, once this opcode is called in the older ITE demos,
224 // exit the game. Known non-interactive demos are GID_ITE_MACDEMO1 and
225 // GID_ITE_WINDEMO1
226 if (_vm->_script->isNonInteractiveDemo())
227 _vm->quitGame();
228 }
229
230 // Script function #6 (0x06) blocking
231 // Param1: actor id
232 // Param2: actor x
233 // Param3: actor y
sfScriptWalkTo(SCRIPTFUNC_PARAMS)234 void Script::sfScriptWalkTo(SCRIPTFUNC_PARAMS) {
235 uint16 actorId = thread->pop();
236 ActorData *actor = _vm->_actor->getActor(actorId);
237 Location actorLocation;
238 actorLocation.x = thread->pop();
239 actorLocation.y = thread->pop();
240 actorLocation.z = actor->_location.z;
241
242 actor->_flags &= ~kFollower;
243
244 if (_vm->_actor->actorWalkTo(actorId, actorLocation)) {
245 thread->waitWalk(actor);
246 }
247 }
248
249 // Script function #7 (0x07)
250 // Param1: actor id
251 // Param2: action
252 // Param3: theObject
253 // Param4: withObject
sfScriptDoAction(SCRIPTFUNC_PARAMS)254 void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) {
255 uint16 objectId = thread->pop();
256 uint16 action = thread->pop();
257 uint16 theObject = thread->pop();
258 uint16 withObject = thread->pop();
259 int16 scriptEntryPointNumber;
260 int16 moduleNumber;
261 ActorData *actor;
262 ObjectData *obj;
263 const HitZone *hitZone;
264 Event event;
265
266 // If the player uses an object and then immediately reuses that object
267 // (without it being shown in the verb area), the object returned is wrong (0),
268 // so we make it equal to the second object here.
269 // Fixes bug #1861863 - "ITE: Crash when using Eeah with Eeah"
270 if (theObject == 0 && objectId == 0 && withObject > 0)
271 theObject = objectId = withObject;
272
273 switch (objectTypeId(objectId)) {
274 case kGameObjectObject:
275 obj = _vm->_actor->getObj(objectId);
276 scriptEntryPointNumber = obj->_scriptEntrypointNumber;
277 if (scriptEntryPointNumber <= 0) {
278 return;
279 }
280 moduleNumber = 0;
281 if (_vm->getGameId() == GID_IHNM)
282 moduleNumber = _vm->_scene->getScriptModuleNumber();
283 break;
284 case kGameObjectActor:
285 actor = _vm->_actor->getActor(objectId);
286 scriptEntryPointNumber = actor->_scriptEntrypointNumber;
287 if (scriptEntryPointNumber <= 0) {
288 return;
289 }
290 if (actor->_flags & (kProtagonist | kFollower)) {
291 moduleNumber = 0;
292 } else {
293 moduleNumber = _vm->_scene->getScriptModuleNumber();
294 }
295 if (_vm->getGameId() == GID_IHNM)
296 moduleNumber = _vm->_scene->getScriptModuleNumber();
297 break;
298 case kGameObjectHitZone:
299 case kGameObjectStepZone:
300 if (objectTypeId(objectId) == kGameObjectHitZone)
301 hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(objectId));
302 else
303 hitZone = _vm->_scene->_actionMap->getHitZone(objectIdToIndex(objectId));
304
305 if (hitZone == NULL)
306 return;
307
308 scriptEntryPointNumber = hitZone->getScriptNumber();
309 moduleNumber = _vm->_scene->getScriptModuleNumber();
310 break;
311 default:
312 // Unknown case, do nothing
313 warning("Script::sfScriptDoAction wrong object type 0x%X", objectId);
314 return;
315 }
316
317 event.type = kEvTOneshot;
318 event.code = kScriptEvent;
319 event.op = kEventExecNonBlocking;
320 event.time = 0;
321 event.param = moduleNumber;
322 event.param2 = scriptEntryPointNumber;
323 event.param3 = action; // Action
324 event.param4 = theObject; // Object
325 event.param5 = withObject; // With Object
326 event.param6 = objectId;
327 _vm->_events->queue(event);
328 }
329
330 // Script function #8 (0x08) nonblocking
331 // Param1: actor id
332 // Param2: actor orientation
sfSetActorFacing(SCRIPTFUNC_PARAMS)333 void Script::sfSetActorFacing(SCRIPTFUNC_PARAMS) {
334 ActorData *actor = _vm->_actor->getActor(thread->pop());
335 int actorDirection = thread->pop();
336
337 actor->_facingDirection = actor->_actionDirection = actorDirection;
338 actor->_targetObject = ID_NOTHING;
339 }
340
341 // Script function #9 (0x09)
sfStartBgdAnim(SCRIPTFUNC_PARAMS)342 void Script::sfStartBgdAnim(SCRIPTFUNC_PARAMS) {
343 int16 animId = thread->pop();
344 int16 cycles = thread->pop();
345
346 _vm->_anim->setCycles(animId, cycles);
347 _vm->_anim->setFrameTime(animId, _vm->ticksToMSec(kRepeatSpeedTicks));
348
349 if (!_vm->_anim->isPlaying(animId))
350 _vm->_anim->play(animId, 0);
351
352 debug(1, "sfStartBgdAnim(%d, %d)", animId, cycles);
353 }
354
355 // Script function #10 (0x0A)
sfStopBgdAnim(SCRIPTFUNC_PARAMS)356 void Script::sfStopBgdAnim(SCRIPTFUNC_PARAMS) {
357 int16 animId = thread->pop();
358
359 _vm->_anim->stop(animId);
360
361 debug(1, "sfStopBgdAnim(%d)", animId);
362 }
363
364 // Script function #11 (0x0B) nonblocking
365 // If the parameter is true, the user interface is disabled while script
366 // continues to run. If the parameter is false, the user interface is
367 // reenabled.
368 // Param1: boolean
sfLockUser(SCRIPTFUNC_PARAMS)369 void Script::sfLockUser(SCRIPTFUNC_PARAMS) {
370 int16 param = thread->pop();
371
372 if (param != 0) {
373 _vm->_interface->deactivate();
374 } else {
375 _vm->_interface->activate();
376 }
377
378 debug(1, "sfLockUser(%d)", param);
379 }
380
381 // Script function #12 (0x0C)
382 // Disables mouse input, etc.
sfPreDialog(SCRIPTFUNC_PARAMS)383 void Script::sfPreDialog(SCRIPTFUNC_PARAMS) {
384 _vm->_interface->deactivate();
385 _vm->_interface->converseClear();
386
387 if (_vm->_interface->isInMainMode())
388 _vm->_interface->setMode(kPanelConverse);
389 else
390 _vm->_interface->converseDisplayText();
391
392 _vm->_interface->setMode(kPanelNull);
393 }
394
395 // Script function #13 (0x0D)
sfKillActorThreads(SCRIPTFUNC_PARAMS)396 void Script::sfKillActorThreads(SCRIPTFUNC_PARAMS) {
397 ScriptThreadList::iterator threadIterator;
398 int16 actorId = thread->pop();
399
400 for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
401 ScriptThread &anotherThread = *threadIterator;
402 if ((&anotherThread != thread) && (anotherThread._threadVars[kThreadVarActor] == actorId)) {
403 anotherThread._flags &= ~kTFlagWaiting;
404 anotherThread._flags |= kTFlagAborted;
405 }
406 }
407 }
408
409 // Script function #14 (0x0E)
410 // Param1: actor id
411 // Param2: object id
sfFaceTowards(SCRIPTFUNC_PARAMS)412 void Script::sfFaceTowards(SCRIPTFUNC_PARAMS) {
413 ActorData *actor = _vm->_actor->getActor(thread->pop());
414 actor->_targetObject = thread->pop();
415 }
416
417 // Script function #15 (0x0F)
418 // Param1: actor id
419 // Param2: target object
sfSetFollower(SCRIPTFUNC_PARAMS)420 void Script::sfSetFollower(SCRIPTFUNC_PARAMS) {
421 int16 actorId = thread->pop();
422 ActorData *actor = _vm->_actor->getActor(actorId);
423 actor->_targetObject = thread->pop();
424
425 debug(1, "sfSetFollower(%d, %d) [%d]", actorId, actor->_targetObject, _vm->_actor->actorIdToIndex(actorId));
426
427 if (actor->_targetObject != ID_NOTHING) {
428 actor->_flags |= kFollower;
429 actor->_actorFlags &= ~kActorNoFollow;
430 } else {
431 actor->_flags &= ~kFollower;
432 }
433 }
434
435 // Script function #16 (0x10)
sfScriptGotoScene(SCRIPTFUNC_PARAMS)436 void Script::sfScriptGotoScene(SCRIPTFUNC_PARAMS) {
437 int16 sceneNumber = thread->pop();
438 int16 entrance = thread->pop();
439
440 #ifdef ENABLE_IHNM
441 if (_vm->getGameId() == GID_IHNM) {
442 _vm->_gfx->setCursor(kCursorBusy);
443 }
444 #endif
445
446 if (_vm->getGameId() == GID_ITE && sceneNumber < 0) {
447 _vm->quitGame();
448 return;
449 }
450
451 #ifdef ENABLE_IHNM
452 if (_vm->getGameId() == GID_IHNM && sceneNumber == 0) {
453 _vm->_scene->creditsScene();
454 return;
455 }
456 #endif
457
458 // It is possible to leave scene when converse panel is on,
459 // particulalrly it may happen at Moneychanger tent. This
460 // prevents this from happening.
461 if (_vm->_interface->getMode() == kPanelConverse) {
462 _vm->_interface->setMode(kPanelMain);
463 }
464
465 // changeScene calls loadScene which calls setVerb. setVerb resets all pending objects and object flags
466 if (sceneNumber == -1 && _vm->getGameId() == GID_IHNM) {
467 // Return back to the character selection screen in IHNM
468 _vm->_scene->changeScene(154, entrance, kTransitionFade, 8);
469 } else {
470 _vm->_scene->changeScene(sceneNumber, entrance, (sceneNumber == ITE_SCENE_ENDCREDIT1) ? kTransitionFade : kTransitionNoFade);
471 }
472
473 if (_vm->_interface->getMode() == kPanelPlacard ||
474 _vm->_interface->getMode() == kPanelCutaway ||
475 _vm->_interface->getMode() == kPanelVideo) {
476 _vm->_gfx->showCursor(true);
477 _vm->_interface->setMode(kPanelMain);
478 }
479
480 _pendingVerb = _vm->_script->getVerbType(kVerbNone);
481 _currentObject[0] = _currentObject[1] = ID_NOTHING;
482 showVerb(); // calls setStatusText("")
483
484 #ifdef ENABLE_IHNM
485 if (_vm->getGameId() == GID_IHNM) {
486 // There are some cutaways which are not removed by game scripts, like the cutaway
487 // after the intro of IHNM or the cutaway at the end of Ellen's part in the IHNM demo.
488 // Clear any remaining cutaways here
489 _vm->_anim->clearCutaway();
490 _vm->_gfx->setCursor(kCursorNormal);
491 }
492 #endif
493
494 }
495
496 // Script function #17 (0x11)
497 // Param1: object id
498 // Param2: sprite index
sfSetObjImage(SCRIPTFUNC_PARAMS)499 void Script::sfSetObjImage(SCRIPTFUNC_PARAMS) {
500 uint16 objectId = thread->pop();
501 uint16 spriteId = thread->pop();
502
503 _vm->_actor->getObj(objectId)->_spriteListResourceId = spriteId + (_vm->getGameId() == GID_ITE ? 9 : 0);
504 _vm->_interface->refreshInventory();
505 }
506
507 // Script function #18 (0x12)
508 // Param1: object id
509 // Param2: name index
sfSetObjName(SCRIPTFUNC_PARAMS)510 void Script::sfSetObjName(SCRIPTFUNC_PARAMS) {
511 uint16 objectId = thread->pop();
512 uint16 nameIdx = thread->pop();
513 _vm->_actor->getObj(objectId)->_nameIndex = nameIdx;
514 }
515
516 // Script function #19 (0x13)
517 // Param1: object id
sfGetObjImage(SCRIPTFUNC_PARAMS)518 void Script::sfGetObjImage(SCRIPTFUNC_PARAMS) {
519 uint16 objectId = thread->pop();
520 thread->_returnValue = _vm->_actor->getObj(objectId)->_spriteListResourceId - (_vm->getGameId() == GID_ITE ? 9 : 0);
521 }
522
523 // Script function #20 (0x14)
sfGetNumber(SCRIPTFUNC_PARAMS)524 void Script::sfGetNumber(SCRIPTFUNC_PARAMS) {
525 if (_vm->_interface->_statusTextInputState == kStatusTextInputFirstRun) {
526 _vm->_interface->enterStatusString();
527 thread->wait(kWaitTypeStatusTextInput);
528 disContinue = true;
529 } else {
530 if (_vm->_interface->_statusTextInputState == kStatusTextInputAborted) {
531 thread->_returnValue = -1;
532 } else {
533 thread->_returnValue = atoi(_vm->_interface->_statusTextInputString);
534 }
535
536 _vm->_interface->_statusTextInputState = kStatusTextInputFirstRun;
537 }
538 }
539
540 // Script function #21 (0x15)
541 // Param1: door #
sfScriptOpenDoor(SCRIPTFUNC_PARAMS)542 void Script::sfScriptOpenDoor(SCRIPTFUNC_PARAMS) {
543 int16 doorNumber = thread->pop();
544
545 if (_vm->_scene->getFlags() & kSceneFlagISO) {
546 _vm->_isoMap->setTileDoorState(doorNumber, 1);
547 } else {
548 _vm->_scene->setDoorState(doorNumber, 0);
549 }
550 }
551
552 // Script function #22 (0x16)
553 // Param1: door #
sfScriptCloseDoor(SCRIPTFUNC_PARAMS)554 void Script::sfScriptCloseDoor(SCRIPTFUNC_PARAMS) {
555 int16 doorNumber = thread->pop();
556
557 if (_vm->_scene->getFlags() & kSceneFlagISO) {
558 _vm->_isoMap->setTileDoorState(doorNumber, 0);
559 } else {
560 _vm->_scene->setDoorState(doorNumber, 0xff);
561 }
562 }
563
564 // Script function #23 (0x17)
sfSetBgdAnimSpeed(SCRIPTFUNC_PARAMS)565 void Script::sfSetBgdAnimSpeed(SCRIPTFUNC_PARAMS) {
566 int16 animId = thread->pop();
567 int16 speed = thread->pop();
568
569 _vm->_anim->setFrameTime(animId, _vm->ticksToMSec(speed));
570 debug(1, "sfSetBgdAnimSpeed(%d, %d)", animId, speed);
571 }
572
573 // Script function #24 (0x18)
sfCycleColors(SCRIPTFUNC_PARAMS)574 void Script::sfCycleColors(SCRIPTFUNC_PARAMS) {
575 sfStub("sfCycleColors", thread, nArgs);
576
577 error("Please, report this to sev");
578 }
579
580 // Script function #25 (0x19)
581 // Param1: actor id
sfDoCenterActor(SCRIPTFUNC_PARAMS)582 void Script::sfDoCenterActor(SCRIPTFUNC_PARAMS) {
583 _vm->_actor->_centerActor = _vm->_actor->getActor(thread->pop());
584 }
585
586 // Script function #26 (0x1A) nonblocking
587 // Starts the specified animation
sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS)588 void Script::sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS) {
589 int16 animId = thread->pop();
590 int16 cycles = thread->pop();
591 int16 speed = thread->pop();
592
593 _vm->_anim->setCycles(animId, cycles);
594 _vm->_anim->setFrameTime(animId, _vm->ticksToMSec(speed));
595
596 if (!_vm->_anim->isPlaying(animId))
597 _vm->_anim->play(animId, 0);
598
599 debug(1, "sfStartBgdAnimSpeed(%d, %d, %d)", animId, cycles, speed);
600 }
601
602 // Script function #27 (0x1B) nonblocking
603 // Param1: actor id
604 // Param2: actor x
605 // Param3: actor y
sfScriptWalkToAsync(SCRIPTFUNC_PARAMS)606 void Script::sfScriptWalkToAsync(SCRIPTFUNC_PARAMS) {
607 int16 actorId = thread->pop();
608 ActorData *actor = _vm->_actor->getActor(actorId);
609 Location actorLocation;
610 actorLocation.x = thread->pop();
611 actorLocation.y = thread->pop();
612 actorLocation.z = actor->_location.z;
613
614 actor->_flags &= ~kFollower;
615 _vm->_actor->actorWalkTo(actorId, actorLocation);
616 }
617
618 // Script function #28 (0x1C)
sfEnableZone(SCRIPTFUNC_PARAMS)619 void Script::sfEnableZone(SCRIPTFUNC_PARAMS) {
620 uint16 objectId = thread->pop();
621 int16 flag = thread->pop();
622 HitZone *hitZone;
623
624 if (objectTypeId(objectId) == 0)
625 return;
626 else if (objectTypeId(objectId) == kGameObjectHitZone)
627 hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(objectId));
628 else
629 hitZone = _vm->_scene->_actionMap->getHitZone(objectIdToIndex(objectId));
630
631 if (hitZone == NULL)
632 return;
633
634 if (flag) {
635 hitZone->setFlag(kHitZoneEnabled);
636 } else {
637 hitZone->clearFlag(kHitZoneEnabled);
638 _vm->_actor->_protagonist->_lastZone = NULL;
639 }
640 }
641
642 // Script function #29 (0x1D)
643 // Param1: actor id
644 // Param2: current action
sfSetActorState(SCRIPTFUNC_PARAMS)645 void Script::sfSetActorState(SCRIPTFUNC_PARAMS) {
646 ActorData *actor = _vm->_actor->getActor(thread->pop());
647 int currentAction = thread->pop();
648
649 if ((currentAction >= kActionWalkToPoint) && (currentAction <= kActionWalkToPoint)) {
650 wakeUpActorThread(kWaitTypeWalk, actor);
651 }
652 actor->_currentAction = currentAction;
653 actor->_actorFlags &= ~kActorBackwards;
654 }
655
656 // Script function #30 (0x1E) nonblocking
657 // Param1: actor id
658 // Param2: actor pos x
659 // Param3: actor pos y
sfScriptMoveTo(SCRIPTFUNC_PARAMS)660 void Script::sfScriptMoveTo(SCRIPTFUNC_PARAMS) {
661 int16 objectId = thread->pop();
662 Location location;
663 location.x = thread->pop();
664 location.y = thread->pop();
665 ActorData *actor;
666 ObjectData *obj;
667
668 if (_vm->_actor->validActorId(objectId)) {
669 actor = _vm->_actor->getActor(objectId);
670
671 actor->_location.x = location.x;
672 actor->_location.y = location.y;
673 } else {
674 if (_vm->_actor->validObjId(objectId)) {
675 obj = _vm->_actor->getObj(objectId);
676 obj->_location.x = location.x;
677 obj->_location.y = location.y;
678 }
679 }
680 }
681
682 // Script function #31 (0x21)
683 // Param1: sceneNumber
sfSceneEq(SCRIPTFUNC_PARAMS)684 void Script::sfSceneEq(SCRIPTFUNC_PARAMS) {
685 int16 sceneNumber = thread->pop();
686
687 if (_vm->_scene->getSceneResourceId(sceneNumber) == _vm->_scene->currentSceneResourceId())
688 thread->_returnValue = 1;
689 else
690 thread->_returnValue = 0;
691 }
692
693 // Script function #32 (0x20)
sfDropObject(SCRIPTFUNC_PARAMS)694 void Script::sfDropObject(SCRIPTFUNC_PARAMS) {
695 uint16 objectId = thread->pop();
696 ObjectData *obj = _vm->_actor->getObj(objectId);
697 uint16 spriteId = thread->pop();
698 obj->_location.x = thread->pop();
699 obj->_location.y = thread->pop();
700
701 if (obj->_sceneNumber == ITE_SCENE_INV) {
702 _vm->_interface->removeFromInventory(objectId);
703 }
704
705 obj->_sceneNumber = _vm->_scene->currentSceneNumber();
706
707 if (_vm->getGameId() == GID_IHNM) {
708 // Don't update _spriteListResourceId if spriteId is 0 and the object is not the
709 // psychic profile. If spriteId == 0, the object's sprite is incorrectly reset.
710 // This occurs in the IHNM demo and with some incorrect scripts in the retail version
711 // of the game
712 if (spriteId > 0 || (spriteId == 0 && objectId == IHNM_OBJ_PROFILE))
713 obj->_spriteListResourceId = spriteId;
714 } else {
715 obj->_spriteListResourceId = spriteId + 9;
716 }
717 }
718
719 // Script function #33 (0x21)
sfFinishBgdAnim(SCRIPTFUNC_PARAMS)720 void Script::sfFinishBgdAnim(SCRIPTFUNC_PARAMS) {
721 int16 animId = thread->pop();
722
723 _vm->_anim->finish(animId);
724
725 debug(1, "sfFinishBgdAnim(%d)", animId);
726 }
727
728 // Script function #34 (0x22)
729 // Param1: actor id 1
730 // Param2: actor id 2
sfSwapActors(SCRIPTFUNC_PARAMS)731 void Script::sfSwapActors(SCRIPTFUNC_PARAMS) {
732 int16 actorId1 = thread->pop();
733 int16 actorId2 = thread->pop();
734 ActorData *actor1 = _vm->_actor->getActor(actorId1);
735 ActorData *actor2 = _vm->_actor->getActor(actorId2);
736
737 SWAP(actor1->_location, actor2->_location);
738 SWAP(actor1->_lastZone, actor2->_lastZone);
739
740 if (actor1->_flags & kProtagonist) {
741 actor1->_flags &= ~kProtagonist;
742 actor2->_flags |= kProtagonist;
743 _vm->_actor->_protagonist = _vm->_actor->_centerActor = actor2;
744 } else if (actor2->_flags & kProtagonist) {
745 actor2->_flags &= ~kProtagonist;
746 actor1->_flags |= kProtagonist;
747 _vm->_actor->_protagonist = _vm->_actor->_centerActor = actor1;
748 }
749 }
750
751 // Script function #35 (0x23)
752 // Param1: string rid
753 // Param2: actorscount
754 // Param3: actor id1
755 ///....
756 // Param3: actor idN
sfSimulSpeech(SCRIPTFUNC_PARAMS)757 void Script::sfSimulSpeech(SCRIPTFUNC_PARAMS) {
758 int16 stringId = thread->pop();
759 int16 actorsCount = thread->pop();
760 int i;
761 uint16 actorsIds[ACTOR_SPEECH_ACTORS_MAX];
762 const char *string = thread->_strings->getString(stringId);
763 int16 sampleResourceId = -1;
764
765 if (actorsCount > ACTOR_SPEECH_ACTORS_MAX)
766 error("sfSimulSpeech actorsCount=0x%X exceed ACTOR_SPEECH_ACTORS_MAX", actorsCount);
767
768 for (i = 0; i < actorsCount; i++)
769 actorsIds[i] = thread->pop();
770
771 if (!thread->_voiceLUT->empty()) {
772 if (_vm->getGameId() == GID_IHNM && stringId >= 338) {
773 sampleResourceId = -1;
774 } else {
775 sampleResourceId = (*thread->_voiceLUT)[stringId];
776 if (sampleResourceId <= 0 || sampleResourceId > 4000)
777 sampleResourceId = -1;
778 }
779 }
780
781 _vm->_actor->simulSpeech(string, actorsIds, actorsCount, 0, sampleResourceId);
782 thread->wait(kWaitTypeSpeech);
783 }
784
785 // Script function #36 (0x24) ?
786 // Param1: actor id
787 // Param2: actor x
788 // Param3: actor y
789 // Param4: actor walk flag
sfScriptWalk(SCRIPTFUNC_PARAMS)790 void Script::sfScriptWalk(SCRIPTFUNC_PARAMS) {
791 int16 actorId = thread->pop();
792 ActorData *actor = _vm->_actor->getActor(actorId);
793 Location actorLocation;
794 actorLocation.x = thread->pop();
795 actorLocation.y = thread->pop();
796 actorLocation.z = actor->_location.z;
797 uint16 walkFlags = thread->pop();
798
799 actor->_flags &= ~kFollower;
800 _vm->_actor->realLocation(actorLocation, ID_NOTHING, walkFlags);
801
802 if (_vm->_actor->actorWalkTo(actorId, actorLocation) && !(walkFlags & kWalkAsync)) {
803 thread->waitWalk(actor);
804 }
805
806 if (walkFlags & kWalkBackPedal) {
807 actor->_actorFlags |= kActorBackwards;
808 }
809
810 actor->_actorFlags = (actor->_actorFlags & ~kActorFacingMask) | (walkFlags & kActorFacingMask);
811 }
812
813 // Script function #37 (0x25) nonblocking
814 // Param1: actor id
815 // Param2: flags telling how to cycle the frames
816 // Param3: cycle frame number
817 // Param4: cycle delay
sfCycleFrames(SCRIPTFUNC_PARAMS)818 void Script::sfCycleFrames(SCRIPTFUNC_PARAMS) {
819 ActorData *actor = _vm->_actor->getActor(thread->pop());
820 int16 flags = thread->pop();
821 int cycleFrameSequence = thread->pop();
822 int cycleDelay = thread->pop();
823
824 if (flags & kCyclePong) {
825 actor->_currentAction = kActionPongFrames;
826 } else {
827 actor->_currentAction = kActionCycleFrames;
828 }
829
830 actor->_actorFlags &= ~(kActorContinuous | kActorRandom | kActorBackwards);
831
832 if (!(flags & kCycleOnce)) {
833 actor->_actorFlags |= kActorContinuous;
834 }
835 if (flags & kCycleRandom) {
836 actor->_actorFlags |= kActorRandom;
837 }
838 if (flags & kCycleReverse) {
839 if (_vm->getGameId() == GID_IHNM &&
840 _vm->_scene->currentChapterNumber() == 2 && _vm->_scene->currentSceneNumber() == 41) {
841 // WORKAROUND: Prevent Benny from walking backwards after talking to the child via the monitor. This
842 // occurs in the original as well, and is fixed by not setting the kActorBackwards flag at this point
843 } else {
844 actor->_actorFlags |= kActorBackwards;
845 }
846 }
847
848 actor->_cycleFrameSequence = cycleFrameSequence;
849 actor->_cycleTimeCount = 0;
850 actor->_cycleDelay = cycleDelay;
851 actor->_actionCycle = 0;
852 }
853
854 // Script function #38 (0x26) nonblocking
855 // Param1: actor id
856 // Param2: frame type
857 // Param3: frame offset
sfSetFrame(SCRIPTFUNC_PARAMS)858 void Script::sfSetFrame(SCRIPTFUNC_PARAMS) {
859 int16 actorId = thread->pop();
860 ActorData *actor = _vm->_actor->getActor(actorId);
861 int frameType = thread->pop();
862 int frameOffset = thread->pop();
863 ActorFrameRange *frameRange = _vm->_actor->getActorFrameRange(actorId, frameType);
864
865 actor->_frameNumber = frameRange->frameIndex + frameOffset;
866
867 if (actor->_currentAction != kActionFall) {
868 actor->_currentAction = kActionFreeze;
869 }
870 }
871
872 // Script function #39 (0x27)
873 // Sets the right-hand portrait
sfSetPortrait(SCRIPTFUNC_PARAMS)874 void Script::sfSetPortrait(SCRIPTFUNC_PARAMS) {
875 _vm->_interface->setRightPortrait(thread->pop());
876 }
877
878 // Script function #40 (0x28)
879 // Sets the left-hand portrait
sfSetProtagPortrait(SCRIPTFUNC_PARAMS)880 void Script::sfSetProtagPortrait(SCRIPTFUNC_PARAMS) {
881 _vm->_interface->setLeftPortrait(thread->pop());
882 }
883
884 // Script function #41 (0x29) nonblocking
885 // Links the specified animations for playback
886
887 // Param1: ?
888 // Param2: total linked frame count
889 // Param3: animation id link target
890 // Param4: animation id link source
sfChainBgdAnim(SCRIPTFUNC_PARAMS)891 void Script::sfChainBgdAnim(SCRIPTFUNC_PARAMS) {
892 int16 animId1 = thread->pop();
893 int16 animId = thread->pop();
894 int16 cycles = thread->pop();
895 int16 speed = thread->pop();
896
897 if (speed >= 0) {
898 _vm->_anim->setCycles(animId, cycles);
899 _vm->_anim->stop(animId);
900 _vm->_anim->setFrameTime(animId, _vm->ticksToMSec(speed));
901 }
902
903 _vm->_anim->link(animId1, animId);
904 debug(1, "sfChainBgdAnim(%d, %d, %d, %d)", animId1, animId, cycles, speed);
905 }
906
907 // Script function #42 (0x2A)
908 // Param1: actor id
909 // Param2: actor x
910 // Param3: actor y
911 // Param4: frame seq
sfScriptSpecialWalk(SCRIPTFUNC_PARAMS)912 void Script::sfScriptSpecialWalk(SCRIPTFUNC_PARAMS) {
913 int16 actorId = thread->pop();
914 ActorData *actor = _vm->_actor->getActor(actorId);
915 Location actorLocation;
916 actorLocation.x = thread->pop();
917 actorLocation.y = thread->pop();
918 actorLocation.z = actor->_location.z;
919 int16 walkFrameSequence = thread->pop();
920
921 _vm->_actor->actorWalkTo(actorId, actorLocation);
922 actor->_walkFrameSequence = walkFrameSequence;
923 }
924
925 // Script function #43 (0x2B) nonblocking
926 // Param1: actor id
927 // Param2: actor x
928 // Param3: actor y
929 // Param4: actor direction
930 // Param5: actor action
931 // Param6: actor frame number
sfPlaceActor(SCRIPTFUNC_PARAMS)932 void Script::sfPlaceActor(SCRIPTFUNC_PARAMS) {
933 int16 actorId = thread->pop();
934 ActorData *actor = _vm->_actor->getActor(actorId);
935 actor->_location.x = thread->pop();
936 actor->_location.y = thread->pop();
937 actor->_facingDirection = actor->_actionDirection = thread->pop();
938 int frameType = thread->pop();
939 int frameOffset = thread->pop();
940 ActorFrameRange *frameRange;
941
942 debug(1, "sfPlaceActor(id = 0x%X, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actor->_location.x,
943 actor->_location.y, actor->_facingDirection, frameType, frameOffset);
944
945 if (frameType >= 0) {
946 frameRange = _vm->_actor->getActorFrameRange(actorId, frameType);
947 actor->_frameNumber = frameRange->frameIndex + frameOffset;
948 actor->_currentAction = kActionFreeze;
949 } else {
950 actor->_currentAction = kActionWait;
951 }
952
953 actor->_targetObject = ID_NOTHING;
954 }
955
956 // Script function #44 (0x2C) nonblocking
957 // Checks to see if the user has interrupted a currently playing
958 // game cinematic. Pushes a zero or positive value if the game
959 // has not been interrupted.
sfCheckUserInterrupt(SCRIPTFUNC_PARAMS)960 void Script::sfCheckUserInterrupt(SCRIPTFUNC_PARAMS) {
961 thread->_returnValue = (_skipSpeeches == true);
962 }
963
964 // Script function #45 (0x2D)
965 // Param1: actor id
966 // Param2: object id
967 // Param3: actor x
968 // Param4: actor y
969 // Param5: actor walk flag
sfScriptWalkRelative(SCRIPTFUNC_PARAMS)970 void Script::sfScriptWalkRelative(SCRIPTFUNC_PARAMS) {
971 int16 actorId = thread->pop();
972 ActorData *actor = _vm->_actor->getActor(actorId);
973 int16 objectId = thread->pop();
974 Location actorLocation;
975 actorLocation.x = thread->pop();
976 actorLocation.y = thread->pop();
977 actorLocation.z = actor->_location.z;
978 uint16 walkFlags = thread->pop();
979
980 actor->_flags &= ~kFollower;
981 _vm->_actor->realLocation(actorLocation, objectId, walkFlags);
982
983 if (_vm->_actor->actorWalkTo(actorId, actorLocation) && !(walkFlags & kWalkAsync)) {
984 thread->waitWalk(actor);
985 }
986
987 if (walkFlags & kWalkBackPedal) {
988 actor->_actorFlags |= kActorBackwards;
989 }
990
991 actor->_actorFlags = (actor->_actorFlags & ~kActorFacingMask) | (walkFlags & kActorFacingMask);
992 }
993
994 // Script function #46 (0x2E)
995 // Param1: actor id
996 // Param2: object id
997 // Param3: actor x
998 // Param4: actor y
999 // Param5: actor walk flag
sfScriptMoveRelative(SCRIPTFUNC_PARAMS)1000 void Script::sfScriptMoveRelative(SCRIPTFUNC_PARAMS) {
1001 ActorData *actor = _vm->_actor->getActor(thread->pop());
1002 int16 objectId = thread->pop();
1003 Location actorLocation;
1004 actorLocation.x = thread->pop();
1005 actorLocation.y = thread->pop();
1006 actorLocation.z = actor->_location.z;
1007 uint16 walkFlags = thread->pop();
1008
1009 _vm->_actor->realLocation(actorLocation, objectId, walkFlags);
1010
1011 actor->_location = actorLocation;
1012 actor->_actorFlags = (actor->_actorFlags & ~kActorFacingMask) | (walkFlags & kActorFacingMask);
1013 }
1014
1015 // Script function #47 (0x2F)
sfSimulSpeech2(SCRIPTFUNC_PARAMS)1016 void Script::sfSimulSpeech2(SCRIPTFUNC_PARAMS) {
1017 int16 stringId = thread->pop();
1018 const char *string = thread->_strings->getString(stringId);
1019 int16 actorsCount = thread->pop();
1020 int16 speechFlags = thread->pop();
1021 int i;
1022 uint16 actorsIds[ACTOR_SPEECH_ACTORS_MAX];
1023 int16 sampleResourceId = -1;
1024
1025 if (actorsCount > ACTOR_SPEECH_ACTORS_MAX)
1026 error("sfSimulSpeech2 actorsCount=0x%X exceed ACTOR_SPEECH_ACTORS_MAX", actorsCount);
1027
1028 for (i = 0; i < actorsCount; i++)
1029 actorsIds[i] = thread->pop();
1030
1031 if (!thread->_voiceLUT->empty()) {
1032 sampleResourceId = (*thread->_voiceLUT)[stringId];
1033 if (sampleResourceId <= 0 || sampleResourceId > 4000)
1034 sampleResourceId = -1;
1035 }
1036
1037 _vm->_actor->simulSpeech(string, actorsIds, actorsCount, speechFlags, sampleResourceId);
1038 thread->wait(kWaitTypeSpeech);
1039 }
1040
1041
1042 // Script function #48 (0x30)
1043 // Param1: string rid
sfPlacard(SCRIPTFUNC_PARAMS)1044 void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
1045 int stringId = thread->pop();
1046 static PalEntry cur_pal[PAL_ENTRIES];
1047 PalEntry *pal;
1048 Event event;
1049 EventColumns *eventColumns;
1050
1051 thread->wait(kWaitTypePlacard);
1052
1053 _vm->_interface->rememberMode();
1054 _vm->_interface->setMode(kPanelPlacard);
1055
1056 event.type = kEvTOneshot;
1057 event.code = kCursorEvent;
1058 event.op = kEventHide;
1059 eventColumns = _vm->_events->queue(event);
1060
1061 _vm->_interface->setFadeMode(kFadeOut);
1062
1063 // Fade to black out
1064 _vm->_gfx->getCurrentPal(cur_pal);
1065 event.type = kEvTImmediate;
1066 event.code = kPalEvent;
1067 event.op = kEventPalToBlack;
1068 event.time = 0;
1069 event.duration = kNormalFadeDuration;
1070 event.data = cur_pal;
1071 _vm->_events->chain(eventColumns, event);
1072
1073 // set fade mode
1074 event.type = kEvTImmediate;
1075 event.code = kInterfaceEvent;
1076 event.op = kEventSetFadeMode;
1077 event.param = kNoFade;
1078 event.time = 0;
1079 event.duration = 0;
1080 _vm->_events->chain(eventColumns, event);
1081
1082 event.type = kEvTOneshot;
1083 event.code = kInterfaceEvent;
1084 event.op = kEventClearStatus;
1085 _vm->_events->chain(eventColumns, event);
1086
1087 event.type = kEvTOneshot;
1088 event.code = kGraphicsEvent;
1089 event.op = kEventFillRect;
1090 event.param = 138;
1091 event.param2 = 0;
1092 event.param3 = _vm->_scene->getHeight();
1093 event.param4 = 0;
1094 event.param5 = _vm->getDisplayInfo().width;
1095 _vm->_events->chain(eventColumns, event);
1096
1097 // Put the text in the center of the viewport, assuming it will fit on
1098 // one line. If we cannot make that assumption we'll need to extend
1099 // the text drawing function so that it can center text around a point.
1100 // It doesn't end up in exactly the same spot as the original did it,
1101 // but it's close enough for now at least.
1102
1103 TextListEntry textEntry;
1104
1105 textEntry.knownColor = kKnownColorBrightWhite;
1106 textEntry.effectKnownColor = kKnownColorBlack;
1107 textEntry.point.x = _vm->getDisplayInfo().width / 2;
1108 textEntry.point.y = (_vm->_scene->getHeight() - _vm->_font->getHeight(kKnownFontMedium)) / 2;
1109 textEntry.font = kKnownFontMedium;
1110 textEntry.flags = (FontEffectFlags)(kFontOutline | kFontCentered);
1111 textEntry.text = thread->_strings->getString(stringId);
1112
1113 _placardTextEntry = _vm->_scene->_textList.addEntry(textEntry);
1114
1115 event.type = kEvTOneshot;
1116 event.code = kTextEvent;
1117 event.op = kEventDisplay;
1118 event.data = _placardTextEntry;
1119 _vm->_events->chain(eventColumns, event);
1120
1121 _vm->_scene->getBGPal(pal);
1122 event.type = kEvTImmediate;
1123 event.code = kPalEvent;
1124 event.op = kEventBlackToPal;
1125 event.time = 0;
1126 event.duration = kNormalFadeDuration;
1127 event.data = pal;
1128 _vm->_events->chain(eventColumns, event);
1129
1130 event.type = kEvTOneshot;
1131 event.code = kScriptEvent;
1132 event.op = kEventThreadWake;
1133 event.param = kWaitTypePlacard;
1134 _vm->_events->chain(eventColumns, event);
1135
1136 }
1137
1138 // Script function #49 (0x31)
sfPlacardOff(SCRIPTFUNC_PARAMS)1139 void Script::sfPlacardOff(SCRIPTFUNC_PARAMS) {
1140 thread->wait(kWaitTypePlacard);
1141
1142 _vm->_scene->clearPlacard();
1143 }
1144
1145 // Script function #50 (0x32)
sfSetProtagState(SCRIPTFUNC_PARAMS)1146 void Script::sfSetProtagState(SCRIPTFUNC_PARAMS) {
1147 _vm->_actor->setProtagState(thread->pop());
1148 }
1149
1150 // Script function #51 (0x33)
sfResumeBgdAnim(SCRIPTFUNC_PARAMS)1151 void Script::sfResumeBgdAnim(SCRIPTFUNC_PARAMS) {
1152 int16 animId = thread->pop();
1153 int16 cycles = thread->pop();
1154
1155 _vm->_anim->resume(animId, cycles);
1156 debug(1, "sfResumeBgdAnimSpeed(%d, %d)", animId, cycles);
1157
1158 }
1159
1160 // Script function #52 (0x34)
1161 // Param1: actor id
1162 // Param2: x
1163 // Param3: y
1164 // Param4: unknown
1165 // Param5: actionCycle
1166 // Param6: flags
sfThrowActor(SCRIPTFUNC_PARAMS)1167 void Script::sfThrowActor(SCRIPTFUNC_PARAMS) {
1168 ActorData *actor = _vm->_actor->getActor(thread->pop());
1169 actor->_finalTarget.x = thread->pop();
1170 actor->_finalTarget.y = thread->pop();
1171 actor->_finalTarget.z = actor->_location.z;
1172 thread->pop(); // not used
1173 int32 actionCycle = thread->pop();
1174 int16 flags = thread->pop();
1175
1176 actor->_currentAction = kActionFall;
1177 actor->_actionCycle = actionCycle;
1178 actor->_fallAcceleration = -20;
1179 actor->_fallVelocity = - (actor->_fallAcceleration * actor->_actionCycle) / 2;
1180 actor->_fallPosition = actor->_location.z << 4;
1181
1182 actor->_actionCycle--;
1183 if (!(flags & kWalkAsync)) {
1184 thread->waitWalk(actor);
1185 }
1186 }
1187
1188 // Script function #53 (0x35)
1189 // Param1: actor id
1190 // Param2: target object
sfWaitWalk(SCRIPTFUNC_PARAMS)1191 void Script::sfWaitWalk(SCRIPTFUNC_PARAMS) {
1192 ActorData *actor = _vm->_actor->getActor(thread->pop());
1193
1194 if ((actor->_currentAction == kActionWalkToPoint) ||
1195 (actor->_currentAction == kActionWalkToLink) ||
1196 (actor->_currentAction == kActionFall)) {
1197 thread->waitWalk(actor);
1198 }
1199 }
1200
1201 // Script function #54 (0x36)
sfScriptSceneID(SCRIPTFUNC_PARAMS)1202 void Script::sfScriptSceneID(SCRIPTFUNC_PARAMS) {
1203 thread->_returnValue = _vm->_scene->currentSceneNumber();
1204 }
1205
1206 // Script function #55 (0x37)
1207 // Param1: actor id
1208 // Param2: scene number
sfChangeActorScene(SCRIPTFUNC_PARAMS)1209 void Script::sfChangeActorScene(SCRIPTFUNC_PARAMS) {
1210 ActorData *actor = _vm->_actor->getActor(thread->pop());
1211 actor->_sceneNumber = thread->pop();
1212 }
1213
1214 // Script function #56 (0x38)
1215 // Param1: actor id
1216 // Param2: z
1217 // Param3: frame seq
1218 // Param4: flags
sfScriptClimb(SCRIPTFUNC_PARAMS)1219 void Script::sfScriptClimb(SCRIPTFUNC_PARAMS) {
1220 ActorData *actor = _vm->_actor->getActor(thread->pop());
1221 actor->_finalTarget.z = thread->pop();
1222 int cycleFrameSequence = thread->pop();
1223 uint16 flags = thread->pop();
1224
1225 actor->_flags &= ~kFollower;
1226 actor->_actionCycle = 1;
1227 actor->_cycleFrameSequence = cycleFrameSequence;
1228 actor->_currentAction = kActionClimb;
1229 if (!(flags & kWalkAsync)) {
1230 thread->waitWalk(actor);
1231 }
1232 }
1233
1234 // Script function #57 (0x39)
1235 // Param1: door #
1236 // Param2: door state
sfSetDoorState(SCRIPTFUNC_PARAMS)1237 void Script::sfSetDoorState(SCRIPTFUNC_PARAMS) {
1238 int16 doorNumber = thread->pop();
1239 int16 doorState = thread->pop();
1240
1241 if (_vm->_scene->getFlags() & kSceneFlagISO) {
1242 _vm->_isoMap->setTileDoorState(doorNumber, doorState);
1243 } else {
1244 _vm->_scene->setDoorState(doorNumber, doorState);
1245 }
1246 }
1247
1248 // Script function #58 (0x3A)
1249 // Param1: actor id
1250 // Param2: z
sfSetActorZ(SCRIPTFUNC_PARAMS)1251 void Script::sfSetActorZ(SCRIPTFUNC_PARAMS) {
1252 int16 objectId = thread->pop();
1253 int16 z = thread->pop();
1254 ActorData *actor;
1255 ObjectData *obj;
1256
1257 if (_vm->_actor->validActorId(objectId)) {
1258 actor = _vm->_actor->getActor(objectId);
1259 actor->_location.z = z;
1260 } else {
1261 if (_vm->_actor->validObjId(objectId)) {
1262 obj = _vm->_actor->getObj(objectId);
1263 obj->_location.z = z;
1264 }
1265 }
1266 }
1267
1268 // Script function #59 (0x3B)
1269 // Param1: stringId
1270 // Param2: flags
1271 // Param3: color
1272 // Param4: x
1273 // Param5: y
sfScriptText(SCRIPTFUNC_PARAMS)1274 void Script::sfScriptText(SCRIPTFUNC_PARAMS) {
1275 const char *text = thread->_strings->getString(thread->pop());
1276 int16 flags = thread->pop();
1277 int color = thread->pop();
1278 Point point;
1279 point.x = thread->pop();
1280 point.y = thread->pop();
1281 Rect rect;
1282 int width = _vm->_font->getStringWidth(kKnownFontScript, text, 0, kFontOutline);
1283
1284 rect.top = point.y - 6;
1285 rect.setHeight(12);
1286 rect.left = point.x - width / 2;
1287 rect.setWidth(width);
1288
1289 _vm->_actor->setSpeechColor(color, _vm->KnownColor2ColorId(kKnownColorBlack));
1290 _vm->_actor->nonActorSpeech(rect, &text, 1, -1, flags);
1291 }
1292
1293 // Script function #60 (0x3C)
1294 // Param1: actor id
sfGetActorX(SCRIPTFUNC_PARAMS)1295 void Script::sfGetActorX(SCRIPTFUNC_PARAMS) {
1296 ActorData *actor = _vm->_actor->getActor(thread->pop());
1297
1298 thread->_returnValue = actor->_location.x >> 2;
1299 }
1300
1301 // Script function #61 (0x3D)
1302 // Param1: actor id
sfGetActorY(SCRIPTFUNC_PARAMS)1303 void Script::sfGetActorY(SCRIPTFUNC_PARAMS) {
1304 ActorData *actor = _vm->_actor->getActor(thread->pop());
1305
1306 thread->_returnValue = actor->_location.y >> 2;
1307 }
1308
1309 // Script function #62 (0x3E)
sfEraseDelta(SCRIPTFUNC_PARAMS)1310 void Script::sfEraseDelta(SCRIPTFUNC_PARAMS) {
1311 BGInfo backGroundInfo;
1312 _vm->_scene->getBGInfo(backGroundInfo);
1313 _vm->_render->getBackGroundSurface()->blit(backGroundInfo.bounds, backGroundInfo.buffer);
1314 _vm->_render->addDirtyRect(backGroundInfo.bounds);
1315 }
1316
1317 // Script function #63 (0x3F)
sfPlayMusic(SCRIPTFUNC_PARAMS)1318 void Script::sfPlayMusic(SCRIPTFUNC_PARAMS) {
1319 if (_vm->getGameId() == GID_ITE) {
1320 int16 param = thread->pop() + 9;
1321
1322 if (param >= 9 && param <= 34) {
1323 _vm->_music->setVolume(_vm->_musicVolume, 1);
1324 _vm->_music->play(param);
1325 } else {
1326 _vm->_music->stop();
1327 }
1328 #ifdef ENABLE_IHNM
1329 } else if (_vm->getGameId() == GID_IHNM) {
1330 int16 param1 = thread->pop();
1331 int16 param2 = thread->pop();
1332
1333 if (param1 < 0) {
1334 _vm->_music->stop();
1335 return;
1336 }
1337
1338 if (uint(param1) >= _vm->_music->_songTable.size()) {
1339 warning("sfPlayMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTable.size() - 1);
1340 } else {
1341 _vm->_music->setVolume(_vm->_musicVolume, 1);
1342 _vm->_music->play(_vm->_music->_songTable[param1], param2 ? MUSIC_LOOP : MUSIC_NORMAL);
1343 if (!_vm->_scene->haveChapterPointsChanged()) {
1344 _vm->_scene->setCurrentMusicTrack(param1);
1345 _vm->_scene->setCurrentMusicRepeat(param2);
1346 } else {
1347 // Don't save this music track when saving in IHNM
1348 _vm->_scene->setChapterPointsChanged(false);
1349 }
1350 }
1351 #endif
1352 }
1353 }
1354
1355 // Script function #64 (0x40)
sfPickClimbOutPos(SCRIPTFUNC_PARAMS)1356 void Script::sfPickClimbOutPos(SCRIPTFUNC_PARAMS) {
1357 int16 u, v, t;
1358 ActorData *protagonist = _vm->_actor->_protagonist;
1359 while (true) {
1360
1361 u = (_vm->_rnd.getRandomNumber(63) & 63) + 40;
1362 v = (_vm->_rnd.getRandomNumber(63) & 63) + 40;
1363 t = _vm->_isoMap->getTileIndex(u, v, 6);
1364 if (t == 65) {
1365 protagonist->_location.u() = (u << 4) + 4;
1366 protagonist->_location.v() = (v << 4) + 4;
1367 protagonist->_location.z = 48;
1368 break;
1369 }
1370
1371 }
1372 }
1373
1374 // Script function #65 (0x41)
sfTossRif(SCRIPTFUNC_PARAMS)1375 void Script::sfTossRif(SCRIPTFUNC_PARAMS) {
1376 uint16 direction;
1377 ActorData *protagonist = _vm->_actor->_protagonist;
1378 int16 uc = protagonist->_location.u() >> 4;
1379 int16 vc = protagonist->_location.v() >> 4;
1380
1381 if (_vm->_isoMap->findNearestChasm(uc, vc, direction)) {
1382 uc <<= 4;
1383 vc <<= 4;
1384 protagonist->_facingDirection = direction;
1385
1386 protagonist->_finalTarget.u() = uc;
1387 protagonist->_finalTarget.v() = vc;
1388 protagonist->_finalTarget.z = -40;
1389 protagonist->_currentAction = kActionFall;
1390 protagonist->_actionCycle = 24;
1391 protagonist->_fallAcceleration = - 20;
1392 protagonist->_fallVelocity = - (protagonist->_fallAcceleration * 16) / 2 - (44 / 12);
1393 protagonist->_fallPosition = protagonist->_location.z << 4;
1394 protagonist->_actionCycle--;
1395 }
1396 }
1397
1398 // Script function #66 (0x42)
sfShowControls(SCRIPTFUNC_PARAMS)1399 void Script::sfShowControls(SCRIPTFUNC_PARAMS) {
1400 // It has zero implementation in Win rerelase, and in DOS
1401 // release it deals with video ports.
1402 }
1403
1404 // Script function #67 (0x43)
sfShowMap(SCRIPTFUNC_PARAMS)1405 void Script::sfShowMap(SCRIPTFUNC_PARAMS) {
1406 _vm->_interface->setMode(kPanelMap);
1407 }
1408
1409 // Script function #68 (0x44)
sfPuzzleWon(SCRIPTFUNC_PARAMS)1410 void Script::sfPuzzleWon(SCRIPTFUNC_PARAMS) {
1411 thread->_returnValue = _vm->_puzzle->isSolved();
1412 }
1413
1414 // Script function #69 (0x45)
sfEnableEscape(SCRIPTFUNC_PARAMS)1415 void Script::sfEnableEscape(SCRIPTFUNC_PARAMS) {
1416 if (thread->pop())
1417 _abortEnabled = true;
1418 else {
1419 _skipSpeeches = false;
1420 _abortEnabled = false;
1421 }
1422 }
1423
1424 // Script function #70 (0x46)
sfPlaySound(SCRIPTFUNC_PARAMS)1425 void Script::sfPlaySound(SCRIPTFUNC_PARAMS) {
1426 int16 param = thread->pop();
1427 int res;
1428
1429 if (uint(param) < _vm->_sndRes->_fxTable.size()) {
1430 res = _vm->_sndRes->_fxTable[param].res;
1431 if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
1432 res -= 14;
1433 _vm->_sndRes->playSound(res, _vm->_sndRes->_fxTable[param].vol, false);
1434 } else {
1435 _vm->_sound->stopSound();
1436 }
1437 }
1438
1439 // Script function #71 (0x47)
sfPlayLoopedSound(SCRIPTFUNC_PARAMS)1440 void Script::sfPlayLoopedSound(SCRIPTFUNC_PARAMS) {
1441 int16 param = thread->pop();
1442 int res;
1443
1444 if (uint(param) < _vm->_sndRes->_fxTable.size()) {
1445 res = _vm->_sndRes->_fxTable[param].res;
1446 if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
1447 res -= 14;
1448
1449 _vm->_sndRes->playSound(res, _vm->_sndRes->_fxTable[param].vol, true);
1450 } else {
1451 _vm->_sound->stopSound();
1452 }
1453
1454 debug(1, "sfPlayLoopedSound(%d)", param);
1455 }
1456
1457 // Script function #72 (0x48)
1458 // Param1: animation id
sfGetDeltaFrame(SCRIPTFUNC_PARAMS)1459 void Script::sfGetDeltaFrame(SCRIPTFUNC_PARAMS) {
1460 thread->_returnValue = _vm->_anim->getCurrentFrame((uint16)thread->pop());
1461 }
1462
1463 // Script function #73 (0x49)
sfShowProtect(SCRIPTFUNC_PARAMS)1464 void Script::sfShowProtect(SCRIPTFUNC_PARAMS) {
1465 if (_vm->_copyProtection) {
1466 thread->wait(kWaitTypeRequest);
1467
1468 _vm->_interface->setMode(kPanelProtect);
1469 }
1470 }
1471
1472 // Script function #74 (0x4A)
sfProtectResult(SCRIPTFUNC_PARAMS)1473 void Script::sfProtectResult(SCRIPTFUNC_PARAMS) {
1474 if (_vm->_copyProtection) {
1475 thread->_returnValue = _vm->_interface->getProtectHash();
1476 } else {
1477 //cheating
1478 int protectHash = thread->pop();
1479 thread->push(protectHash);
1480 thread->_returnValue = protectHash;
1481 }
1482 }
1483
1484 // Script function #75 (0x4b)
sfRand(SCRIPTFUNC_PARAMS)1485 void Script::sfRand(SCRIPTFUNC_PARAMS) {
1486 thread->_returnValue = _vm->_rnd.getRandomNumber(thread->pop() - 1);
1487 }
1488
1489 // Script function #76 (0x4c)
sfFadeMusic(SCRIPTFUNC_PARAMS)1490 void Script::sfFadeMusic(SCRIPTFUNC_PARAMS) {
1491 _vm->_music->setVolume(0, 1000);
1492 }
1493
1494 // Script function #77 (0x4d)
sfPlayVoice(SCRIPTFUNC_PARAMS)1495 void Script::sfPlayVoice(SCRIPTFUNC_PARAMS) {
1496 int16 param = thread->pop();
1497
1498 if (param > 0) {
1499 _vm->_sndRes->playVoice(param + 3712);
1500 } else {
1501 _vm->_sound->stopSound();
1502 }
1503 }
1504
finishDialog(int strID,int replyID,int flags,int bitOffset)1505 void Script::finishDialog(int strID, int replyID, int flags, int bitOffset) {
1506 byte *addr;
1507
1508 if (_conversingThread) {
1509 _vm->_interface->setMode(kPanelNull);
1510
1511 #ifdef ENABLE_IHNM
1512 if (_vm->getGameId() == GID_IHNM) {
1513 const char *str = _conversingThread->_strings->getString(strID);
1514 if (*str != '[') {
1515 int sampleResourceId = -1;
1516 sampleResourceId = (*_conversingThread->_voiceLUT)[strID];
1517 if (sampleResourceId < 0 || sampleResourceId > 4000)
1518 sampleResourceId = -1;
1519
1520 _vm->_actor->actorSpeech(_vm->_actor->_protagonist->_id, &str, 1, sampleResourceId, 0);
1521 }
1522 }
1523 #endif
1524
1525 _conversingThread->_flags &= ~kTFlagWaiting;
1526
1527 _conversingThread->push(replyID);
1528
1529 if (flags & kReplyOnce) {
1530 addr = _conversingThread->_staticBase + (bitOffset >> 3);
1531 *addr |= (1 << (bitOffset & 7));
1532 }
1533 }
1534
1535 _conversingThread = NULL;
1536 wakeUpThreads(kWaitTypeDialogBegin);
1537 }
1538
sfNull(SCRIPTFUNC_PARAMS)1539 void Script::sfNull(SCRIPTFUNC_PARAMS) {
1540 for (int i = 0; i < nArgs; i++)
1541 thread->pop();
1542 }
1543
sfStub(const char * name,ScriptThread * thread,int nArgs)1544 void Script::sfStub(const char *name, ScriptThread *thread, int nArgs) {
1545 debugN(0, "STUB: %s(", name);
1546
1547 for (int i = 0; i < nArgs; i++) {
1548 debugN(0, "%d", thread->pop());
1549 if (i + 1 < nArgs)
1550 debugN(0, ", ");
1551 }
1552
1553 debug(0, ")");
1554 }
1555
1556 } // End of namespace Saga
1557