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 &param, 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 &param, 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 &param, 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