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 "voyeur/files.h"
24 #include "voyeur/screen.h"
25 #include "voyeur/voyeur.h"
26 #include "voyeur/staticres.h"
27 #include "common/config-manager.h"
28
29 namespace Voyeur {
30
31 int ThreadResource::_useCount[8];
32
init()33 void ThreadResource::init() {
34 Common::fill(&_useCount[0], &_useCount[8], 0);
35 }
36
ThreadResource(BoltFilesState & state,const byte * src)37 ThreadResource::ThreadResource(BoltFilesState &state, const byte *src):_vm(state._vm) {
38 _stateId = READ_LE_UINT16(&src[0]);
39 _stackId = READ_LE_UINT16(&src[0]);
40 _savedStateId = READ_LE_UINT16(&src[0]);
41 _savedStackId = READ_LE_UINT16(&src[0]);
42 _ctlPtr = nullptr;
43 _aptPos = Common::Point(-1, -1);
44
45 _newStateId = -1;
46 _newStackId = -1;
47 _stateFlags = 0;
48 _stateCount = 0;
49 _parseCount = 0;
50 _nextStateId = 0;
51 _threadInfoPtr = nullptr;
52 _playCommandsPtr = nullptr;
53 }
54
initThreadStruct(int idx,int id)55 void ThreadResource::initThreadStruct(int idx, int id) {
56 _stackId = -1;
57 if (loadAStack(idx)) {
58 _savedStateId = _savedStackId = -1;
59 _stateId = id;
60 _newStateId = -1;
61 _newStackId = -1;
62
63 doState();
64 }
65 }
66
loadAStack(int stackId)67 bool ThreadResource::loadAStack(int stackId) {
68 if (_vm->_stampFlags & 1) {
69 if (stackId < 0)
70 error("loadAStack() - Invalid stackId %d", stackId);
71
72 unloadAStack(_stackId);
73 if (!_useCount[stackId]) {
74 BoltEntry &boltEntry = _vm->_stampLibPtr->boltEntry(_vm->_controlPtr->_memberIds[stackId]);
75 if (!boltEntry._data)
76 return false;
77
78 _vm->_controlPtr->_entries[stackId] = boltEntry._data;
79 }
80
81 ++_useCount[stackId];
82 }
83
84 _ctlPtr = _vm->_controlPtr->_entries[stackId];
85 _stackId = stackId;
86 return true;
87 }
88
unloadAStack(int stackId)89 void ThreadResource::unloadAStack(int stackId) {
90 if (stackId < 0)
91 return;
92
93 if ((_vm->_stampFlags & 1) && _useCount[stackId]) {
94 if (--_useCount[stackId] == 0) {
95 _vm->_stampLibPtr->freeBoltMember(_vm->_controlPtr->_memberIds[stackId]);
96 }
97 }
98 }
99
doState()100 bool ThreadResource::doState() {
101 if (!getStateInfo())
102 return false;
103
104 getButtonsFlags();
105
106 _vm->_glGoState = -1;
107 _vm->_glGoStack = -1;
108
109 performOpenCard();
110 if (_stateFlags & 1) {
111 return chooseSTAMPButton(_vm->getRandomNumber(_stateCount - 1));
112 } else {
113 return true;
114 }
115 }
116
getStateInfo()117 bool ThreadResource::getStateInfo() {
118 int id = READ_LE_UINT16(_ctlPtr);
119
120 if (id <= _stateId) {
121 return false;
122 } else {
123 uint32 fld = READ_LE_UINT32(_ctlPtr + 2);
124 fld += _stateId << 3;
125 _nextStateId = READ_LE_UINT32(_ctlPtr + fld + 4);
126
127 fld = READ_LE_UINT32(_ctlPtr + fld);
128 byte *baseP = _ctlPtr + fld;
129 _stateCount = READ_LE_UINT16(baseP);
130 _stateFlags = READ_LE_UINT16(baseP + 2);
131 _parseCount = READ_LE_UINT16(baseP + 4);
132
133 _playCommandsPtr = getDataOffset();
134 _playCommandsPtr += (READ_LE_UINT32(baseP + 6) / 2) << 1;
135
136 _threadInfoPtr = baseP + 10;
137
138 getButtonsText();
139 return true;
140 }
141 }
142
getDataOffset()143 byte *ThreadResource::getDataOffset() {
144 uint32 offset = READ_LE_UINT32(_ctlPtr + 10);
145 return _ctlPtr + offset;
146 }
147
getButtonsText()148 void ThreadResource::getButtonsText() {
149 int idx = 0;
150
151 for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
152 if (*p == 0xC0) {
153 ++p;
154 if (*p++ & 0x80) {
155 assert(idx < 63);
156 p += 4;
157 }
158
159 ++idx;
160 }
161 }
162 }
163
getButtonsFlags()164 void ThreadResource::getButtonsFlags() {
165 int idx = 0;
166
167 for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
168 if (*p == 0xC0) {
169 if (*++p & 0x20)
170 _stateFlags |= 2;
171
172 _buttonFlags[idx] = *p++;
173 _buttonIds[idx] = *p++;
174
175 if (_buttonFlags[idx] & 0x80)
176 p += 4;
177
178 ++idx;
179 }
180 }
181 }
182
unloadAllStacks(VoyeurEngine * vm)183 void ThreadResource::unloadAllStacks(VoyeurEngine *vm) {
184 if (vm->_stampFlags & 1) {
185 for (int i = 0; i < 8; ++i) {
186 if (_useCount[i])
187 vm->_stampLibPtr->freeBoltMember(vm->_controlPtr->_memberIds[i]);
188 }
189 }
190 }
191
performOpenCard()192 void ThreadResource::performOpenCard() {
193 for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
194 if (*p == 0x47) {
195 cardAction(p + 1);
196 return;
197 }
198 }
199 }
200
initUseCount()201 void ThreadResource::initUseCount() {
202 Common::fill(&_useCount[0], &_useCount[8], 0);
203 }
204
getRecordOffset(const byte * p)205 const byte *ThreadResource::getRecordOffset(const byte *p) {
206 uint32 recSize = READ_LE_UINT32(p) + READ_LE_UINT32(_ctlPtr + 6);
207 return _ctlPtr + recSize;
208 }
209
getNextRecord(const byte * p)210 const byte *ThreadResource::getNextRecord(const byte *p) {
211 byte v = *p++;
212
213 switch (v) {
214 case 2:
215 case 4:
216 case 6:
217 case 8:
218 case 10:
219 return p + 8;
220 case 1:
221 case 3:
222 case 5:
223 case 7:
224 case 9:
225 case 11:
226 case 21:
227 case 22:
228 case 25:
229 case 26:
230 return p + 5;
231 case 17:
232 case 23:
233 case 24:
234 case 27:
235 case 28:
236 return p + 2;
237 case 19:
238 case 41:
239 return p + 6;
240 case 18:
241 case 51:
242 case 52:
243 return p + 1;
244 case 74:
245 return p + 4;
246 case 192:
247 if (*p & 0x80)
248 p += 4;
249 return p + 2;
250 default:
251 return p;
252 }
253 }
254
getSTAMPCard(int cardId)255 const byte *ThreadResource::getSTAMPCard(int cardId) {
256 const byte *p;
257 int count = 0;
258
259 for (p = _threadInfoPtr; count <= cardId && *p != 0x49; p = getNextRecord(p)) {
260 if (*p == 0xC0)
261 ++count;
262 }
263
264 return p;
265 }
266
getStateFromID(uint32 id)267 int ThreadResource::getStateFromID(uint32 id) {
268 int count = READ_LE_UINT16(_ctlPtr);
269
270 for (int i = 0; i < count; ++i) {
271 uint32 sid = getSID(i);
272 if (sid == id)
273 return i;
274 }
275
276 return -1;
277 }
278
getSID(int sid)279 uint32 ThreadResource::getSID(int sid) {
280 uint32 offset = READ_LE_UINT32(_ctlPtr + 2) + (sid << 3) + 4;
281 return READ_LE_UINT32(_ctlPtr + offset);
282 }
283
doSTAMPCardAction()284 void ThreadResource::doSTAMPCardAction() {
285 for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
286 if (*p == 0x48) {
287 cardAction(p + 1);
288 return;
289 }
290 }
291 }
292
cardAction(const byte * card)293 void ThreadResource::cardAction(const byte *card) {
294 _vm->_glGoState = -1;
295 _vm->_glGoStack = -1;
296
297 // Loop to perform card commands
298 while (!_vm->shouldQuit() && *card < 70 && _vm->_glGoState == -1) {
299 card = cardPerform(card);
300 }
301 }
302
chooseSTAMPButton(int buttonId)303 bool ThreadResource::chooseSTAMPButton(int buttonId) {
304 for (int idx = 0; idx < _stateCount; ++idx) {
305 if (_buttonIds[idx] == buttonId) {
306 const byte *card = getSTAMPCard(idx);
307 cardAction(card);
308
309 bool flag = true;
310 while (!_vm->shouldQuit() && _vm->_glGoStack != -1 && flag) {
311 doSTAMPCardAction();
312 flag = goToStateID(_vm->_glGoStack, _vm->_glGoState);
313 }
314
315 while (!_vm->shouldQuit() && _vm->_glGoState != -1 && flag) {
316 doSTAMPCardAction();
317 flag = goToState(-1, _vm->_glGoState);
318 }
319
320 return flag;
321 }
322 }
323
324 return false;
325 }
326
parsePlayCommands()327 void ThreadResource::parsePlayCommands() {
328 _vm->_voy->_playStampMode = -1;
329 _vm->_voy->_audioVisualStartTime = 0;
330 _vm->_voy->_audioVisualDuration = 0;
331 _vm->_voy->_boltGroupId2 = -1;
332 _vm->_voy->_computerTextId = -1;
333 _vm->_voy->_eventFlags &= ~EVTFLAG_8;
334 _vm->_eventsManager->_videoDead = -1;
335
336 // Reset hotspot data
337 _vm->_voy->_videoHotspotTimes.reset();
338 _vm->_voy->_audioHotspotTimes.reset();
339 _vm->_voy->_evidenceHotspotTimes.reset();
340 Common::fill(&_vm->_voy->_roomHotspotsEnabled[0], &_vm->_voy->_roomHotspotsEnabled[20], false);
341 byte *dataP = _playCommandsPtr;
342 int v2, v3;
343 PictureResource *pic;
344 CMapResource *pal;
345
346 for (int parseIndex = 0; parseIndex < _parseCount; ++parseIndex) {
347 uint16 id = READ_LE_UINT16(dataP);
348 debugC(DEBUG_BASIC, kDebugScripts, "parsePlayCommands (%d of %d) - cmd #%d",
349 parseIndex + 1, _parseCount, id);
350 dataP += 2;
351
352 switch (id) {
353 case 1:
354 _vm->_currentVocId = READ_LE_UINT16(dataP);
355 dataP += 2;
356 break;
357
358 case 2:
359 // Play an audio event
360 v2 = READ_LE_UINT16(dataP);
361
362 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
363 _vm->_audioVideoId = READ_LE_UINT16(dataP + 2) - 1;
364 _vm->_voy->_audioVisualStartTime = READ_LE_UINT16(dataP + 4);
365 _vm->_voy->_audioVisualDuration = READ_LE_UINT16(dataP + 6);
366
367 if (_vm->_voy->_RTVNum < _vm->_voy->_audioVisualStartTime ||
368 (_vm->_voy->_audioVisualStartTime + _vm->_voy->_audioVisualDuration) < _vm->_voy->_RTVNum) {
369 _vm->_audioVideoId = -1;
370 } else {
371 _vm->_voy->_vocSecondsOffset = _vm->_voy->_RTVNum - _vm->_voy->_audioVisualStartTime;
372 _vm->_voy->addAudioEventStart();
373
374 // Play the audio
375 assert(_vm->_audioVideoId < 38);
376 _vm->playAudio(_vm->_audioVideoId);
377
378 _vm->_voy->addAudioEventEnd();
379 _vm->_eventsManager->incrementTime(1);
380 _vm->_eventsManager->incrementTime(1);
381 _vm->_audioVideoId = -1;
382 parseIndex = 999;
383 }
384 }
385
386 dataP += 8;
387 break;
388
389 case 3:
390 // Play a video event
391 v2 = READ_LE_UINT16(dataP);
392
393 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
394 _vm->_audioVideoId = READ_LE_UINT16(dataP + 2) - 1;
395 _vm->_voy->_audioVisualStartTime = READ_LE_UINT16(dataP + 4);
396 _vm->_voy->_audioVisualDuration = READ_LE_UINT16(dataP + 6);
397
398 if (_vm->_voy->_RTVNum < _vm->_voy->_audioVisualStartTime ||
399 (_vm->_voy->_audioVisualStartTime + _vm->_voy->_audioVisualDuration) < _vm->_voy->_RTVNum) {
400 _vm->_audioVideoId = -1;
401 } else {
402 _vm->_voy->_vocSecondsOffset = _vm->_voy->_RTVNum - _vm->_voy->_audioVisualStartTime;
403 _vm->_voy->addVideoEventStart();
404 _vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
405 _vm->_voy->_eventFlags |= EVTFLAG_RECORDING;
406 _vm->playAVideo(_vm->_audioVideoId);
407
408 _vm->_voy->_eventFlags &= ~EVTFLAG_RECORDING;
409 _vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
410 _vm->_voy->addVideoEventEnd();
411 _vm->_eventsManager->incrementTime(1);
412
413 _vm->_audioVideoId = -1;
414 _vm->_playStampGroupId = -1;
415
416 if (_vm->_eventsManager->_videoDead != -1) {
417 _vm->_bVoy->freeBoltGroup(0xE00);
418 _vm->_eventsManager->_videoDead = -1;
419 _vm->flipPageAndWait();
420 }
421
422 _vm->_eventsManager->_videoDead = -1;
423 if (_stateCount == 2 && _vm->_eventsManager->_mouseClicked == 0) {
424 _vm->_voy->_playStampMode = 132;
425 parseIndex = 999;
426 } else {
427 _vm->_voy->_playStampMode = 129;
428 }
429 }
430 }
431
432 dataP += 8;
433 break;
434
435 case 4:
436 case 22:
437 // Case 22: Endgame news reports
438 _vm->_audioVideoId = READ_LE_UINT16(dataP) - 1;
439 dataP += 2;
440
441 if (id == 22) {
442 int resolveIndex = READ_LE_UINT16(dataP);
443 dataP += 2;
444 _vm->_playStampGroupId = _vm->_resolvePtr[resolveIndex];
445 }
446
447 _vm->_voy->_vocSecondsOffset = 0;
448 _vm->_voy->_audioVisualStartTime = _vm->_voy->_RTVNum;
449 _vm->_voy->_eventFlags &= ~(EVTFLAG_TIME_DISABLED | EVTFLAG_RECORDING);
450 _vm->playAVideo(_vm->_audioVideoId);
451 _vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
452
453 if (id != 22) {
454 _vm->_audioVideoId = -1;
455 parseIndex = 999;
456 } else {
457 int count = _vm->_bVoy->getBoltGroup(_vm->_playStampGroupId)->_entries.size() / 2;
458 _vm->_soundManager->stopVOCPlay();
459 _vm->_eventsManager->getMouseInfo();
460
461 for (int i = 0; i < count; ++i) {
462 pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2)._picResource;
463 pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2 + 1)._cMapResource;
464
465 _vm->_screen->_vPort->setupViewPort(pic);
466 pal->startFade();
467
468 _vm->flipPageAndWaitForFade();
469
470 if (i > 0) {
471 _vm->_bVoy->freeBoltMember(_vm->_playStampGroupId + i * 2);
472 _vm->_bVoy->freeBoltMember(_vm->_playStampGroupId + i * 2 + 1);
473 }
474
475 Common::String file = Common::String::format("news%d.voc", i + 1);
476 _vm->_soundManager->startVOCPlay(file);
477
478 while (!_vm->shouldQuit() && !_vm->_eventsManager->_mouseClicked &&
479 _vm->_soundManager->getVOCStatus()) {
480 _vm->_eventsManager->delayClick(1);
481 _vm->_eventsManager->getMouseInfo();
482 }
483
484 _vm->_soundManager->stopVOCPlay();
485
486 if (i == (count - 1))
487 _vm->_eventsManager->delayClick(480);
488
489 if (_vm->shouldQuit() || _vm->_eventsManager->_mouseClicked)
490 break;
491 }
492
493 _vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
494 _vm->_playStampGroupId = -1;
495 _vm->_audioVideoId = -1;
496 parseIndex = 999;
497 }
498 break;
499
500 case 5:
501 // Check whether transition to a given time period is allowed, and
502 // if so, load the time information for the new time period
503 v2 = READ_LE_UINT16(dataP);
504 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
505 _vm->_voy->_playStampMode = 5;
506 int count = READ_LE_UINT16(dataP + 2);
507 _vm->_voy->_RTVLimit = READ_LE_UINT16(dataP + 4);
508
509 if (_vm->_voy->_transitionId != count) {
510 if (_vm->_voy->_transitionId > 1)
511 _vm->_voy->_eventFlags &= ~EVTFLAG_100;
512
513 _vm->_voy->_transitionId = count;
514 _vm->_gameMinute = LEVEL_M[count - 1];
515 _vm->_gameHour = LEVEL_H[count - 1];
516 //_vm->_v2A0A2 = 0;
517 _vm->_voy->_RTVNum = 0;
518 _vm->_voy->_RTANum = 255;
519 }
520
521 _vm->_voy->_isAM = (_vm->_voy->_transitionId == 6);
522 }
523
524 dataP += 6;
525 break;
526
527 case 6:
528 _vm->_voy->_playStampMode = 6;
529 v2 = READ_LE_UINT16(dataP);
530 _vm->_playStampGroupId = _vm->_resolvePtr[v2];
531 dataP += 2;
532 break;
533
534 case 7:
535 // Load the video event scene hotspot times data
536 v2 = READ_LE_UINT16(dataP);
537 v3 = READ_LE_UINT16(dataP + 2) - 1;
538
539 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
540 int idx = 0;
541 while (_vm->_voy->_videoHotspotTimes._min[idx][v3] != 9999)
542 ++idx;
543
544 v2 = READ_LE_UINT16(dataP + 4);
545 _vm->_voy->_videoHotspotTimes._min[idx][v3] = v2;
546 _vm->_voy->_videoHotspotTimes._max[idx][v3] = v2 + READ_LE_UINT16(dataP + 6) - 2;
547 }
548
549 dataP += 8;
550 break;
551
552 case 8:
553 // Load the audio event scene hotspot times data
554 v2 = READ_LE_UINT16(dataP);
555 v3 = READ_LE_UINT16(dataP + 2) - 1;
556
557 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
558 int idx = 0;
559 while (_vm->_voy->_audioHotspotTimes._min[idx][v3] != 9999)
560 ++idx;
561
562 v2 = READ_LE_UINT16(dataP + 4);
563 _vm->_voy->_audioHotspotTimes._min[idx][v3] = v2;
564 _vm->_voy->_audioHotspotTimes._max[idx][v3] = v2 + READ_LE_UINT16(dataP + 6) - 2;
565 }
566
567 dataP += 8;
568 break;
569
570 case 9:
571 // Load up evidence event scene hotspot times data
572 v2 = READ_LE_UINT16(dataP);
573 v3 = READ_LE_UINT16(dataP + 2) - 1;
574
575 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
576 int idx = 0;
577 while (_vm->_voy->_evidenceHotspotTimes._min[idx][v3] != 9999)
578 ++idx;
579
580 v2 = READ_LE_UINT16(dataP + 4);
581 _vm->_voy->_evidenceHotspotTimes._min[idx][v3] = v2;
582 _vm->_voy->_evidenceHotspotTimes._max[idx][v3] = v2 + READ_LE_UINT16(dataP + 6) - 2;
583 }
584
585 dataP += 8;
586 break;
587
588 case 10:
589 // Pick the person who is to die, during startup
590 if (_vm->_iForceDeath == -1) {
591 // No specific person has been preset to be killed, so pick one randomly.
592 // The loop below ensures that a different victim is picked.
593 int lastVictim = ConfMan.hasKey("lastVictim") ? ConfMan.getInt("lastVictim") : -1;
594 int randomVictim;
595 do {
596 randomVictim = _vm->getRandomNumber(3) + 1;
597 } while (randomVictim == lastVictim);
598
599 _vm->_controlPtr->_state->_victimIndex = randomVictim;
600 } else {
601 // Victim selected from command line
602 _vm->_controlPtr->_state->_victimIndex = _vm->_iForceDeath;
603 }
604
605 ConfMan.setInt("lastVictim", _vm->_controlPtr->_state->_victimIndex);
606 ConfMan.flushToDisk();
607 break;
608
609 case 11:
610 _vm->_voy->_eventFlags |= EVTFLAG_2;
611 break;
612
613 case 12:
614 v2 = READ_LE_UINT16(dataP);
615
616 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
617 _vm->_voy->_boltGroupId2 = _vm->_resolvePtr[READ_LE_UINT16(dataP + 2)];
618 _vm->_voy->_roomHotspotsEnabled[READ_LE_UINT16(dataP + 4) - 1] = true;
619 }
620
621 dataP += 6;
622 break;
623
624 case 13:
625 v2 = READ_LE_UINT16(dataP);
626
627 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
628 _vm->_voy->_computerTextId = READ_LE_UINT16(dataP + 2) - 1;
629 _vm->_voy->_computerTimeMin = READ_LE_UINT16(dataP + 4);
630 _vm->_voy->_computerTimeMax = READ_LE_UINT16(dataP + 6);
631
632 _vm->_voy->_computerScreenRect.left = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4];
633 _vm->_voy->_computerScreenRect.top = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4 + 1];
634 _vm->_voy->_computerScreenRect.right = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4 + 2];
635 _vm->_voy->_computerScreenRect.bottom = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4 + 3];
636 }
637
638 dataP += 8;
639 break;
640
641 case 14:
642 _vm->_playStampGroupId = 2048;
643 _vm->_voy->_playStampMode = 130;
644 break;
645
646 case 15:
647 _vm->showEndingNews();
648 break;
649
650 case 16:
651 _vm->_voy->_playStampMode = 16;
652 break;
653
654 case 17:
655 _vm->_voy->_playStampMode = 17;
656 break;
657
658 case 18:
659 // Called during the murder (Sunday 10:30PM) time period, to specify the
660 // time expired point at which the murder takes place
661 v2 = READ_LE_UINT16(dataP);
662 v3 = READ_LE_UINT16(dataP + 2);
663
664 if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2)
665 _vm->_voy->_murderThreshold = v3;
666
667 dataP += 4;
668 break;
669
670 case 19:
671 _vm->_voy->_aptLoadMode = 140;
672 loadTheApt();
673 _vm->_voy->_aptLoadMode = 141;
674 freeTheApt();
675 break;
676
677 case 20:
678 _vm->_voy->_aptLoadMode = -1;
679 loadTheApt();
680 _vm->_voy->_aptLoadMode = 141;
681 freeTheApt();
682 break;
683
684 case 21:
685 _vm->_voy->_aptLoadMode = -1;
686 loadTheApt();
687 _vm->_voy->_aptLoadMode = 140;
688 freeTheApt();
689 break;
690
691 case 23:
692 _vm->_voy->_transitionId = 17;
693 _vm->_voy->_aptLoadMode = -1;
694 loadTheApt();
695 _vm->_voy->_aptLoadMode = 144;
696 freeTheApt();
697 break;
698
699 default:
700 break;
701 }
702 }
703 }
704
cardPerform(const byte * card)705 const byte *ThreadResource::cardPerform(const byte *card) {
706 uint16 id = *card++;
707 int subId = 5;
708 uint32 v2;
709 byte bVal;
710 uint32 idx1, idx2;
711 debugC(DEBUG_BASIC, kDebugScripts, "cardPerform - %d", id);
712
713 switch (id) {
714 case 1:
715 v2 = READ_LE_UINT32(card);
716 card += 4;
717 _vm->_controlPtr->_state->_vals[*card++] = v2;
718 break;
719
720 case 2:
721 v2 = _vm->_controlPtr->_state->_vals[*card++];
722 _vm->_controlPtr->_state->_vals[*card++] = v2;
723 break;
724
725 case 3:
726 v2 = READ_LE_UINT32(card);
727 card += 4;
728 _vm->_controlPtr->_state->_vals[*card++] = v2;
729 break;
730
731 case 4:
732 v2 = _vm->_controlPtr->_state->_vals[*card++];
733 _vm->_controlPtr->_state->_vals[*card++] = v2;
734 break;
735
736 case 5: {
737 v2 = READ_LE_UINT32(card);
738 card += 4;
739 int &v = _vm->_controlPtr->_state->_vals[*card++];
740 v -= v2;
741 break;
742 }
743
744 case 6: {
745 idx1 = *card++;
746 idx2 = *card++;
747
748 v2 = _vm->_controlPtr->_state->_vals[idx1];
749 int &v = _vm->_controlPtr->_state->_vals[idx2];
750 v -= v2;
751 break;
752 }
753
754 case 7: {
755 int v3 = *card++;
756 v2 = READ_LE_UINT32(card);
757 card += 4;
758 int &v = _vm->_controlPtr->_state->_vals[v3];
759 v *= v2;
760 break;
761 }
762
763 case 8: {
764 idx1 = *card++;
765 idx2 = *card++;
766
767 int &v1 = _vm->_controlPtr->_state->_vals[idx1];
768 v2 = _vm->_controlPtr->_state->_vals[idx2];
769 v1 *= v2;
770 break;
771 }
772
773 case 9: {
774 idx1 = *card++;
775 v2 = READ_LE_UINT32(card);
776 card += 4;
777
778 int &v = _vm->_controlPtr->_state->_vals[idx1];
779 v /= v2;
780 break;
781 }
782
783 case 10: {
784 idx1 = *card++;
785 idx2 = *card++;
786
787 int &v1 = _vm->_controlPtr->_state->_vals[idx1];
788 v2 = _vm->_controlPtr->_state->_vals[idx2];
789 v1 /= v2;
790 break;
791 }
792
793 case 11:
794 v2 = READ_LE_UINT32(card);
795 card += 4;
796 v2 = _vm->getRandomNumber(v2 - 1) + 1;
797 _vm->_controlPtr->_state->_vals[*card++] = v2;
798 break;
799
800 case 17:
801 _vm->_glGoState = READ_LE_UINT16(card);
802 card += 2;
803 _vm->_glGoStack = -1;
804 break;
805
806 case 18:
807 v2 = _vm->_controlPtr->_state->_vals[*card++];
808 _vm->_glGoState = getStateFromID(v2);
809 break;
810
811 case 19:
812 _vm->_glGoState = READ_LE_UINT32(card);
813 card += 4;
814 _vm->_glGoStack = READ_LE_UINT16(card);
815 card += 2;
816 break;
817
818 case 23:
819 case 24:
820 case 27:
821 case 28:
822 subId -= 3;
823 // fall through
824
825 case 21:
826 case 22:
827 case 25:
828 case 26:
829 bVal = card[subId];
830 if (bVal == 61) {
831 if (cardPerform2(card, id)) {
832 card += subId;
833 while (*card != 30 && *card != 29)
834 card = cardPerform(card);
835
836 if (*card == 29) {
837 int count = 1;
838 while (count > 0) {
839 card = getNextRecord(card);
840 if (*card == 30)
841 --count;
842 if (*card >= 21 && *card <= 28)
843 ++count;
844 }
845 }
846 } else {
847 card += subId;
848 int count = 1;
849 while (count > 0) {
850 card = getNextRecord(card);
851 if (*card == 29 || *card == 30)
852 --count;
853 if (*card < 21 || *card > 28)
854 continue;
855
856 const byte *nextP = getNextRecord(card + 2);
857 if (*nextP == 61)
858 ++count;
859 }
860 }
861
862 ++card;
863 } else {
864 if (cardPerform2(card, id)) {
865 card += subId;
866 card = cardPerform(card);
867 while (*card++ != 61) {}
868 } else {
869 card += subId;
870 while (*card != 61 && *card != 29)
871 ++card;
872 }
873 }
874 break;
875
876 case 41:
877 bVal = *card++;
878 assert(bVal < 8);
879 card += 6;
880 break;
881
882 case 45:
883 _newStateId = _nextStateId;
884 _newStackId = _stackId;
885 break;
886
887 case 46:
888 _vm->_glGoState = _newStateId;
889 _vm->_glGoStack = _newStackId;
890 _newStateId = -1;
891 _newStackId = -1;
892 break;
893
894 case 51:
895 setButtonFlag(READ_LE_UINT16(card), 64);
896 break;
897
898 case 52:
899 clearButtonFlag(READ_LE_UINT16(card), 64);
900 break;
901
902 default:
903 break;
904 }
905
906 return card;
907 }
908
cardPerform2(const byte * pSrc,int cardCmdId)909 bool ThreadResource::cardPerform2(const byte *pSrc, int cardCmdId) {
910 int vLong, vLong2;
911
912 switch (cardCmdId) {
913 case 21:
914 vLong = (int32)READ_LE_UINT32(pSrc + 1);
915 return _vm->_controlPtr->_state->_vals[*pSrc] == vLong;
916
917 case 22:
918 vLong = (int32)READ_LE_UINT32(pSrc + 1);
919 return _vm->_controlPtr->_state->_vals[*pSrc] != vLong;
920
921 case 23:
922 vLong = _vm->_controlPtr->_state->_vals[*pSrc];
923 vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
924 return vLong == vLong2;
925
926 case 24:
927 vLong = _vm->_controlPtr->_state->_vals[*pSrc];
928 vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
929 return vLong != vLong2;
930
931 case 25:
932 vLong = _vm->_controlPtr->_state->_vals[*pSrc];
933 vLong2 = (int32)READ_LE_UINT32(pSrc + 1);
934 return vLong < vLong2;
935
936 case 26:
937 vLong = _vm->_controlPtr->_state->_vals[*pSrc];
938 vLong2 = (int32)READ_LE_UINT32(pSrc + 1);
939 return vLong > vLong2;
940
941 case 27:
942 vLong = _vm->_controlPtr->_state->_vals[*pSrc];
943 vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
944 return vLong < vLong2;
945
946 case 28:
947 vLong = _vm->_controlPtr->_state->_vals[*pSrc];
948 vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
949 return vLong > vLong2;
950
951 default:
952 return false;
953 }
954 }
955
doApt()956 int ThreadResource::doApt() {
957 loadTheApt();
958
959 _vm->_currentVocId = 151;
960 _vm->_voy->_viewBounds = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._rectResource;
961 Common::Array<RectEntry> &hotspots = _vm->_bVoy->boltEntry(
962 _vm->_playStampGroupId + 1)._rectResource->_entries;
963 _vm->_eventsManager->getMouseInfo();
964
965 // Very first time apartment is shown, start the phone message
966 if (_aptPos.x == -1) {
967 _aptPos.x = hotspots[2].left;
968 _aptPos.y = hotspots[2].top;
969 _vm->_currentVocId = 153;
970 }
971
972 if (_vm->_voy->_playStampMode == 16) {
973 hotspots[0].left = 999;
974 hotspots[3].left = 999;
975 _aptPos.x = hotspots[4].left + 28;
976 _aptPos.y = hotspots[4].top + 28;
977 }
978
979 _vm->_eventsManager->setMousePos(Common::Point(_aptPos.x, _aptPos.y));
980 _vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId));
981 _vm->_currentVocId = 151;
982
983 _vm->_screen->setColor(129, 82, 82, 82);
984 _vm->_screen->setColor(130, 112, 112, 112);
985 _vm->_screen->setColor(131, 215, 215, 215);
986 _vm->_screen->setColor(132, 235, 235, 235);
987
988 _vm->_eventsManager->_intPtr._hasPalette = true;
989
990 // Set up the cursors
991 PictureResource *unselectedCursor = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 2)._picResource;
992 PictureResource *selectedCursor = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 3)._picResource;
993 unselectedCursor->_keyColor = 0xff;
994 selectedCursor->_keyColor = 0xff;
995 _vm->_eventsManager->setCursor(unselectedCursor);
996 _vm->_eventsManager->showCursor();
997
998 // Main loop to allow users to move the cursor and select hotspots
999 int hotspotId;
1000 int prevHotspotId = -1;
1001 Common::Point pt;
1002 PictureResource *pic;
1003 Common::Rect gmmHotspot(75, 125, 130, 140);
1004
1005 do {
1006 _vm->_voyeurArea = AREA_APARTMENT;
1007
1008 if (_vm->_loadGameSlot != -1) {
1009 // Load a savegame
1010 int slot = _vm->_loadGameSlot;
1011 _vm->_loadGameSlot = -1;
1012 _vm->loadGame(slot);
1013
1014 _vm->_eventsManager->showCursor();
1015 }
1016
1017 _vm->_eventsManager->getMouseInfo();
1018 if (!_vm->_soundManager->getVOCStatus()) {
1019 // Previous sound ended, so start up a new one
1020 _vm->_currentVocId = 151 - _vm->getRandomNumber(4);
1021 _vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId));
1022 }
1023
1024 // Loop through the hotspot list
1025 hotspotId = -1;
1026 pt = _vm->_eventsManager->getMousePos() + Common::Point(16, 16);
1027 for (int idx = 0; idx < (int)hotspots.size(); ++idx) {
1028 if (hotspots[idx].contains(pt)) {
1029 // Cursor is within hotspot area
1030
1031 // Don't allow the camera to be highlighted on Monday morning.
1032 if (idx == 0 && _vm->_voy->_transitionId == 17)
1033 continue;
1034
1035 // Set the highlighted hotspot Id
1036 hotspotId = idx;
1037
1038 if (hotspotId != prevHotspotId) {
1039 // Check for whether to replace hotspot Id for "Watch TV" for
1040 // "Review the Tape" if player has already watched the TV
1041 if ((_vm->_voy->_eventFlags & EVTFLAG_100) && (hotspotId == 2))
1042 hotspotId = 5;
1043
1044 // Draw the text description for the highlighted hotspot
1045 pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId +
1046 hotspotId + 6)._picResource;
1047 _vm->_screen->sDrawPic(pic, _vm->_screen->_vPort,
1048 Common::Point(106, 200));
1049 }
1050
1051 break;
1052 }
1053 }
1054
1055 // Check for presence in ScummVM GMM
1056 if (gmmHotspot.contains(pt))
1057 hotspotId = 42;
1058
1059 // Update the cursor to either standard or highlighted eye cursor
1060 _vm->_eventsManager->setCursor((hotspotId == -1) ? unselectedCursor : selectedCursor);
1061 _vm->flipPageAndWait();
1062
1063 if (hotspotId == 42 && _vm->_eventsManager->_leftClick) {
1064 // Show the ScummVM GMM
1065 _vm->_eventsManager->getMouseInfo();
1066 _vm->openMainMenuDialog();
1067 }
1068
1069 } while (!_vm->shouldQuit() && (!_vm->_eventsManager->_leftClick || hotspotId == -1));
1070
1071 _vm->_eventsManager->hideCursor();
1072 pt = _vm->_eventsManager->getMousePos();
1073 _aptPos.x = pt.x;
1074 _aptPos.y = pt.y;
1075
1076 switch (hotspotId) {
1077 case 0:
1078 _vm->_voy->_aptLoadMode = 140;
1079 break;
1080 case 1:
1081 _vm->_voy->_aptLoadMode = 143;
1082 break;
1083 case 2:
1084 _vm->_voy->_aptLoadMode = 142;
1085 break;
1086 case 5:
1087 _vm->_voy->_aptLoadMode = 141;
1088 break;
1089 default:
1090 _vm->_voy->_aptLoadMode = -1;
1091 break;
1092 }
1093
1094 freeTheApt();
1095
1096 if (_vm->_voy->_transitionId == 1 && hotspotId == 0)
1097 _vm->checkTransition();
1098
1099 if (!hotspotId)
1100 _vm->makeViewFinder();
1101
1102 return hotspotId;
1103 }
1104
doRoom()1105 void ThreadResource::doRoom() {
1106 VoyeurEngine &vm = *_vm;
1107 SVoy voy = *vm._voy;
1108
1109 vm.makeViewFinderP();
1110 voy._fadingType = 0;
1111
1112 if (!vm._bVoy->getBoltGroup(vm._playStampGroupId))
1113 return;
1114
1115 vm._screen->_backColors = vm._bVoy->boltEntry(vm._playStampGroupId + 1)._cMapResource;
1116 vm._screen->_backgroundPage = vm._bVoy->boltEntry(vm._playStampGroupId)._picResource;
1117 vm._screen->_vPort->setupViewPort(vm._screen->_backgroundPage);
1118 vm._screen->_backColors->startFade();
1119
1120 voy._fadingStep1 = 2;
1121 voy._fadingStep2 = 0;
1122 voy._fadingType = 1;
1123
1124 Common::Array<RectEntry> &hotspots = vm._bVoy->boltEntry(vm._playStampGroupId + 4)._rectResource->_entries;
1125 int hotspotId = -1;
1126
1127 PictureResource *crosshairsCursor = vm._bVoy->boltEntry(vm._playStampGroupId + 2)._picResource;
1128 PictureResource *magnifierCursor = vm._bVoy->boltEntry(vm._playStampGroupId + 3)._picResource;
1129 vm._eventsManager->showCursor();
1130
1131 RectResource viewBounds(48, 38, 336, 202);
1132 voy._viewBounds = &viewBounds;
1133
1134 vm._eventsManager->getMouseInfo();
1135 vm._eventsManager->setMousePos(Common::Point(192, 120));
1136 voy._fadingType = 0;
1137 vm._currentVocId = 146;
1138 voy._musicStartTime = voy._RTVNum;
1139
1140 voy._vocSecondsOffset = 0;
1141 vm._soundManager->startVOCPlay(vm._currentVocId);
1142 voy._eventFlags &= ~EVTFLAG_TIME_DISABLED;
1143
1144 bool breakFlag = false;
1145 while (!vm.shouldQuit() && !breakFlag) {
1146 _vm->_voyeurArea = AREA_ROOM;
1147 vm._screen->setColor(128, 0, 255, 0);
1148 vm._eventsManager->_intPtr._hasPalette = true;
1149
1150 do {
1151 if (vm._currentVocId != -1 && !vm._soundManager->getVOCStatus()) {
1152 voy._musicStartTime = voy._RTVNum;
1153 voy._vocSecondsOffset = 0;
1154 vm._soundManager->startVOCPlay(vm._currentVocId);
1155 }
1156
1157 vm._eventsManager->getMouseInfo();
1158 Common::Point pt = vm._eventsManager->getMousePos();
1159 pt += Common::Point(30, 15);
1160
1161 hotspotId = -1;
1162
1163 if (voy._computerTextId != -1 && voy._computerScreenRect.contains(pt))
1164 hotspotId = 999;
1165
1166 for (uint idx = 0; idx < hotspots.size(); ++idx) {
1167 if (hotspots[idx].contains(pt)) {
1168 int arrIndex = hotspots[idx]._arrIndex;
1169 if (voy._roomHotspotsEnabled[arrIndex - 1]) {
1170 hotspotId = idx;
1171 break;
1172 }
1173 }
1174 }
1175
1176 if (hotspotId == -1) {
1177 vm._eventsManager->setCursorColor(128, 0);
1178 vm._eventsManager->setCursor(crosshairsCursor);
1179 } else if (hotspotId != 999 || voy._RTVNum < voy._computerTimeMin ||
1180 (voy._computerTimeMax - 2) < voy._RTVNum) {
1181 vm._eventsManager->setCursorColor(128, 1);
1182 vm._eventsManager->setCursor(magnifierCursor);
1183 } else {
1184 vm._eventsManager->setCursorColor(128, 2);
1185 vm._eventsManager->setCursor(magnifierCursor);
1186 }
1187
1188 vm._eventsManager->_intPtr._hasPalette = true;
1189 vm._screen->_vPort->_flags |= DISPFLAG_8;
1190 vm._screen->flipPage();
1191 vm._eventsManager->sWaitFlip();
1192 } while (!vm.shouldQuit() && !vm._eventsManager->_mouseClicked);
1193
1194 if (!vm._eventsManager->_leftClick || hotspotId == -1) {
1195 if (vm._eventsManager->_rightClick)
1196 breakFlag = true;
1197
1198 Common::Point pt = vm._eventsManager->getMousePos();
1199 vm._eventsManager->getMouseInfo();
1200 vm._eventsManager->setMousePos(pt);
1201 } else {
1202 voy._eventFlags |= EVTFLAG_RECORDING;
1203 vm._eventsManager->hideCursor();
1204 vm._eventsManager->startCursorBlink();
1205
1206 if (hotspotId == 999) {
1207 _vm->flipPageAndWait();
1208
1209 if (vm._currentVocId != -1) {
1210 voy._vocSecondsOffset = voy._RTVNum - voy._musicStartTime;
1211 vm._soundManager->stopVOCPlay();
1212 }
1213
1214 vm.getComputerBrush();
1215 _vm->flipPageAndWait();
1216
1217 vm._voy->addComputerEventStart();
1218
1219 vm._eventsManager->_mouseClicked = false;
1220 vm._eventsManager->startCursorBlink();
1221
1222 int totalChars = vm.doComputerText(9999);
1223 if (totalChars)
1224 vm._voy->addComputerEventEnd(totalChars);
1225
1226 vm._bVoy->freeBoltGroup(0x4900);
1227 } else {
1228 vm.doEvidDisplay(hotspotId, 999);
1229 }
1230
1231 voy._eventFlags &= ~EVTFLAG_RECORDING;
1232 if (!vm._eventsManager->_mouseClicked)
1233 vm._eventsManager->delayClick(18000);
1234
1235 // WORKAROUND: Skipped code from the original, that freed the group,
1236 // reloaded it, and reloaded the cursors
1237
1238 vm._screen->_backColors = vm._bVoy->boltEntry(
1239 vm._playStampGroupId + 1)._cMapResource;
1240 vm._screen->_backgroundPage = vm._bVoy->boltEntry(
1241 vm._playStampGroupId)._picResource;
1242
1243 vm._screen->_vPort->setupViewPort();
1244 vm._screen->_backColors->startFade();
1245 _vm->flipPageAndWait();
1246
1247 while (!vm.shouldQuit() && (vm._eventsManager->_fadeStatus & 1))
1248 vm._eventsManager->delay(1);
1249 vm._eventsManager->hideCursor();
1250
1251 while (!vm.shouldQuit() && voy._fadingAmount2 > 0) {
1252 if (voy._fadingAmount1 < 63) {
1253 voy._fadingAmount1 += 4;
1254 if (voy._fadingAmount1 > 63)
1255 voy._fadingAmount1 = 63;
1256 }
1257
1258 if (voy._fadingAmount2 > 0) {
1259 voy._fadingAmount2 -= 8;
1260 if (voy._fadingAmount2 < 0)
1261 voy._fadingAmount2 = 0;
1262 }
1263
1264 vm._eventsManager->delay(1);
1265 }
1266
1267 _vm->flipPageAndWait();
1268
1269 vm._screen->fadeUpICF1();
1270 voy._eventFlags &= EVTFLAG_RECORDING;
1271 vm._eventsManager->showCursor();
1272 }
1273 }
1274
1275 voy._eventFlags = EVTFLAG_TIME_DISABLED;
1276 vm._eventsManager->incrementTime(1);
1277 voy._viewBounds = nullptr;
1278 voy._fadingType = 0;
1279 vm.makeViewFinderP();
1280
1281 if (voy._boltGroupId2 != -1) {
1282 vm._bVoy->freeBoltGroup(voy._boltGroupId2);
1283 voy._boltGroupId2 = -1;
1284 }
1285
1286 if (vm._playStampGroupId != -1) {
1287 vm._bVoy->freeBoltGroup(vm._playStampGroupId);
1288 vm._playStampGroupId = -1;
1289 }
1290
1291 if (vm._currentVocId != -1) {
1292 vm._soundManager->stopVOCPlay();
1293 vm._currentVocId = -1;
1294 }
1295
1296 vm._eventsManager->hideCursor();
1297 chooseSTAMPButton(0);
1298 }
1299
doInterface()1300 int ThreadResource::doInterface() {
1301 PictureResource *pic;
1302 Common::Point pt;
1303
1304 _vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1305 if (_vm->_voy->_abortInterface) {
1306 _vm->_voy->_abortInterface = false;
1307 return -2;
1308 }
1309
1310 _vm->_voy->_eventFlags &= ~EVTFLAG_100;
1311 _vm->_playStampGroupId = -1;
1312 _vm->_eventsManager->_intPtr._flashStep = 1;
1313 _vm->_eventsManager->_intPtr._flashTimer = 0;
1314
1315 if (_vm->_voy->_RTVNum >= _vm->_voy->_RTVLimit || _vm->_voy->_RTVNum < 0)
1316 _vm->_voy->_RTVNum = _vm->_voy->_RTVLimit - 1;
1317
1318 if (_vm->_voy->_transitionId < 15 && _vm->_debugger->_isTimeActive
1319 && (_vm->_voy->_RTVLimit - 3) < _vm->_voy->_RTVNum) {
1320 _vm->_voy->_RTVNum = _vm->_voy->_RTVLimit;
1321 _vm->makeViewFinder();
1322
1323 _vm->initIFace();
1324 _vm->_eventsManager->hideCursor();
1325 _vm->_voy->_RTVNum = _vm->_voy->_RTVLimit - 4;
1326 _vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
1327
1328 while (!_vm->shouldQuit() && _vm->_voy->_RTVNum < _vm->_voy->_RTVLimit) {
1329 _vm->flashTimeBar();
1330 _vm->_eventsManager->delayClick(1);
1331 }
1332
1333 _vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1334 chooseSTAMPButton(20);
1335 parsePlayCommands();
1336
1337 _vm->_eventsManager->showCursor();
1338 }
1339
1340 _vm->checkTransition();
1341 _vm->makeViewFinder();
1342 _vm->_eventsManager->getMouseInfo();
1343 _vm->initIFace();
1344
1345 Common::Array<RectEntry> *hotspots = &_vm->_bVoy->boltEntry(
1346 _vm->_playStampGroupId + 1)._rectResource->_entries;
1347 _vm->_currentVocId = 151 - _vm->getRandomNumber(5);
1348 _vm->_voy->_vocSecondsOffset = _vm->getRandomNumber(29);
1349
1350 Common::String fname = _vm->_soundManager->getVOCFileName(_vm->_currentVocId);
1351 _vm->_soundManager->startVOCPlay(fname);
1352 _vm->_eventsManager->getMouseInfo();
1353
1354 _vm->_screen->setColor(240, 220, 220, 220);
1355 _vm->_eventsManager->_intPtr._hasPalette = true;
1356 _vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
1357
1358 // Set the cusor
1359 PictureResource *crosshairsCursor = _vm->_bVoy->boltEntry(0x112)._picResource;
1360 PictureResource *eyeCursor = _vm->_bVoy->boltEntry(0x113)._picResource;
1361 PictureResource *listenCursor = _vm->_bVoy->boltEntry(0x114)._picResource;
1362 PictureResource *mangifyCursor = _vm->_bVoy->boltEntry(0x115)._picResource;
1363
1364 _vm->_eventsManager->setCursor(crosshairsCursor);
1365
1366 // Main loop
1367 int regionIndex = 0;
1368 Common::Rect mansionViewBounds(MANSION_VIEW_X, MANSION_VIEW_Y,
1369 MANSION_VIEW_X + MANSION_VIEW_WIDTH, MANSION_VIEW_Y + MANSION_VIEW_HEIGHT);
1370
1371 do {
1372 _vm->_voyeurArea = AREA_INTERFACE;
1373 _vm->doTimeBar();
1374 _vm->_eventsManager->getMouseInfo();
1375
1376 if (checkMansionScroll())
1377 _vm->doScroll(_vm->_mansionViewPos);
1378
1379 _vm->checkPhoneCall();
1380 if (!_vm->_soundManager->getVOCStatus()) {
1381 _vm->_currentVocId = 151 - _vm->getRandomNumber(5);
1382 _vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId));
1383 }
1384
1385 // Calculate the mouse position within the entire mansion
1386 pt = _vm->_eventsManager->getMousePos();
1387 if (!mansionViewBounds.contains(pt))
1388 pt = Common::Point(-1, -1);
1389 else
1390 pt = _vm->_mansionViewPos +
1391 Common::Point(pt.x - MANSION_VIEW_X, pt.y - MANSION_VIEW_Y);
1392 regionIndex = -1;
1393
1394 for (uint hotspotIdx = 0; hotspotIdx < hotspots->size(); ++hotspotIdx) {
1395 if ((*hotspots)[hotspotIdx].contains(pt)) {
1396 // Rect check done
1397 for (int arrIndex = 0; arrIndex < 3; ++arrIndex) {
1398 if (_vm->_voy->_audioHotspotTimes.isInRange(arrIndex, hotspotIdx, _vm->_voy->_RTVNum)) {
1399 // Set the ear cursor for an audio event
1400 _vm->_eventsManager->setCursor(listenCursor);
1401 regionIndex = hotspotIdx;
1402 }
1403
1404 if (_vm->_voy->_evidenceHotspotTimes.isInRange(arrIndex, hotspotIdx, _vm->_voy->_RTVNum)) {
1405 // Set the magnifier cursor for an evidence event
1406 _vm->_eventsManager->setCursor(mangifyCursor);
1407 regionIndex = hotspotIdx;
1408 }
1409 }
1410
1411 for (int arrIndex = 0; arrIndex < 8; ++arrIndex) {
1412 if (_vm->_voy->_videoHotspotTimes.isInRange(arrIndex, hotspotIdx, _vm->_voy->_RTVNum)) {
1413 // Set the eye cursor for a video event
1414 _vm->_eventsManager->setCursor(eyeCursor);
1415 regionIndex = hotspotIdx;
1416 }
1417 }
1418 }
1419 }
1420
1421 if (regionIndex == -1) {
1422 // Reset back to the crosshairs cursor
1423 _vm->_eventsManager->setCursor(crosshairsCursor);
1424 }
1425
1426 // Regularly update the time display
1427 if (_vm->_voy->_RTANum & 2) {
1428 _vm->_screen->drawANumber(_vm->_screen->_vPort,
1429 _vm->_gameMinute / 10, Common::Point(190, 25));
1430 _vm->_screen->drawANumber(_vm->_screen->_vPort,
1431 _vm->_gameMinute % 10, Common::Point(201, 25));
1432
1433 if (_vm->_voy->_RTANum & 4) {
1434 int v = _vm->_gameHour / 10;
1435 _vm->_screen->drawANumber(_vm->_screen->_vPort,
1436 v == 0 ? 10 : v, Common::Point(161, 25));
1437 _vm->_screen->drawANumber(_vm->_screen->_vPort,
1438 _vm->_gameHour % 10, Common::Point(172, 25));
1439
1440 pic = _vm->_bVoy->boltEntry(_vm->_voy->_isAM ? 272 : 273)._picResource;
1441 _vm->_screen->sDrawPic(pic, _vm->_screen->_vPort,
1442 Common::Point(215, 27));
1443 }
1444 }
1445
1446 _vm->_voy->_RTANum = 0;
1447 _vm->flipPageAndWait();
1448
1449 pt = _vm->_eventsManager->getMousePos();
1450 if ((_vm->_voy->_RTVNum >= _vm->_voy->_RTVLimit) || ((_vm->_voy->_eventFlags & EVTFLAG_VICTIM_PRESET) &&
1451 _vm->_eventsManager->_rightClick && (pt.x == 0))) {
1452 // Time to transition to the next time period
1453 _vm->_eventsManager->getMouseInfo();
1454
1455 if (_vm->_voy->_transitionId == 15) {
1456 regionIndex = 20;
1457 _vm->_voy->_transitionId = 17;
1458 _vm->_soundManager->stopVOCPlay();
1459 _vm->checkTransition();
1460 _vm->_eventsManager->_leftClick = true;
1461 } else {
1462 _vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1463
1464 chooseSTAMPButton(20);
1465 parsePlayCommands();
1466 _vm->checkTransition();
1467 _vm->makeViewFinder();
1468
1469 _vm->initIFace();
1470
1471 hotspots = &_vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._rectResource->_entries;
1472 _vm->_eventsManager->getMouseInfo();
1473
1474 _vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
1475 _vm->_eventsManager->_intPtr._flashStep = 1;
1476 _vm->_eventsManager->_intPtr._flashTimer = 0;
1477 }
1478 }
1479 } while (!_vm->_eventsManager->_rightClick && !_vm->shouldQuit() &&
1480 (!_vm->_eventsManager->_leftClick || regionIndex == -1));
1481
1482 _vm->_eventsManager->hideCursor();
1483 _vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1484 _vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
1485 if (_vm->_currentVocId != -1)
1486 _vm->_soundManager->stopVOCPlay();
1487
1488 return !_vm->_eventsManager->_rightClick ? regionIndex : -2;
1489 }
1490
checkMansionScroll()1491 bool ThreadResource::checkMansionScroll() {
1492 Common::Point pt = _vm->_eventsManager->getMousePos() -
1493 Common::Point(MANSION_VIEW_X, MANSION_VIEW_Y);
1494 Common::Point &viewPos = _vm->_mansionViewPos;
1495 bool result = false;
1496
1497 // Scroll mansion view if close to any of the mansion edges
1498 if (pt.x >= 0 && pt.x < MANSION_SCROLL_AREA_X && viewPos.x > 0) {
1499 viewPos.x = MAX(viewPos.x - MANSION_SCROLL_INC_X, 0);
1500 result = true;
1501 }
1502 if (pt.x >= (MANSION_VIEW_WIDTH - MANSION_SCROLL_AREA_X) &&
1503 pt.x < MANSION_VIEW_WIDTH && viewPos.x < MANSION_MAX_X) {
1504 viewPos.x = MIN(viewPos.x + MANSION_SCROLL_INC_X, MANSION_MAX_X);
1505 result = true;
1506 }
1507 if (pt.y >= 0 && pt.y < MANSION_SCROLL_AREA_Y && viewPos.y > 0) {
1508 viewPos.y = MAX(viewPos.y - MANSION_SCROLL_INC_Y, 0);
1509 result = true;
1510 }
1511 if (pt.y >= (MANSION_VIEW_HEIGHT - MANSION_SCROLL_AREA_Y) &&
1512 pt.y < MANSION_VIEW_HEIGHT && viewPos.y < MANSION_MAX_Y) {
1513 viewPos.y = MIN(viewPos.y + MANSION_SCROLL_INC_Y, MANSION_MAX_Y);
1514 result = true;
1515 }
1516
1517 // Return whether mansion view area has changed
1518 return result;
1519 }
1520
goToStateID(int stackId,int id)1521 bool ThreadResource::goToStateID(int stackId, int id) {
1522 debugC(DEBUG_BASIC, kDebugScripts, "goToStateID - %d, %d", stackId, id);
1523
1524 // Save current stack
1525 savePrevious();
1526
1527 if (_stackId == stackId || stackId == -1 || loadAStack(stackId)) {
1528 // Now in the correct state
1529 _stateId = getStateFromID(id);
1530
1531 if (_stateId != -1) {
1532 return doState();
1533 } else {
1534 _stateId = _savedStateId;
1535 _stackId = _savedStackId;
1536 }
1537 }
1538
1539 return false;
1540 }
1541
goToState(int stackId,int stateId)1542 bool ThreadResource::goToState(int stackId, int stateId) {
1543 debugC(DEBUG_BASIC, kDebugScripts, "goToState - %d, %d", stackId, stateId);
1544
1545 savePrevious();
1546 if (stackId == -1 || loadAStack(stackId)) {
1547 if (stateId != -1)
1548 _stateId = stateId;
1549
1550 return doState();
1551 } else {
1552 return false;
1553 }
1554 }
1555
savePrevious()1556 void ThreadResource::savePrevious() {
1557 if (_savedStateId != _stateId || _stackId != _savedStackId) {
1558 _savedStateId = _stateId;
1559 _savedStackId = _stackId;
1560 }
1561 }
1562
setButtonFlag(int idx,byte bits)1563 void ThreadResource::setButtonFlag(int idx, byte bits) {
1564 _buttonFlags[idx] |= bits;
1565 }
1566
clearButtonFlag(int idx,byte bits)1567 void ThreadResource::clearButtonFlag(int idx, byte bits) {
1568 _buttonFlags[idx] &= ~bits;
1569 }
1570
loadTheApt()1571 void ThreadResource::loadTheApt() {
1572 switch (_vm->_voy->_transitionId) {
1573 case 1:
1574 case 2:
1575 case 5:
1576 case 6:
1577 case 7:
1578 case 8:
1579 case 9:
1580 case 17:
1581 _vm->_playStampGroupId = 0x5700;
1582 break;
1583 case 3:
1584 _vm->_playStampGroupId = 0x5800;
1585 break;
1586 case 4:
1587 case 10:
1588 case 11:
1589 case 12:
1590 case 13:
1591 case 14:
1592 case 15:
1593 case 16:
1594 _vm->_playStampGroupId = 0x5900;
1595 break;
1596 default:
1597 break;
1598 }
1599
1600 if (_vm->_voy->_aptLoadMode == 143)
1601 _vm->_voy->_aptLoadMode = -1;
1602
1603 if (_vm->_voy->_aptLoadMode != -1) {
1604 if (_vm->_loadGameSlot != -1)
1605 doAptAnim(1);
1606
1607 _vm->_bVoy->getBoltGroup(_vm->_playStampGroupId);
1608 _vm->_voy->_aptLoadMode = -1;
1609 _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(
1610 _vm->_playStampGroupId + 5)._picResource;
1611 _vm->_screen->_vPort->setupViewPort(
1612 _vm->_screen->_backgroundPage);
1613 } else {
1614 _vm->_bVoy->getBoltGroup(_vm->_playStampGroupId);
1615 _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(
1616 _vm->_playStampGroupId + 5)._picResource;
1617 _vm->_screen->_vPort->setupViewPort(
1618 _vm->_screen->_backgroundPage);
1619 }
1620
1621 CMapResource *pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 4)._cMapResource;
1622 pal->_steps = 1;
1623 pal->startFade();
1624 _vm->flipPageAndWaitForFade();
1625 }
1626
freeTheApt()1627 void ThreadResource::freeTheApt() {
1628 _vm->_screen->fadeDownICF1(5);
1629 _vm->flipPageAndWaitForFade();
1630
1631 _vm->_screen->fadeUpICF1();
1632
1633 if (_vm->_currentVocId != -1) {
1634 _vm->_soundManager->stopVOCPlay();
1635 _vm->_currentVocId = -1;
1636 }
1637
1638 if (_vm->_voy->_aptLoadMode == -1) {
1639 _vm->_screen->fadeDownICF(6);
1640 } else {
1641 doAptAnim(2);
1642 }
1643
1644 if (_vm->_voy->_aptLoadMode == 140) {
1645 _vm->_screen->screenReset();
1646 _vm->_screen->resetPalette();
1647 }
1648
1649 _vm->_screen->_vPort->setupViewPort(nullptr);
1650 _vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
1651 _vm->_playStampGroupId = -1;
1652 _vm->_voy->_viewBounds = nullptr;
1653 }
1654
doAptAnim(int mode)1655 void ThreadResource::doAptAnim(int mode) {
1656 _vm->_bVoy->freeBoltGroup(0x100);
1657
1658 // Figure out the resource to use
1659 int id = 0;
1660 switch (_vm->_voy->_aptLoadMode) {
1661 case 140:
1662 id = 0x5A00;
1663 break;
1664 case 141:
1665 id = 0x6000;
1666 break;
1667 case 142:
1668 id = 0x6600;
1669 break;
1670 case 143:
1671 id = 0x6C00;
1672 break;
1673 case 144:
1674 id = 0x6F00;
1675 break;
1676 default:
1677 break;
1678 }
1679
1680 int id2 = (id == 0x6C00 || id == 0x6F00) ? 1 : 2;
1681 switch (_vm->_voy->_transitionId) {
1682 case 3:
1683 id += id2 << 8;
1684 break;
1685 case 4:
1686 case 10:
1687 case 11:
1688 case 12:
1689 case 13:
1690 case 14:
1691 case 15:
1692 case 16:
1693 id += id2 << 9;
1694 break;
1695 default:
1696 break;
1697 }
1698
1699 if (mode == 1)
1700 id += 0x100;
1701
1702 // Do the display
1703 if (_vm->_bVoy->getBoltGroup(id)) {
1704 CMapResource *pal = _vm->_bVoy->boltEntry(id)._cMapResource;
1705 pal->_steps = 1;
1706
1707 for (int idx = 0; (idx < 6) && !_vm->shouldQuit(); ++idx) {
1708 PictureResource *pic = _vm->_bVoy->boltEntry(id + idx + 1)._picResource;
1709 _vm->_screen->_vPort->setupViewPort(pic);
1710 pal->startFade();
1711
1712 _vm->flipPageAndWait();
1713 _vm->_eventsManager->delayClick(5);
1714 }
1715
1716 _vm->_bVoy->freeBoltGroup(id);
1717 }
1718
1719 _vm->_bVoy->getBoltGroup(0x100);
1720 }
1721
synchronize(Common::Serializer & s)1722 void ThreadResource::synchronize(Common::Serializer &s) {
1723 s.syncAsSint16LE(_aptPos.x);
1724 s.syncAsSint16LE(_aptPos.y);
1725
1726 int stateId = _stateId;
1727 int stackId = _stackId;
1728 s.syncAsSint16LE(stateId);
1729 s.syncAsSint16LE(stackId);
1730
1731 if (s.isLoading() && (stateId != _stateId || stackId != _stackId))
1732 goToState(stackId, stateId);
1733
1734 s.syncAsSint16LE(_savedStateId);
1735 s.syncAsSint16LE(_savedStackId);
1736 }
1737
1738 } // End of namespace Voyeur
1739