1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "illusions/duckman/illusions_duckman.h"
24 #include "illusions/duckman/duckman_dialog.h"
25 #include "illusions/duckman/duckman_specialcode.h"
26 #include "illusions/duckman/gamestate_duckman.h"
27 #include "illusions/duckman/menusystem_duckman.h"
28 #include "illusions/duckman/scriptopcodes_duckman.h"
29 #include "illusions/duckman/duckman_videoplayer.h"
30 #include "illusions/actor.h"
31 #include "illusions/camera.h"
32 #include "illusions/cursor.h"
33 #include "illusions/dictionary.h"
34 #include "illusions/gamresourcereader.h"
35 #include "illusions/graphics.h"
36 #include "illusions/input.h"
37 #include "illusions/resources/actorresource.h"
38 #include "illusions/resources/backgroundresource.h"
39 #include "illusions/resources/fontresource.h"
40 #include "illusions/resources/genericresource.h"
41 #include "illusions/resources/midiresource.h"
42 #include "illusions/resources/scriptresource.h"
43 #include "illusions/resources/soundresource.h"
44 #include "illusions/resources/talkresource.h"
45 #include "illusions/resourcesystem.h"
46 #include "illusions/screen.h"
47 #include "illusions/screentext.h"
48 #include "illusions/scriptstack.h"
49 #include "illusions/sound.h"
50 #include "illusions/specialcode.h"
51 #include "illusions/textdrawer.h"
52 #include "illusions/thread.h"
53 #include "illusions/time.h"
54 #include "illusions/updatefunctions.h"
55
56 #include "illusions/threads/abortablethread.h"
57 #include "illusions/threads/causethread_duckman.h"
58 #include "illusions/threads/scriptthread.h"
59 #include "illusions/threads/talkthread_duckman.h"
60 #include "illusions/threads/timerthread.h"
61
62 #include "audio/audiostream.h"
63 #include "common/config-manager.h"
64 #include "common/debug-channels.h"
65 #include "common/error.h"
66 #include "common/fs.h"
67 #include "common/timer.h"
68 #include "engines/util.h"
69 #include "graphics/cursorman.h"
70 #include "graphics/font.h"
71 #include "graphics/fontman.h"
72 #include "graphics/palette.h"
73 #include "graphics/surface.h"
74
75 namespace Illusions {
76
77 // IllusionsEngine_Duckman
78
IllusionsEngine_Duckman(OSystem * syst,const IllusionsGameDescription * gd)79 IllusionsEngine_Duckman::IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd)
80 : IllusionsEngine(syst, gd) {
81 }
82
run()83 Common::Error IllusionsEngine_Duckman::run() {
84
85 // Init search paths
86 const Common::FSNode gameDataDir(ConfMan.get("path"));
87 SearchMan.addSubDirectoryMatching(gameDataDir, "music");
88 SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
89 SearchMan.addSubDirectoryMatching(gameDataDir, "video");
90 SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
91
92 _dict = new Dictionary();
93
94 _resReader = new ResourceReaderGamArchive("duckman.gam");
95
96 _resSys = new ResourceSystem(this);
97 _resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
98 _resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
99 _resSys->addResourceLoader(0x000A0000, new MidiGroupResourceLoader(this));
100 _resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
101 _resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
102 _resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
103 _resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
104 _resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
105 _resSys->addResourceLoader(0x00190000, new GenericResourceLoader(this));
106
107 _screen = new Screen8Bit(this, 320, 200);
108 _screenPalette = new ScreenPalette(this);
109 _screenText = new ScreenText(this);
110 _input = new Input();
111 _actorInstances = new ActorInstanceList(this);
112 _backgroundInstances = new BackgroundInstanceList(this);
113 _camera = new Camera(this);
114 _controls = new Controls(this);
115 _talkItems = new TalkInstanceList(this);
116 _threads = new ThreadList(this);
117 _updateFunctions = new UpdateFunctions();
118 _soundMan = new SoundMan(this);
119 _menuSystem = new DuckmanMenuSystem(this);
120 _videoPlayer = new DuckmanVideoPlayer(this);
121 _gameState = new Duckman_GameState(this);
122
123 _fader = new Fader();
124
125 _dialogSys = new DuckmanDialogSystem(this);
126
127 _screen->setColorKey1(0);
128
129 initInput();
130
131 initUpdateFunctions();
132
133 _scriptOpcodes = new ScriptOpcodes_Duckman(this);
134 _stack = new ScriptStack();
135
136 // TODO Move to own class
137 _resGetCtr = 0;
138 _unpauseControlActorFlag = false;
139 _lastUpdateTime = 0;
140
141 _currWalkOverlappedControl = 0;
142
143 _pauseCtr = 0;
144 _doScriptThreadInit = false;
145 _field8 = 1;
146 _fieldA = 0;
147
148 ConfMan.registerDefault("talkspeed", 240);
149 _subtitleDuration = (uint16)ConfMan.getInt("talkspeed");
150 debug(0, "talkspeed: %d", _subtitleDuration);
151
152 _globalSceneId = 0x00010003;
153
154 _savedInventoryActorIndex = 0;
155
156 loadSpecialCode(0);
157 setDefaultTextCoords();
158 initCursor();
159 initActiveScenes();
160
161 _resSys->loadResource(0x120001, 0x00010001, 0);
162 _resSys->loadResource(0x120002, 0x00010001, 0);
163 _resSys->loadResource(0x120003, 0x00010001, 0);
164 _resSys->loadResource(0x000D0001, 0x00010001, 0);
165
166 #if 0
167 //DEBUG
168 _scriptResource->_properties.set(0x000E003A, true);
169 _scriptResource->_properties.set(0x000E0020, true);
170 _scriptResource->_properties.set(0x000E003A, true);
171 _scriptResource->_properties.set(0x000E003B, true);
172 _scriptResource->_properties.set(0x000E0009, true);
173 _scriptResource->_properties.set(0x000E003D, true);
174 _scriptResource->_properties.set(0x000E0024, true);
175 #endif
176
177 #if 0
178 // DEBUG Enterprise
179 _scriptResource->_blockCounters.set(238, 1);
180 #endif
181
182 #if 0
183 // DEBUG Map / special code 0016001A
184 _scriptResource->_properties.set(0x000E0017, true);
185 _scriptResource->_properties.set(0x000E0022, false);
186 #endif
187
188 if (ConfMan.hasKey("save_slot")) {
189 _doScriptThreadInit = true;
190 // Load global resources manually, usually done by the game script
191 enterScene(0x00010003, 0);
192 loadGameState(ConfMan.getInt("save_slot"));
193 } else {
194 startScriptThread(0x00020004, 0);
195 _doScriptThreadInit = true;
196 }
197
198 while (!shouldQuit()) {
199 if (_resumeFromSavegameRequested) {
200 activateSavegame(0);
201 resumeFromSavegame(0);
202 _resumeFromSavegameRequested = false;
203 }
204 runUpdateFunctions();
205 _system->updateScreen();
206 updateEvents();
207 }
208
209 unloadSpecialCode(0);
210
211 delete _stack;
212 delete _scriptOpcodes;
213
214 delete _dialogSys;
215
216 delete _fader;
217
218 delete _gameState;
219 delete _menuSystem;
220 delete _videoPlayer;
221 delete _soundMan;
222 delete _updateFunctions;
223 delete _threads;
224 delete _talkItems;
225 delete _controls;
226 delete _camera;
227 delete _backgroundInstances;
228 delete _actorInstances;
229 delete _input;
230 delete _screenText;
231 delete _screenPalette;
232 delete _screen;
233 delete _resSys;
234 delete _resReader;
235 delete _dict;
236
237 debug("Ok");
238
239 return Common::kNoError;
240 }
241
hasFeature(EngineFeature f) const242 bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
243 return
244 (f == kSupportsRTL) ||
245 (f == kSupportsLoadingDuringRuntime) ||
246 (f == kSupportsSavingDuringRuntime);
247 }
248
initInput()249 void IllusionsEngine_Duckman::initInput() {
250 _input->setInputEvent(kEventLeftClick, 0x01)
251 .addMouseButton(MOUSE_LEFT_BUTTON)
252 .addKey(Common::KEYCODE_RETURN);
253 _input->setInputEvent(kEventRightClick, 0x02)
254 .addMouseButton(MOUSE_RIGHT_BUTTON)
255 .addKey(Common::KEYCODE_BACKSPACE);
256 // 0x04 is unused
257 _input->setInputEvent(kEventInventory, 0x08)
258 .addKey(Common::KEYCODE_TAB);
259 _input->setInputEvent(kEventAbort, 0x10)
260 .addKey(Common::KEYCODE_ESCAPE);
261 _input->setInputEvent(kEventSkip, 0x20)
262 .addMouseButton(MOUSE_LEFT_BUTTON)
263 .addKey(Common::KEYCODE_SPACE);
264 _input->setInputEvent(kEventUp, 0x40)
265 .addKey(Common::KEYCODE_UP);
266 _input->setInputEvent(kEventDown, 0x80)
267 .addMouseButton(MOUSE_RIGHT_BUTTON)
268 .addKey(Common::KEYCODE_DOWN);
269 _input->setInputEvent(kEventF1, 0x100)
270 .addKey(Common::KEYCODE_F1);
271 }
272
273 #define UPDATEFUNCTION(priority, sceneId, callback) \
274 _updateFunctions->add(priority, sceneId, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
275 (this, &IllusionsEngine_Duckman::callback));
276
initUpdateFunctions()277 void IllusionsEngine_Duckman::initUpdateFunctions() {
278 UPDATEFUNCTION(25, 0, updateVideoPlayer);
279 UPDATEFUNCTION(30, 0, updateScript);
280 UPDATEFUNCTION(50, 0, updateActors);
281 UPDATEFUNCTION(60, 0, updateSequences);
282 UPDATEFUNCTION(70, 0, updateGraphics);
283 UPDATEFUNCTION(90, 0, updateSprites);
284 UPDATEFUNCTION(120, 0, updateSoundMan);
285 }
286
287 #undef UPDATEFUNCTION
288
updateScript(uint flags)289 int IllusionsEngine_Duckman::updateScript(uint flags) {
290 if (_screen->isDisplayOn() && !_screenPalette->isFaderActive() && _pauseCtr == 0) {
291 if (_input->pollEvent(kEventAbort)) {
292 startScriptThread(0x00020342, 0);
293 } else if (_input->isCheatModeActive() && _input->pollEvent(kEventF1)) {
294 startScriptThread(0x0002033F, 0);
295 }
296 }
297 _threads->updateThreads();
298
299 //TODO need to call startScriptThread2(0x10002, 0x20001, 0);
300 return kUFNext;
301 }
302
startScreenShaker(uint pointsCount,uint32 duration,const ScreenShakerPoint * points,uint32 threadId)303 void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId) {
304 _screenShaker = new ScreenShaker();
305 _screenShaker->_pointsIndex = 0;
306 _screenShaker->_pointsCount = pointsCount;
307 _screenShaker->_finished = false;
308 _screenShaker->_duration = duration;
309 _screenShaker->_nextTime = duration + getCurrentTime();
310 _screenShaker->_points = points;
311 _screenShaker->_notifyThreadId = threadId;
312 _updateFunctions->add(71, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
313 (this, &IllusionsEngine_Duckman::updateScreenShaker));
314 }
315
stopScreenShaker()316 void IllusionsEngine_Duckman::stopScreenShaker() {
317 if (_screenShaker)
318 _screenShaker->_finished = true;
319 }
320
updateScreenShaker(uint flags)321 int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
322 if (_pauseCtr > 0 || getCurrentScene() == 0x10038) {
323 _screenShaker->_nextTime = getCurrentTime();
324 return 1;
325 }
326
327 if (flags & 1)
328 _screenShaker->_finished = true;
329
330 if (!_screenShaker->_finished) {
331 if (getCurrentTime() >= _screenShaker->_nextTime) {
332 ++_screenShaker->_pointsIndex;
333 if (_screenShaker->_pointsIndex <= _screenShaker->_pointsCount) {
334 ScreenShakerPoint shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
335 if (shakePt.x == (int16)0x8000) {
336 // Loop
337 _screenShaker->_pointsIndex = 1;
338 shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
339 }
340 _screenShaker->_nextTime = getCurrentTime() + _screenShaker->_duration;
341 _screen->setScreenOffset(Common::Point(shakePt.x, shakePt.y));
342 } else
343 _screenShaker->_finished = true;
344 }
345 }
346
347 if (_screenShaker->_finished) {
348 notifyThreadId(_screenShaker->_notifyThreadId);
349 delete _screenShaker;
350 _screenShaker = 0;
351 _screen->setScreenOffset(Common::Point(0, 0));
352 return 2;
353 }
354
355 return 1;
356 }
357
startFader(int duration,int minValue,int maxValue,int firstIndex,int lastIndex,uint32 threadId)358 void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId) {
359 _fader->_active = true;
360 _fader->_currValue = minValue;
361 _fader->_minValue = minValue;
362 _fader->_maxValue = maxValue;
363 _fader->_firstIndex = firstIndex;
364 _fader->_lastIndex = lastIndex;
365 _fader->_startTime = getCurrentTime();
366 _fader->_duration = duration;
367 _fader->_notifyThreadId = threadId;
368 }
369
updateFader()370 void IllusionsEngine_Duckman::updateFader() {
371 if (_fader && !_fader->_paused && _fader->_active) {
372 int32 currTime = getCurrentTime();
373 int32 currDuration = currTime - _fader->_startTime;
374 if (currDuration) {
375 int newValue;
376 if (currDuration >= _fader->_duration) {
377 newValue = _fader->_maxValue;
378 } else {
379 newValue = (currDuration * (_fader->_maxValue - _fader->_minValue) / _fader->_duration) + _fader->_minValue;
380 }
381 if (_fader->_currValue != newValue) {
382 _fader->_currValue = newValue;
383 _screenPalette->setFader(newValue, _fader->_firstIndex, _fader->_lastIndex);
384 }
385 if (_fader->_currValue == _fader->_maxValue) {
386 _fader->_active = false;
387 notifyThreadId(_fader->_notifyThreadId);
388 }
389 }
390 }
391 }
392
clearFader()393 void IllusionsEngine_Duckman::clearFader() {
394 _fader->_active = false;
395 _fader->_currValue = 255;
396 _fader->_minValue = 255;
397 _fader->_maxValue = 255;
398 _fader->_firstIndex = 1;
399 _fader->_lastIndex = 256;
400 _fader->_startTime = 0;
401 _fader->_duration = 0;
402 _fader->_notifyThreadId = 0;
403 }
404
pauseFader()405 void IllusionsEngine_Duckman::pauseFader() {
406 _fader->_paused = true;
407 _fader->_startTime = getCurrentTime() - _fader->_startTime;
408 }
409
unpauseFader()410 void IllusionsEngine_Duckman::unpauseFader() {
411 _fader->_startTime = getCurrentTime() - _fader->_startTime;
412 _fader->_paused = false;
413 }
414
updateVideoPlayer(uint flags)415 int IllusionsEngine_Duckman::updateVideoPlayer(uint flags) {
416 if (_videoPlayer->isPlaying())
417 _videoPlayer->update();
418 return kUFNext;
419 }
420
playVideo(uint32 videoId,uint32 callingThreadId)421 void IllusionsEngine_Duckman::playVideo(uint32 videoId, uint32 callingThreadId) {
422 _videoPlayer->start(videoId, callingThreadId);
423 }
424
isVideoPlaying()425 bool IllusionsEngine_Duckman::isVideoPlaying() {
426 return _videoPlayer->isPlaying();
427 }
428
setDefaultTextCoords()429 void IllusionsEngine_Duckman::setDefaultTextCoords() {
430 WidthHeight dimensions;
431 dimensions._width = 300;
432 dimensions._height = 32;
433 Common::Point pt(160, 176);
434 setDefaultTextDimensions(dimensions);
435 setDefaultTextPosition(pt);
436 }
437
loadSpecialCode(uint32 resId)438 void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
439 _specialCode = new DuckmanSpecialCode(this);
440 _specialCode->init();
441 }
442
unloadSpecialCode(uint32 resId)443 void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
444 delete _specialCode;
445 }
446
notifyThreadId(uint32 & threadId)447 void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
448 if (threadId) {
449 uint32 tempThreadId = threadId;
450 threadId = 0;
451 _threads->notifyId(tempThreadId);
452 }
453 }
454
testMainActorFastWalk(Control * control)455 bool IllusionsEngine_Duckman::testMainActorFastWalk(Control *control) {
456 return
457 control->_objectId == _scriptResource->getMainActorObjectId() &&
458 _input->pollEvent(kEventSkip);
459 }
460
testMainActorCollision(Control * control)461 bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
462 bool result = false;
463 Control *overlappedControl;
464 if (_controls->getOverlappedWalkObject(control, control->_actor->_position, &overlappedControl)) {
465 if (_currWalkOverlappedControl != overlappedControl) {
466 _currWalkOverlappedControl = overlappedControl;
467 if (runTriggerCause(9, 0, overlappedControl->_objectId)) {
468 delete control->_actor->_pathNode;
469 control->_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
470 control->_actor->_pathNode = 0;
471 control->_actor->_pathPoints = 0;
472 control->_actor->_pathPointsCount = 0;
473 _threads->terminateThreadChain(control->_actor->_walkCallerThreadId1);
474 if (control->_actor->_notifyId3C) {
475 notifyThreadId(control->_actor->_notifyId3C);
476 control->_actor->_walkCallerThreadId1 = 0;
477 }
478 result = true;
479 }
480 }
481 } else {
482 _currWalkOverlappedControl = 0;
483 }
484 return result;
485 }
486
getObjectControl(uint32 objectId)487 Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
488 return _dict->getObjectControl(objectId);
489 }
490
491 static const uint8 kPointConversionTable[] = {
492 0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
493 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
494 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
495 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
496 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
497 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
498 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
499 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 1, 2, 3,
500 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 35,
501 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
502 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
503 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
504 35, 35, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
505 33, 34
506 };
getNamedPointPosition(uint32 namedPointId)507 Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId) {
508 Common::Point pt;
509 Common::Point currPan = _camera->getCurrentPan();
510 if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt)) {
511 return pt;
512 } else if (namedPointId - 0x00070001 > 209) {
513 if (_controls->findNamedPoint(namedPointId, pt)) {
514 return pt;
515 } else {
516 return currPan;
517 }
518 } else {
519 debug(1, "getNamedPointPosition(%02d)", kPointConversionTable[namedPointId - 0x00070001]);
520 switch(kPointConversionTable[namedPointId - 0x00070001]) {
521 case 0 : return Common::Point(160, 100);
522 case 1 : return currPan;
523 case 2 : return Common::Point(currPan.x - 160, currPan.y);
524 case 3 : return Common::Point(currPan.x + 160, currPan.y);
525 case 4 : return Common::Point(currPan.x, currPan.y - 100);
526 case 5 : return Common::Point(currPan.x, currPan.y + 100);
527 case 6 : return Common::Point(currPan.x - 160, currPan.y - 100);
528 case 7 : return Common::Point((uint16)(currPan.x + 160), currPan.y - 100);
529 case 8 : return Common::Point(currPan.x - 160, currPan.y + 100);
530 case 9 : return Common::Point(currPan.x + 160, currPan.y + 100);
531 /* TODO implement these
532 case 10 : return Common::Point(0, 0);
533 case 11 : return Common::Point(0, 0);
534 case 12 : return Common::Point(0, 0);
535 case 13 : return Common::Point(0, 0);
536 case 14 : return Common::Point(0, 0);
537 */
538 case 15 : return Common::Point(0, 0);
539 /* TODO implement these
540 case 16 : return Common::Point(0, 0);
541 case 17 : return Common::Point(0, 0);
542 case 18 : return Common::Point(0, 0);
543 */
544 case 19 : return Common::Point(0, 0);
545 case 20 : return Common::Point(320, 0);
546 case 21 : return Common::Point(640, 0);
547 case 22 : return Common::Point(960, 0);
548 case 23 : return Common::Point(0, 200);
549 case 24 : return Common::Point(320, 200);
550 case 25 : return Common::Point(640, 200);
551 case 26 : return Common::Point(960, 200);
552 case 27 : return Common::Point(0, 400);
553 case 28 : return Common::Point(320, 400);
554 case 29 : return Common::Point(640, 400);
555 case 30 : return Common::Point(960, 400);
556 case 31 : return Common::Point(0, 600);
557 case 32 : return Common::Point(320, 0);
558 case 33 : return Common::Point(640, 0);
559 case 34 : return Common::Point(960, 0);
560 default : break;
561 }
562 error("getNamedPointPosition(%02d) UNKNOWN", kPointConversionTable[namedPointId - 0x00070001]);
563 return Common::Point(0, 0);
564 }
565 }
566
getPriorityFromBase(int16 priority)567 uint32 IllusionsEngine_Duckman::getPriorityFromBase(int16 priority) {
568 return priority << 16;
569 }
570
getCurrentScene()571 uint32 IllusionsEngine_Duckman::getCurrentScene() {
572 return _activeScenes[_activeScenesCount];
573 }
574
getPrevScene()575 uint32 IllusionsEngine_Duckman::getPrevScene() {
576 uint index = _activeScenesCount - 1;
577 if (_activeScenesCount == 1)
578 index = 5;
579 return _activeScenes[index];
580 }
581
isCursorObject(uint32 actorTypeId,uint32 objectId)582 bool IllusionsEngine_Duckman::isCursorObject(uint32 actorTypeId, uint32 objectId) {
583 return actorTypeId == 0x50001;
584 }
585
setCursorControlRoutine(Control * control)586 void IllusionsEngine_Duckman::setCursorControlRoutine(Control *control) {
587 control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_Duckman>
588 (this, &IllusionsEngine_Duckman::cursorControlRoutine));
589 }
590
placeCursorControl(Control * control,uint32 sequenceId)591 void IllusionsEngine_Duckman::placeCursorControl(Control *control, uint32 sequenceId) {
592 _cursor._gameState = 2;
593 _cursor._control = control;
594 _cursor._actorIndex = 1;
595 _cursor._savedActorIndex = 1;
596 _cursor._currOverlappedControl = 0;
597 _cursor._sequenceId1 = sequenceId;
598 _cursor._field14[0] = true;
599 _cursor._field14[1] = true;
600 _cursor._field14[2] = false;
601 _cursor._field14[3] = false;
602 _cursor._field14[4] = false;
603 _cursor._field14[5] = false;
604 _cursor._field14[9] = false;
605 _cursor._field14[10] = false;
606 _cursor._field14[11] = false;
607 _cursor._field14[12] = false;
608 _cursor._field14[6] = _cursor._sequenceId2 != 0 && _cursor._objectId != 0;
609 _cursor._field14[7] = false;
610 _cursor._field14[8] = false;
611 _cursor._op113_choiceOfsPtr = 0;
612 _cursor._notifyThreadId30 = 0;
613 _cursor._dialogItemsCount = 0;
614 _cursor._overlappedObjectId = 0;
615 _cursor._field40 = 0;
616 control->_flags |= 8;
617 setCursorActorIndex(_cursor._actorIndex, 1, 0);
618 // TODO Input_setMousePos(cursorControl->actor->position);
619 // TODO
620 //control->_actor->_actorIndex = 2;
621 // TODO _cursor->place(control, sequenceId);
622 }
623
setCursorControl(Control * control)624 void IllusionsEngine_Duckman::setCursorControl(Control *control) {
625 _cursor._control = control;
626 }
627
showCursor()628 void IllusionsEngine_Duckman::showCursor() {
629 // TODO
630 }
631
hideCursor()632 void IllusionsEngine_Duckman::hideCursor() {
633 // TODO
634 }
635
initCursor()636 void IllusionsEngine_Duckman::initCursor() {
637 _cursor._gameState = 1;
638 _cursor._control = 0;
639 _cursor._position.x = 160;
640 _cursor._position.y = 100;
641 _cursor._objectId = 0;
642 _cursor._actorIndex = 1;
643 _cursor._savedActorIndex = 1;
644 _cursor._currOverlappedControl = 0;
645 _cursor._sequenceId1 = 0;
646 _cursor._sequenceId2 = 0;
647 _cursor._field14[0] = true;
648 _cursor._field14[1] = true;
649 _cursor._field14[2] = false;
650 _cursor._field14[3] = false;
651 _cursor._field14[4] = false;
652 _cursor._field14[5] = false;
653 _cursor._field14[6] = false;
654 _cursor._field14[7] = false;
655 _cursor._field14[8] = false;
656 _cursor._field14[9] = false;
657 _cursor._field14[10] = false;
658 _cursor._field14[11] = false;
659 _cursor._field14[12] = false;
660 _cursor._op113_choiceOfsPtr = 0;
661 _cursor._notifyThreadId30 = 0;
662 _cursor._dialogItemsCount = 0;
663 _cursor._overlappedObjectId = 0;
664 _cursor._field40 = 0;
665 }
666
setCursorActorIndex(int actorIndex,int a,int b)667 void IllusionsEngine_Duckman::setCursorActorIndex(int actorIndex, int a, int b) {
668 static int kCursorMap[13][2][2] = {
669 {{ 1, 2}, { 0, 0}},
670 {{ 3, 4}, { 0, 0}},
671 {{ 5, 6}, {13, 14}},
672 {{ 7, 8}, { 0, 0}},
673 {{ 9, 10}, { 0, 0}},
674 {{11, 12}, { 0, 0}},
675 {{ 1, 2}, { 0, 0}},
676 {{ 0, 0}, { 0, 0}},
677 {{ 0, 0}, { 0, 0}},
678 {{15, 16}, { 0, 0}},
679 {{17, 18}, { 0, 0}},
680 {{19, 20}, { 0, 0}},
681 {{21, 22}, { 0, 0}}
682 };
683 _cursor._control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
684 }
685
enableCursorVerb(int verbNum)686 void IllusionsEngine_Duckman::enableCursorVerb(int verbNum) {
687 if (verbNum != 7 || _cursor._sequenceId2)
688 _cursor._field14[verbNum - 1] = true;
689 }
690
disableCursorVerb(int verbNum)691 void IllusionsEngine_Duckman::disableCursorVerb(int verbNum) {
692 _cursor._field14[verbNum - 1] = false;
693 if (_cursor._actorIndex == verbNum) {
694 _cursor._actorIndex = getCursorActorIndex();
695 setCursorActorIndex(_cursor._actorIndex, 1, 0);
696 startCursorSequence();
697 _cursor._currOverlappedControl = 0;
698 }
699 }
700
setCursorHandMode(int mode)701 void IllusionsEngine_Duckman::setCursorHandMode(int mode) {
702 if (mode == 1) {
703 enableCursorVerb(4);
704 disableCursorVerb(1);
705 disableCursorVerb(2);
706 disableCursorVerb(7);
707 _cursor._actorIndex = 4;
708 } else {
709 enableCursorVerb(1);
710 enableCursorVerb(2);
711 enableCursorVerb(7);
712 disableCursorVerb(4);
713 _cursor._actorIndex = 1;
714 }
715 _cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
716 if (_cursor._currOverlappedControl)
717 setCursorActorIndex(_cursor._actorIndex, 2, 0);
718 else
719 setCursorActorIndex(_cursor._actorIndex, 1, 0);
720 }
721
setCursorInventoryMode(int mode,int value)722 void IllusionsEngine_Duckman::setCursorInventoryMode(int mode, int value) {
723 _cursor._control = _cursor._control;
724 if (mode == 1) {
725 _savedInventoryActorIndex = _cursor._actorIndex;
726 if (_cursor._actorIndex == 3 || _cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
727 _cursor._savedActorIndex = _cursor._savedActorIndex;
728 if (_cursor._savedActorIndex == 1 || _cursor._savedActorIndex == 2 || _cursor._savedActorIndex == 7)
729 _savedInventoryActorIndex = _cursor._savedActorIndex;
730 else
731 _savedInventoryActorIndex = 0;
732 }
733 if (value == 1 && _cursor._objectId && _savedInventoryActorIndex != 7) {
734 _cursor._actorIndex = 7;
735 stopCursorHoldingObject();
736 _cursor._actorIndex = _savedInventoryActorIndex;
737 }
738 } else if (mode == 2) {
739 if (_savedInventoryActorIndex)
740 _cursor._actorIndex = _savedInventoryActorIndex;
741 else
742 _cursor._actorIndex = 1;
743 if (_cursor._actorIndex == 7)
744 _cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
745 else
746 _cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
747 if (_cursor._currOverlappedControl)
748 setCursorActorIndex(_cursor._actorIndex, 2, 0);
749 else
750 setCursorActorIndex(_cursor._actorIndex, 1, 0);
751 _savedInventoryActorIndex = 0;
752 }
753 }
754
startCursorHoldingObject(uint32 objectId,uint32 sequenceId)755 void IllusionsEngine_Duckman::startCursorHoldingObject(uint32 objectId, uint32 sequenceId) {
756 _cursor._objectId = objectId;
757 _cursor._sequenceId2 = sequenceId;
758 _cursor._actorIndex = 7;
759 _cursor._savedActorIndex = 7;
760 _cursor._field14[6] = true;
761 _cursor._control->startSequenceActor(sequenceId, 2, 0);
762 setCursorActorIndex(_cursor._actorIndex, 1, 0);
763 _cursor._currOverlappedControl = 0;
764 }
765
stopCursorHoldingObject()766 void IllusionsEngine_Duckman::stopCursorHoldingObject() {
767 _cursor._field14[6] = false;
768 _cursor._objectId = 0;
769 _cursor._sequenceId2 = 0;
770 if (_cursor._actorIndex == 7) {
771 _cursor._actorIndex = getCursorActorIndex();
772 _cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
773 if (_cursor._currOverlappedControl)
774 setCursorActorIndex(_cursor._actorIndex, 2, 0);
775 else
776 setCursorActorIndex(_cursor._actorIndex, 1, 0);
777 }
778 }
779
cursorControlRoutine(Control * control,uint32 deltaTime)780 void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
781 control->_actor->_seqCodeValue1 = 100 * deltaTime;
782 if (control->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
783 switch (_cursor._gameState) {
784 case 2:
785 updateGameState2();
786 break;
787 case 3:
788 _dialogSys->updateDialogState();
789 break;
790 case 4:
791 _menuSystem->update(_cursor._control);
792 break;
793 }
794 }
795 }
796
startScriptThreadSimple(uint32 threadId,uint32 callingThreadId)797 void IllusionsEngine_Duckman::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
798 startScriptThread(threadId, callingThreadId);
799 }
800
startScriptThread(uint32 threadId,uint32 callingThreadId)801 void IllusionsEngine_Duckman::startScriptThread(uint32 threadId, uint32 callingThreadId) {
802 debug(2, "Starting script thread %08X", threadId);
803 byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
804 newScriptThread(threadId, callingThreadId, 0, scriptCodeIp);
805 }
806
startScriptThread2(uint32 sceneId,uint32 threadId,uint32 callingThreadId)807 void IllusionsEngine_Duckman::startScriptThread2(uint32 sceneId, uint32 threadId, uint32 callingThreadId) {
808 debug(2, "Starting script thread2");
809 _savegameSceneId = sceneId;
810 _savegameThreadId = threadId;
811 startScriptThread(0x20002, callingThreadId);
812 }
813
startAbortableTimerThread(uint32 duration,uint32 threadId)814 uint32 IllusionsEngine_Duckman::startAbortableTimerThread(uint32 duration, uint32 threadId) {
815 return newTimerThread(duration, threadId, true);
816 }
817
startTimerThread(uint32 duration,uint32 threadId)818 uint32 IllusionsEngine_Duckman::startTimerThread(uint32 duration, uint32 threadId) {
819 return newTimerThread(duration, threadId, false);
820 }
821
startAbortableThread(byte * scriptCodeIp1,byte * scriptCodeIp2,uint32 callingThreadId)822 uint32 IllusionsEngine_Duckman::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
823 uint32 tempThreadId = newTempThreadId();
824 debug(2, "Starting abortable thread %08X", tempThreadId);
825 uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
826 AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
827 scriptThreadId, scriptCodeIp2);
828 _threads->startThread(abortableThread);
829 return tempThreadId;
830 }
831
startTalkThread(uint32 objectId,uint32 talkId,uint32 sequenceId1,uint32 sequenceId2,uint32 callingThreadId)832 uint32 IllusionsEngine_Duckman::startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
833 uint32 sequenceId2, uint32 callingThreadId) {
834 debug(2, "Starting talk thread");
835 uint32 tempThreadId = newTempThreadId();
836 TalkThread_Duckman *talkThread = new TalkThread_Duckman(this, tempThreadId, callingThreadId, 0,
837 objectId, talkId, sequenceId1, sequenceId2);
838 _threads->startThread(talkThread);
839 return tempThreadId;
840 }
841
startTempScriptThread(byte * scriptCodeIp,uint32 callingThreadId,uint32 value8,uint32 valueC,uint32 value10)842 uint32 IllusionsEngine_Duckman::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
843 uint32 value8, uint32 valueC, uint32 value10) {
844 uint32 tempThreadId = newTempThreadId();
845 debug(2, "Starting temp script thread %08X", tempThreadId);
846 newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp);
847 return tempThreadId;
848 }
849
resumeFromSavegame(uint32 callingThreadId)850 void IllusionsEngine_Duckman::resumeFromSavegame(uint32 callingThreadId) {
851 _input->discardAllEvents();
852 if (changeScene(_savegameSceneId, _savegameThreadId, callingThreadId)) {
853 _savegameSceneId = 0;
854 _savegameThreadId = 0;
855 }
856 }
857
newScriptThread(uint32 threadId,uint32 callingThreadId,uint notifyFlags,byte * scriptCodeIp)858 void IllusionsEngine_Duckman::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
859 byte *scriptCodeIp) {
860 ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
861 scriptCodeIp, 0, 0, 0);
862 _threads->startThread(scriptThread);
863 }
864
newTimerThread(uint32 duration,uint32 callingThreadId,bool isAbortable)865 uint32 IllusionsEngine_Duckman::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
866 uint32 tempThreadId = newTempThreadId();
867 TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
868 duration, isAbortable);
869 _threads->startThread(timerThread);
870 return tempThreadId;
871 }
872
newTempThreadId()873 uint32 IllusionsEngine_Duckman::newTempThreadId() {
874 uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
875 if (threadId > 65535) {
876 _nextTempThreadId = 0;
877 threadId = 2 * _scriptResource->_codeCount;
878 }
879 ++_nextTempThreadId;
880 return 0x00020000 | threadId;
881 }
882
initActiveScenes()883 void IllusionsEngine_Duckman::initActiveScenes() {
884 _activeScenesCount = 0;
885 _activeScenes[0] = 0xEFEF;
886 pushActiveScene(0x10000);
887 }
888
pushActiveScene(uint32 sceneId)889 void IllusionsEngine_Duckman::pushActiveScene(uint32 sceneId) {
890 ++_activeScenesCount;
891 if (_activeScenesCount >= 6)
892 _activeScenesCount = 1;
893 _activeScenes[_activeScenesCount] = sceneId;
894 }
895
popActiveScene()896 void IllusionsEngine_Duckman::popActiveScene() {
897 --_activeScenesCount;
898 if (_activeScenesCount == 0)
899 _activeScenesCount = 5;
900 }
901
loadScene(uint32 sceneId)902 bool IllusionsEngine_Duckman::loadScene(uint32 sceneId) {
903 SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
904 if (!sceneInfo)
905 return false;
906 pushActiveScene(sceneId);
907 uint resourcesCount;
908 uint32 *resources;
909 sceneInfo->getResources(resourcesCount, resources);
910 for (uint i = 0; i < resourcesCount; ++i) {
911 _resSys->loadResource(resources[i], sceneId, 0);
912 }
913 return true;
914 }
915
enterScene(uint32 sceneId,uint32 threadId)916 bool IllusionsEngine_Duckman::enterScene(uint32 sceneId, uint32 threadId) {
917 if (loadScene(sceneId)) {
918 if (threadId)
919 startScriptThread(threadId, 0);
920 return true;
921 }
922 startScriptThread2(0x10002, 0x20001, 0);
923 return false;
924 }
925
exitScene()926 void IllusionsEngine_Duckman::exitScene() {
927 popActiveScene();
928 }
929
changeScene(uint32 sceneId,uint32 threadId,uint32 callerThreadId)930 bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId) {
931 uint32 currSceneId = getCurrentScene();
932 if (currSceneId != 0x10003)
933 dumpCurrSceneFiles(currSceneId, callerThreadId);
934 _soundMan->stopLoopingSounds(); //Fix for global looping sound not stopping in falling scene.
935 _threads->terminateThreads(callerThreadId);
936 _controls->destroyControls();
937 _resSys->unloadSceneResources(0x10003, 0x10001);
938 if (enterScene(sceneId, threadId)) {
939 _gameState->writeState(sceneId, threadId);
940 return true;
941 }
942 return false;
943 }
944
enterPause(uint32 sceneId,uint32 threadId)945 void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
946 _threads->suspendThreads(threadId);
947 _controls->pauseControls();
948 _actorInstances->pauseBySceneId(sceneId);
949 _backgroundInstances->pauseBySceneId(sceneId);
950 }
951
leavePause(uint32 sceneId,uint32 threadId)952 void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
953 _backgroundInstances->unpauseBySceneId(sceneId);
954 _actorInstances->unpauseBySceneId(sceneId);
955 _controls->unpauseControls();
956 _threads->notifyThreads(threadId);
957 }
958
dumpActiveScenes(uint32 sceneId,uint32 threadId)959 void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
960 // TODO?
961 }
962
dumpCurrSceneFiles(uint32 sceneId,uint32 threadId)963 void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
964 _updateFunctions->terminateByScene(sceneId);
965 _threads->terminateActiveThreads(threadId);
966 _threads->terminateThreadsBySceneId(sceneId, threadId);
967 _controls->destroyActiveControls();
968 _resSys->unloadResourcesBySceneId(sceneId);
969 }
970
pause(uint32 callerThreadId)971 void IllusionsEngine_Duckman::pause(uint32 callerThreadId) {
972 if (++_pauseCtr == 1) {
973 _threads->pauseThreads(callerThreadId);
974 _camera->pause();
975 pauseFader();
976 _controls->pauseActors(Illusions::CURSOR_OBJECT_ID);
977 }
978 }
979
unpause(uint32 callerThreadId)980 void IllusionsEngine_Duckman::unpause(uint32 callerThreadId) {
981 if (--_pauseCtr == 0) {
982 _controls->unpauseActors(Illusions::CURSOR_OBJECT_ID);
983 unpauseFader();
984 _camera->unpause();
985 _threads->unpauseThreads(callerThreadId);
986 }
987 }
988
setSceneIdThreadId(uint32 theSceneId,uint32 theThreadId)989 void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
990 _theSceneId = theSceneId;
991 _theThreadId = theThreadId;
992 }
993
findTriggerCause(uint32 sceneId,uint32 verbId,uint32 objectId2,uint32 objectId,uint32 & codeOffs)994 bool IllusionsEngine_Duckman::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
995 SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
996 if (sceneInfo)
997 return sceneInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
998 return false;
999 }
1000
reset()1001 void IllusionsEngine_Duckman::reset() {
1002 _scriptResource->_blockCounters.clear();
1003 _scriptResource->_properties.clear();
1004 setTextDuration(1, 0);
1005 // TODO resetCursor();
1006 }
1007
getObjectActorTypeId(uint32 objectId)1008 uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
1009 return _scriptResource->getObjectActorTypeId(objectId);
1010 }
1011
convertMousePos(Common::Point mousePos)1012 Common::Point IllusionsEngine_Duckman::convertMousePos(Common::Point mousePos) {
1013 return mousePos + _camera->getScreenOffset();
1014 }
1015
startCursorSequence()1016 void IllusionsEngine_Duckman::startCursorSequence() {
1017 // NOTE Calls to startCursorSequence were put after calls to setCursorActorIndex
1018 // to make the cursor switch more immediate. In the original these calls are swapped.
1019 if (_cursor._actorIndex == 7)
1020 _cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
1021 else
1022 _cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
1023 }
1024
getCursorActorIndex()1025 int IllusionsEngine_Duckman::getCursorActorIndex() {
1026 int result = _cursor._actorIndex;
1027 do {
1028 ++result;
1029 if (result > 13)
1030 result = 1;
1031 } while (!_cursor._field14[result - 1]);
1032 return result;
1033 }
1034
updateGameState2()1035 void IllusionsEngine_Duckman::updateGameState2() {
1036 Common::Point cursorPos = _input->getCursorPosition();
1037 Common::Point convMousePos = convertMousePos(cursorPos);
1038 int trackCursorIndex = -1;
1039 bool foundOverlapped;
1040 Control *overlappedControl;
1041
1042 _cursor._control->_actor->_position = cursorPos;
1043
1044 foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
1045
1046 if (cursorPos.y < 8 && !_camera->isAtPanLimit(1)) {
1047 trackCursorIndex = 10;
1048 } else if (cursorPos.y >= 192 && !_camera->isAtPanLimit(2)) {
1049 trackCursorIndex = 11;
1050 } else if (cursorPos.x < 8 && !_camera->isAtPanLimit(3)) {
1051 trackCursorIndex = 12;
1052 } else if (cursorPos.x >= 312 && !_camera->isAtPanLimit(4)) {
1053 trackCursorIndex = 13;
1054 } else if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
1055 _cursor._actorIndex = _cursor._savedActorIndex;
1056 if (_cursor._currOverlappedControl)
1057 setCursorActorIndex(_cursor._actorIndex, 2, 0);
1058 else
1059 setCursorActorIndex(_cursor._actorIndex, 1, 0);
1060 startCursorSequence();
1061 }
1062
1063 if (trackCursorIndex >= 0) {
1064 if (_cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13 && _cursor._actorIndex != 3)
1065 _cursor._savedActorIndex = _cursor._actorIndex;
1066 if (_cursor._actorIndex != trackCursorIndex) {
1067 _cursor._actorIndex = trackCursorIndex;
1068 setCursorActorIndex(_cursor._actorIndex, 1, 0);
1069 startCursorSequence();
1070 }
1071 _cursor._currOverlappedControl = 0;
1072 foundOverlapped = false;
1073 }
1074
1075 if (foundOverlapped) {
1076 if (_cursor._currOverlappedControl != overlappedControl) {
1077 int cursorValue2 = 0;
1078 if (overlappedControl->_flags & 2) {
1079 if (_cursor._actorIndex != 3) {
1080 _cursor._savedActorIndex = _cursor._actorIndex;
1081 _cursor._actorIndex = 3;
1082 }
1083 if (overlappedControl->_flags & 0x40)
1084 cursorValue2 = 1;
1085 } else if (_cursor._actorIndex == 3) {
1086 _cursor._actorIndex = _cursor._savedActorIndex;
1087 }
1088 setCursorActorIndex(_cursor._actorIndex, 2, cursorValue2);
1089 startCursorSequence();
1090 _cursor._currOverlappedControl = overlappedControl;
1091 }
1092 } else if (_cursor._currOverlappedControl) {
1093 if (_cursor._actorIndex == 3)
1094 _cursor._actorIndex = _cursor._savedActorIndex;
1095 setCursorActorIndex(_cursor._actorIndex, 1, 0);
1096 startCursorSequence();
1097 _cursor._currOverlappedControl = 0;
1098 }
1099
1100 if (_input->pollEvent(kEventLeftClick)) {
1101 if (_cursor._currOverlappedControl) {
1102 runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
1103 } else {
1104 _cursor._position = convertMousePos(_cursor._control->_actor->_position);
1105 // TODO clipMousePos(&_cursor._position);
1106 if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13)
1107 runTriggerCause(1, _cursor._objectId, 0x40003);
1108 else
1109 runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
1110 }
1111 } else if (_input->pollEvent(kEventRightClick)) {
1112 if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
1113 int newActorIndex = getCursorActorIndex();
1114 if (newActorIndex != _cursor._actorIndex) {
1115 _cursor._actorIndex = newActorIndex;
1116 if (_cursor._currOverlappedControl)
1117 setCursorActorIndex(_cursor._actorIndex, 2, 0);
1118 else
1119 setCursorActorIndex(_cursor._actorIndex, 1, 0);
1120 startCursorSequence();
1121 }
1122 }
1123 } else if (_input->pollEvent(kEventInventory)) {
1124 if (_cursor._field14[0] == 1) {
1125 runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
1126 } else if (_cursor._field14[1] == 1) {
1127 runTriggerCause(2, 0, _scriptResource->getMainActorObjectId());
1128 }
1129 }
1130
1131 }
1132
playSoundEffect(int index)1133 void IllusionsEngine_Duckman::playSoundEffect(int index) {
1134 uint32 soundEffectId = 0;
1135 uint32 *soundIds = _scriptResource->_soundIds;
1136 switch (index) {
1137 case 1:
1138 soundEffectId = soundIds[0];
1139 break;
1140 case 2:
1141 soundEffectId = soundIds[1];
1142 break;
1143 case 3:
1144 soundEffectId = soundIds[2];
1145 break;
1146 case 4:
1147 soundEffectId = soundIds[3];
1148 break;
1149 case 5:
1150 soundEffectId = soundIds[4];
1151 break;
1152 case 6:
1153 soundEffectId = soundIds[getRandom(4) + 5];
1154 break;
1155 case 7:
1156 soundEffectId = soundIds[getRandom(4) + 9];
1157 break;
1158 case 8:
1159 soundEffectId = soundIds[13];
1160 break;
1161 case 9:
1162 soundEffectId = soundIds[14];
1163 break;
1164 case 10:
1165 soundEffectId = soundIds[15];
1166 break;
1167 case 11:
1168 soundEffectId = soundIds[16];
1169 break;
1170 case 12:
1171 soundEffectId = soundIds[getRandom(4) + 17];
1172 break;
1173 case 13:
1174 soundEffectId = soundIds[21];
1175 break;
1176 case 14:
1177 soundEffectId = soundIds[22];
1178 break;
1179 case 15:
1180 soundEffectId = soundIds[23];
1181 break;
1182 case 16:
1183 soundEffectId = soundIds[24];
1184 break;
1185 case 17:
1186 soundEffectId = soundIds[25];
1187 break;
1188 case 18:
1189 soundEffectId = soundIds[26];
1190 break;
1191 }
1192 if (soundEffectId)
1193 _soundMan->playSound(soundEffectId, 255, 0);
1194 }
1195
getTriggerCause(uint32 verbId,uint32 objectId2,uint32 objectId,uint32 & outThreadId)1196 bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId) {
1197 SceneInfo *sceneInfo = _scriptResource->getSceneInfo(getCurrentScene() & 0xFFFF);
1198 bool found =
1199 sceneInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
1200 sceneInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
1201 if (!found) {
1202 sceneInfo = _scriptResource->getSceneInfo(3);
1203 found =
1204 sceneInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
1205 sceneInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
1206 }
1207 return found;
1208 }
1209
runTriggerCause(uint32 verbId,uint32 objectId2,uint32 objectId)1210 uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId) {
1211 debug(1, "runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
1212 uint32 triggerThreadId;
1213
1214 if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
1215 return 0;
1216
1217 playTriggerCauseSound(verbId, objectId2, objectId);
1218
1219 uint32 tempThreadId = newTempThreadId();
1220 debug(1, "Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
1221 CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
1222 triggerThreadId);
1223 _threads->startThread(causeThread);
1224
1225 return tempThreadId;
1226 }
1227
playTriggerCauseSound(uint32 verbId,uint32 objectId2,uint32 objectId)1228 void IllusionsEngine_Duckman::playTriggerCauseSound(uint32 verbId, uint32 objectId2, uint32 objectId) {
1229 bool soundWasPlayed = false;
1230 if (_scriptResource->_properties.get(0x000E003C)) {
1231 if (verbId == 7 && objectId == 0x40003) {
1232 playSoundEffect(7);
1233 soundWasPlayed = true;
1234 } else if (objectId == 0x40003) {
1235 playSoundEffect(14);
1236 soundWasPlayed = true;
1237 } else if (verbId == 3) {
1238 playSoundEffect(16);
1239 soundWasPlayed = true;
1240 } else if (verbId == 2) {
1241 soundWasPlayed = true;
1242 }
1243 }
1244 if (!soundWasPlayed) {
1245 if (objectId == 0x40003) {
1246 playSoundEffect(14);
1247 } else if ((verbId == 1 || verbId == 2) && _scriptResource->getMainActorObjectId() == objectId) {
1248 playSoundEffect(15);
1249 } else if (verbId == 7 && _scriptResource->getMainActorObjectId() == objectId) {
1250 playSoundEffect(15);
1251 } else if (verbId == 1) {
1252 playSoundEffect(1);
1253 } else if (verbId == 2) {
1254 playSoundEffect(2);
1255 } else if (verbId == 3) {
1256 playSoundEffect(3);
1257 } else if (verbId == 4 || verbId == 7) {
1258 playSoundEffect(4);
1259 } else if (verbId == 9) {
1260 playSoundEffect(5);
1261 }
1262 }
1263 }
1264
loadSavegameFromScript(int16 slotNum,uint32 callingThreadId)1265 bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
1266 if (_savegameSlotNum < 0) {
1267 return false; // TODO need to handle reset from new game (without exising savegame).
1268 }
1269
1270 const char *fileName = getSavegameFilename(_savegameSlotNum);
1271 bool success = loadgame(fileName);
1272 if (success)
1273 activateSavegame(callingThreadId);
1274 _gameState->deleteReadStream();
1275 return success;
1276 }
1277
saveSavegameFromScript(int16 slotNum,uint32 callingThreadId)1278 bool IllusionsEngine_Duckman::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
1279 const char *fileName = getSavegameFilename(_savegameSlotNum);
1280 return savegame(fileName, _savegameDescription.c_str());
1281 }
1282
activateSavegame(uint32 callingThreadId)1283 void IllusionsEngine_Duckman::activateSavegame(uint32 callingThreadId) {
1284 // TODO _screen->setDisplayOn(false);
1285 uint32 currSceneId = getCurrentScene();
1286 if (currSceneId != 0x10003)
1287 dumpCurrSceneFiles(currSceneId, callingThreadId);
1288 reset();
1289 // TODO stopMidi();
1290 // TODO clearMidiPlayList();
1291 _gameState->readState(_savegameSceneId, _savegameThreadId);
1292 pushActiveScene(0x10000);
1293 _gameState->deleteReadStream();
1294 }
1295
1296 } // End of namespace Illusions
1297