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/modules/module2800.h"
24 #include "neverhood/modules/module2800_sprites.h"
25 
26 namespace Neverhood {
27 
AsScene2803LightCord(NeverhoodEngine * vm,Scene * parentScene,uint32 fileHash1,uint32 fileHash2,int16 x,int16 y)28 AsScene2803LightCord::AsScene2803LightCord(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int16 x, int16 y)
29 	: AnimatedSprite(vm, 1100), _parentScene(parentScene), _fileHash1(fileHash1), _fileHash2(fileHash2),
30 	_isPulled(false), _isBusy(false) {
31 
32 	createSurface(1010, 28, 379);
33 	SetUpdateHandler(&AnimatedSprite::update);
34 	SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
35 	_x = x;
36 	_y = y;
37 	stIdle();
38 }
39 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)40 uint32 AsScene2803LightCord::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
41 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
42 	switch (messageNum) {
43 	case NM_ANIMATION_START:
44 		if (!_isBusy && param.asInteger() == calcHash("ClickSwitch")) {
45 			sendMessage(_parentScene, NM_KLAYMEN_LOWER_LEVER, 0);
46 			playSound(0, 0x4E1CA4A0);
47 		}
48 		break;
49 	case NM_KLAYMEN_LOWER_LEVER:
50 		stPulled();
51 		break;
52 	case NM_MOVE_TO_BACK:
53 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 990);
54 		break;
55 	case NM_MOVE_TO_FRONT:
56 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 1010);
57 		break;
58 	default:
59 		break;
60 	}
61 	return messageResult;
62 }
63 
hmPulled(int messageNum,const MessageParam & param,Entity * sender)64 uint32 AsScene2803LightCord::hmPulled(int messageNum, const MessageParam &param, Entity *sender) {
65 	uint32 messageResult = handleMessage(messageNum, param, sender);
66 	switch (messageNum) {
67 	case NM_ANIMATION_STOP:
68 		gotoNextState();
69 		break;
70 	default:
71 		break;
72 	}
73 	return messageResult;
74 }
75 
stPulled()76 void AsScene2803LightCord::stPulled() {
77 	_isBusy = false;
78 	_isPulled = true;
79 	startAnimation(_fileHash2, 0, -1);
80 	SetMessageHandler(&AsScene2803LightCord::hmPulled);
81 	NextState(&AsScene2803LightCord::stIdle);
82 }
83 
stIdle()84 void AsScene2803LightCord::stIdle() {
85 	_isPulled = false;
86 	startAnimation(_fileHash1, 0, -1);
87 	SetMessageHandler(&AsScene2803LightCord::handleMessage);
88 }
89 
setFileHashes(uint32 fileHash1,uint32 fileHash2)90 void AsScene2803LightCord::setFileHashes(uint32 fileHash1, uint32 fileHash2) {
91 	_fileHash1 = fileHash1;
92 	_fileHash2 = fileHash2;
93 	if (_isPulled) {
94 		startAnimation(_fileHash2, _currFrameIndex, -1);
95 		_isBusy = true;
96 	} else {
97 		startAnimation(_fileHash1, 0, -1);
98 	}
99 }
100 
AsScene2803TestTubeOne(NeverhoodEngine * vm,uint32 fileHash1,uint32 fileHash2)101 AsScene2803TestTubeOne::AsScene2803TestTubeOne(NeverhoodEngine *vm, uint32 fileHash1, uint32 fileHash2)
102 	: AnimatedSprite(vm, 1200), _fileHash1(fileHash1), _fileHash2(fileHash2) {
103 
104 	createSurface1(fileHash1, 100);
105 	SetUpdateHandler(&AnimatedSprite::update);
106 	SetMessageHandler(&AsScene2803TestTubeOne::handleMessage);
107 	_x = 529;
108 	_y = 326;
109 }
110 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)111 uint32 AsScene2803TestTubeOne::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
112 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
113 	switch (messageNum) {
114 	case NM_ANIMATION_UPDATE:
115 		if (param.asInteger())
116 			startAnimation(_fileHash2, 0, -1);
117 		else
118 			startAnimation(_fileHash1, 0, -1);
119 		break;
120 	default:
121 		break;
122 	}
123 	return messageResult;
124 }
125 
AsScene2803Rope(NeverhoodEngine * vm,Scene * parentScene,int16 x)126 AsScene2803Rope::AsScene2803Rope(NeverhoodEngine *vm, Scene *parentScene, int16 x)
127 	: AnimatedSprite(vm, 1100), _parentScene(parentScene) {
128 
129 	createSurface(990, 68, 476);
130 	SetUpdateHandler(&AnimatedSprite::update);
131 	SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
132 	SetMessageHandler(&AsScene2803Rope::handleMessage);
133 	startAnimation(0x9D098C23, 35, 53);
134 	NextState(&AsScene2803Rope::stReleased);
135 	_x = x;
136 	_y = -276;
137 }
138 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)139 uint32 AsScene2803Rope::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
140 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
141 	switch (messageNum) {
142 	case NM_ANIMATION_STOP:
143 		startAnimation(0x9D098C23, 50, -1);
144 		SetMessageHandler(&AsScene2803Rope::hmReleased);
145 		break;
146 	case NM_MOVE_TO_BACK:
147 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 990);
148 		break;
149 	case NM_MOVE_TO_FRONT:
150 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 1010);
151 		break;
152 	default:
153 		break;
154 	}
155 	return messageResult;
156 }
157 
hmReleased(int messageNum,const MessageParam & param,Entity * sender)158 uint32 AsScene2803Rope::hmReleased(int messageNum, const MessageParam &param, Entity *sender) {
159 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
160 	switch (messageNum) {
161 	case NM_ANIMATION_STOP:
162 		gotoNextState();
163 		break;
164 	case NM_MOVE_TO_BACK:
165 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 990);
166 		break;
167 	case NM_MOVE_TO_FRONT:
168 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 1010);
169 		break;
170 	default:
171 		break;
172 	}
173 	return messageResult;
174 }
175 
stReleased()176 void AsScene2803Rope::stReleased() {
177 	startAnimation(0x8258A030, 0, 1);
178 	NextState(&AsScene2803Rope::stHide);
179 }
180 
stHide()181 void AsScene2803Rope::stHide() {
182 	stopAnimation();
183 	setVisible(false);
184 }
185 
SsScene2804RedButton(NeverhoodEngine * vm,Scene2804 * parentScene)186 SsScene2804RedButton::SsScene2804RedButton(NeverhoodEngine *vm, Scene2804 *parentScene)
187 	: StaticSprite(vm, 900), _countdown(0), _parentScene(parentScene) {
188 
189 	loadSprite(getGlobalVar(V_SHRINK_LIGHTS_ON) ? 0x51A10202 : 0x11814A21, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400);
190 	setVisible(false);
191 	SetUpdateHandler(&SsScene2804RedButton::update);
192 	SetMessageHandler(&SsScene2804RedButton::handleMessage);
193 	loadSound(0, 0x44241240);
194 }
195 
update()196 void SsScene2804RedButton::update() {
197 	updatePosition();
198 	if (_countdown != 0 && (--_countdown) == 0) {
199 		setVisible(false);
200 	}
201 }
202 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)203 uint32 SsScene2804RedButton::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
204 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
205 	switch (messageNum) {
206 	case 0x1011:
207 		if (_countdown == 0 && !_parentScene->isWorking()) {
208 			playSound(0);
209 			setVisible(true);
210 			_countdown = 4;
211 			sendMessage(_parentScene, 0x2000, 0);
212 		}
213 		messageResult = 1;
214 		break;
215 	default:
216 		break;
217 	}
218 	return messageResult;
219 }
220 
SsScene2804LightCoil(NeverhoodEngine * vm)221 SsScene2804LightCoil::SsScene2804LightCoil(NeverhoodEngine *vm)
222 	: StaticSprite(vm, 900) {
223 
224 	loadSprite(0x8889B008, kSLFDefDrawOffset | kSLFDefPosition, 400);
225 	setVisible(false);
226 	SetMessageHandler(&SsScene2804LightCoil::handleMessage);
227 }
228 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)229 uint32 SsScene2804LightCoil::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
230 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
231 	switch (messageNum) {
232 	case NM_POSITION_CHANGE:
233 		setVisible(true);
234 		updatePosition();
235 		messageResult = 1;
236 		break;
237 	case 0x2003:
238 		setVisible(false);
239 		updatePosition();
240 		messageResult = 1;
241 		break;
242 	default:
243 		break;
244 	}
245 	return messageResult;
246 }
247 
SsScene2804LightTarget(NeverhoodEngine * vm)248 SsScene2804LightTarget::SsScene2804LightTarget(NeverhoodEngine *vm)
249 	: StaticSprite(vm, 900) {
250 
251 	loadSprite(0x06092132, kSLFDefDrawOffset | kSLFDefPosition, 400);
252 	setVisible(false);
253 	SetMessageHandler(&SsScene2804LightTarget::handleMessage);
254 }
255 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)256 uint32 SsScene2804LightTarget::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
257 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
258 	switch (messageNum) {
259 	case 0x2004:
260 		setVisible(true);
261 		updatePosition();
262 		messageResult = 1;
263 		break;
264 	case NM_KLAYMEN_CLIMB_LADDER:
265 		setVisible(false);
266 		updatePosition();
267 		messageResult = 1;
268 		break;
269 	default:
270 		break;
271 	}
272 	return messageResult;
273 }
274 
SsScene2804Flash(NeverhoodEngine * vm)275 SsScene2804Flash::SsScene2804Flash(NeverhoodEngine *vm)
276 	: StaticSprite(vm, 900) {
277 
278 	loadSprite(0x211003A0, kSLFDefDrawOffset | kSLFDefPosition, 400);
279 	setVisible(false);
280 	loadSound(0, 0xCB36BA54);
281 }
282 
show()283 void SsScene2804Flash::show() {
284 	setVisible(true);
285 	updatePosition();
286 	playSound(0);
287 }
288 
SsScene2804BeamCoilBody(NeverhoodEngine * vm)289 SsScene2804BeamCoilBody::SsScene2804BeamCoilBody(NeverhoodEngine *vm)
290 	: StaticSprite(vm, 900) {
291 
292 	loadSprite(0x9A816000, kSLFDefDrawOffset | kSLFDefPosition, 400);
293 	setVisible(false);
294 }
295 
AsScene2804CrystalWaves(NeverhoodEngine * vm,uint crystalIndex)296 AsScene2804CrystalWaves::AsScene2804CrystalWaves(NeverhoodEngine *vm, uint crystalIndex)
297 	: AnimatedSprite(vm, 1100), _crystalIndex(crystalIndex) {
298 
299 	static const NPoint kAsScene2804CrystalWavesPoints[] = {
300 		{323, 245},
301 		{387, 76},
302 		{454, 260},
303 		{527, 70}
304 	};
305 
306 	_x = kAsScene2804CrystalWavesPoints[crystalIndex].x;
307 	_y = kAsScene2804CrystalWavesPoints[crystalIndex].y;
308 	createSurface1(0x840C41F0, 1200);
309 	if (crystalIndex & 1)
310 		setDoDeltaY(1);
311 	setVisible(false);
312 	_needRefresh = true;
313 	SetUpdateHandler(&AnimatedSprite::update);
314 	SetMessageHandler(&Sprite::handleMessage);
315 }
316 
show()317 void AsScene2804CrystalWaves::show() {
318 	setVisible(true);
319 	startAnimation(0x840C41F0, 0, -1);
320 }
321 
hide()322 void AsScene2804CrystalWaves::hide() {
323 	setVisible(false);
324 	stopAnimation();
325 }
326 
327 static const int16 kAsScene2804CrystalFrameNums[] = {
328 	0, 6, 2, 8, 1, 10, 0, 0
329 };
330 
331 static const uint32 kAsScene2804CrystalFileHashes[] = {
332 	0x000540B0,
333 	0x001280D0,
334 	0x003D0010,
335 	0x00620190,
336 	0x00DC0290
337 };
338 
AsScene2804Crystal(NeverhoodEngine * vm,AsScene2804CrystalWaves * asCrystalWaves,uint crystalIndex)339 AsScene2804Crystal::AsScene2804Crystal(NeverhoodEngine *vm, AsScene2804CrystalWaves *asCrystalWaves, uint crystalIndex)
340 	: AnimatedSprite(vm, 1100), _asCrystalWaves(asCrystalWaves), _crystalIndex(crystalIndex), _isShowing(false) {
341 
342 	static const NPoint kAsScene2804CrystalPoints[] = {
343 		{204, 196},
344 		{272, 316},
345 		{334, 206},
346 		{410, 334},
347 		{470, 180}
348 	};
349 
350 	_colorNum = (int16)getSubVar(VA_CURR_CRYSTAL_COLORS, crystalIndex);
351 	_isLightOn = getGlobalVar(V_SHRINK_LIGHTS_ON) != 0;
352 	if (_isLightOn) {
353 		_x = kAsScene2804CrystalPoints[crystalIndex].x;
354 		_y = kAsScene2804CrystalPoints[crystalIndex].y;
355 		createSurface1(0x108DFB12, 1200);
356 		startAnimation(0x108DFB12, kAsScene2804CrystalFrameNums[_colorNum], -1);
357 		_needRefresh = true;
358 		_newStickFrameIndex = kAsScene2804CrystalFrameNums[_colorNum];
359 	} else {
360 		_x = 320;
361 		_y = 240;
362 		createSurface1(kAsScene2804CrystalFileHashes[crystalIndex], 1200);
363 		startAnimation(kAsScene2804CrystalFileHashes[crystalIndex], _colorNum, -1);
364 		setVisible(false);
365 		_needRefresh = true;
366 		_newStickFrameIndex = _colorNum;
367 	}
368 	loadSound(0, 0x725294D4);
369 	SetUpdateHandler(&AnimatedSprite::update);
370 }
371 
show()372 void AsScene2804Crystal::show() {
373 	if (!_isLightOn) {
374 		setVisible(true);
375 		_isShowing = true;
376 		if (_asCrystalWaves)
377 			_asCrystalWaves->show();
378 		playSound(0);
379 	}
380 }
381 
hide()382 void AsScene2804Crystal::hide() {
383 	if (!_isLightOn) {
384 		setVisible(false);
385 		_isShowing = false;
386 		if (_asCrystalWaves)
387 			_asCrystalWaves->hide();
388 	}
389 }
390 
activate()391 void AsScene2804Crystal::activate() {
392 	if (!_isShowing) {
393 		int16 frameNum = kAsScene2804CrystalFrameNums[_colorNum];
394 		_colorNum++;
395 		if (_colorNum >= 6)
396 			_colorNum = 0;
397 		if (_isLightOn) {
398 			startAnimation(0x108DFB12, frameNum, kAsScene2804CrystalFrameNums[_colorNum]);
399 			_playBackwards = kAsScene2804CrystalFrameNums[_colorNum] < _colorNum;
400 			_newStickFrameIndex = kAsScene2804CrystalFrameNums[_colorNum];
401 		} else {
402 			startAnimation(kAsScene2804CrystalFileHashes[_crystalIndex], _colorNum, -1);
403 			_newStickFrameIndex = _colorNum;
404 		}
405 		setSubVar(VA_CURR_CRYSTAL_COLORS, _crystalIndex, _colorNum);
406 	}
407 }
408 
SsScene2804CrystalButton(NeverhoodEngine * vm,Scene2804 * parentScene,AsScene2804Crystal * asCrystal,uint crystalIndex)409 SsScene2804CrystalButton::SsScene2804CrystalButton(NeverhoodEngine *vm, Scene2804 *parentScene, AsScene2804Crystal *asCrystal, uint crystalIndex)
410 	: StaticSprite(vm, 900), _countdown(0), _parentScene(parentScene), _asCrystal(asCrystal), _crystalIndex(crystalIndex) {
411 
412 	static const uint32 kSsScene2804CrystalButtonFileHashes1[] = {
413 		0x911101B0,
414 		0x22226001,
415 		0x4444A362,
416 		0x888925A4,
417 		0x11122829
418 	};
419 
420 	static const uint32 kSsScene2804CrystalButtonFileHashes2[] = {
421 		0xB500A1A0,
422 		0x6A012021,
423 		0xD4022322,
424 		0xA8042525,
425 		0x5008292B
426 	};
427 
428 	loadSprite(getGlobalVar(V_SHRINK_LIGHTS_ON) ? kSsScene2804CrystalButtonFileHashes1[crystalIndex] : kSsScene2804CrystalButtonFileHashes2[crystalIndex],
429 		kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400);
430 	setVisible(false);
431 	loadSound(0, 0x44045140);
432 	SetUpdateHandler(&SsScene2804CrystalButton::update);
433 	SetMessageHandler(&SsScene2804CrystalButton::handleMessage);
434 }
435 
update()436 void SsScene2804CrystalButton::update() {
437 	updatePosition();
438 	if (_countdown != 0 && (--_countdown) == 0) {
439 		setVisible(false);
440 	}
441 }
442 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)443 uint32 SsScene2804CrystalButton::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
444 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
445 	switch (messageNum) {
446 	case 0x1011:
447 		if (_countdown == 0 && !_parentScene->isWorking()) {
448 			playSound(0);
449 			setVisible(true);
450 			_countdown = 4;
451 			_asCrystal->activate();
452 		}
453 		messageResult = 1;
454 		break;
455 	default:
456 		break;
457 	}
458 	return messageResult;
459 }
460 
AsScene2804BeamCoil(NeverhoodEngine * vm,Scene * parentScene,SsScene2804BeamCoilBody * ssBeamCoilBody)461 AsScene2804BeamCoil::AsScene2804BeamCoil(NeverhoodEngine *vm, Scene *parentScene, SsScene2804BeamCoilBody *ssBeamCoilBody)
462 	: AnimatedSprite(vm, 1400), _parentScene(parentScene), _ssBeamCoilBody(ssBeamCoilBody), _countdown(0) {
463 
464 	createSurface1(0x00494891, 1000);
465 	_x = 125;
466 	_y = 184;
467 	setVisible(false);
468 	_needRefresh = true;
469 	AnimatedSprite::updatePosition();
470 	loadSound(0, 0x6352F051);
471 	_vm->_soundMan->addSound(0xC5EA0B28, 0xEF56B094);
472 	SetUpdateHandler(&AsScene2804BeamCoil::update);
473 	SetMessageHandler(&AsScene2804BeamCoil::handleMessage);
474 }
475 
~AsScene2804BeamCoil()476 AsScene2804BeamCoil::~AsScene2804BeamCoil() {
477 	_vm->_soundMan->deleteSoundGroup(0xC5EA0B28);
478 }
479 
update()480 void AsScene2804BeamCoil::update() {
481 	updateAnim();
482 	updatePosition();
483 	if (_countdown != 0 && (--_countdown) == 0) {
484 		sendMessage(_parentScene, 0x2001, 0);
485 	}
486 }
487 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)488 uint32 AsScene2804BeamCoil::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
489 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
490 	switch (messageNum) {
491 	case NM_POSITION_CHANGE:
492 		show();
493 		_countdown = 92;
494 		messageResult = 1;
495 		break;
496 	case 0x2003:
497 		hide();
498 		messageResult = 1;
499 		break;
500 	default:
501 		break;
502 	}
503 	return messageResult;
504 }
505 
show()506 void AsScene2804BeamCoil::show() {
507 	_ssBeamCoilBody->setVisible(true);
508 	setVisible(true);
509 	startAnimation(0x00494891, 0, -1);
510 	playSound(0);
511 	SetMessageHandler(&AsScene2804BeamCoil::hmBeaming);
512 	NextState(&AsScene2804BeamCoil::stBeaming);
513 }
514 
hide()515 void AsScene2804BeamCoil::hide() {
516 	stopAnimation();
517 	SetMessageHandler(&AsScene2804BeamCoil::handleMessage);
518 	setVisible(false);
519 	_ssBeamCoilBody->setVisible(false);
520 	_vm->_soundMan->stopSound(0xEF56B094);
521 }
522 
stBeaming()523 void AsScene2804BeamCoil::stBeaming() {
524 	startAnimation(0x00494891, 93, -1);
525 	NextState(&AsScene2804BeamCoil::stBeaming);
526 	_vm->_soundMan->playSoundLooping(0xEF56B094);
527 }
528 
hmBeaming(int messageNum,const MessageParam & param,Entity * sender)529 uint32 AsScene2804BeamCoil::hmBeaming(int messageNum, const MessageParam &param, Entity *sender) {
530 	uint32 messageResult = handleMessage(messageNum, param, sender);
531 	switch (messageNum) {
532 	case NM_ANIMATION_STOP:
533 		gotoNextState();
534 		break;
535 	default:
536 		break;
537 	}
538 	return messageResult;
539 }
540 
AsScene2804BeamTarget(NeverhoodEngine * vm)541 AsScene2804BeamTarget::AsScene2804BeamTarget(NeverhoodEngine *vm)
542 	: AnimatedSprite(vm, 1400) {
543 
544 	createSurface1(0x03842000, 1000);
545 	_x = 475;
546 	_y = 278;
547 	setVisible(false);
548 	_needRefresh = true;
549 	updatePosition();
550 	SetUpdateHandler(&AnimatedSprite::update);
551 	SetMessageHandler(&AsScene2804BeamTarget::handleMessage);
552 }
553 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)554 uint32 AsScene2804BeamTarget::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
555 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
556 	switch (messageNum) {
557 	case 0x2004:
558 		setVisible(true);
559 		startAnimation(0x03842000, 0, -1);
560 		messageResult = 1;
561 		break;
562 	case NM_KLAYMEN_CLIMB_LADDER:
563 		setVisible(false);
564 		stopAnimation();
565 		messageResult = 1;
566 		break;
567 	default:
568 		break;
569 	}
570 	return messageResult;
571 }
572 
AsScene2806Spew(NeverhoodEngine * vm)573 AsScene2806Spew::AsScene2806Spew(NeverhoodEngine *vm)
574 	: AnimatedSprite(vm, 1200) {
575 
576 	createSurface1(0x04211490, 1200);
577 	_x = 378;
578 	_y = 423;
579 	SetUpdateHandler(&AnimatedSprite::update);
580 	SetMessageHandler(&AsScene2806Spew::handleMessage);
581 	setDoDeltaX(1);
582 	setVisible(false);
583 }
584 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)585 uint32 AsScene2806Spew::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
586 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
587 	switch (messageNum) {
588 	case NM_ANIMATION_UPDATE:
589 		playSound(0, 0x48640244);
590 		startAnimation(0x04211490, 0, -1);
591 		setVisible(true);
592 		break;
593 	case NM_ANIMATION_STOP:
594 		stopAnimation();
595 		setVisible(false);
596 		break;
597 	default:
598 		break;
599 	}
600 	return messageResult;
601 }
602 
603 static const uint32 kClass428FileHashes[] = {
604 	0x140022CA,
605 	0x4C30A602,
606 	0xB1633402,
607 	0x12982135,
608 	0x0540B728,
609 	0x002A81E3,
610 	0x08982841,
611 	0x10982841,
612 	0x20982841,
613 	0x40982841,
614 	0x80982841,
615 	0x40800711
616 };
617 
618 static const int kClass428Countdowns1[] = {
619 	18, 16, 10, 0
620 };
621 
622 static const int kClass428Countdowns2[] = {
623 	9, 9, 8, 8, 5, 5, 0, 0
624 };
625 
626 static const uint32 kClass490FileHashes[] = {
627 	0x08100071,
628 	0x24084215,
629 	0x18980A10
630 };
631 
632 static const int16 kClass490FrameIndices1[] = {
633 	0, 8, 15, 19
634 };
635 
636 static const int16 kClass490FrameIndices2[] = {
637 	0, 4, 8, 11, 15, 17, 19, 0
638 };
639 
SsScene2808Dispenser(NeverhoodEngine * vm,Scene * parentScene,int testTubeSetNum,int testTubeIndex)640 SsScene2808Dispenser::SsScene2808Dispenser(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum, int testTubeIndex)
641 	: StaticSprite(vm, 900), _parentScene(parentScene), _countdown(0), _testTubeSetNum(testTubeSetNum),
642 	_testTubeIndex(testTubeIndex) {
643 
644 	loadSprite(kClass428FileHashes[testTubeSetNum * 3 + testTubeIndex], kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 1500);
645 	setVisible(false);
646 	SetUpdateHandler(&SsScene2808Dispenser::update);
647 	SetMessageHandler(&SsScene2808Dispenser::handleMessage);
648 }
649 
update()650 void SsScene2808Dispenser::update() {
651 	updatePosition();
652 	if (_countdown != 0 && (--_countdown) == 0) {
653 		setVisible(false);
654 	}
655 }
656 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)657 uint32 SsScene2808Dispenser::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
658 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
659 	switch (messageNum) {
660 	case 0x1011:
661 		sendMessage(_parentScene, 0x2000, _testTubeIndex);
662 		messageResult = 1;
663 		break;
664 	default:
665 		break;
666 	}
667 	return messageResult;
668 }
669 
startCountdown(int index)670 void SsScene2808Dispenser::startCountdown(int index) {
671 	setVisible(true);
672 	updatePosition();
673 	if (_testTubeSetNum == 0) {
674 		_countdown = kClass428Countdowns1[index];
675 	} else {
676 		_countdown = kClass428Countdowns2[index];
677 	}
678 }
679 
AsScene2808TestTube(NeverhoodEngine * vm,int testTubeSetNum,int testTubeIndex,SsScene2808Dispenser * ssDispenser)680 AsScene2808TestTube::AsScene2808TestTube(NeverhoodEngine *vm, int testTubeSetNum, int testTubeIndex, SsScene2808Dispenser *ssDispenser)
681 	: AnimatedSprite(vm, 1100), _testTubeSetNum(testTubeSetNum), _testTubeIndex(testTubeIndex), _ssDispenser(ssDispenser), _fillLevel(0) {
682 
683 	if (testTubeSetNum == 0) {
684 		_x = 504;
685 		_y = 278;
686 	} else {
687 		setDoDeltaX(1);
688 		_x = 136;
689 		_y = 278;
690 	}
691 
692 	createSurface1(kClass490FileHashes[testTubeIndex], 1100);
693 
694 	if (testTubeSetNum == 0) {
695 		loadSound(0, 0x30809E2D);
696 		loadSound(1, 0x72811E2D);
697 		loadSound(2, 0x78B01625);
698 	} else {
699 		loadSound(3, 0x70A41E0C);
700 		loadSound(4, 0x50205E2D);
701 		loadSound(5, 0xF8621E2D);
702 		loadSound(6, 0xF1A03C2D);
703 		loadSound(7, 0x70A43D2D);
704 		loadSound(8, 0xF0601E2D);
705 	}
706 
707 	startAnimation(kClass490FileHashes[testTubeIndex], 0, -1);
708 	_newStickFrameIndex = 0;
709 
710 	SetUpdateHandler(&AnimatedSprite::update);
711 	SetMessageHandler(&AsScene2808TestTube::handleMessage);
712 
713 	if (_fillLevel == 0)
714 		setVisible(false);
715 
716 }
717 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)718 uint32 AsScene2808TestTube::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
719 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
720 	switch (messageNum) {
721 	case 0x1011:
722 		fill();
723 		messageResult = 1;
724 		break;
725 	default:
726 		break;
727 	}
728 	return messageResult;
729 }
730 
fill()731 void AsScene2808TestTube::fill() {
732 	if ((int)_fillLevel < _testTubeSetNum * 3 + 3) {
733 		if (_testTubeSetNum == 0) {
734 			playSound(_fillLevel);
735 			setVisible(true);
736 			startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices1[_fillLevel], kClass490FrameIndices1[_fillLevel + 1]);
737 			_newStickFrameIndex = kClass490FrameIndices1[_fillLevel + 1];
738 		} else {
739 			playSound(3 + _fillLevel);
740 			setVisible(true);
741 			startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices2[_fillLevel], kClass490FrameIndices2[_fillLevel + 1]);
742 			_newStickFrameIndex = kClass490FrameIndices2[_fillLevel + 1];
743 		}
744 		_ssDispenser->startCountdown(_fillLevel);
745 		_fillLevel++;
746 	}
747 }
748 
flush()749 void AsScene2808TestTube::flush() {
750 	if (_fillLevel != 0) {
751 		if (_testTubeSetNum == 0) {
752 			startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices1[_fillLevel], -1);
753 		} else {
754 			startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices2[_fillLevel], -1);
755 		}
756 		_newStickFrameIndex = 0;
757 		_playBackwards = true;
758 		setVisible(true);
759 	}
760 }
761 
AsScene2808Handle(NeverhoodEngine * vm,Scene * parentScene,int testTubeSetNum)762 AsScene2808Handle::AsScene2808Handle(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum)
763 	: AnimatedSprite(vm, 1300), _parentScene(parentScene), _testTubeSetNum(testTubeSetNum), _isActivated(false) {
764 
765 	loadSound(0, 0xE18D1F30);
766 	_x = 320;
767 	_y = 240;
768 	if (_testTubeSetNum == 1)
769 		setDoDeltaX(1);
770 	createSurface1(0x040900D0, 1300);
771 	startAnimation(0x040900D0, 0, -1);
772 	_needRefresh = true;
773 	_newStickFrameIndex = 0;
774 	SetUpdateHandler(&AnimatedSprite::update);
775 	SetMessageHandler(&AsScene2808Handle::handleMessage);
776 	AnimatedSprite::updatePosition();
777 }
778 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)779 uint32 AsScene2808Handle::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
780 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
781 	switch (messageNum) {
782 	case 0x1011:
783 		if (!_isActivated) {
784 			sendMessage(_parentScene, 0x2001, 0);
785 			playSound(0);
786 			activate();
787 		}
788 		messageResult = 1;
789 		break;
790 	default:
791 		break;
792 	}
793 	return messageResult;
794 }
795 
hmActivating(int messageNum,const MessageParam & param,Entity * sender)796 uint32 AsScene2808Handle::hmActivating(int messageNum, const MessageParam &param, Entity *sender) {
797 	uint32 messageResult = handleMessage(messageNum, param, sender);
798 	switch (messageNum) {
799 	case NM_ANIMATION_STOP:
800 		gotoNextState();
801 		break;
802 	default:
803 		break;
804 	}
805 	return messageResult;
806 }
807 
activate()808 void AsScene2808Handle::activate() {
809 	startAnimation(0x040900D0, 0, -1);
810 	SetMessageHandler(&AsScene2808Handle::hmActivating);
811 	NextState(&AsScene2808Handle::stActivated);
812 	_isActivated = true;
813 	_newStickFrameIndex = -1;
814 }
815 
stActivated()816 void AsScene2808Handle::stActivated() {
817 	stopAnimation();
818 	sendMessage(_parentScene, NM_POSITION_CHANGE, 0);
819 }
820 
AsScene2808Flow(NeverhoodEngine * vm,Scene * parentScene,int testTubeSetNum)821 AsScene2808Flow::AsScene2808Flow(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum)
822 	: AnimatedSprite(vm, 1100), _parentScene(parentScene), _testTubeSetNum(testTubeSetNum) {
823 
824 	if (testTubeSetNum == 0) {
825 		_x = 312;
826 		_y = 444;
827 	} else {
828 		_x = 328;
829 		_y = 444;
830 	}
831 	createSurface1(0xB8414818, 1200);
832 	startAnimation(0xB8414818, 0, -1);
833 	setVisible(false);
834 	_newStickFrameIndex = 0;
835 	_needRefresh = true;
836 	loadSound(0, 0x6389B652);
837 	SetUpdateHandler(&AnimatedSprite::update);
838 	AnimatedSprite::updatePosition();
839 }
840 
hmFlowing(int messageNum,const MessageParam & param,Entity * sender)841 uint32 AsScene2808Flow::hmFlowing(int messageNum, const MessageParam &param, Entity *sender) {
842 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
843 	switch (messageNum) {
844 	case NM_ANIMATION_STOP:
845 		gotoNextState();
846 		break;
847 	default:
848 		break;
849 	}
850 	return messageResult;
851 }
852 
start()853 void AsScene2808Flow::start() {
854 	startAnimation(0xB8414818, 0, -1);
855 	setVisible(true);
856 	SetMessageHandler(&AsScene2808Flow::hmFlowing);
857 	NextState(&AsScene2808Flow::stKeepFlowing);
858 	playSound(0);
859 }
860 
stKeepFlowing()861 void AsScene2808Flow::stKeepFlowing() {
862 	startAnimation(0xB8414818, 1, -1);
863 	NextState(&AsScene2808Flow::stKeepFlowing);
864 }
865 
AsScene2808LightEffect(NeverhoodEngine * vm,int testTubeSetNum)866 AsScene2808LightEffect::AsScene2808LightEffect(NeverhoodEngine *vm, int testTubeSetNum)
867 	: AnimatedSprite(vm, 800), _countdown(1) {
868 
869 	_x = 320;
870 	_y = 240;
871 	if (testTubeSetNum == 1)
872 		setDoDeltaX(1);
873 	createSurface1(0x804C2404, 800);
874 	SetUpdateHandler(&AsScene2808LightEffect::update);
875 	_needRefresh = true;
876 	AnimatedSprite::updatePosition();
877 }
878 
update()879 void AsScene2808LightEffect::update() {
880 	if (_countdown != 0 && (--_countdown) == 0) {
881 		int16 frameIndex = _vm->_rnd->getRandomNumber(3 - 1);
882 		startAnimation(0x804C2404, frameIndex, frameIndex);
883 		updateAnim();
884 		updatePosition();
885 		_countdown = _vm->_rnd->getRandomNumber(3 - 1) + 1;
886 	}
887 }
888 
AsScene2809Spew(NeverhoodEngine * vm)889 AsScene2809Spew::AsScene2809Spew(NeverhoodEngine *vm)
890 	: AnimatedSprite(vm, 1200) {
891 
892 	SetUpdateHandler(&AnimatedSprite::update);
893 	SetMessageHandler(&AsScene2809Spew::handleMessage);
894 	createSurface1(0x04211490, 1200);
895 	_x = 262;
896 	_y = 423;
897 	setDoDeltaX(0);
898 	setVisible(false);
899 }
900 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)901 uint32 AsScene2809Spew::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
902 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
903 	switch (messageNum) {
904 	case NM_ANIMATION_UPDATE:
905 		playSound(0, 0x48640244);
906 		startAnimation(0x04211490, 0, -1);
907 		setVisible(true);
908 		break;
909 	case NM_ANIMATION_STOP:
910 		stopAnimation();
911 		setVisible(false);
912 		break;
913 	default:
914 		break;
915 	}
916 	return messageResult;
917 }
918 
AsScene2810Rope(NeverhoodEngine * vm,Scene * parentScene,int16 x)919 AsScene2810Rope::AsScene2810Rope(NeverhoodEngine *vm, Scene *parentScene, int16 x)
920 	: AnimatedSprite(vm, 1100), _parentScene(parentScene) {
921 
922 	createSurface(990, 68, 476);
923 	SetUpdateHandler(&AnimatedSprite::update);
924 	SetMessageHandler(&AsScene2810Rope::handleMessage);
925 	SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
926 	_x = x;
927 	_y = -276;
928 	startAnimation(0x9D098C23, 35, 53);
929 }
930 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)931 uint32 AsScene2810Rope::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
932 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
933 	switch (messageNum) {
934 	case NM_ANIMATION_STOP:
935 		startAnimation(0x9D098C23, 35, 53);
936 		break;
937 	case NM_MOVE_TO_BACK:
938 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 990);
939 		break;
940 	case NM_MOVE_TO_FRONT:
941 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 1010);
942 		break;
943 	default:
944 		break;
945 	}
946 	return messageResult;
947 }
948 
AsScene2812Winch(NeverhoodEngine * vm)949 AsScene2812Winch::AsScene2812Winch(NeverhoodEngine *vm)
950 	: AnimatedSprite(vm, 1100) {
951 
952 	createSurface1(0x20DA08A0, 1200);
953 	SetUpdateHandler(&AnimatedSprite::update);
954 	SetMessageHandler(&AsScene2812Winch::handleMessage);
955 	setVisible(false);
956 	_x = 280;
957 	_y = 184;
958 }
959 
~AsScene2812Winch()960 AsScene2812Winch::~AsScene2812Winch() {
961 	_vm->_soundMan->deleteSoundGroup(0x00B000E2);
962 }
963 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)964 uint32 AsScene2812Winch::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
965 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
966 	switch (messageNum) {
967 	case NM_ANIMATION_UPDATE:
968 		startAnimation(0x20DA08A0, 0, -1);
969 		setVisible(true);
970 		_vm->_soundMan->addSound(0x00B000E2, 0xC874EE6C);
971 		_vm->_soundMan->playSoundLooping(0xC874EE6C);
972 		break;
973 	case NM_ANIMATION_STOP:
974 		startAnimation(0x20DA08A0, 7, -1);
975 		break;
976 	default:
977 		break;
978 	}
979 	return messageResult;
980 }
981 
AsScene2812Rope(NeverhoodEngine * vm,Scene * parentScene)982 AsScene2812Rope::AsScene2812Rope(NeverhoodEngine *vm, Scene *parentScene)
983 	: AnimatedSprite(vm, 1100), _parentScene(parentScene) {
984 
985 	createSurface(990, 68, 476);
986 	SetUpdateHandler(&AnimatedSprite::update);
987 	SetMessageHandler(&AsScene2812Rope::handleMessage);
988 	SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
989 	startAnimation(0xAE080551, 0, -1);
990 	_x = 334;
991 	_y = 201;
992 }
993 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)994 uint32 AsScene2812Rope::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
995 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
996 	switch (messageNum) {
997 	case NM_KLAYMEN_USE_OBJECT:
998 		setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0);
999 		stRopingDown();
1000 		break;
1001 	case NM_MOVE_TO_BACK:
1002 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 990);
1003 		break;
1004 	case NM_MOVE_TO_FRONT:
1005 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 1010);
1006 		break;
1007 	default:
1008 		break;
1009 	}
1010 	return messageResult;
1011 }
1012 
hmRopingDown(int messageNum,const MessageParam & param,Entity * sender)1013 uint32 AsScene2812Rope::hmRopingDown(int messageNum, const MessageParam &param, Entity *sender) {
1014 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
1015 	switch (messageNum) {
1016 	case NM_ANIMATION_STOP:
1017 		gotoNextState();
1018 		break;
1019 	default:
1020 		break;
1021 	}
1022 	return messageResult;
1023 }
1024 
stRopingDown()1025 void AsScene2812Rope::stRopingDown() {
1026 	sendMessage(_parentScene, NM_KLAYMEN_USE_OBJECT, 0);
1027 	startAnimation(0x9D098C23, 0, -1);
1028 	SetMessageHandler(&AsScene2812Rope::hmRopingDown);
1029 }
1030 
AsScene2812TrapDoor(NeverhoodEngine * vm)1031 AsScene2812TrapDoor::AsScene2812TrapDoor(NeverhoodEngine *vm)
1032 	: AnimatedSprite(vm, 0x805D0029, 100, 320, 240) {
1033 
1034 	SetMessageHandler(&AsScene2812TrapDoor::handleMessage);
1035 	_newStickFrameIndex = 0;
1036 }
1037 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)1038 uint32 AsScene2812TrapDoor::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
1039 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
1040 	switch (messageNum) {
1041 	case NM_ANIMATION_UPDATE:
1042 		startAnimation(0x805D0029, 0, -1);
1043 		playSound(0, 0xEA005F40);
1044 		_newStickFrameIndex = STICK_LAST_FRAME;
1045 		break;
1046 	default:
1047 		break;
1048 	}
1049 	return messageResult;
1050 }
1051 
KmScene2801(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)1052 KmScene2801::KmScene2801(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
1053 	: Klaymen(vm, parentScene, x, y) {
1054 
1055 	// Empty
1056 }
1057 
xHandleMessage(int messageNum,const MessageParam & param)1058 uint32 KmScene2801::xHandleMessage(int messageNum, const MessageParam &param) {
1059 	switch (messageNum) {
1060 	case 0x4001:
1061 	case 0x4800:
1062 		startWalkToX(param.asPoint().x, false);
1063 		break;
1064 	case NM_KLAYMEN_STAND_IDLE:
1065 		GotoState(&Klaymen::stTryStandIdle);
1066 		break;
1067 	case NM_KLAYMEN_PICKUP:
1068 		GotoState(&Klaymen::stPickUpGeneric);
1069 		break;
1070 	case 0x4817:
1071 		setDoDeltaX(param.asInteger());
1072 		gotoNextStateExt();
1073 		break;
1074 	case 0x481B:
1075 		if (param.asPoint().y != 0)
1076 			startWalkToXDistance(param.asPoint().y, param.asPoint().x);
1077 		else
1078 			startWalkToAttachedSpriteXDistance(param.asPoint().x);
1079 		break;
1080 	case NM_KLAYMEN_TURN_TO_USE:
1081 		GotoState(&Klaymen::stTurnToUse);
1082 		break;
1083 	case NM_KLAYMEN_RETURN_FROM_USE:
1084 		GotoState(&Klaymen::stReturnFromUse);
1085 		break;
1086 	case 0x481F:
1087 		if (param.asInteger() == 1)
1088 			GotoState(&Klaymen::stWonderAboutAfter);
1089 		else if (param.asInteger() == 0)
1090 			GotoState(&Klaymen::stWonderAboutHalf);
1091 		else if (param.asInteger() == 4)
1092 			GotoState(&Klaymen::stTurnAwayFromUse);
1093 		else if (param.asInteger() == 3)
1094 			GotoState(&Klaymen::stTurnToUseHalf);
1095 		else
1096 			GotoState(&Klaymen::stWonderAbout);
1097 		break;
1098 	case 0x482D:
1099 		setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
1100 		gotoNextStateExt();
1101 		break;
1102 	case 0x482E:
1103 		if (param.asInteger() == 1)
1104 			GotoState(&Klaymen::stWalkToFrontNoStep);
1105 		else
1106 			GotoState(&Klaymen::stWalkToFront);
1107 		break;
1108 	case 0x482F:
1109 		if (param.asInteger() == 1)
1110 			GotoState(&Klaymen::stTurnToFront);
1111 		else
1112 			GotoState(&Klaymen::stTurnToBack);
1113 		break;
1114 	case 0x4837:
1115 		stopWalking();
1116 		break;
1117 	default:
1118 		break;
1119 	}
1120 	return 0;
1121 }
1122 
KmScene2803(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y,NRect * clipRects,int clipRectsCount)1123 KmScene2803::KmScene2803(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, int clipRectsCount)
1124 	: Klaymen(vm, parentScene, x, y) {
1125 
1126 	_surface->setClipRects(clipRects, clipRectsCount);
1127 	_dataResource.load(0x00900849);
1128 }
1129 
xHandleMessage(int messageNum,const MessageParam & param)1130 uint32 KmScene2803::xHandleMessage(int messageNum, const MessageParam &param) {
1131 	switch (messageNum) {
1132 	case 0x4001:
1133 	case 0x4800:
1134 		startWalkToX(param.asPoint().x, false);
1135 		break;
1136 	case NM_KLAYMEN_STAND_IDLE:
1137 		GotoState(&Klaymen::stTryStandIdle);
1138 		break;
1139 	case 0x4803:
1140 		_destY = param.asInteger();
1141 		GotoState(&Klaymen::stJumpToGrab);
1142 		break;
1143 	case 0x4804:
1144 		if (param.asInteger() == 3)
1145 			GotoState(&Klaymen::stFinishGrow);
1146 		break;
1147 	case 0x480D:
1148 		GotoState(&Klaymen::stPullCord);
1149 		break;
1150 	case 0x4817:
1151 		setDoDeltaX(param.asInteger());
1152 		gotoNextStateExt();
1153 		break;
1154 	case 0x4818:
1155 		startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
1156 		break;
1157 	case NM_KLAYMEN_TURN_TO_USE:
1158 		GotoState(&Klaymen::stTurnToUse);
1159 		break;
1160 	case NM_KLAYMEN_RETURN_FROM_USE:
1161 		GotoState(&Klaymen::stReturnFromUse);
1162 		break;
1163 	case 0x481F:
1164 		if (param.asInteger() == 1)
1165 			GotoState(&Klaymen::stWonderAboutAfter);
1166 		else
1167 			GotoState(&Klaymen::stWonderAboutHalf);
1168 		break;
1169 	case 0x482E:
1170 		GotoState(&Klaymen::stWalkToFront);
1171 		break;
1172 	case 0x482F:
1173 		GotoState(&Klaymen::stTurnToBack);
1174 		break;
1175 	case 0x4834:
1176 		GotoState(&Klaymen::stStepOver);
1177 		break;
1178 	case 0x4838:
1179 		GotoState(&Klaymen::stJumpToGrabRelease);
1180 		break;
1181 	default:
1182 		break;
1183 	}
1184 	return 0;
1185 }
1186 
KmScene2803Small(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)1187 KmScene2803Small::KmScene2803Small(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
1188 	: Klaymen(vm, parentScene, x, y) {
1189 
1190 	_dataResource.load(0x81120132);
1191 }
1192 
xHandleMessage(int messageNum,const MessageParam & param)1193 uint32 KmScene2803Small::xHandleMessage(int messageNum, const MessageParam &param) {
1194 	switch (messageNum) {
1195 	case 0x4001:
1196 	case 0x4800:
1197 		startWalkToXSmall(param.asPoint().x);
1198 		break;
1199 	case NM_KLAYMEN_STAND_IDLE:
1200 		GotoState(&Klaymen::stStandIdleSmall);
1201 		break;
1202 	case 0x4817:
1203 		setDoDeltaX(param.asInteger());
1204 		gotoNextStateExt();
1205 		break;
1206 	case 0x4818:
1207 		startWalkToXSmall(_dataResource.getPoint(param.asInteger()).x);
1208 		break;
1209 	case 0x481F:
1210 		if (param.asInteger() == 1)
1211 			GotoState(&Klaymen::stWonderAboutAfterSmall);
1212 		else if (param.asInteger() == 0)
1213 			GotoState(&Klaymen::stWonderAboutHalfSmall);
1214 		else
1215 			GotoState(&Klaymen::stWonderAboutSmall);
1216 		break;
1217 	case 0x482E:
1218 		if (param.asInteger() == 1)
1219 			GotoState(&Klaymen::stWalkToFrontNoStepSmall);
1220 		else if (param.asInteger() == 2)
1221 			GotoState(&Klaymen::stWalkToFront2Small);
1222 		else
1223 			GotoState(&Klaymen::stWalkToFrontSmall);
1224 		break;
1225 	case 0x482F:
1226 		if (param.asInteger() == 1)
1227 			GotoState(&Klaymen::stTurnToBackHalfSmall);
1228 		else if (param.asInteger() == 2)
1229 			GotoState(&Klaymen::stTurnToBackWalkSmall);
1230 		else
1231 			GotoState(&Klaymen::stTurnToBackSmall);
1232 		break;
1233 	case 0x4830:
1234 		GotoState(&KmScene2803Small::stShrink);
1235 		break;
1236 	default:
1237 		break;
1238 	}
1239 	return 0;
1240 }
1241 
hmShrink(int messageNum,const MessageParam & param,Entity * sender)1242 uint32 KmScene2803Small::hmShrink(int messageNum, const MessageParam &param, Entity *sender) {
1243 	uint32 messageResult = hmLowLevelAnimation(messageNum, param, sender);
1244 	switch (messageNum) {
1245 	case NM_ANIMATION_START:
1246 		if (param.asInteger() == 0x80C110B5)
1247 			sendMessage(_parentScene, NM_MOVE_TO_BACK, 0);
1248 		else if (param.asInteger() == 0x33288344)
1249 			playSound(2, 0x10688664);
1250 		break;
1251 	default:
1252 		break;
1253 	}
1254 	return messageResult;
1255 }
1256 
stShrink()1257 void KmScene2803Small::stShrink() {
1258 	_busyStatus = 0;
1259 	_acceptInput = false;
1260 	playSound(0, 0x4C69EA53);
1261 	startAnimation(0x1AE88904, 0, -1);
1262 	SetUpdateHandler(&Klaymen::update);
1263 	SetMessageHandler(&KmScene2803Small::hmShrink);
1264 	SetSpriteUpdate(&AnimatedSprite::updateDeltaXY);
1265 }
1266 
KmScene2805(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)1267 KmScene2805::KmScene2805(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
1268 	: Klaymen(vm, parentScene, x, y) {
1269 
1270 	// Empty
1271 }
1272 
xHandleMessage(int messageNum,const MessageParam & param)1273 uint32 KmScene2805::xHandleMessage(int messageNum, const MessageParam &param) {
1274 	uint32 messageResult = 0;
1275 	switch (messageNum) {
1276 	case NM_ANIMATION_UPDATE:
1277 		_isSittingInTeleporter = param.asInteger() != 0;
1278 		messageResult = 1;
1279 		break;
1280 	case 0x4001:
1281 	case 0x4800:
1282 		startWalkToX(param.asPoint().x, false);
1283 		break;
1284 	case NM_KLAYMEN_STAND_IDLE:
1285 		if (_isSittingInTeleporter)
1286 			GotoState(&Klaymen::stSitIdleTeleporter);
1287 		else
1288 			GotoState(&Klaymen::stTryStandIdle);
1289 		break;
1290 	case 0x4817:
1291 		setDoDeltaX(param.asInteger());
1292 		gotoNextStateExt();
1293 		break;
1294 	case NM_KLAYMEN_TURN_TO_USE:
1295 		if (_isSittingInTeleporter)
1296 			GotoState(&Klaymen::stTurnToUseInTeleporter);
1297 		break;
1298 	case NM_KLAYMEN_RETURN_FROM_USE:
1299 		if (_isSittingInTeleporter)
1300 			GotoState(&Klaymen::stReturnFromUseInTeleporter);
1301 		break;
1302 	case 0x4834:
1303 		GotoState(&Klaymen::stStepOver);
1304 		break;
1305 	case 0x4835:
1306 		sendMessage(_parentScene, 0x2000, 1);
1307 		_isSittingInTeleporter = true;
1308 		GotoState(&Klaymen::stSitInTeleporter);
1309 		break;
1310 	case 0x4836:
1311 		sendMessage(_parentScene, 0x2000, 0);
1312 		_isSittingInTeleporter = false;
1313 		GotoState(&Klaymen::stGetUpFromTeleporter);
1314 		break;
1315 	case 0x483D:
1316 		teleporterAppear(0xDE284B74);
1317 		break;
1318 	case 0x483E:
1319 		teleporterDisappear(0xD82A4094);
1320 		break;
1321 	default:
1322 		break;
1323 	}
1324 	return messageResult;
1325 }
1326 
KmScene2806(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y,bool needsLargeSurface,NRect * clipRects,uint clipRectsCount)1327 KmScene2806::KmScene2806(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y,
1328 	bool needsLargeSurface, NRect *clipRects, uint clipRectsCount)
1329 	: Klaymen(vm, parentScene, x, y) {
1330 
1331 	if (needsLargeSurface) {
1332 		NDimensions dimensions = _animResource.loadSpriteDimensions(0x2838C010);
1333 		delete _surface;
1334 		createSurface(1000, dimensions.width, dimensions.height);
1335 		loadSound(3, 0x58E0C341);
1336 		loadSound(4, 0x40A00342);
1337 		loadSound(5, 0xD0A1C348);
1338 		loadSound(6, 0x166FC6E0);
1339 		loadSound(7, 0x00018040);
1340 	}
1341 
1342 	_dataResource.load(0x98182003);
1343 	_surface->setClipRects(clipRects, clipRectsCount);
1344 }
1345 
xHandleMessage(int messageNum,const MessageParam & param)1346 uint32 KmScene2806::xHandleMessage(int messageNum, const MessageParam &param) {
1347 	switch (messageNum) {
1348 	case 0x4001:
1349 	case 0x4800:
1350 		startWalkToX(param.asPoint().x, false);
1351 		break;
1352 	case NM_KLAYMEN_STAND_IDLE:
1353 		GotoState(&Klaymen::stTryStandIdle);
1354 		break;
1355 	case 0x4804:
1356 		startWalkToX(440, true);
1357 		break;
1358 	case 0x480D:
1359 		GotoState(&Klaymen::stPullCord);
1360 		break;
1361 	case NM_KLAYMEN_PRESS_BUTTON:
1362 		if (param.asInteger() == 0)
1363 			GotoState(&Klaymen::stPressButtonSide);
1364 		break;
1365 	case 0x4817:
1366 		setDoDeltaX(param.asInteger());
1367 		gotoNextStateExt();
1368 		break;
1369 	case 0x4818:
1370 		startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
1371 		break;
1372 	case 0x4831:
1373 		GotoState(&Klaymen::stGrow);
1374 		break;
1375 	case 0x4832:
1376 		if (param.asInteger() == 1)
1377 			GotoState(&Klaymen::stDrinkPotion);
1378 		else
1379 			GotoState(&Klaymen::stUseTube);
1380 		break;
1381 	default:
1382 		break;
1383 	}
1384 	return 0;
1385 }
1386 
KmScene2809(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y,bool needsLargeSurface,NRect * clipRects,uint clipRectsCount)1387 KmScene2809::KmScene2809(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y,
1388 	bool needsLargeSurface, NRect *clipRects, uint clipRectsCount)
1389 	: Klaymen(vm, parentScene, x, y) {
1390 
1391 	if (needsLargeSurface) {
1392 		NDimensions dimensions = _animResource.loadSpriteDimensions(0x2838C010);
1393 		delete _surface;
1394 		createSurface(1000, dimensions.width, dimensions.height);
1395 		loadSound(3, 0x58E0C341);
1396 		loadSound(4, 0x40A00342);
1397 		loadSound(5, 0xD0A1C348);
1398 		loadSound(6, 0x166FC6E0);
1399 		loadSound(7, 0x00018040);
1400 	}
1401 
1402 	_dataResource.load(0x1830009A);
1403 	_surface->setClipRects(clipRects, clipRectsCount);
1404 }
1405 
xHandleMessage(int messageNum,const MessageParam & param)1406 uint32 KmScene2809::xHandleMessage(int messageNum, const MessageParam &param) {
1407 	switch (messageNum) {
1408 	case 0x4001:
1409 	case 0x4800:
1410 		startWalkToX(param.asPoint().x, false);
1411 		break;
1412 	case NM_KLAYMEN_STAND_IDLE:
1413 		GotoState(&Klaymen::stTryStandIdle);
1414 		break;
1415 	case 0x4804:
1416 		startWalkToX(226, true);
1417 		break;
1418 	case 0x480D:
1419 		GotoState(&Klaymen::stPullCord);
1420 		break;
1421 	case NM_KLAYMEN_PRESS_BUTTON:
1422 		if (param.asInteger() == 0)
1423 			GotoState(&Klaymen::stPressButtonSide);
1424 		break;
1425 	case 0x4817:
1426 		setDoDeltaX(param.asInteger());
1427 		gotoNextStateExt();
1428 		break;
1429 	case 0x4818:
1430 		startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
1431 		break;
1432 	case 0x4831:
1433 		GotoState(&Klaymen::stGrow);
1434 		break;
1435 	case 0x4832:
1436 		if (param.asInteger() == 1)
1437 			GotoState(&Klaymen::stDrinkPotion);
1438 		else
1439 			GotoState(&Klaymen::stUseTube);
1440 		break;
1441 	default:
1442 		break;
1443 	}
1444 	return 0;
1445 }
1446 
KmScene2810Small(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)1447 KmScene2810Small::KmScene2810Small(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
1448 	: Klaymen(vm, parentScene, x, y) {
1449 
1450 	// Empty
1451 }
1452 
xHandleMessage(int messageNum,const MessageParam & param)1453 uint32 KmScene2810Small::xHandleMessage(int messageNum, const MessageParam &param) {
1454 	switch (messageNum) {
1455 	case 0x4001:
1456 	case 0x4800:
1457 		startWalkToXSmall(param.asPoint().x);
1458 		break;
1459 	case NM_KLAYMEN_STAND_IDLE:
1460 		GotoState(&Klaymen::stStandIdleSmall);
1461 		break;
1462 	case 0x4817:
1463 		setDoDeltaX(param.asInteger());
1464 		gotoNextStateExt();
1465 		break;
1466 	case 0x4818:
1467 		startWalkToXSmall(_dataResource.getPoint(param.asInteger()).x);
1468 		break;
1469 	case 0x481F:
1470 		if (param.asInteger() == 1)
1471 			GotoState(&Klaymen::stWonderAboutAfterSmall);
1472 		else if (param.asInteger() == 0)
1473 			GotoState(&Klaymen::stWonderAboutHalfSmall);
1474 		else
1475 			GotoState(&Klaymen::stWonderAboutSmall);
1476 		break;
1477 	case 0x482E:
1478 		if (param.asInteger() == 1)
1479 			GotoState(&Klaymen::stWalkToFrontNoStepSmall);
1480 		else
1481 			GotoState(&Klaymen::stWalkToFrontSmall);
1482 		break;
1483 	case 0x482F:
1484 		if (param.asInteger() == 1)
1485 			GotoState(&Klaymen::stTurnToBackHalfSmall);
1486 		else
1487 			GotoState(&Klaymen::stTurnToBackSmall);
1488 		break;
1489 	case 0x4837:
1490 		stopWalking();
1491 		break;
1492 	default:
1493 		break;
1494 	}
1495 	return 0;
1496 }
1497 
KmScene2810(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y,NRect * clipRects,uint clipRectsCount)1498 KmScene2810::KmScene2810(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, NRect *clipRects, uint clipRectsCount)
1499 	: Klaymen(vm, parentScene, x, y) {
1500 
1501 	_surface->setClipRects(clipRects, clipRectsCount);
1502 }
1503 
xHandleMessage(int messageNum,const MessageParam & param)1504 uint32 KmScene2810::xHandleMessage(int messageNum, const MessageParam &param) {
1505 	switch (messageNum) {
1506 	case 0x4001:
1507 	case 0x4800:
1508 		startWalkToX(param.asPoint().x, false);
1509 		break;
1510 	case NM_KLAYMEN_STAND_IDLE:
1511 		GotoState(&Klaymen::stTryStandIdle);
1512 		break;
1513 	case 0x4803:
1514 		_destY = param.asInteger();
1515 		GotoState(&Klaymen::stJumpToGrab);
1516 		break;
1517 	case 0x4804:
1518 		if (param.asInteger() == 3)
1519 			GotoState(&Klaymen::stFinishGrow);
1520 		break;
1521 	case NM_KLAYMEN_PICKUP:
1522 		GotoState(&Klaymen::stPickUpGeneric);
1523 		break;
1524 	case 0x4817:
1525 		setDoDeltaX(param.asInteger());
1526 		gotoNextStateExt();
1527 		break;
1528 	case 0x4818:
1529 		startWalkToX(_dataResource.getPoint(param.asInteger()).x, false);
1530 		break;
1531 	case 0x481B:
1532 		if (param.asPoint().y != 0)
1533 			startWalkToXDistance(param.asPoint().y, param.asPoint().x);
1534 		else
1535 			startWalkToAttachedSpriteXDistance(param.asPoint().x);
1536 		break;
1537 	case 0x481F:
1538 		if (param.asInteger() == 0)
1539 			GotoState(&Klaymen::stWonderAboutHalf);
1540 		else if (param.asInteger() == 1)
1541 			GotoState(&Klaymen::stWonderAboutAfter);
1542 		else if (param.asInteger() == 3)
1543 			GotoState(&Klaymen::stTurnToUseHalf);
1544 		else if (param.asInteger() == 4)
1545 			GotoState(&Klaymen::stTurnAwayFromUse);
1546 		else if (param.asInteger() == 5)
1547 			GotoState(&Klaymen::stTurnToUseExt);
1548 		else
1549 			GotoState(&Klaymen::stWonderAbout);
1550 		break;
1551 	case 0x4820:
1552 		sendMessage(_parentScene, 0x2000, 0);
1553 		GotoState(&Klaymen::stContinueClimbLadderUp);
1554 		break;
1555 	case 0x4821:
1556 		sendMessage(_parentScene, 0x2000, 0);
1557 		_destY = param.asInteger();
1558 		GotoState(&Klaymen::stStartClimbLadderDown);
1559 		break;
1560 	case 0x4822:
1561 		sendMessage(_parentScene, 0x2000, 0);
1562 		_destY = param.asInteger();
1563 		GotoState(&Klaymen::stStartClimbLadderUp);
1564 		break;
1565 	case 0x4823:
1566 		sendMessage(_parentScene, 0x2001, 0);
1567 		GotoState(&Klaymen::stClimbLadderHalf);
1568 		break;
1569 	case 0x4824:
1570 		sendMessage(_parentScene, 0x2000, 0);
1571 		_destY = _dataResource.getPoint(param.asInteger()).y;
1572 		GotoState(&Klaymen::stStartClimbLadderDown);
1573 		break;
1574 	case 0x4825:
1575 		sendMessage(_parentScene, 0x2000, 0);
1576 		_destY = _dataResource.getPoint(param.asInteger()).y;
1577 		GotoState(&Klaymen::stStartClimbLadderUp);
1578 		break;
1579 	case 0x482D:
1580 		setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
1581 		gotoNextStateExt();
1582 		break;
1583 	case 0x4837:
1584 		stopWalking();
1585 		break;
1586 	default:
1587 		break;
1588 	}
1589 	return 0;
1590 }
1591 
KmScene2812(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)1592 KmScene2812::KmScene2812(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
1593 	: Klaymen(vm, parentScene, x, y) {
1594 
1595 	// Empty
1596 }
1597 
xHandleMessage(int messageNum,const MessageParam & param)1598 uint32 KmScene2812::xHandleMessage(int messageNum, const MessageParam &param) {
1599 	switch (messageNum) {
1600 	case 0x4001:
1601 	case 0x4800:
1602 		startWalkToX(param.asPoint().x, false);
1603 		break;
1604 	case NM_KLAYMEN_STAND_IDLE:
1605 		GotoState(&Klaymen::stTryStandIdle);
1606 		break;
1607 	case 0x4805:
1608 		_destY = param.asInteger();
1609 		GotoState(&Klaymen::stJumpToGrabFall);
1610 		break;
1611 	case NM_KLAYMEN_PICKUP:
1612 		if (param.asInteger() == 2)
1613 			GotoState(&Klaymen::stPickUpNeedle);
1614 		else if (param.asInteger() == 1)
1615 			GotoState(&Klaymen::stPickUpTube);
1616 		else
1617 			GotoState(&Klaymen::stPickUpGeneric);
1618 		break;
1619 	case 0x4817:
1620 		setDoDeltaX(param.asInteger());
1621 		gotoNextStateExt();
1622 		break;
1623 	case NM_KLAYMEN_INSERT_DISK:
1624 		GotoState(&Klaymen::stInsertDisk);
1625 		break;
1626 	case 0x481B:
1627 		if (param.asPoint().y != 0)
1628 			startWalkToXDistance(param.asPoint().y, param.asPoint().x);
1629 		else
1630 			startWalkToAttachedSpriteXDistance(param.asPoint().x);
1631 		break;
1632 	case NM_KLAYMEN_TURN_TO_USE:
1633 		GotoState(&Klaymen::stTurnToUse);
1634 		break;
1635 	case NM_KLAYMEN_RETURN_FROM_USE:
1636 		GotoState(&Klaymen::stReturnFromUse);
1637 		break;
1638 	case 0x4820:
1639 		sendMessage(_parentScene, 0x2001, 0);
1640 		GotoState(&Klaymen::stContinueClimbLadderUp);
1641 		break;
1642 	case 0x4821:
1643 		sendMessage(_parentScene, 0x2001, 0);
1644 		_destY = param.asInteger();
1645 		GotoState(&Klaymen::stStartClimbLadderDown);
1646 		break;
1647 	case 0x4822:
1648 		sendMessage(_parentScene, 0x2001, 0);
1649 		_destY = param.asInteger();
1650 		GotoState(&Klaymen::stStartClimbLadderUp);
1651 		break;
1652 	case 0x4823:
1653 		sendMessage(_parentScene, NM_POSITION_CHANGE, 0);
1654 		GotoState(&Klaymen::stClimbLadderHalf);
1655 		break;
1656 	case 0x482D:
1657 		setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
1658 		gotoNextStateExt();
1659 		break;
1660 	case 0x482E:
1661 		if (param.asInteger() == 1)
1662 			GotoState(&Klaymen::stWalkToFrontNoStep);
1663 		else
1664 			GotoState(&Klaymen::stWalkToFront);
1665 		break;
1666 	case 0x482F:
1667 		if (param.asInteger() == 1)
1668 			GotoState(&Klaymen::stTurnToFront);
1669 		else
1670 			GotoState(&Klaymen::stTurnToBack);
1671 		break;
1672 	case 0x483F:
1673 		startSpecialWalkRight(param.asInteger());
1674 		break;
1675 	case 0x4840:
1676 		startSpecialWalkLeft(param.asInteger());
1677 		break;
1678 	default:
1679 		break;
1680 	}
1681 	return 0;
1682 }
1683 
1684 } // End of namespace Neverhood
1685