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 "neverhood/diskplayerscene.h"
24 #include "neverhood/mouse.h"
25 #include "neverhood/smackerplayer.h"
26
27 namespace Neverhood {
28
29 static const uint32 kDiskplayerPaletteFileHashes[] = {
30 0x03B78240,
31 0x34B32B08,
32 0x4F2569D4,
33 0x07620590,
34 0x38422401
35 };
36
37 static const byte kDiskplayerInitArray[] = {
38 2, 1, 4, 5, 3, 11, 8, 6, 7, 9, 10, 17, 16, 18, 19, 20, 15, 14, 13, 12
39 };
40
41 static const uint32 kDiskplayerSmackerFileHashes[] = {
42 0x010A2810,
43 0x020A2810,
44 0x040A2810,
45 0x080A2810,
46 0x100A2810,
47 0x200A2810,
48 0x400A2810,
49 0x800A2810,
50 0x000A2811,
51 0x010C2810,
52 0x020C2810,
53 0x040C2810,
54 0x080C2810,
55 0x100C2810,
56 0x200C2810,
57 0x400C2810,
58 0x800C2810,
59 0x000C2811,
60 0x000C2812,
61 0x02002810,
62 0x04002810
63 };
64
65 static const uint32 kDiskplayerSlotFileHashes1[] = {
66 0x81312280,
67 0x01312281,
68 0x01312282,
69 0x01312284,
70 0x01312288,
71 0x01312290,
72 0x013122A0,
73 0x013122C0,
74 0x01312200,
75 0x82312280,
76 0x02312281,
77 0x02312282,
78 0x02312284,
79 0x02312288,
80 0x02312290,
81 0x023122A0,
82 0x023122C0,
83 0x02312200,
84 0x02312380,
85 0x04312281
86 };
87
88 static const uint32 kDiskplayerSlotFileHashes2[] = {
89 0x90443A00,
90 0x90443A18,
91 0x90443A28,
92 0x90443A48,
93 0x90443A88,
94 0x90443B08,
95 0x90443808,
96 0x90443E08,
97 0x90443208,
98 0xA0443A00,
99 0xA0443A18,
100 0xA0443A28,
101 0xA0443A48,
102 0xA0443A88,
103 0xA0443B08,
104 0xA0443808,
105 0xA0443E08,
106 0xA0443208,
107 0xA0442A08,
108 0xC0443A18
109 };
110
111 static const uint32 kDiskplayerSlotFileHashes3[] = {
112 0x10357320,
113 0x10557320,
114 0x10957320,
115 0x11157320,
116 0x12157320,
117 0x14157320,
118 0x18157320,
119 0x00157320,
120 0x30157320,
121 0x1035B320,
122 0x1055B320,
123 0x1095B320,
124 0x1115B320,
125 0x1215B320,
126 0x1415B320,
127 0x1815B320,
128 0x0015B320,
129 0x3015B320,
130 0x5015B320,
131 0x10543320
132 };
133
134 static const uint32 kDiskplayerSlotFileHashes4[] = {
135 0xDC8020E4,
136 0xDC802164,
137 0xDC802264,
138 0xDC802464,
139 0xDC802864,
140 0xDC803064,
141 0xDC800064,
142 0xDC806064,
143 0xDC80A064,
144 0xDC8020E7,
145 0xDC802167,
146 0xDC802267,
147 0xDC802467,
148 0xDC802867,
149 0xDC803067,
150 0xDC800067,
151 0xDC806067,
152 0xDC80A067,
153 0xDC812067,
154 0xDC802161
155 };
156
AsDiskplayerSceneKey(NeverhoodEngine * vm)157 AsDiskplayerSceneKey::AsDiskplayerSceneKey(NeverhoodEngine *vm)
158 : AnimatedSprite(vm, 1100) {
159
160 createSurface1(0x100B90B4, 1200);
161 _x = 211;
162 _y = 195;
163 startAnimation(0x100B90B4, 0, -1);
164 _newStickFrameIndex = 0;
165 _needRefresh = true;
166 updatePosition();
167 setVisible(false);
168 }
169
handleMessage(int messageNum,const MessageParam & param,Entity * sender)170 uint32 AsDiskplayerSceneKey::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
171 uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
172 switch (messageNum) {
173 case NM_ANIMATION_STOP:
174 gotoNextState();
175 break;
176 default:
177 break;
178 }
179 return messageResult;
180 }
181
stDropKey()182 void AsDiskplayerSceneKey::stDropKey() {
183 startAnimation(0x100B90B4, 0, -1);
184 SetUpdateHandler(&AnimatedSprite::update);
185 SetMessageHandler(&AsDiskplayerSceneKey::handleMessage);
186 NextState(&AsDiskplayerSceneKey::stDropKeyDone);
187 setVisible(true);
188 }
189
stDropKeyDone()190 void AsDiskplayerSceneKey::stDropKeyDone() {
191 stopAnimation();
192 SetUpdateHandler(&AnimatedSprite::update);
193 SetMessageHandler(&Sprite::handleMessage);
194 setVisible(false);
195 }
196
DiskplayerPlayButton(NeverhoodEngine * vm,DiskplayerScene * diskplayerScene)197 DiskplayerPlayButton::DiskplayerPlayButton(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene)
198 : StaticSprite(vm, 1400), _diskplayerScene(diskplayerScene), _isPlaying(false) {
199
200 loadSprite(0x24A4A664, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400);
201 setVisible(false);
202 loadSound(0, 0x44043000);
203 loadSound(1, 0x44045000);
204 SetMessageHandler(&DiskplayerPlayButton::handleMessage);
205 }
206
handleMessage(int messageNum,const MessageParam & param,Entity * sender)207 uint32 DiskplayerPlayButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
208 uint32 messageResult = 0;
209 Sprite::handleMessage(messageNum, param, sender);
210 switch (messageNum) {
211 case 0x1011:
212 if (!_diskplayerScene->getDropKey()) {
213 if (_isPlaying) {
214 sendMessage(_diskplayerScene, 0x2001, 0);
215 release();
216 } else {
217 sendMessage(_diskplayerScene, 0x2000, 0);
218 press();
219 }
220 }
221 updatePosition();
222 messageResult = 1;
223 break;
224 default:
225 break;
226 }
227 return messageResult;
228 }
229
press()230 void DiskplayerPlayButton::press() {
231 if (!_isPlaying) {
232 setVisible(true);
233 updatePosition();
234 playSound(0);
235 _isPlaying = true;
236 }
237 }
238
release()239 void DiskplayerPlayButton::release() {
240 if (_isPlaying) {
241 setVisible(false);
242 updatePosition();
243 playSound(1);
244 _isPlaying = false;
245 }
246 }
247
DiskplayerSlot(NeverhoodEngine * vm,DiskplayerScene * diskplayerScene,int slotIndex,bool isAvailable)248 DiskplayerSlot::DiskplayerSlot(NeverhoodEngine *vm, DiskplayerScene *diskplayerScene, int slotIndex, bool isAvailable)
249 : Entity(vm, 0), _diskplayerScene(diskplayerScene), _isLocked(false), _isBlinking(false),
250 _blinkCountdown(0), _initialBlinkCountdown(2), _inactiveSlot(NULL), _appearSlot(NULL), _activeSlot(NULL) {
251
252 if (isAvailable && slotIndex < 20) {
253 _inactiveSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes1[slotIndex], 1100));
254 _appearSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes2[slotIndex], 1000));
255 _activeSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes3[slotIndex], 1100));
256 _inactiveSlot->setVisible(false);
257 _appearSlot->setVisible(false);
258 _activeSlot->setVisible(false);
259 loadSound(0, 0x46210074);
260 setSoundPan(0, slotIndex * 100 / 19);
261 } else if (slotIndex != 20) {
262 _activeSlot = _diskplayerScene->addSprite(new StaticSprite(_vm, kDiskplayerSlotFileHashes4[slotIndex], 1100));
263 _activeSlot->setVisible(false);
264 }
265 SetUpdateHandler(&DiskplayerSlot::update);
266 }
267
update()268 void DiskplayerSlot::update() {
269 if (_blinkCountdown != 0 && (--_blinkCountdown == 0)) {
270 if (_isBlinking) {
271 if (_inactiveSlot)
272 _inactiveSlot->setVisible(true);
273 if (_activeSlot)
274 _activeSlot->setVisible(false);
275 _blinkCountdown = _initialBlinkCountdown / 2;
276 } else {
277 if (_inactiveSlot)
278 _inactiveSlot->setVisible(false);
279 if (_activeSlot)
280 _activeSlot->setVisible(true);
281 _blinkCountdown = _initialBlinkCountdown;
282 }
283 _isBlinking = !_isBlinking;
284 }
285 }
286
appear()287 void DiskplayerSlot::appear() {
288 if (_inactiveSlot)
289 _inactiveSlot->setVisible(true);
290 if (_appearSlot)
291 _appearSlot->setVisible(true);
292 if (_inactiveSlot)
293 playSound(0);
294 }
295
play()296 void DiskplayerSlot::play() {
297 if (!_isLocked) {
298 if (_inactiveSlot)
299 _inactiveSlot->setVisible(false);
300 if (_activeSlot)
301 _activeSlot->setVisible(true);
302 _isBlinking = true;
303 _blinkCountdown = 0;
304 }
305 }
306
activate()307 void DiskplayerSlot::activate() {
308 if (!_isLocked)
309 _blinkCountdown = _initialBlinkCountdown;
310 }
311
stop()312 void DiskplayerSlot::stop() {
313 if (!_isLocked) {
314 if (_inactiveSlot)
315 _inactiveSlot->setVisible(true);
316 if (_activeSlot)
317 _activeSlot->setVisible(false);
318 _isBlinking = false;
319 _blinkCountdown = 0;
320 }
321 }
322
DiskplayerScene(NeverhoodEngine * vm,Module * parentModule,int paletteIndex)323 DiskplayerScene::DiskplayerScene(NeverhoodEngine *vm, Module *parentModule, int paletteIndex)
324 : Scene(vm, parentModule), _diskIndex(0), _appearCountdown(0), _tuneInCountdown(0),
325 _hasAllDisks(false), _dropKey(false), _inputDisabled(true), _updateStatus(kUSStopped) {
326
327 int availableDisksCount = 0;
328
329 setBackground(0x8A000044);
330 setPalette(kDiskplayerPaletteFileHashes[paletteIndex]);
331
332 _ssPlayButton = insertSprite<DiskplayerPlayButton>(this);
333 addCollisionSprite(_ssPlayButton);
334
335 _asKey = insertSprite<AsDiskplayerSceneKey>();
336
337 for (int i = 0; i < 20; i++) {
338 _diskAvailable[i] = false;
339 if (getSubVar(VA_IS_TAPE_INSERTED, i))
340 availableDisksCount++;
341 }
342
343 for (int i = 0; i < availableDisksCount; i++)
344 _diskAvailable[kDiskplayerInitArray[i] - 1] = true;
345
346 for (int slotIndex = 0; slotIndex < 20; slotIndex++) {
347 _diskSlots[slotIndex] = new DiskplayerSlot(_vm, this, slotIndex, _diskAvailable[slotIndex]);
348 addEntity(_diskSlots[slotIndex]);
349 }
350
351 _hasAllDisks = availableDisksCount == 20;
352
353 if (_hasAllDisks && !getGlobalVar(V_HAS_FINAL_KEY))
354 _dropKey = true;
355
356 _finalDiskSlot = new DiskplayerSlot(_vm, this, 20, false);
357 addEntity(_finalDiskSlot);
358
359 insertPuzzleMouse(0x000408A8, 20, 620);
360 showMouse(false);
361
362 _diskSmackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, 0x08288103, false, true));
363 _diskSmackerPlayer->setDrawPos(154, 86);
364 _vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
365
366 _palette->usePalette();
367
368 SetMessageHandler(&DiskplayerScene::handleMessage);
369 SetUpdateHandler(&DiskplayerScene::update);
370 _appearCountdown = 6;
371
372 }
373
update()374 void DiskplayerScene::update() {
375 Scene::update();
376
377 if (_updateStatus == kUSTuningIn && _diskSmackerPlayer->isDone()) {
378 if (_diskAvailable[_diskIndex])
379 playDisk();
380 else
381 playStatic();
382 } else if (_updateStatus == kUSPlaying && _diskSmackerPlayer->isDone()) {
383 _diskSlots[_diskIndex]->stop();
384 _diskIndex++;
385 if (_hasAllDisks) {
386 if (_diskIndex != 20) {
387 playDisk();
388 } else if (_dropKey) {
389 playDisk();
390 _updateStatus = kUSPlayingFinal;
391 } else {
392 _diskIndex = 0;
393 stop();
394 }
395 } else if (_diskIndex != 20) {
396 tuneIn();
397 } else {
398 _diskIndex = 0;
399 stop();
400 }
401 } else if (_updateStatus == kUSPlayingFinal) {
402 if (_diskSmackerPlayer->getFrameNumber() == 133) {
403 _asKey->stDropKey();
404 setGlobalVar(V_HAS_FINAL_KEY, 1);
405 } else if (_diskSmackerPlayer->isDone()) {
406 for (int i = 0; i < 20; i++) {
407 _diskSlots[i]->setLocked(false);
408 _diskSlots[i]->stop();
409 }
410 _diskIndex = 0;
411 stop();
412 showMouse(true);
413 _dropKey = false;
414 }
415 }
416
417 if (_appearCountdown != 0 && (--_appearCountdown == 0)) {
418 _diskSlots[_diskIndex]->appear();
419 if (_dropKey) {
420 _diskSlots[_diskIndex]->activate();
421 _diskSlots[_diskIndex]->setLocked(true);
422 }
423 _diskIndex++;
424 while (!_diskAvailable[_diskIndex] && _diskIndex < 19)
425 _diskIndex++;
426 if (_diskIndex < 20) {
427 _appearCountdown = 1;
428 } else {
429 _diskIndex = 0;
430 _inputDisabled = false;
431 if (_dropKey) {
432 _ssPlayButton->press();
433 _tuneInCountdown = 2;
434 } else {
435 showMouse(true);
436 _diskSlots[_diskIndex]->activate();
437 }
438 }
439 }
440
441 if (_tuneInCountdown != 0 && (--_tuneInCountdown == 0))
442 playDisk();
443
444 }
445
handleMessage(int messageNum,const MessageParam & param,Entity * sender)446 uint32 DiskplayerScene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
447 Scene::handleMessage(messageNum, param, sender);
448 if (!_inputDisabled) {
449 switch (messageNum) {
450 case NM_MOUSE_CLICK:
451 if (param.asPoint().x <= 20 || param.asPoint().x >= 620) {
452 sendMessage(_parentModule, 0x1009, 0);
453 } else if (!_dropKey &&
454 param.asPoint().x > 38 && param.asPoint().x < 598 &&
455 param.asPoint().y > 400 && param.asPoint().y < 460) {
456
457 _diskSlots[_diskIndex]->stop();
458 _diskIndex = (param.asPoint().x - 38) / 28;
459 _diskSlots[_diskIndex]->activate();
460 if (_updateStatus == kUSPlaying) {
461 if (_diskAvailable[_diskIndex])
462 playDisk();
463 else
464 playStatic();
465 }
466 }
467 break;
468 case NM_ANIMATION_UPDATE:
469 tuneIn();
470 break;
471 case 0x2001:
472 stop();
473 break;
474 default:
475 break;
476 }
477 }
478 return 0;
479 }
480
openSmacker(uint32 fileHash,bool keepLastFrame)481 void DiskplayerScene::openSmacker(uint32 fileHash, bool keepLastFrame) {
482 _diskSmackerPlayer->open(fileHash, keepLastFrame);
483 _vm->_screen->setSmackerDecoder(_diskSmackerPlayer->getSmackerDecoder());
484 _palette->usePalette();
485 }
486
stop()487 void DiskplayerScene::stop() {
488 openSmacker(0x08288103, true);
489 _ssPlayButton->release();
490 _updateStatus = kUSStopped;
491 _diskSlots[_diskIndex]->activate();
492 }
493
tuneIn()494 void DiskplayerScene::tuneIn() {
495 openSmacker(0x900001C1, false);
496 _ssPlayButton->release();
497 _updateStatus = kUSTuningIn;
498 _diskSlots[_diskIndex]->activate();
499 }
500
playDisk()501 void DiskplayerScene::playDisk() {
502 openSmacker(kDiskplayerSmackerFileHashes[_diskIndex], false);
503 _updateStatus = kUSPlaying;
504 _diskSlots[_diskIndex]->play();
505 }
506
playStatic()507 void DiskplayerScene::playStatic() {
508 openSmacker(0x90000101, false);
509 _ssPlayButton->release();
510 _updateStatus = kUSPlaying;
511 _diskSlots[_diskIndex]->activate();
512 }
513
514 } // End of namespace Neverhood
515