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/module1400_sprites.h"
24 #include "neverhood/modules/module1400.h"
25 
26 namespace Neverhood {
27 
AsScene1401Pipe(NeverhoodEngine * vm)28 AsScene1401Pipe::AsScene1401Pipe(NeverhoodEngine *vm)
29 	: AnimatedSprite(vm, 1100), _countdown1(0), _countdown2(0) {
30 
31 	createSurface(900, 152, 147);
32 	_x = 454;
33 	_y = 217;
34 	startAnimation(0x4C210500, 0, -1);
35 	SetUpdateHandler(&AsScene1401Pipe::update);
36 	SetMessageHandler(&AsScene1401Pipe::handleMessage);
37 }
38 
~AsScene1401Pipe()39 AsScene1401Pipe::~AsScene1401Pipe() {
40 	_vm->_soundMan->deleteSoundGroup(0x01104C08);
41 }
42 
update()43 void AsScene1401Pipe::update() {
44 	AnimatedSprite::update();
45 	if (_countdown1 != 0 && (--_countdown1 == 0))
46 		stDoneSucking();
47 	if (_countdown2 != 0 && (--_countdown2 == 0)) {
48 		_vm->_soundMan->addSound(0x01104C08, 0x4A116437);
49 		_vm->_soundMan->playSoundLooping(0x4A116437);
50 	}
51 }
52 
upSuckInProjector()53 void AsScene1401Pipe::upSuckInProjector() {
54 	AnimatedSprite::update();
55 	if (_countdown1 != 0)
56 		_countdown1--;
57 }
58 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)59 uint32 AsScene1401Pipe::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
60 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
61 	switch (messageNum) {
62 	case NM_ANIMATION_START:
63 		if (param.asInteger() == 0x0A8A1490)
64 			playSound(1, 0x6AB6666F);
65 		break;
66 	case NM_ANIMATION_UPDATE:
67 		_countdown1 = 70;
68 		_countdown2 = 8;
69 		stStartSucking();
70 		break;
71 	case 0x483A:
72 		stSuckInProjector();
73 		break;
74 	default:
75 		break;
76 	}
77 	return messageResult;
78 }
79 
hmSuckInProjector(int messageNum,const MessageParam & param,Entity * sender)80 uint32 AsScene1401Pipe::hmSuckInProjector(int messageNum, const MessageParam &param, Entity *sender) {
81 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
82 	switch (messageNum) {
83 	case NM_ANIMATION_STOP:
84 		if (_countdown1 != 0)
85 			stStartSucking();
86 		else
87 			stDoneSucking();
88 		SetMessageHandler(&AsScene1401Pipe::handleMessage);
89 		SetUpdateHandler(&AsScene1401Pipe::update);
90 		break;
91 	default:
92 		break;
93 	}
94 	return messageResult;
95 }
96 
stStartSucking()97 void AsScene1401Pipe::stStartSucking() {
98 	startAnimation(0x4C240100, 0, -1);
99 	playSound(0, 0x4A30063F);
100 }
101 
stDoneSucking()102 void AsScene1401Pipe::stDoneSucking() {
103 	_vm->_soundMan->deleteSound(0x4A116437);
104 	playSound(0, 0x4A120435);
105 	startAnimation(0x4C210500, 0, -1);
106 }
107 
stSuckInProjector()108 void AsScene1401Pipe::stSuckInProjector() {
109 	startAnimation(0x6C210810, 0, -1);
110 	SetUpdateHandler(&AsScene1401Pipe::upSuckInProjector);
111 	SetMessageHandler(&AsScene1401Pipe::hmSuckInProjector);
112 }
113 
AsScene1401Mouse(NeverhoodEngine * vm)114 AsScene1401Mouse::AsScene1401Mouse(NeverhoodEngine *vm)
115 	: AnimatedSprite(vm, 1100) {
116 
117 	createSurface(100, 71, 41);
118 	_x = 478;
119 	_y = 433;
120 	startAnimation(0xA282C472, 0, -1);
121 	SetUpdateHandler(&AnimatedSprite::update);
122 	SetMessageHandler(&AsScene1401Mouse::handleMessage);
123 }
124 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)125 uint32 AsScene1401Mouse::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
126 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
127 	switch (messageNum) {
128 	case NM_ANIMATION_START:
129 		if (param.asInteger() == 0x66382026)
130 			playSound(0, 0x0CD84468);
131 		else if (param.asInteger() == 0x6E28061C)
132 			playSound(0, 0x78C8402C);
133 		else if (param.asInteger() == 0x462F0410)
134 			playSound(0, 0x60984E28);
135 		break;
136 	case 0x4839:
137 		stSuckedIn();
138 		break;
139 	default:
140 		break;
141 	}
142 	return messageResult;
143 }
144 
suSuckedIn()145 void AsScene1401Mouse::suSuckedIn() {
146 	AnimatedSprite::updateDeltaXY();
147 	if (_collisionBounds.y1 <= 150) {
148 		playSound(0, 0x0E32247F);
149 		stopAnimation();
150 		setVisible(false);
151 		SetMessageHandler(NULL);
152 		SetSpriteUpdate(NULL);
153 	}
154 }
155 
stSuckedIn()156 void AsScene1401Mouse::stSuckedIn() {
157 	startAnimation(0x34880040, 0, -1);
158 	SetSpriteUpdate(&AsScene1401Mouse::suSuckedIn);
159 }
160 
AsScene1401Cheese(NeverhoodEngine * vm)161 AsScene1401Cheese::AsScene1401Cheese(NeverhoodEngine *vm)
162 	: AnimatedSprite(vm, 1100) {
163 
164 	createSurface(200, 152, 147);
165 	_x = 427;
166 	_y = 433;
167 	startAnimation(0x461A1490, 0, -1);
168 	SetUpdateHandler(&AnimatedSprite::update);
169 	SetMessageHandler(&AsScene1401Cheese::handleMessage);
170 }
171 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)172 uint32 AsScene1401Cheese::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
173 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
174 	switch (messageNum) {
175 	case 0x4839:
176 		stSuckedIn();
177 		break;
178 	default:
179 		break;
180 	}
181 	return messageResult;
182 }
183 
suSuckedIn()184 void AsScene1401Cheese::suSuckedIn() {
185 	AnimatedSprite::updateDeltaXY();
186 	if (_collisionBounds.y1 <= 150) {
187 		playSound(0, 0x18020439);
188 		stopAnimation();
189 		setVisible(false);
190 		SetMessageHandler(NULL);
191 		SetSpriteUpdate(NULL);
192 	}
193 }
194 
stSuckedIn()195 void AsScene1401Cheese::stSuckedIn() {
196 	startAnimation(0x103B8020, 0, -1);
197 	SetSpriteUpdate(&AsScene1401Cheese::suSuckedIn);
198 }
199 
AsScene1401BackDoor(NeverhoodEngine * vm,Sprite * klaymen,bool isOpen)200 AsScene1401BackDoor::AsScene1401BackDoor(NeverhoodEngine *vm, Sprite *klaymen, bool isOpen)
201 	: AnimatedSprite(vm, 1100), _klaymen(klaymen), _countdown(0), _isOpen(isOpen) {
202 
203 	_x = 320;
204 	_y = 240;
205 	createSurface1(0x04551900, 100);
206 	if (isOpen) {
207 		startAnimation(0x04551900, -1, -1);
208 		_countdown = 48;
209 	} else {
210 		stopAnimation();
211 		setVisible(false);
212 	}
213 	_newStickFrameIndex = STICK_LAST_FRAME;
214 	SetUpdateHandler(&AsScene1401BackDoor::update);
215 	SetMessageHandler(&AsScene1401BackDoor::handleMessage);
216 }
217 
update()218 void AsScene1401BackDoor::update() {
219 	if (_countdown != 0 && (--_countdown == 0))
220 		stCloseDoor();
221 	AnimatedSprite::update();
222 }
223 
224 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)225 uint32 AsScene1401BackDoor::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
226 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
227 	switch (messageNum) {
228 	case 0x2001:
229 		if (_isOpen)
230 			_countdown = 168;
231 		messageResult = _isOpen ? 1 : 0;
232 		break;
233 	case NM_ANIMATION_STOP:
234 		gotoNextState();
235 		break;
236 	case NM_KLAYMEN_OPEN_DOOR:
237 		_countdown = 168;
238 		if (!_isOpen)
239 			stOpenDoor();
240 		break;
241 	default:
242 		break;
243 	}
244 	return messageResult;
245 }
246 
stOpenDoor()247 void AsScene1401BackDoor::stOpenDoor() {
248 	_isOpen = true;
249 	setVisible(true);
250 	startAnimation(0x04551900, 0, -1);
251 	_newStickFrameIndex = STICK_LAST_FRAME;
252 	playSound(0, calcHash("fxDoorOpen24"));
253 }
254 
stCloseDoor()255 void AsScene1401BackDoor::stCloseDoor() {
256 	_isOpen = false;
257 	setVisible(true);
258 	startAnimation(0x04551900, -1, -1);
259 	playSound(0, calcHash("fxDoorClose24"));
260 	_playBackwards = true;
261 	NextState(&AsScene1401BackDoor::stCloseDoorDone);
262 }
263 
stCloseDoorDone()264 void AsScene1401BackDoor::stCloseDoorDone() {
265 	stopAnimation();
266 	setVisible(false);
267 }
268 
269 static const AsCommonProjectorItem kAsCommonProjectorItems[] = {
270 	{{154, 453}, 4,  2,  0, 0, 1},
271 	{{104, 391}, 4, -1, -1, 1, 1},
272 	{{ 22, 447}, 6, -1, -1, 1, 1},
273 	{{112, 406}, 2, -1, -1, 1, 0},
274 	{{262, 433}, 1,  1,  0, 0, 0}
275 };
276 
AsCommonProjector(NeverhoodEngine * vm,Scene * parentScene,Sprite * klaymen,Sprite * asPipe)277 AsCommonProjector::AsCommonProjector(NeverhoodEngine *vm, Scene *parentScene, Sprite *klaymen, Sprite *asPipe)
278 	: AnimatedSprite(vm, 1100), _parentScene(parentScene), _klaymen(klaymen), _asPipe(asPipe) {
279 
280 	_asProjectorItem = &kAsCommonProjectorItems[getGlobalVar(V_PROJECTOR_LOCATION)];
281 	createSurface(990, 101, 182);
282 	startAnimation(0x10E3042B, 0, -1);
283 	SetUpdateHandler(&AnimatedSprite::update);
284 	SetMessageHandler(&AsCommonProjector::handleMessage);
285 	_x = getGlobalVar(V_PROJECTOR_SLOT) * 108 + _asProjectorItem->point.x;
286 	_lockedInSlot = true;
287 	moveProjector();
288 	setDoDeltaX(1);
289 	if ((int8)getGlobalVar(V_PROJECTOR_SLOT) == _asProjectorItem->lockSlotIndex)
290 		stStayLockedInSlot();
291 	loadSound(2, 0xC8C2507C);
292 }
293 
~AsCommonProjector()294 AsCommonProjector::~AsCommonProjector() {
295 	_vm->_soundMan->deleteSoundGroup(0x05331081);
296 }
297 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)298 uint32 AsCommonProjector::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
299 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
300 	switch (messageNum) {
301 	case 0x1011:
302 		sendMessage(_parentScene, 0x4826, 0);
303 		messageResult = 1;
304 		break;
305 	case NM_KLAYMEN_RAISE_LEVER:
306 		setGlobalVar(V_PROJECTOR_SLOT, (_x - _asProjectorItem->point.x) / 108);
307 		if ((int8)getGlobalVar(V_PROJECTOR_SLOT) == _asProjectorItem->lockSlotIndex)
308 			stStartLockedInSlot();
309 		else
310 			stIdle();
311 		break;
312 	case 0x480B:
313 		if (param.asInteger() != 1) {
314 			if ((int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount)
315 				incGlobalVar(V_PROJECTOR_SLOT, 1);
316 		} else if (getGlobalVar(V_PROJECTOR_SLOT) > 0)
317 			incGlobalVar(V_PROJECTOR_SLOT, -1);
318 		stMoving();
319 		break;
320 	case 0x480C:
321 		// Check if the projector can be moved
322 		if (param.asInteger() != 1)
323 			messageResult = (int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount ? 1 : 0;
324 		else
325 			messageResult = getGlobalVar(V_PROJECTOR_SLOT) > 0 ? 1 : 0;
326 		break;
327 	case NM_MOVE_TO_BACK:
328 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 990);
329 		break;
330 	case NM_MOVE_TO_FRONT:
331 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 1010);
332 		break;
333 	case 0x4839:
334 		stStartSuckedIn();
335 		break;
336 	default:
337 		break;
338 	}
339 	return messageResult;
340 }
341 
hmLockedInSlot(int messageNum,const MessageParam & param,Entity * sender)342 uint32 AsCommonProjector::hmLockedInSlot(int messageNum, const MessageParam &param, Entity *sender) {
343 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
344 	switch (messageNum) {
345 	case 0x1011:
346 		if (param.asPoint().x - _x >= 17 && param.asPoint().x - _x <= 56 &&
347 			param.asPoint().y - _y >= -120 && param.asPoint().y - _y <= -82) {
348 			sendMessage(_parentScene, 0x4826, 1);
349 		} else
350 			sendMessage(_parentScene, 0x4826, 0);
351 		messageResult = 1;
352 		break;
353 	case NM_KLAYMEN_RAISE_LEVER:
354 		sendMessage(_parentScene, NM_KLAYMEN_RAISE_LEVER, 0);
355 		stStopProjecting();
356 		break;
357 	case 0x480B:
358 		if (param.asInteger() != 1) {
359 			if ((int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount)
360 				incGlobalVar(V_PROJECTOR_SLOT, 1);
361 		} else if (getGlobalVar(V_PROJECTOR_SLOT) > 0)
362 			incGlobalVar(V_PROJECTOR_SLOT, -1);
363 		stTurnToFront();
364 		break;
365 	case 0x480C:
366 		// Check if the projector can be moved
367 		if (param.asInteger() != 1)
368 			messageResult = (int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount ? 1 : 0;
369 		else
370 			messageResult = getGlobalVar(V_PROJECTOR_SLOT) > 0 ? 1 : 0;
371 		break;
372 	case NM_KLAYMEN_LOWER_LEVER:
373 		stStartProjecting();
374 		break;
375 	case NM_MOVE_TO_BACK:
376 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 990);
377 		break;
378 	case NM_MOVE_TO_FRONT:
379 		sendMessage(_parentScene, NM_PRIORITY_CHANGE, 1010);
380 		break;
381 	default:
382 		break;
383 	}
384 	return messageResult;
385 }
386 
hmAnimation(int messageNum,const MessageParam & param,Entity * sender)387 uint32 AsCommonProjector::hmAnimation(int messageNum, const MessageParam &param, Entity *sender) {
388 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
389 	switch (messageNum) {
390 	case NM_ANIMATION_STOP:
391 		gotoNextState();
392 		break;
393 	default:
394 		break;
395 	}
396 	return messageResult;
397 }
398 
suMoving()399 void AsCommonProjector::suMoving() {
400 	if (_x <= _klaymen->getX())
401 		_x = _klaymen->getX() - 100;
402 	else
403 		_x = _klaymen->getX() + 100;
404 	moveProjector();
405 	if (_beforeMoveX == _x) {
406 		if (getGlobalVar(V_PROJECTOR_SLOT) == 0 && _asProjectorItem->leftBorderLeaves != 0) {
407 			sendMessage(_parentScene, NM_SCENE_LEAVE, 0);
408 			incGlobalVar(V_PROJECTOR_LOCATION, -1);
409 			setGlobalVar(V_PROJECTOR_SLOT, kAsCommonProjectorItems[getGlobalVar(V_PROJECTOR_LOCATION)].maxSlotCount);
410 		} else if ((int8)getGlobalVar(V_PROJECTOR_SLOT) == _asProjectorItem->maxSlotCount && _asProjectorItem->rightBorderLeaves != 0) {
411 			sendMessage(_parentScene, NM_SCENE_LEAVE, 1);
412 			incGlobalVar(V_PROJECTOR_LOCATION, +1);
413 			setGlobalVar(V_PROJECTOR_SLOT, 0);
414 		}
415 	}
416 	Sprite::updateBounds();
417 }
418 
moveProjector()419 void AsCommonProjector::moveProjector() {
420 
421 	bool nowLockedInSlot = false;
422 
423 	_y = _asProjectorItem->point.y;
424 
425 	if (_asProjectorItem->index1 != -1) {
426 		int16 elX = _asProjectorItem->index1 * 108 + _asProjectorItem->point.x;
427 		if (elX - 20 < _x && elX + 20 > _x) {
428 			nowLockedInSlot = true;
429 			_y = _asProjectorItem->point.y + 10;
430 		}
431 	}
432 
433 	if (_asProjectorItem->lockSlotIndex != -1) {
434 		int16 elX = _asProjectorItem->lockSlotIndex * 108 + _asProjectorItem->point.x;
435 		if (elX - 20 < _x && elX + 20 > _x) {
436 			nowLockedInSlot = true;
437 			_y = _asProjectorItem->point.y + 10;
438 		}
439 	}
440 
441 	if (_lockedInSlot && !nowLockedInSlot)
442 		_lockedInSlot = false;
443 	else if (!_lockedInSlot && nowLockedInSlot) {
444 		playSound(1, 0x5440E474);
445 		_lockedInSlot = true;
446 	}
447 
448 }
449 
stSuckedIn()450 void AsCommonProjector::stSuckedIn() {
451 	AnimatedSprite::updateDeltaXY();
452 	if (_collisionBounds.y1 <= 150) {
453 		sendMessage(_asPipe, 0x483A, 0);
454 		stopAnimation();
455 		setVisible(false);
456 		SetMessageHandler(&Sprite::handleMessage);
457 		SetSpriteUpdate(NULL);
458 	}
459 }
460 
stIdle()461 void AsCommonProjector::stIdle() {
462 	startAnimation(0x10E3042B, 0, -1);
463 	SetMessageHandler(&AsCommonProjector::handleMessage);
464 	SetSpriteUpdate(NULL);
465 }
466 
stMoving()467 void AsCommonProjector::stMoving() {
468 	_beforeMoveX = getGlobalVar(V_PROJECTOR_SLOT) * 108 + _asProjectorItem->point.x;
469 	startAnimation(0x14A10137, 0, -1);
470 	playSound(1, 0xEC008474);
471 	SetMessageHandler(&AsCommonProjector::handleMessage);
472 	SetSpriteUpdate(&AsCommonProjector::suMoving);
473 }
474 
stStartLockedInSlot()475 void AsCommonProjector::stStartLockedInSlot() {
476 	startAnimation(0x80C32213, 0, -1);
477 	SetMessageHandler(&AsCommonProjector::hmAnimation);
478 	SetSpriteUpdate(NULL);
479 	NextState(&AsCommonProjector::stStayLockedInSlot);
480 }
481 
stStayLockedInSlot()482 void AsCommonProjector::stStayLockedInSlot() {
483 	startAnimation(0xD23B207F, 0, -1);
484 	SetMessageHandler(&AsCommonProjector::hmLockedInSlot);
485 	SetSpriteUpdate(NULL);
486 }
487 
stStartProjecting()488 void AsCommonProjector::stStartProjecting() {
489 	startAnimation(0x50A80517, 0, -1);
490 	setGlobalVar(V_PROJECTOR_ACTIVE, 1);
491 	playSound(0, 0xCC4A8456);
492 	_vm->_soundMan->addSound(0x05331081, 0xCE428854);
493 	_vm->_soundMan->playSoundLooping(0xCE428854);
494 	SetMessageHandler(&AsCommonProjector::hmAnimation);
495 	SetSpriteUpdate(NULL);
496 	NextState(&AsCommonProjector::stLockedInSlot);
497 }
498 
stLockedInSlot()499 void AsCommonProjector::stLockedInSlot() {
500 	sendMessage(_parentScene, NM_KLAYMEN_LOWER_LEVER, 0);
501 	startAnimation(0xD833207F, 0, -1);
502 	SetMessageHandler(&AsCommonProjector::hmLockedInSlot);
503 	SetSpriteUpdate(NULL);
504 }
505 
stStopProjecting()506 void AsCommonProjector::stStopProjecting() {
507 	startAnimation(0x50A94417, 0, -1);
508 	setGlobalVar(V_PROJECTOR_ACTIVE, 0);
509 	playSound(0, 0xCC4A8456);
510 	_vm->_soundMan->deleteSound(0xCE428854);
511 	SetMessageHandler(&AsCommonProjector::hmAnimation);
512 	SetSpriteUpdate(NULL);
513 	NextState(&AsCommonProjector::stStayLockedInSlot);
514 }
515 
stTurnToFront()516 void AsCommonProjector::stTurnToFront() {
517 	_beforeMoveX = getGlobalVar(V_PROJECTOR_SLOT) * 108 + _asProjectorItem->point.x;
518 	startAnimation(0x22CB4A33, 0, -1);
519 	SetMessageHandler(&AsCommonProjector::hmAnimation);
520 	SetSpriteUpdate(&AsCommonProjector::suMoving);
521 	NextState(&AsCommonProjector::stMoving);
522 }
523 
stStartSuckedIn()524 void AsCommonProjector::stStartSuckedIn() {
525 	setGlobalVar(V_PROJECTOR_LOCATION, 4);
526 	setGlobalVar(V_PROJECTOR_SLOT, 0);
527 	startAnimation(0x708D4712, 0, -1);
528 	playSound(2);
529 	SetMessageHandler(&Sprite::handleMessage);
530 	SetSpriteUpdate(&AsCommonProjector::stSuckedIn);
531 }
532 
SsScene1402BridgePart(NeverhoodEngine * vm,uint32 fileHash,int surfacePriority)533 SsScene1402BridgePart::SsScene1402BridgePart(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority)
534 	: StaticSprite(vm, fileHash, surfacePriority) {
535 
536 	SetFilterY(&Sprite::defFilterY);
537 	SetUpdateHandler(&StaticSprite::updatePosition);
538 }
539 
AsScene1402PuzzleBox(NeverhoodEngine * vm,Scene * parentScene,int status)540 AsScene1402PuzzleBox::AsScene1402PuzzleBox(NeverhoodEngine *vm, Scene *parentScene, int status)
541 	: AnimatedSprite(vm, 1100), _parentScene(parentScene) {
542 
543 	createSurface(900, 347, 230);
544 
545 	SetFilterY(&Sprite::defFilterY);
546 	SetUpdateHandler(&AnimatedSprite::update);
547 	SetMessageHandler(&AsScene1402PuzzleBox::handleMessage);
548 	_x = 279;
549 	_y = 270;
550 	if (status == 2) {
551 		// Puzzle box after the puzzle was solved
552 		startAnimation(0x20060259, 0, -1);
553 		playSound(0, 0x419014AC);
554 		loadSound(1, 0x61901C29);
555 		NextState(&AsScene1402PuzzleBox::stMoveDownSolvedDone);
556 	} else if (status == 1) {
557 		// Puzzle box appears
558 		startAnimation(0x210A0213, 0, -1);
559 		playSound(0, 0x41809C6C);
560 		NextState(&AsScene1402PuzzleBox::stMoveUpDone);
561 	} else {
562 		// Puzzle box is here
563 		startAnimation(0x20060259, -1, -1);
564 		loadSound(1, 0x61901C29);
565 		_newStickFrameIndex = STICK_LAST_FRAME;
566 	}
567 }
568 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)569 uint32 AsScene1402PuzzleBox::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
570 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
571 	switch (messageNum) {
572 	case NM_POSITION_CHANGE:
573 		playSound(1);
574 		startAnimation(0x20060259, -1, -1);
575 		_playBackwards = true;
576 		NextState(&AsScene1402PuzzleBox::stMoveDownDone);
577 		break;
578 	case NM_ANIMATION_STOP:
579 		gotoNextState();
580 		break;
581 	default:
582 		break;
583 	}
584 	return messageResult;
585 }
586 
stMoveUpDone()587 void AsScene1402PuzzleBox::stMoveUpDone() {
588 	sendMessage(_parentScene, 0x2000, 0);
589 	stopAnimation();
590 	setVisible(false);
591 }
592 
stMoveDownDone()593 void AsScene1402PuzzleBox::stMoveDownDone() {
594 	sendMessage(_parentScene, 0x2001, 0);
595 	stopAnimation();
596 	setVisible(false);
597 }
598 
stMoveDownSolvedDone()599 void AsScene1402PuzzleBox::stMoveDownSolvedDone() {
600 	sendMessage(_parentScene, 0x2003, 0);
601 	stopAnimation();
602 }
603 
604 static const int16 kScene1407MouseFloorY[] = {
605 	106, 150, 191, 230, 267, 308, 350, 395
606 };
607 
608 static const struct {
609 	int16 x;
610 	int16 floorIndex;
611 	int16 sectionIndex;
612 	int16 nextHoleIndex;
613 } kScene1407MouseHoles[] = {
614 	{125, 0,  0,  7},
615 	{452, 7, 21,  0},
616 	{337, 4, 11,  4},
617 	{286, 6, 17,  6},
618 	{348, 6, 17, 39},
619 	{536, 6, 18, 42},
620 	{111, 1,  3, 18},
621 	{203, 1,  3, 38},
622 	{270, 1,  3,  9},
623 	{197, 5, 14,  3},
624 	{252, 5, 14, 35},
625 	{297, 5, 14,  7},
626 	{359, 5, 14,  8},
627 	{422, 4, 12, 26},
628 	{467, 4, 12,  2},
629 	{539, 4, 12, 40},
630 	{111, 5, 13, 17},
631 	{211, 0,  1, 20},
632 	{258, 0,  1, 11},
633 	{322, 0,  1, 16},
634 	{ 99, 6, 16, 31},
635 	{142, 6, 16, 27},
636 	{194, 6, 16, 12},
637 	{205, 2,  6, 45},
638 	{264, 2,  6, 10},
639 	{ 98, 4, 10,  2},
640 	{152, 4, 10, 37},
641 	{199, 4, 10, 13},
642 	{258, 4, 10, 16},
643 	{100, 7, 19, 43},
644 	{168, 7, 19, 23},
645 	{123, 3,  8, 14},
646 	{181, 3,  8, 39},
647 	{230, 3,  8, 28},
648 	{292, 3,  8, 22},
649 	{358, 3,  8, 36},
650 	{505, 3,  9, 44},
651 	{400, 2,  7, 34},
652 	{454, 2,  7, 32},
653 	{532, 2,  7, 46},
654 	{484, 5, 15, 25},
655 	{529, 5, 15, 30},
656 	{251, 7, 20, 48},
657 	{303, 7, 20, 21},
658 	{360, 7, 20, 33},
659 	{503, 0,  2,  5},
660 	{459, 1,  4, 19},
661 	{530, 1,  4, 42},
662 	{111, 2,  5, 47},
663 	{442, 6, 18,  1}
664 };
665 
666 static const struct {
667 	int16 x1, x2;
668 	int16 goodHoleIndex;
669 } kScene1407MouseSections[] = {
670 	{100, 149,  0},
671 	{182, 351, 17},
672 	{430, 524, 45},
673 	{ 89, 293,  7},
674 	{407, 555, 47},
675 	{ 89, 132, 48},
676 	{178, 303, 23},
677 	{367, 551, 38},
678 	{105, 398, 31},
679 	{480, 537, 36},
680 	{ 84, 275, 27},
681 	{318, 359,  2},
682 	{402, 560, 15},
683 	{ 91, 132, 16},
684 	{179, 400, 10},
685 	{461, 552, 41},
686 	{ 86, 218, 21},
687 	{267, 376,  4},
688 	{420, 560, 49},
689 	{ 77, 188, 30},
690 	{237, 394, 44},
691 	{438, 515,  5}
692 };
693 
AsScene1407Mouse(NeverhoodEngine * vm,Scene * parentScene)694 AsScene1407Mouse::AsScene1407Mouse(NeverhoodEngine *vm, Scene *parentScene)
695 	: AnimatedSprite(vm, 1100), _parentScene(parentScene), _currSectionIndex(0) {
696 
697 	createSurface(100, 117, 45);
698 	_x = 108;
699 	_y = 106;
700 	stIdleLookAtGoodHole();
701 	SetUpdateHandler(&AnimatedSprite::update);
702 }
703 
suWalkTo()704 void AsScene1407Mouse::suWalkTo() {
705 	int16 xdelta = _walkDestX - _x;
706 	if (xdelta > _deltaX)
707 		xdelta = _deltaX;
708 	else if (xdelta < -_deltaX)
709 		xdelta = -_deltaX;
710 	_deltaX = 0;
711 	if (_walkDestX == _x)
712 		sendMessage(this, NM_SCENE_LEAVE, 0);
713 	else {
714 		_x += xdelta;
715 		updateBounds();
716 	}
717 }
718 
upGoThroughHole()719 void AsScene1407Mouse::upGoThroughHole() {
720 	if (_countdown != 0 && (--_countdown == 0)) {
721 		SetUpdateHandler(&AnimatedSprite::update);
722 		gotoNextState();
723 	}
724 	AnimatedSprite::update();
725 }
726 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)727 uint32 AsScene1407Mouse::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
728 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
729 	switch (messageNum) {
730 	case NM_MOUSE_CLICK:
731 		{
732 			int16 mouseX = param.asPoint().x;
733 			int16 mouseY = param.asPoint().y;
734 			int holeIndex;
735 			for (holeIndex = 0; holeIndex < 50; holeIndex++) {
736 				int16 holeX = kScene1407MouseHoles[holeIndex].x;
737 				int16 holeY = kScene1407MouseFloorY[kScene1407MouseHoles[holeIndex].floorIndex];
738 				if (mouseX >= holeX - 14 && mouseX <= holeX + 14 && mouseY >= holeY - 36 && mouseY <= holeY)
739 					break;
740 			}
741 			if (holeIndex < 50 && kScene1407MouseHoles[holeIndex].sectionIndex == _currSectionIndex) {
742 				_nextHoleIndex = kScene1407MouseHoles[holeIndex].nextHoleIndex;
743 				_walkDestX = kScene1407MouseHoles[holeIndex].x;
744 				stWalkToHole();
745 			} else {
746 				if (mouseX < kScene1407MouseSections[_currSectionIndex].x1)
747 					_walkDestX = kScene1407MouseSections[_currSectionIndex].x1;
748 				else if (mouseX > kScene1407MouseSections[_currSectionIndex].x2)
749 					_walkDestX = kScene1407MouseSections[_currSectionIndex].x2;
750 				else
751 					_walkDestX = mouseX;
752 				stWalkToDest();
753 			}
754 		}
755 		break;
756 	case NM_SCENE_LEAVE:
757 		gotoNextState();
758 		break;
759 	case 0x2001:
760 		{
761 			// Reset the position
762 			// Find the nearest hole and go through it, and exit at the first hole
763 			int16 distance = 640;
764 			int matchIndex = 50;
765 			for (int index = 0; index < 50; index++)
766 				if (kScene1407MouseHoles[index].sectionIndex == _currSectionIndex &&
767 					ABS(kScene1407MouseHoles[index].x - _x) < distance) {
768 					matchIndex = index;
769 					distance = ABS(kScene1407MouseHoles[index].x - _x);
770 				}
771 			if (matchIndex < 50) {
772 				_nextHoleIndex = 0;
773 				_walkDestX = kScene1407MouseHoles[matchIndex].x;
774 				stWalkToHole();
775 			}
776 		}
777 		break;
778 	default:
779 		break;
780 	}
781 	return messageResult;
782 }
783 
stIdleLookAtGoodHole()784 void AsScene1407Mouse::stIdleLookAtGoodHole() {
785 	setDoDeltaX(kScene1407MouseHoles[kScene1407MouseSections[_currSectionIndex].goodHoleIndex].x < _x ? 1 : 0);
786 	startAnimation(0x72215194, 0, -1);
787 	SetMessageHandler(&AsScene1407Mouse::handleMessage);
788 	SetSpriteUpdate(NULL);
789 }
790 
stWalkToDest()791 void AsScene1407Mouse::stWalkToDest() {
792 	if (_walkDestX != _x) {
793 		setDoDeltaX(_walkDestX < _x ? 1 : 0);
794 		startAnimation(0x22291510, 0, -1);
795 		SetMessageHandler(&AsScene1407Mouse::handleMessage);
796 		SetSpriteUpdate(&AsScene1407Mouse::suWalkTo);
797 		NextState(&AsScene1407Mouse::stIdleLookAtGoodHole);
798 	}
799 }
800 
stWalkToHole()801 void AsScene1407Mouse::stWalkToHole() {
802 	setDoDeltaX(_walkDestX < _x ? 1 : 0);
803 	startAnimation(0x22291510, 0, -1);
804 	SetMessageHandler(&AsScene1407Mouse::handleMessage);
805 	SetSpriteUpdate(&AsScene1407Mouse::suWalkTo);
806 	NextState(&AsScene1407Mouse::stGoThroughHole);
807 }
808 
stGoThroughHole()809 void AsScene1407Mouse::stGoThroughHole() {
810 	startAnimation(0x72215194, 0, -1);
811 	setVisible(false);
812 	_countdown = 12;
813 	SetUpdateHandler(&AsScene1407Mouse::upGoThroughHole);
814 	SetMessageHandler(NULL);
815 	SetSpriteUpdate(NULL);
816 	NextState(&AsScene1407Mouse::stArriveAtHole);
817 }
818 
stArriveAtHole()819 void AsScene1407Mouse::stArriveAtHole() {
820 	_currSectionIndex = kScene1407MouseHoles[_nextHoleIndex].sectionIndex;
821 	_x = kScene1407MouseHoles[_nextHoleIndex].x;
822 	_y = kScene1407MouseFloorY[kScene1407MouseHoles[_nextHoleIndex].floorIndex];
823 	if (_nextHoleIndex == 1) {
824 		sendMessage(_parentScene, 0x2000, 0);
825 		_walkDestX = 512;
826 		stWalkToDest();
827 		setVisible(true);
828 	} else {
829 		_walkDestX = _x + 14;
830 		stWalkToDest();
831 		setVisible(true);
832 	}
833 }
834 
835 static const NPoint kAsScene1405TileItemPositions[] = {
836 	{100,  80}, {162,  78}, {222,  76}, {292,  76},
837 	{356,  82}, {422,  84}, {488,  86}, {550,  90},
838 	{102, 134}, {164, 132}, {224, 136},	{294, 136},
839 	{360, 136},	{422, 138},	{484, 144},	{548, 146},
840 	{ 98, 196},	{160, 200},	{228, 200},	{294, 202},
841 	{360, 198},	{424, 200},	{482, 202},	{548, 206},
842 	{ 98, 260},	{160, 264},	{226, 260},	{296, 262},
843 	{358, 260},	{424, 262},	{486, 264},	{550, 266},
844 	{ 94, 322},	{160, 316},	{226, 316},	{296, 320},
845 	{358, 322},	{422, 324},	{488, 322},	{550, 322},
846 	{ 98, 380},	{160, 376},	{226, 376},	{294, 378},
847 	{356, 380},	{420, 380},	{490, 378},	{552, 376}
848 };
849 
AsScene1405Tile(NeverhoodEngine * vm,Scene1405 * parentScene,uint32 tileIndex)850 AsScene1405Tile::AsScene1405Tile(NeverhoodEngine *vm, Scene1405 *parentScene, uint32 tileIndex)
851 	: AnimatedSprite(vm, 1100), _parentScene(parentScene), _tileIndex(tileIndex), _countdown(0), _isShowing(false) {
852 
853 	loadSound(0, 0x05308101);
854 	setSoundPan(0, (tileIndex % 8 * 4 + 4) * 25 / 8);
855 	_x = kAsScene1405TileItemPositions[_tileIndex].x;
856 	_y = kAsScene1405TileItemPositions[_tileIndex].y;
857 	createSurface1(0x844B805C, 1100);
858 	setVisible(false);
859 	if (getSubVar(VA_IS_TILE_MATCH, _tileIndex))
860 		_countdown = _vm->_rnd->getRandomNumber(36 - 1) + 1;
861 	startAnimation(0x844B805C, getSubVar(VA_TILE_SYMBOLS, _tileIndex), -1);
862 	_newStickFrameIndex = (int16)getSubVar(VA_TILE_SYMBOLS, _tileIndex);
863 	SetUpdateHandler(&AsScene1405Tile::update);
864 	SetMessageHandler(&AsScene1405Tile::handleMessage);
865 }
866 
update()867 void AsScene1405Tile::update() {
868 	updateAnim();
869 	updatePosition();
870 	if (_countdown != 0 && (--_countdown == 0))
871 		show();
872 }
873 
handleMessage(int messageNum,const MessageParam & param,Entity * sender)874 uint32 AsScene1405Tile::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
875 	uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
876 	switch (messageNum) {
877 	case 0x1011:
878 		if (getSubVar(VA_IS_TILE_MATCH, _tileIndex) == 0 && _parentScene->getCountdown() == 0) {
879 			show();
880 			sendMessage(_parentScene, 0x2000, _tileIndex);
881 		}
882 		messageResult = 1;
883 		break;
884 	default:
885 		break;
886 	}
887 	return messageResult;
888 }
889 
show()890 void AsScene1405Tile::show() {
891 	if (!_isShowing) {
892 		_isShowing = true;
893 		playSound(0);
894 		setVisible(true);
895 	}
896 }
897 
hide(bool playClickSound)898 void AsScene1405Tile::hide(bool playClickSound) {
899 	if (_isShowing) {
900 		_isShowing = false;
901 		if (playClickSound)
902 			playSound(0);
903 		setVisible(false);
904 	}
905 }
906 
KmScene1401(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)907 KmScene1401::KmScene1401(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
908 	: Klaymen(vm, parentScene, x, y) {
909 
910 	// Empty
911 }
912 
xHandleMessage(int messageNum,const MessageParam & param)913 uint32 KmScene1401::xHandleMessage(int messageNum, const MessageParam &param) {
914 	switch (messageNum) {
915 	case 0x4001:
916 	case 0x4800:
917 		startWalkToX(param.asPoint().x, false);
918 		break;
919 	case NM_KLAYMEN_STAND_IDLE:
920 		GotoState(&Klaymen::stTryStandIdle);
921 		break;
922 	case NM_KLAYMEN_MOVE_OBJECT:
923 		if (param.asInteger() == 1)
924 			GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
925 		else
926 			GotoState(&Klaymen::stMoveObjectFaceObject);
927 		break;
928 	case NM_KLAYMEN_PRESS_BUTTON:
929 		if (param.asInteger() == 1)
930 			GotoState(&Klaymen::stPressButton);
931 		else if (param.asInteger() == 2)
932 			GotoState(&Klaymen::stPressFloorButton);
933 		else
934 			GotoState(&Klaymen::stPressButtonSide);
935 		break;
936 	case 0x4817:
937 		setDoDeltaX(param.asInteger());
938 		gotoNextStateExt();
939 		break;
940 	case 0x481B:
941 		if (param.asPoint().y != 0)
942 			startWalkToXDistance(param.asPoint().y, param.asPoint().x);
943 		else
944 			startWalkToAttachedSpriteXDistance(param.asPoint().x);
945 		break;
946 	case 0x481F:
947 		if (param.asInteger() == 1)
948 			GotoState(&Klaymen::stTurnAwayFromUse);
949 		else if (param.asInteger() == 0)
950 			GotoState(&Klaymen::stTurnToUseHalf);
951 		else
952 			GotoState(&Klaymen::stWonderAbout);
953 		break;
954 	case 0x482D:
955 		setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
956 		gotoNextStateExt();
957 		break;
958 	case 0x482E:
959 		if (param.asInteger() == 1)
960 			GotoState(&Klaymen::stWalkToFrontNoStep);
961 		else
962 			GotoState(&Klaymen::stWalkToFront);
963 		break;
964 	case 0x482F:
965 		if (param.asInteger() == 1)
966 			GotoState(&Klaymen::stTurnToFront);
967 		else
968 			GotoState(&Klaymen::stTurnToBack);
969 		break;
970 	default:
971 		break;
972 	}
973 	return 0;
974 }
975 
KmScene1402(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)976 KmScene1402::KmScene1402(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
977 	: Klaymen(vm, parentScene, x, y) {
978 
979 	SetFilterY(&Sprite::defFilterY);
980 }
981 
xHandleMessage(int messageNum,const MessageParam & param)982 uint32 KmScene1402::xHandleMessage(int messageNum, const MessageParam &param) {
983 	switch (messageNum) {
984 	case 0x4001:
985 	case 0x4800:
986 		startWalkToX(param.asPoint().x, false);
987 		break;
988 	case NM_KLAYMEN_STAND_IDLE:
989 		GotoState(&Klaymen::stTryStandIdle);
990 		break;
991 	case NM_KLAYMEN_MOVE_OBJECT:
992 		if (param.asInteger() == 1)
993 			GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
994 		else
995 			GotoState(&Klaymen::stMoveObjectFaceObject);
996 		break;
997 	case 0x4817:
998 		setDoDeltaX(param.asInteger());
999 		gotoNextStateExt();
1000 		break;
1001 	case 0x481B:
1002 		if (param.asPoint().y != 0)
1003 			startWalkToXDistance(param.asPoint().y, param.asPoint().x);
1004 		else
1005 			startWalkToAttachedSpriteXDistance(param.asPoint().x);
1006 		break;
1007 	case NM_KLAYMEN_TURN_TO_USE:
1008 		GotoState(&Klaymen::stTurnToUse);
1009 		break;
1010 	case NM_KLAYMEN_RETURN_FROM_USE:
1011 		GotoState(&Klaymen::stReturnFromUse);
1012 		break;
1013 	default:
1014 		break;
1015 	}
1016 	return 0;
1017 }
1018 
1019 static const KlaymenIdleTableItem klaymenIdleTable1403[] = {
1020 	{1, kIdleSpinHead},
1021 	{1, kIdleChest},
1022 	{1, kIdleHeadOff},
1023 };
1024 
KmScene1403(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)1025 KmScene1403::KmScene1403(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
1026 	: Klaymen(vm, parentScene, x, y) {
1027 
1028 	setKlaymenIdleTable(klaymenIdleTable1403, ARRAYSIZE(klaymenIdleTable1403));
1029 }
1030 
xHandleMessage(int messageNum,const MessageParam & param)1031 uint32 KmScene1403::xHandleMessage(int messageNum, const MessageParam &param) {
1032 	switch (messageNum) {
1033 	case 0x4001:
1034 	case 0x4800:
1035 		startWalkToX(param.asPoint().x, false);
1036 		break;
1037 	case NM_KLAYMEN_STAND_IDLE:
1038 		GotoState(&Klaymen::stTryStandIdle);
1039 		break;
1040 	case NM_KLAYMEN_MOVE_OBJECT:
1041 		if (param.asInteger() == 1)
1042 			GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
1043 		else
1044 			GotoState(&Klaymen::stMoveObjectFaceObject);
1045 		break;
1046 	case 0x480D:
1047 		GotoState(&Klaymen::stUseLever);
1048 		break;
1049 	case NM_KLAYMEN_PICKUP:
1050 		if (param.asInteger() == 2)
1051 			GotoState(&Klaymen::stPickUpNeedle);
1052 		else if (param.asInteger() == 1)
1053 			GotoState(&Klaymen::stPickUpTube);
1054 		else
1055 			GotoState(&Klaymen::stPickUpGeneric);
1056 		break;
1057 	case 0x4817:
1058 		setDoDeltaX(param.asInteger());
1059 		gotoNextStateExt();
1060 		break;
1061 	case 0x481B:
1062 		if (param.asPoint().y != 0)
1063 			startWalkToXDistance(param.asPoint().y, param.asPoint().x);
1064 		else
1065 			startWalkToAttachedSpriteXDistance(param.asPoint().x);
1066 		break;
1067 	case NM_KLAYMEN_RELEASE_LEVER:
1068 		GotoState(&Klaymen::stReleaseLever);
1069 		break;
1070 	case 0x483F:
1071 		startSpecialWalkRight(param.asInteger());
1072 		break;
1073 	case 0x4840:
1074 		startSpecialWalkLeft(param.asInteger());
1075 		break;
1076 	default:
1077 		break;
1078 	}
1079 	return 0;
1080 }
1081 
1082 // KmScene1404
1083 
KmScene1404(NeverhoodEngine * vm,Scene * parentScene,int16 x,int16 y)1084 KmScene1404::KmScene1404(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y)
1085 	: Klaymen(vm, parentScene, x, y) {
1086 
1087 	// Empty
1088 }
1089 
xHandleMessage(int messageNum,const MessageParam & param)1090 uint32 KmScene1404::xHandleMessage(int messageNum, const MessageParam &param) {
1091 	switch (messageNum) {
1092 	case 0x4001:
1093 	case 0x4800:
1094 		startWalkToX(param.asPoint().x, false);
1095 		break;
1096 	case NM_KLAYMEN_STAND_IDLE:
1097 		GotoState(&Klaymen::stTryStandIdle);
1098 		break;
1099 	case NM_KLAYMEN_MOVE_OBJECT:
1100 		if (param.asInteger() == 1)
1101 			GotoState(&Klaymen::stMoveObjectSkipTurnFaceObject);
1102 		else
1103 			GotoState(&Klaymen::stMoveObjectFaceObject);
1104 		break;
1105 	case NM_KLAYMEN_PICKUP:
1106 		if (param.asInteger() == 2)
1107 			GotoState(&Klaymen::stPickUpNeedle);
1108 		else if (param.asInteger() == 1)
1109 			GotoState(&Klaymen::stPickUpTube);
1110 		else
1111 			GotoState(&Klaymen::stPickUpGeneric);
1112 		break;
1113 	case 0x4817:
1114 		setDoDeltaX(param.asInteger());
1115 		gotoNextStateExt();
1116 		break;
1117 	case NM_KLAYMEN_INSERT_DISK:
1118 		GotoState(&Klaymen::stInsertDisk);
1119 		break;
1120 	case 0x481B:
1121 		if (param.asPoint().y != 0)
1122 			startWalkToXDistance(param.asPoint().y, param.asPoint().x);
1123 		else
1124 			startWalkToAttachedSpriteXDistance(param.asPoint().x);
1125 		break;
1126 	case NM_KLAYMEN_TURN_TO_USE:
1127 		GotoState(&Klaymen::stTurnToUse);
1128 		break;
1129 	case NM_KLAYMEN_RETURN_FROM_USE:
1130 		GotoState(&Klaymen::stReturnFromUse);
1131 		break;
1132 	case 0x481F:
1133 		if (param.asInteger() == 1)
1134 			GotoState(&Klaymen::stWonderAboutAfter);
1135 		else if (param.asInteger() == 0)
1136 			GotoState(&Klaymen::stWonderAboutHalf);
1137 		else if (param.asInteger() == 4)
1138 			GotoState(&Klaymen::stTurnAwayFromUse);
1139 		else if (param.asInteger() == 3)
1140 			GotoState(&Klaymen::stTurnToUseHalf);
1141 		else
1142 			GotoState(&Klaymen::stWonderAbout);
1143 		break;
1144 	case 0x482D:
1145 		setDoDeltaX(_x > (int16)param.asInteger() ? 1 : 0);
1146 		gotoNextStateExt();
1147 		break;
1148 	case 0x483F:
1149 		startSpecialWalkRight(param.asInteger());
1150 		break;
1151 	case 0x4840:
1152 		startSpecialWalkLeft(param.asInteger());
1153 		break;
1154 	default:
1155 		break;
1156 	}
1157 	return 0;
1158 }
1159 
1160 } // End of namespace Neverhood
1161