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 
24 #include "common/config-manager.h"
25 #include "common/textconsole.h"
26 
27 #include "queen/logic.h"
28 
29 #include "queen/bankman.h"
30 #include "queen/command.h"
31 #include "queen/credits.h"
32 #include "queen/cutaway.h"
33 #include "queen/debug.h"
34 #include "queen/defs.h"
35 #include "queen/display.h"
36 #include "queen/graphics.h"
37 #include "queen/grid.h"
38 #include "queen/input.h"
39 #include "queen/journal.h"
40 #include "queen/queen.h"
41 #include "queen/resource.h"
42 #include "queen/sound.h"
43 #include "queen/state.h"
44 #include "queen/talk.h"
45 #include "queen/walk.h"
46 
47 namespace Queen {
48 
Logic(QueenEngine * vm)49 Logic::Logic(QueenEngine *vm)
50 	: _credits(NULL), _objectData(NULL), _roomData(NULL), _sfxName(NULL),
51 	_itemData(NULL), _graphicData(NULL), _walkOffData(NULL), _objectDescription(NULL),
52 	_furnitureData(NULL), _actorData(NULL), _graphicAnim(NULL), _vm(vm) {
53 	_joe.x = _joe.y = 0;
54 	_joe.scale = 100;
55 	_joe.walk = JWM_NORMAL;
56 	memset(_gameState, 0, sizeof(_gameState));
57 	memset(_talkSelected, 0, sizeof(_talkSelected));
58 	_puzzleAttemptCount = 0;
59 	_journal = new Journal(vm);
60 	_scene = 0;
61 	memset(_specialMoves, 0, sizeof(_specialMoves));
62 	readQueenJas();
63 }
64 
~Logic()65 Logic::~Logic() {
66 	delete _journal;
67 	delete _credits;
68 	delete[] _objectData;
69 	delete[] _roomData;
70 	delete[] _sfxName;
71 	delete[] _itemData;
72 	delete[] _graphicData;
73 	delete[] _walkOffData;
74 	delete[] _objectDescription;
75 	delete[] _furnitureData;
76 	delete[] _actorData;
77 	delete[] _graphicAnim;
78 }
79 
readQueenJas()80 void Logic::readQueenJas() {
81 	int16 i;
82 
83 	uint8 *jas = _vm->resource()->loadFile("QUEEN.JAS", 20);
84 	uint8 *ptr = jas;
85 
86 	_numRooms = READ_BE_UINT16(ptr); ptr += 2;
87 	_numNames = READ_BE_UINT16(ptr); ptr += 2;
88 	_numObjects = READ_BE_UINT16(ptr); ptr += 2;
89 	_numDescriptions = READ_BE_UINT16(ptr); ptr += 2;
90 
91 	_objectData = new ObjectData[_numObjects + 1];
92 	memset(&_objectData[0], 0, sizeof(ObjectData));
93 	for (i = 1; i <= _numObjects; i++) {
94 		_objectData[i].readFromBE(ptr);
95 	}
96 
97 	_roomData = new uint16[_numRooms + 2];
98 	_roomData[0] = 0;
99 	for (i = 1; i <= (_numRooms + 1); i++) {
100 		_roomData[i] = READ_BE_UINT16(ptr);	ptr += 2;
101 	}
102 	_roomData[_numRooms + 1] = _numObjects;
103 
104 	if ((_vm->resource()->isDemo() && _vm->resource()->getPlatform() == Common::kPlatformDOS) ||
105 		(_vm->resource()->isInterview() && _vm->resource()->getPlatform() == Common::kPlatformAmiga)) {
106 		_sfxName = NULL;
107 	} else {
108 		_sfxName = new uint16[_numRooms + 1];
109 		_sfxName[0] = 0;
110 		for (i = 1; i <= _numRooms; i++) {
111 			_sfxName[i] = READ_BE_UINT16(ptr); ptr += 2;
112 		}
113 	}
114 
115 	_numItems = READ_BE_UINT16(ptr); ptr += 2;
116 	_itemData = new ItemData[_numItems + 1];
117 	memset(&_itemData[0], 0, sizeof(ItemData));
118 	for (i = 1; i <= _numItems; i++) {
119 		_itemData[i].readFromBE(ptr);
120 	}
121 
122 	_numGraphics = READ_BE_UINT16(ptr); ptr += 2;
123 	_graphicData = new GraphicData[_numGraphics + 1];
124 	memset(&_graphicData[0], 0, sizeof(GraphicData));
125 	for (i = 1; i <= _numGraphics; i++) {
126 		_graphicData[i].readFromBE(ptr);
127 	}
128 
129 	_vm->grid()->readDataFrom(_numObjects, _numRooms, ptr);
130 
131 	_numWalkOffs = READ_BE_UINT16(ptr);	ptr += 2;
132 	_walkOffData = new WalkOffData[_numWalkOffs + 1];
133 	memset(&_walkOffData[0], 0, sizeof(WalkOffData));
134 	for (i = 1; i <= _numWalkOffs; i++) {
135 		_walkOffData[i].readFromBE(ptr);
136 	}
137 
138 	_numObjDesc = READ_BE_UINT16(ptr); ptr += 2;
139 	_objectDescription = new ObjectDescription[_numObjDesc + 1];
140 	memset(&_objectDescription[0], 0, sizeof(ObjectDescription));
141 	for (i = 1; i <= _numObjDesc; i++) {
142 		_objectDescription[i].readFromBE(ptr);
143 	}
144 
145 	_vm->command()->readCommandsFrom(ptr);
146 
147 	_entryObj = READ_BE_UINT16(ptr); ptr += 2;
148 
149 	_numFurniture = READ_BE_UINT16(ptr); ptr += 2;
150 	_furnitureData = new FurnitureData[_numFurniture + 1];
151 	memset(&_furnitureData[0], 0, sizeof(FurnitureData));
152 	for (i = 1; i <= _numFurniture; i++) {
153 		_furnitureData[i].readFromBE(ptr);
154 	}
155 
156 	// Actors
157 	_numActors = READ_BE_UINT16(ptr); ptr += 2;
158 	_numAAnim = READ_BE_UINT16(ptr); ptr += 2;
159 	_numAName = READ_BE_UINT16(ptr); ptr += 2;
160 	_numAFile = READ_BE_UINT16(ptr); ptr += 2;
161 
162 	_actorData = new ActorData[_numActors + 1];
163 	memset(&_actorData[0], 0, sizeof(ActorData));
164 	for (i = 1; i <= _numActors; i++) {
165 		_actorData[i].readFromBE(ptr);
166 	}
167 
168 	_numGraphicAnim = READ_BE_UINT16(ptr); ptr += 2;
169 
170 	_graphicAnim = new GraphicAnim[_numGraphicAnim + 1];
171 	if (_numGraphicAnim == 0) {
172 		_graphicAnim[0].readFromBE(ptr);
173 	} else {
174 		memset(&_graphicAnim[0], 0, sizeof(GraphicAnim));
175 		for (i = 1; i <= _numGraphicAnim; i++) {
176 			_graphicAnim[i].readFromBE(ptr);
177 		}
178 	}
179 
180 	_currentRoom = _objectData[_entryObj].room;
181 	_entryObj = 0;
182 
183 	if (memcmp(ptr, _vm->resource()->getJASVersion(), 5) != 0) {
184 		warning("Unexpected queen.jas file format");
185 	}
186 	delete[] jas;
187 
188 	_vm->resource()->loadTextFile("QUEEN2.JAS", _jasStringList);
189 	_jasStringOffset[0] = 0;
190 	_jasStringOffset[1] = _jasStringOffset[0] + _numDescriptions;
191 	_jasStringOffset[2] = _jasStringOffset[1] + _numNames;
192 	_jasStringOffset[3] = _jasStringOffset[2] + _numRooms;
193 	_jasStringOffset[4] = _jasStringOffset[3] + 12;
194 	_jasStringOffset[5] = _jasStringOffset[4] + JOE_RESPONSE_MAX;
195 	_jasStringOffset[6] = _jasStringOffset[5] + _numAAnim;
196 	_jasStringOffset[7] = _jasStringOffset[6] + _numAName;
197 
198 	// Patch for German text bug
199 	if (_vm->resource()->getLanguage() == Common::DE_DEU) {
200 		_jasStringList[_jasStringOffset[JSO_OBJECT_DESCRIPTION] + 296 - 1] = "Es bringt nicht viel, das festzubinden.";
201 	}
202 }
203 
start()204 void Logic::start() {
205 	setupSpecialMoveTable();
206 	_vm->command()->clear(false);
207 	_vm->display()->setupPanel();
208 	_vm->graphics()->unpackControlBank();
209 	_vm->graphics()->setupMouseCursor();
210 	setupJoe();
211 	_vm->grid()->setupPanel();
212 	inventorySetup();
213 
214 	_oldRoom = 0;
215 	_newRoom = _currentRoom;
216 }
217 
findBob(uint16 obj) const218 uint16 Logic::findBob(uint16 obj) const {
219 	assert(obj <= _numObjects);
220 
221 	uint16 room = _objectData[obj].room;
222 	assert(room <= _numRooms);
223 
224 	uint16 bobnum = 0;
225 	int16 img = _objectData[obj].image;
226 	if (img != 0) {
227 		if (img == -3 || img == -4) {
228 			// a person object
229 			bobnum = findPersonNumber(obj, room);
230 		} else {
231 			uint16 bobtype = 0; // 1 for animated, 0 for static
232 
233 			if (img <= -10) {
234 				// object has been turned off, but the image order hasn't been updated
235 				if (_graphicData[-(img + 10)].lastFrame != 0) {
236 					bobtype = 1;
237 				}
238 			} else if (img == -2) {
239 				// -1 static, -2 animated
240 				bobtype = 1;
241 			} else if (img > 0) {
242 				if (_graphicData[img].lastFrame != 0) {
243 					bobtype = 1;
244 				}
245 			}
246 
247 			uint16 idxAnimated = 0;
248 			uint16 idxStatic = 0;
249 			for (uint16 i = _roomData[room] + 1; i <= obj; ++i) {
250 				img = _objectData[i].image;
251 				if (img <= -10) {
252 					if (_graphicData[-(img + 10)].lastFrame != 0) {
253 						++idxAnimated;
254 					} else {
255 						++idxStatic;
256 					}
257 				} else if (img > 0) {
258 					if (img > 5000) {
259 						img -= 5000;
260 					}
261 
262 					assert (img <= _numGraphics);
263 
264 					if (_graphicData[img].lastFrame != 0) {
265 						++idxAnimated;
266 					} else {
267 						++idxStatic;
268 					}
269 				} else if (img == -1) {
270 					++idxStatic;
271 				} else if (img == -2) {
272 					++idxAnimated;
273 				}
274 			}
275 			if (bobtype == 0) {
276 				// static bob
277 				if (idxStatic > 0) {
278 					bobnum = 19 + _vm->graphics()->numStaticFurniture() + idxStatic;
279 				}
280 			} else {
281 				// animated bob
282 				if (idxAnimated > 0) {
283 					bobnum = 4 + _vm->graphics()->numAnimatedFurniture() + idxAnimated;
284 				}
285 			}
286 		}
287 	}
288 	return bobnum;
289 }
290 
findFrame(uint16 obj) const291 uint16 Logic::findFrame(uint16 obj) const {
292 	uint16 framenum = 0;
293 	uint16 room = _objectData[obj].room;
294 	int16 img = _objectData[obj].image;
295 	if (img == -3 || img == -4) {
296 		uint16 bobnum = findPersonNumber(obj, room);
297 		if (bobnum <= 3) {
298 			framenum = 31 + bobnum;
299 		}
300 	} else {
301 		uint16 idx = 0;
302 		for (uint16 i = _roomData[room] + 1; i < obj; ++i) {
303 			img = _objectData[i].image;
304 			if (img <= -10) {
305 				const GraphicData* pgd = &_graphicData[-(img + 10)];
306 				if (pgd->lastFrame != 0) {
307 					// skip all the frames of the animation
308 					idx += ABS(pgd->lastFrame) - pgd->firstFrame + 1;
309 				} else {
310 					// static bob, skip one frame
311 					++idx;
312 				}
313 			} else if (img == -1) {
314 				++idx;
315 			} else if (img > 0) {
316 				if (img > 5000) {
317 					img -= 5000;
318 				}
319 				const GraphicData* pgd = &_graphicData[img];
320 				uint16 lastFrame = ABS(pgd->lastFrame);
321 				if (pgd->firstFrame < 0) {
322 					idx += lastFrame;
323 				} else if (lastFrame != 0) {
324 					idx += (lastFrame - pgd->firstFrame) + 1;
325 				} else {
326 					++idx;
327 				}
328 			}
329 		}
330 
331 		img = _objectData[obj].image;
332 		if (img <= -10) {
333 			const GraphicData* pgd = &_graphicData[-(img + 10)];
334 			if (pgd->lastFrame != 0) {
335 				idx += ABS(pgd->lastFrame) - pgd->firstFrame + 1;
336 			} else {
337 				++idx;
338 			}
339 		} else if (img == -1 || img > 0) {
340 			++idx;
341 		}
342 
343 		// calculate only if there are person frames
344 		if (idx > 0) {
345 			framenum = FRAMES_JOE + _vm->graphics()->numFurnitureFrames() + idx;
346 		}
347 	}
348 	return framenum;
349 }
350 
objectForPerson(uint16 bobNum) const351 uint16 Logic::objectForPerson(uint16 bobNum) const {
352 	uint16 bobcur = 0;
353 	// first object number in the room
354 	uint16 cur = currentRoomData() + 1;
355 	// last object number in the room
356 	uint16 last = _roomData[_currentRoom + 1];
357 	for (; cur <= last; ++cur) {
358 		int16 image = _objectData[cur].image;
359 		if (image == -3 || image == -4) {
360 			// the object is a bob
361 			++bobcur;
362 		}
363 		if (bobcur == bobNum) {
364 			return cur;
365 		}
366 	}
367 	return 0;
368 }
369 
walkOffPointForObject(int16 obj) const370 WalkOffData *Logic::walkOffPointForObject(int16 obj) const {
371 	for (uint16 i = 1; i <= _numWalkOffs; ++i) {
372 		if (_walkOffData[i].entryObj == obj) {
373 			return &_walkOffData[i];
374 		}
375 	}
376 	return NULL;
377 }
378 
joeWalk(JoeWalkMode walking)379 void Logic::joeWalk(JoeWalkMode walking) {
380 	_joe.walk = walking;
381 	// Do this so that Input doesn't need to know the walk value
382 	_vm->input()->dialogueRunning(JWM_SPEAK == walking);
383 }
384 
gameState(int index) const385 int16 Logic::gameState(int index) const {
386 	assert(index >= 0 && index < GAME_STATE_COUNT);
387 	return _gameState[index];
388 }
389 
gameState(int index,int16 newValue)390 void Logic::gameState(int index, int16 newValue) {
391 	assert(index >= 0 && index < GAME_STATE_COUNT);
392 	debug(8, "Logic::gameState() [%d] = %d", index, newValue);
393 	_gameState[index] = newValue;
394 }
395 
roomName(uint16 roomNum) const396 const char *Logic::roomName(uint16 roomNum) const {
397 	assert(roomNum >= 1 && roomNum <= _numRooms);
398 	return _jasStringList[_jasStringOffset[JSO_ROOM_NAME] + roomNum - 1].c_str();
399 }
400 
objectName(uint16 objNum) const401 const char *Logic::objectName(uint16 objNum) const {
402 	assert(objNum >= 1 && objNum <= _numNames);
403 	return _jasStringList[_jasStringOffset[JSO_OBJECT_NAME] + objNum - 1].c_str();
404 }
405 
objectTextualDescription(uint16 objNum) const406 const char *Logic::objectTextualDescription(uint16 objNum) const {
407 	assert(objNum >= 1 && objNum <= _numDescriptions);
408 	return _jasStringList[_jasStringOffset[JSO_OBJECT_DESCRIPTION] + objNum - 1].c_str();
409 }
410 
joeResponse(int i) const411 const char *Logic::joeResponse(int i) const {
412 	assert(i >= 1 && i <= JOE_RESPONSE_MAX);
413 	return _jasStringList[_jasStringOffset[JSO_JOE_RESPONSE] + i - 1].c_str();
414 }
415 
verbName(Verb v) const416 const char *Logic::verbName(Verb v) const {
417 	assert(v >= 0 && v <= 12);
418 	if (v == 0) {
419 		return "";
420 	}
421 	return _jasStringList[_jasStringOffset[JSO_VERB_NAME] + v - 1].c_str();
422 }
423 
actorAnim(int num) const424 const char *Logic::actorAnim(int num) const {
425 	assert(num >= 1 && num <= _numAAnim);
426 	return _jasStringList[_jasStringOffset[JSO_ACTOR_ANIM] + num - 1].c_str();
427 }
428 
actorName(int num) const429 const char *Logic::actorName(int num) const {
430 	assert(num >= 1 && num <= _numAName);
431 	return _jasStringList[_jasStringOffset[JSO_ACTOR_NAME] + num - 1].c_str();
432 }
433 
actorFile(int num) const434 const char *Logic::actorFile(int num) const {
435 	assert(num >= 1 && num <= _numAFile);
436 	return _jasStringList[_jasStringOffset[JSO_ACTOR_FILE] + num - 1].c_str();
437 }
438 
eraseRoom()439 void Logic::eraseRoom() {
440 	_vm->bankMan()->eraseFrames(false);
441 	_vm->bankMan()->close(15);
442 	_vm->bankMan()->close(11);
443 	_vm->bankMan()->close(10);
444 	_vm->bankMan()->close(12);
445 
446 	_vm->display()->palFadeOut(_currentRoom);
447 
448 	// invalidates all persons animations
449 	_vm->graphics()->clearPersonFrames();
450 	_vm->graphics()->eraseAllAnims();
451 
452 	uint16 cur = _roomData[_oldRoom] + 1;
453 	uint16 last = _roomData[_oldRoom + 1];
454 	for (; cur <= last; ++cur) {
455 		ObjectData *pod = &_objectData[cur];
456 		if (pod->name == 0) {
457 			// object has been deleted, invalidate image
458 			pod->image = 0;
459 		} else if (pod->image > -4000 && pod->image <= -10) {
460 			if (_graphicData[ABS(pod->image + 10)].lastFrame == 0) {
461 				// static Bob
462 				pod->image = -1;
463 			} else {
464 				// animated Bob
465 				pod->image = -2;
466 			}
467 		}
468 	}
469 }
470 
setupRoom(const char * room,int comPanel,bool inCutaway)471 void Logic::setupRoom(const char *room, int comPanel, bool inCutaway) {
472 	// load backdrop image, init dynalum, setup colors
473 	_vm->display()->setupNewRoom(room, _currentRoom);
474 
475 	// setup graphics to enter fullscreen/panel mode
476 	_vm->display()->screenMode(comPanel, inCutaway);
477 
478 	_vm->grid()->setupNewRoom(_currentRoom, _roomData[_currentRoom]);
479 
480 	int16 furn[9];
481 	uint16 furnTot = 0;
482 	for (uint16 i = 1; i <= _numFurniture; ++i) {
483 		if (_furnitureData[i].room == _currentRoom) {
484 			++furnTot;
485 			furn[furnTot] = _furnitureData[i].objNum;
486 		}
487 	}
488 	_vm->graphics()->setupNewRoom(room, _currentRoom, furn, furnTot);
489 
490 	_vm->display()->forceFullRefresh();
491 }
492 
displayRoom(uint16 room,RoomDisplayMode mode,uint16 scale,int comPanel,bool inCutaway)493 void Logic::displayRoom(uint16 room, RoomDisplayMode mode, uint16 scale, int comPanel, bool inCutaway) {
494 	debug(6, "Logic::displayRoom(%d, %d, %d, %d, %d)", room, mode, scale, comPanel, inCutaway);
495 
496 	eraseRoom();
497 
498 	if (_credits)
499 		_credits->nextRoom();
500 
501 	setupRoom(roomName(room), comPanel, inCutaway);
502 	if (mode != RDM_FADE_NOJOE) {
503 		setupJoeInRoom(mode != RDM_FADE_JOE_XY, scale);
504 	}
505 	if (mode != RDM_NOFADE_JOE) {
506 		_vm->update();
507 		BobSlot *joe = _vm->graphics()->bob(0);
508 		_vm->display()->palFadeIn(_currentRoom, joe->active, joe->x, joe->y);
509 	}
510 	if (mode != RDM_FADE_NOJOE && joeX() != 0 && joeY() != 0) {
511 		int16 jx = joeX();
512 		int16 jy = joeY();
513 		joePos(0, 0);
514 		_vm->walk()->moveJoe(0, jx, jy, inCutaway);
515 	}
516 }
517 
findActor(uint16 noun,const char * name) const518 ActorData *Logic::findActor(uint16 noun, const char *name) const {
519 	uint16 obj = currentRoomData() + noun;
520 	int16 img = objectData(obj)->image;
521 	if (img != -3 && img != -4) {
522 		warning("Logic::findActor() - Object %d is not a person", obj);
523 		return NULL;
524 	}
525 
526 	// search Bob number for the person
527 	uint16 bobNum = findPersonNumber(obj, _currentRoom);
528 
529 	// search for a matching actor
530 	if (bobNum > 0) {
531 		for (uint16 i = 1; i <= _numActors; ++i) {
532 			ActorData *pad = &_actorData[i];
533 			if (pad->room == _currentRoom && gameState(pad->gsSlot) == pad->gsValue) {
534 				if (bobNum == pad->bobNum || (name && strcmp(actorName(pad->name), name) == 0)) {
535 					return pad;
536 				}
537 			}
538 		}
539 	}
540 	return NULL;
541 }
542 
initPerson(uint16 noun,const char * name,bool loadBank,Person * pp)543 bool Logic::initPerson(uint16 noun, const char *name, bool loadBank, Person *pp) {
544 	const ActorData *pad = findActor(noun, name);
545 	if (pad != NULL) {
546 		pp->actor = pad;
547 		pp->name = actorName(pad->name);
548 		if (pad->anim != 0) {
549 			pp->anim = actorAnim(pad->anim);
550 		} else {
551 			pp->anim = NULL;
552 		}
553 		if (loadBank && pad->file != 0) {
554 			_vm->bankMan()->load(actorFile(pad->file), pad->bankNum);
555 			// if there is no valid actor file (ie pad->file is 0), the person
556 			// data is already loaded as it is included in objects room bank (.bbk)
557 		}
558 		pp->bobFrame = 31 + pp->actor->bobNum;
559 	}
560 	return pad != NULL;
561 }
562 
findPersonNumber(uint16 obj,uint16 room) const563 uint16 Logic::findPersonNumber(uint16 obj, uint16 room) const {
564 	uint16 num = 0;
565 	for (uint16 i = _roomData[room] + 1; i <= obj; ++i) {
566 		int16 img = _objectData[i].image;
567 		if (img == -3 || img == -4) {
568 			++num;
569 		}
570 	}
571 	return num;
572 }
573 
loadJoeBanks(const char * animBank,const char * standBank)574 void Logic::loadJoeBanks(const char *animBank, const char *standBank) {
575 	_vm->bankMan()->load(animBank, 13);
576 	for (int i = 11; i < 31; ++i) {
577 		_vm->bankMan()->unpack(i - 10, i, 13);
578 	}
579 	_vm->bankMan()->close(13);
580 
581 	_vm->bankMan()->load(standBank, 7);
582 	_vm->bankMan()->unpack(1, 35, 7);
583 	_vm->bankMan()->unpack(3, 36, 7);
584 	_vm->bankMan()->unpack(5, 37, 7);
585 }
586 
setupJoe()587 void Logic::setupJoe() {
588 	loadJoeBanks("JOE_A.BBK", "JOE_B.BBK");
589 	joePrevFacing(DIR_FRONT);
590 	joeFacing(DIR_FRONT);
591 }
592 
setupJoeInRoom(bool autoPosition,uint16 scale)593 void Logic::setupJoeInRoom(bool autoPosition, uint16 scale) {
594 	debug(9, "Logic::setupJoeInRoom(%d, %d) joe.x=%d joe.y=%d", autoPosition, scale, _joe.x, _joe.y);
595 
596 	int16 oldx, oldy;
597 	if (!autoPosition || joeX() != 0 || joeY() != 0) {
598 		oldx = joeX();
599 		oldy = joeY();
600 		joePos(0, 0);
601 	} else {
602 		const ObjectData *pod = objectData(_entryObj);
603 		// find the walk off point for the entry object and make
604 		// Joe walking to that point
605 		const WalkOffData *pwo = walkOffPointForObject(_entryObj);
606 		if (pwo != NULL) {
607 			oldx = pwo->x;
608 			oldy = pwo->y;
609 			// entryObj has a walk off point, then walk from there to object x,y
610 			joePos(pod->x, pod->y);
611 		} else {
612 			// no walk off point, use object position
613 			oldx = pod->x;
614 			oldy = pod->y;
615 			joePos(0, 0);
616 		}
617 	}
618 
619 	debug(6, "Logic::setupJoeInRoom() - oldx=%d, oldy=%d scale=%d", oldx, oldy, scale);
620 
621 	if (scale > 0 && scale < 100) {
622 		joeScale(scale);
623 	} else {
624 		uint16 a = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
625 		if (a > 0) {
626 			joeScale(_vm->grid()->area(_currentRoom, a)->calcScale(oldy));
627 		} else {
628 			joeScale(100);
629 		}
630 	}
631 
632 	if (joeCutFacing() > 0) {
633 		joeFacing(joeCutFacing());
634 		joeCutFacing(0);
635 	} else {
636 		// check to see which way Joe entered room
637 		const ObjectData *pod = objectData(_entryObj);
638 		switch (State::findDirection(pod->state)) {
639 		case DIR_BACK:
640 			joeFacing(DIR_FRONT);
641 			break;
642 		case DIR_FRONT:
643 			joeFacing(DIR_BACK);
644 			break;
645 		case DIR_LEFT:
646 			joeFacing(DIR_RIGHT);
647 			break;
648 		case DIR_RIGHT:
649 			joeFacing(DIR_LEFT);
650 			break;
651 		default:
652 			break;
653 		}
654 	}
655 	joePrevFacing(joeFacing());
656 
657 	BobSlot *pbs = _vm->graphics()->bob(0);
658 	pbs->scale = joeScale();
659 
660 	if (_currentRoom == 108) {
661 		_vm->graphics()->putCameraOnBob(-1);
662 		_vm->bankMan()->load("JOE_E.ACT", 7);
663 		_vm->bankMan()->unpack(2, 31, 7);
664 
665 		_vm->display()->horizontalScroll(320);
666 
667 		joeFacing(DIR_RIGHT);
668 		joeCutFacing(DIR_RIGHT);
669 		joePrevFacing(DIR_RIGHT);
670 	}
671 
672 	joeFace();
673 	pbs->curPos(oldx, oldy);
674 	pbs->frameNum = 31;
675 }
676 
joeFace()677 uint16 Logic::joeFace() {
678 	debug(9, "Logic::joeFace() - curFace = %d, prevFace = %d", _joe.facing, _joe.prevFacing);
679 	BobSlot *pbs = _vm->graphics()->bob(0);
680 	uint16 frame;
681 	if (_currentRoom == 108) {
682 		frame = 1;
683 	} else {
684 		frame = 35;
685 		if (joeFacing() == DIR_FRONT) {
686 			if (joePrevFacing() == DIR_BACK) {
687 				pbs->frameNum = 35;
688 				_vm->update();
689 			}
690 			frame = 36;
691 		} else if (joeFacing() == DIR_BACK) {
692 			if (joePrevFacing() == DIR_FRONT) {
693 				pbs->frameNum = 35;
694 				_vm->update();
695 			}
696 			frame = 37;
697 		} else if ((joeFacing() == DIR_LEFT && joePrevFacing() == DIR_RIGHT)
698 			||  (joeFacing() == DIR_RIGHT && joePrevFacing() == DIR_LEFT)) {
699 			pbs->frameNum = 36;
700 			_vm->update();
701 		}
702 		pbs->frameNum = frame;
703 		pbs->scale = joeScale();
704 		pbs->xflip = (joeFacing() == DIR_LEFT);
705 		_vm->update();
706 		joePrevFacing(joeFacing());
707 		switch (frame) {
708 		case 35:
709 			frame = 1;
710 			break;
711 		case 36:
712 			frame = 3;
713 			break;
714 		case 37:
715 			frame = 5;
716 			break;
717 		default:
718 			break;
719 		}
720 	}
721 	pbs->frameNum = 31;
722 	_vm->bankMan()->unpack(frame, pbs->frameNum, 7);
723 	return frame;
724 }
725 
joeGrab(int16 grabState)726 void Logic::joeGrab(int16 grabState) {
727 	uint16 frame = 0;
728 	BobSlot *bobJoe = _vm->graphics()->bob(0);
729 
730 	switch (grabState) {
731 	case STATE_GRAB_NONE:
732 		break;
733 	case STATE_GRAB_MID:
734 		if (joeFacing() == DIR_BACK) {
735 			frame = 6;
736 		} else if (joeFacing() == DIR_FRONT) {
737 			frame = 4;
738 		} else {
739 			frame = 2;
740 		}
741 		break;
742 	case STATE_GRAB_DOWN:
743 		if (joeFacing() == DIR_BACK) {
744 			frame = 9;
745 		} else {
746 			frame = 8;
747 		}
748 		break;
749 	case STATE_GRAB_UP:
750 		// turn back
751 		_vm->bankMan()->unpack(5, 31, 7);
752 		bobJoe->xflip = (joeFacing() == DIR_LEFT);
753 		bobJoe->scale = joeScale();
754 		_vm->update();
755 		// grab up
756 		_vm->bankMan()->unpack(7, 31, 7);
757 		bobJoe->xflip = (joeFacing() == DIR_LEFT);
758 		bobJoe->scale = joeScale();
759 		_vm->update();
760 		// turn back
761 		frame = 7;
762 		break;
763 	default:
764 		break;
765 	}
766 
767 	if (frame != 0) {
768 		_vm->bankMan()->unpack(frame, 31, 7);
769 		bobJoe->xflip = (joeFacing() == DIR_LEFT);
770 		bobJoe->scale = joeScale();
771 		_vm->update();
772 
773 		// extra delay for grab down
774 		if (grabState == STATE_GRAB_DOWN) {
775 			_vm->update();
776 			_vm->update();
777 		}
778 	}
779 }
780 
joeUseDress(bool showCut)781 void Logic::joeUseDress(bool showCut) {
782 	if (showCut) {
783 		joeFacing(DIR_FRONT);
784 		joeFace();
785 		if (gameState(VAR_JOE_DRESSING_MODE) == 0) {
786 			playCutaway("CDRES.CUT");
787 			inventoryInsertItem(ITEM_CLOTHES);
788 		} else {
789 			playCutaway("CUDRS.CUT");
790 		}
791 	}
792 	_vm->display()->palSetJoeDress();
793 	loadJoeBanks("JOED_A.BBK", "JOED_B.BBK");
794 	inventoryDeleteItem(ITEM_DRESS);
795 	gameState(VAR_JOE_DRESSING_MODE, 2);
796 }
797 
joeUseClothes(bool showCut)798 void Logic::joeUseClothes(bool showCut) {
799 	if (showCut) {
800 		joeFacing(DIR_FRONT);
801 		joeFace();
802 		playCutaway("CDCLO.CUT");
803 		inventoryInsertItem(ITEM_DRESS);
804 	}
805 	_vm->display()->palSetJoeNormal();
806 	loadJoeBanks("JOE_A.BBK", "JOE_B.BBK");
807 	inventoryDeleteItem(ITEM_CLOTHES);
808 	gameState(VAR_JOE_DRESSING_MODE, 0);
809 }
810 
joeUseUnderwear()811 void Logic::joeUseUnderwear() {
812 	_vm->display()->palSetJoeNormal();
813 	loadJoeBanks("JOEU_A.BBK", "JOEU_B.BBK");
814 	gameState(VAR_JOE_DRESSING_MODE, 1);
815 }
816 
makePersonSpeak(const char * sentence,Person * person,const char * voiceFilePrefix)817 void Logic::makePersonSpeak(const char *sentence, Person *person, const char *voiceFilePrefix) {
818 	_vm->command()->clear(false);
819 	Talk::speak(sentence, person, voiceFilePrefix, _vm);
820 }
821 
startDialogue(const char * dlgFile,int personInRoom,char * cutaway)822 void Logic::startDialogue(const char *dlgFile, int personInRoom, char *cutaway) {
823 	ObjectData *data = objectData(_roomData[_currentRoom] + personInRoom);
824 	if (data->name > 0 && data->entryObj <= 0) {
825 		if (State::findTalk(data->state) == STATE_TALK_MUTE) {
826 			// 'I can't talk to that'
827 			makeJoeSpeak(24 + _vm->randomizer.getRandomNumber(2));
828 		} else {
829 			char cutawayFile[20];
830 			if (cutaway == NULL) {
831 				cutaway = cutawayFile;
832 			}
833 			_vm->display()->fullscreen(true);
834 			Talk::talk(dlgFile, personInRoom, cutaway, _vm);
835 			if (!cutaway[0]) {
836 				_vm->display()->fullscreen(false);
837 			}
838 		}
839 	}
840 }
841 
playCutaway(const char * cutFile,char * next)842 void Logic::playCutaway(const char *cutFile, char *next) {
843 	char nextFile[20];
844 	if (next == NULL) {
845 		next = nextFile;
846 	}
847 	_vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
848 	Cutaway::run(cutFile, next, _vm);
849 }
850 
makeJoeSpeak(uint16 descNum,bool objectType)851 void Logic::makeJoeSpeak(uint16 descNum, bool objectType) {
852 	const char *text = objectType ? objectTextualDescription(descNum) : joeResponse(descNum);
853 	if (objectType) {
854 		descNum += JOE_RESPONSE_MAX;
855 	}
856 	char descFilePrefix[10];
857 	sprintf(descFilePrefix, "JOE%04i", descNum);
858 	makePersonSpeak(text, NULL, descFilePrefix);
859 }
860 
findInventoryItem(int invSlot) const861 uint16 Logic::findInventoryItem(int invSlot) const {
862 	// queen.c l.3894-3898
863 	if (invSlot >= 0 && invSlot < 4) {
864 		return _inventoryItem[invSlot];
865 	}
866 	return 0;
867 }
868 
inventorySetup()869 void Logic::inventorySetup() {
870 	_vm->bankMan()->load("OBJECTS.BBK", 14);
871 	if (_vm->resource()->isInterview()) {
872 		_inventoryItem[0] = 1;
873 		_inventoryItem[1] = 2;
874 		_inventoryItem[2] = 3;
875 		_inventoryItem[3] = 4;
876 	} else {
877 		_inventoryItem[0] = ITEM_BAT;
878 		_inventoryItem[1] = ITEM_JOURNAL;
879 		_inventoryItem[2] = ITEM_NONE;
880 		_inventoryItem[3] = ITEM_NONE;
881 	}
882 }
883 
inventoryRefresh()884 void Logic::inventoryRefresh() {
885 	uint16 x = 182;
886 	for (int i = 0; i < 4; ++i) {
887 		uint16 itemNum = _inventoryItem[i];
888 		if (itemNum != 0) {
889 			uint16 dstFrame = (i == 0) ? 8 : 9;
890 			// unpack frame for object and draw it
891 			_vm->bankMan()->unpack(_itemData[itemNum].frame, dstFrame, 14);
892 			_vm->graphics()->drawInventoryItem(dstFrame, x, 14);
893 		} else {
894 			// no object, clear the panel
895 			_vm->graphics()->drawInventoryItem(0, x, 14);
896 		}
897 		x += 35;
898 	}
899 }
900 
previousInventoryItem(int16 first) const901 int16 Logic::previousInventoryItem(int16 first) const {
902 	int i;
903 	for (i = first - 1; i >= 1; i--)
904 		if (_itemData[i].name > 0)
905 			return i;
906 	for (i = _numItems; i > first; i--)
907 		if (_itemData[i].name > 0)
908 			return i;
909 
910 	return 0;	//nothing found
911 }
912 
nextInventoryItem(int16 first) const913 int16 Logic::nextInventoryItem(int16 first) const {
914 	int i;
915 	for (i = first + 1; i < _numItems; i++)
916 		if (_itemData[i].name > 0)
917 			return i;
918 	for (i = 1; i < first; i++)
919 		if (_itemData[i].name > 0)
920 			return i;
921 
922 	return 0;	//nothing found
923 }
924 
removeDuplicateItems()925 void Logic::removeDuplicateItems() {
926 	for (int i = 0; i < 4; i++)
927 		for (int j = i + 1; j < 4; j++)
928 			if (_inventoryItem[i] == _inventoryItem[j])
929 				_inventoryItem[j] = ITEM_NONE;
930 }
931 
numItemsInventory() const932 uint16 Logic::numItemsInventory() const {
933 	uint16 count = 0;
934 	for (int i = 1; i < _numItems; i++)
935 		if (_itemData[i].name > 0)
936 			count++;
937 
938 	return count;
939 }
940 
inventoryInsertItem(uint16 itemNum,bool refresh)941 void Logic::inventoryInsertItem(uint16 itemNum, bool refresh) {
942 	int16 item = _inventoryItem[0] = (int16)itemNum;
943 	_itemData[itemNum].name = ABS(_itemData[itemNum].name);	//set visible
944 	for (int i = 1; i < 4; i++) {
945 		item = nextInventoryItem(item);
946 		_inventoryItem[i] = item;
947 		removeDuplicateItems();
948 	}
949 
950 	if (refresh)
951 		inventoryRefresh();
952 }
953 
inventoryDeleteItem(uint16 itemNum,bool refresh)954 void Logic::inventoryDeleteItem(uint16 itemNum, bool refresh) {
955 	int16 item = (int16)itemNum;
956 	_itemData[itemNum].name = -ABS(_itemData[itemNum].name);	//set invisible
957 	for (int i = 0; i < 4; i++) {
958 		item = nextInventoryItem(item);
959 		_inventoryItem[i] = item;
960 		removeDuplicateItems();
961 	}
962 
963 	if (refresh)
964 		inventoryRefresh();
965 }
966 
inventoryScroll(uint16 count,bool up)967 void Logic::inventoryScroll(uint16 count, bool up) {
968 	if (!(numItemsInventory() > 4))
969 		return;
970 	while (count--) {
971 		if (up) {
972 			for (int i = 3; i > 0; i--)
973 				_inventoryItem[i] = _inventoryItem[i - 1];
974 			_inventoryItem[0] = previousInventoryItem(_inventoryItem[0]);
975 		} else {
976 			for (int i = 0; i < 3; i++)
977 				_inventoryItem[i] = _inventoryItem[i + 1];
978 			_inventoryItem[3] = nextInventoryItem(_inventoryItem[3]);
979 		}
980 	}
981 
982 	inventoryRefresh();
983 }
984 
removeHotelItemsFromInventory()985 void Logic::removeHotelItemsFromInventory() {
986 	if (currentRoom() == 1 && gameState(VAR_HOTEL_ITEMS_REMOVED) == 0) {
987 		inventoryDeleteItem(ITEM_CROWBAR, false);
988 		inventoryDeleteItem(ITEM_DRESS, false);
989 		inventoryDeleteItem(ITEM_CLOTHES, false);
990 		inventoryDeleteItem(ITEM_HAY, false);
991 		inventoryDeleteItem(ITEM_OIL, false);
992 		inventoryDeleteItem(ITEM_CHICKEN, false);
993 		gameState(VAR_HOTEL_ITEMS_REMOVED, 1);
994 		inventoryRefresh();
995 	}
996 }
997 
objectCopy(int dummyObjectIndex,int realObjectIndex)998 void Logic::objectCopy(int dummyObjectIndex, int realObjectIndex) {
999 	// copy data from dummy object to real object, if COPY_FROM object
1000 	// images are greater than COPY_TO Object images then swap the objects around.
1001 
1002 	ObjectData *dummyObject = objectData(dummyObjectIndex);
1003 	ObjectData *realObject  = objectData(realObjectIndex);
1004 
1005 	int fromState = (dummyObject->name < 0) ? -1 : 0;
1006 
1007 	int frameCountReal  = 1;
1008 	int frameCountDummy = 1;
1009 
1010 	int graphic = realObject->image;
1011 	if (graphic > 0) {
1012 		if (graphic > 5000)
1013 			graphic -= 5000;
1014 
1015 		GraphicData *data = graphicData(graphic);
1016 
1017 		if (data->lastFrame > 0)
1018 			frameCountReal = data->lastFrame - data->firstFrame + 1;
1019 
1020 		graphic = dummyObject->image;
1021 		if (graphic > 0) {
1022 			if (graphic > 5000)
1023 				graphic -= 5000;
1024 
1025 			data = graphicData(graphic);
1026 
1027 			if (data->lastFrame > 0)
1028 				frameCountDummy = data->lastFrame - data->firstFrame + 1;
1029 		}
1030 	}
1031 
1032 	ObjectData temp = *realObject;
1033 	*realObject = *dummyObject;
1034 
1035 	if (frameCountDummy > frameCountReal)
1036 		*dummyObject = temp;
1037 
1038 	realObject->name = ABS(realObject->name);
1039 
1040 	if (fromState == -1)
1041 		dummyObject->name = -ABS(dummyObject->name);
1042 
1043 	for (int i = 1; i <= _numWalkOffs; i++) {
1044 		WalkOffData *walkOff = &_walkOffData[i];
1045 		if (walkOff->entryObj == (int16)dummyObjectIndex) {
1046 			walkOff->entryObj = (int16)realObjectIndex;
1047 			break;
1048 		}
1049 	}
1050 }
1051 
handleSpecialArea(Direction facing,uint16 areaNum,uint16 walkDataNum)1052 void Logic::handleSpecialArea(Direction facing, uint16 areaNum, uint16 walkDataNum) {
1053 	// queen.c l.2838-2911
1054 	debug(9, "handleSpecialArea(%d, %d, %d)\n", facing, areaNum, walkDataNum);
1055 
1056 	// Stop animating Joe
1057 	_vm->graphics()->bob(0)->animating = false;
1058 
1059 	// Make Joe face the right direction
1060 	joeFacing(facing);
1061 	joeFace();
1062 
1063 	_newRoom = 0;
1064 	_entryObj = 0;
1065 
1066 	char nextCut[20];
1067 	memset(nextCut, 0, sizeof(nextCut));
1068 
1069 	switch (_currentRoom) {
1070 	case ROOM_JUNGLE_BRIDGE:
1071 		makeJoeSpeak(16);
1072 		break;
1073 	case ROOM_JUNGLE_GORILLA_1:
1074 		playCutaway("C6C.CUT", nextCut);
1075 		break;
1076 	case ROOM_JUNGLE_GORILLA_2:
1077 		playCutaway("C14B.CUT", nextCut);
1078 		break;
1079 	case ROOM_AMAZON_ENTRANCE:
1080 		if (areaNum == 3) {
1081 			playCutaway("C16A.CUT", nextCut);
1082 		}
1083 		break;
1084 	case ROOM_AMAZON_HIDEOUT:
1085 		if (walkDataNum == 4) {
1086 			playCutaway("C17A.CUT", nextCut);
1087 		} else if (walkDataNum == 2) {
1088 			playCutaway("C17B.CUT", nextCut);
1089 		}
1090 		break;
1091 	case ROOM_FLODA_OUTSIDE:
1092 		playCutaway("C22A.CUT", nextCut);
1093 		break;
1094 	case ROOM_FLODA_KITCHEN:
1095 		playCutaway("C26B.CUT", nextCut);
1096 		break;
1097 	case ROOM_FLODA_KLUNK:
1098 		playCutaway("C30A.CUT", nextCut);
1099 		break;
1100 	case ROOM_FLODA_HENRY:
1101 		playCutaway("C32C.CUT", nextCut);
1102 		break;
1103 	case ROOM_TEMPLE_ZOMBIES:
1104 		if (areaNum == 6) {
1105 			switch (gameState(VAR_BYPASS_ZOMBIES)) {
1106 			case 0:
1107 				playCutaway("C50D.CUT", nextCut);
1108 				while (nextCut[0] != '\0') {
1109 					playCutaway(nextCut, nextCut);
1110 				}
1111 				gameState(VAR_BYPASS_ZOMBIES, 1);
1112 				break;
1113 			case 1:
1114 				playCutaway("C50H.CUT", nextCut);
1115 				break;
1116 			default:
1117 				break;
1118 			}
1119 		}
1120 		break;
1121 	case ROOM_TEMPLE_SNAKE:
1122 		playCutaway("C53B.CUT", nextCut);
1123 		break;
1124 	case ROOM_TEMPLE_LIZARD_LASER:
1125 		makeJoeSpeak(19);
1126 		break;
1127 	case ROOM_HOTEL_DOWNSTAIRS:
1128 		makeJoeSpeak(21);
1129 		break;
1130 	case ROOM_HOTEL_LOBBY:
1131 		switch (gameState(VAR_HOTEL_ESCAPE_STATE)) {
1132 		case 0:
1133 			playCutaway("C73A.CUT");
1134 			joeUseUnderwear();
1135 			joeFace();
1136 			gameState(VAR_HOTEL_ESCAPE_STATE, 1);
1137 			break;
1138 		case 1:
1139 			playCutaway("C73B.CUT");
1140 			gameState(VAR_HOTEL_ESCAPE_STATE, 2);
1141 			break;
1142 		case 2:
1143 			playCutaway("C73C.CUT");
1144 			break;
1145 		default:
1146 			break;
1147 		}
1148 		break;
1149 	case ROOM_TEMPLE_MAZE_5:
1150 		if (areaNum == 7) {
1151 			makeJoeSpeak(17);
1152 		}
1153 		break;
1154 	case ROOM_TEMPLE_MAZE_6:
1155 		if (areaNum == 5 && gameState(187) == 0) {
1156 			playCutaway("C101B.CUT", nextCut);
1157 		}
1158 		break;
1159 	case ROOM_FLODA_FRONTDESK:
1160 		if (areaNum == 3) {
1161 			switch (gameState(VAR_BYPASS_FLODA_RECEPTIONIST)) {
1162 			case 0:
1163 				playCutaway("C103B.CUT", nextCut);
1164 				gameState(VAR_BYPASS_FLODA_RECEPTIONIST, 1);
1165 				break;
1166 			case 1:
1167 				playCutaway("C103E.CUT", nextCut);
1168 				break;
1169 			default:
1170 				break;
1171 			}
1172 		}
1173 		break;
1174 	default:
1175 		break;
1176 	}
1177 
1178 	while (strlen(nextCut) > 4 &&
1179 		scumm_stricmp(nextCut + strlen(nextCut) - 4, ".CUT") == 0) {
1180 		playCutaway(nextCut, nextCut);
1181 	}
1182 }
1183 
handlePinnacleRoom()1184 void Logic::handlePinnacleRoom() {
1185 	// camera does not follow Joe anymore
1186 	_vm->graphics()->putCameraOnBob(-1);
1187 	displayRoom(ROOM_JUNGLE_PINNACLE, RDM_NOFADE_JOE, 100, 2, true);
1188 
1189 	BobSlot *joe   = _vm->graphics()->bob(6);
1190 	BobSlot *piton = _vm->graphics()->bob(7);
1191 
1192 	// set scrolling value to mouse position to avoid glitch
1193 	Common::Point mouse = _vm->input()->getMousePos();
1194 	_vm->display()->horizontalScroll(mouse.x);
1195 
1196 	joe->x = piton->x = 3 * mouse.x / 4 + 200;
1197 	joe->frameNum = mouse.x / 36 + 45;
1198 
1199 	// bobs have been unpacked from animating objects, we don't need them
1200 	// to animate anymore ; so turn animation off
1201 	joe->animating = piton->animating = false;
1202 
1203 	_vm->update();
1204 	_vm->display()->palFadeIn(ROOM_JUNGLE_PINNACLE, joe->active, joe->x, joe->y);
1205 
1206 	_entryObj = 0;
1207 	uint16 prevObj = 0;
1208 	CmdText *cmdText = CmdText::makeCmdTextInstance(5, _vm);
1209 	cmdText->setVerb(VERB_WALK_TO);
1210 	while (!_vm->shouldQuit() && (_vm->input()->mouseButton() == 0 || _entryObj == 0)) {
1211 
1212 		_vm->update();
1213 		mouse = _vm->input()->getMousePos();
1214 
1215 		// update bobs position / frame
1216 		joe->x = piton->x = 3 * mouse.x / 4 + 200;
1217 		joe->frameNum = mouse.x / 36 + 45;
1218 
1219 		_vm->display()->clearTexts(5, 5);
1220 
1221 		uint16 curObj = _vm->grid()->findObjectUnderCursor(mouse.x, mouse.y);
1222 		if (curObj != 0 && curObj != prevObj) {
1223 			_entryObj = 0;
1224 			curObj += currentRoomData(); // global object number
1225 			ObjectData *objData = objectData(curObj);
1226 			if (objData->name > 0) {
1227 				_entryObj = objData->entryObj;
1228 				cmdText->displayTemp(INK_PINNACLE_ROOM, objectName(objData->name), true);
1229 			}
1230 			prevObj = curObj;
1231 		}
1232 
1233 		// update screen scrolling
1234 		_vm->display()->horizontalScroll(mouse.x);
1235 	}
1236 	delete cmdText;
1237 	_vm->input()->clearMouseButton();
1238 
1239 	_newRoom = objectData(_entryObj)->room;
1240 
1241 	// Only a few commands can be triggered from this room :
1242 	// piton -> crash  : 0x216 (obj1=0x2a, song=3)
1243 	// piton -> floda  : 0x217 (obj1=0x29, song=16)
1244 	// piton -> bob    : 0x219 (obj1=0x2f, song=6)
1245 	// piton -> embark : 0x218 (obj1=0x2c, song=7)
1246 	// piton -> jungle : 0x20B (obj1=0x2b, song=3)
1247 	// piton -> amazon : 0x21A (obj1=0x30, song=3)
1248 	//
1249 	// Because none of these update objects/areas/gamestate, the EXECUTE_ACTION()
1250 	// call, as the original does, is useless. All we have to do is the playsong
1251 	// call (all songs have the PLAY_BEFORE type). This way we could get rid of
1252 	// the hack described in execute.c l.334-339.
1253 	struct {
1254 		uint16 obj;
1255 		int16 song;
1256 	} cmds[] = {
1257 		{ 0x2A,  3 },
1258 		{ 0x29, 16 },
1259 		{ 0x2F,  6 },
1260 		{ 0x2C,  7 },
1261 		{ 0x2B,  3 },
1262 		{ 0x30,  3 }
1263 	};
1264 	for (int i = 0; i < ARRAYSIZE(cmds); ++i) {
1265 		if (cmds[i].obj == prevObj) {
1266 			_vm->sound()->playSong(cmds[i].song);
1267 			break;
1268 		}
1269 	}
1270 
1271 	joe->active = piton->active = false;
1272 	_vm->display()->clearTexts(5, 5);
1273 
1274 	// camera follows Joe again
1275 	_vm->graphics()->putCameraOnBob(0);
1276 
1277 	_vm->display()->palFadeOut(ROOM_JUNGLE_PINNACLE);
1278 }
1279 
update()1280 void Logic::update() {
1281 	if (_credits)
1282 		_credits->update();
1283 
1284 	if (_vm->debugger()->flags() & Debugger::DF_DRAW_AREAS) {
1285 		_vm->grid()->drawZones();
1286 	}
1287 }
1288 
saveState(byte * & ptr)1289 void Logic::saveState(byte *&ptr) {
1290 	uint16 i;
1291 	for (i = 0; i < 4; i++) {
1292 		WRITE_BE_UINT16(ptr, _inventoryItem[i]); ptr += 2;
1293 	}
1294 
1295 	WRITE_BE_UINT16(ptr, _vm->graphics()->bob(0)->x); ptr += 2;
1296 	WRITE_BE_UINT16(ptr, _vm->graphics()->bob(0)->y); ptr += 2;
1297 
1298 	WRITE_BE_UINT16(ptr, _currentRoom); ptr += 2;
1299 
1300 	for (i = 1; i <= _numObjects; i++)
1301 		_objectData[i].writeToBE(ptr);
1302 
1303 	for (i = 1; i <= _numItems; i++)
1304 		_itemData[i].writeToBE(ptr);
1305 
1306 	for (i = 0; i < GAME_STATE_COUNT; i++) {
1307 		WRITE_BE_UINT16(ptr, _gameState[i]); ptr += 2;
1308 	}
1309 
1310 	for (i = 0; i < TALK_SELECTED_COUNT; i++)
1311 		_talkSelected[i].writeToBE(ptr);
1312 
1313 	for (i = 1; i <= _numWalkOffs; i++)
1314 		_walkOffData[i].writeToBE(ptr);
1315 
1316 	WRITE_BE_UINT16(ptr, _joe.facing); ptr += 2;
1317 
1318 	// V1
1319 	WRITE_BE_UINT16(ptr, _puzzleAttemptCount); ptr += 2;
1320 	for (i = 1; i <= _numObjDesc; i++)
1321 		_objectDescription[i].writeToBE(ptr);
1322 }
1323 
loadState(uint32 ver,byte * & ptr)1324 void Logic::loadState(uint32 ver, byte *&ptr) {
1325 	uint16 i;
1326 	for (i = 0; i < 4; i++) {
1327 		_inventoryItem[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
1328 	}
1329 
1330 	_joe.x = (int16)READ_BE_INT16(ptr); ptr += 2;
1331 	_joe.y = (int16)READ_BE_INT16(ptr); ptr += 2;
1332 
1333 	_currentRoom = READ_BE_UINT16(ptr); ptr += 2;
1334 
1335 	for (i = 1; i <= _numObjects; i++)
1336 		_objectData[i].readFromBE(ptr);
1337 
1338 	for (i = 1; i <= _numItems; i++)
1339 		_itemData[i].readFromBE(ptr);
1340 
1341 	for (i = 0; i < GAME_STATE_COUNT; i++) {
1342 		_gameState[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
1343 	}
1344 
1345 	for (i = 0; i < TALK_SELECTED_COUNT; i++)
1346 		_talkSelected[i].readFromBE(ptr);
1347 
1348 	for (i = 1; i <= _numWalkOffs; i++)
1349 		_walkOffData[i].readFromBE(ptr);
1350 
1351 	_joe.facing = READ_BE_UINT16(ptr); ptr += 2;
1352 
1353 	if (ver >= 1) {
1354 		_puzzleAttemptCount = READ_BE_UINT16(ptr); ptr += 2;
1355 
1356 		for (i = 1; i <= _numObjDesc; i++)
1357 			_objectDescription[i].readFromBE(ptr);
1358 	}
1359 }
1360 
setupRestoredGame()1361 void Logic::setupRestoredGame() {
1362 	_vm->sound()->playLastSong();
1363 
1364 	switch (gameState(VAR_JOE_DRESSING_MODE)) {
1365 	case 0:
1366 		_vm->display()->palSetJoeNormal();
1367 		loadJoeBanks("JOE_A.BBK", "JOE_B.BBK");
1368 		break;
1369 	case 1:
1370 		_vm->display()->palSetJoeNormal();
1371 		loadJoeBanks("JOEU_A.BBK", "JOEU_B.BBK");
1372 		break;
1373 	case 2:
1374 		_vm->display()->palSetJoeDress();
1375 		loadJoeBanks("JOED_A.BBK", "JOED_B.BBK");
1376 		break;
1377 	default:
1378 		break;
1379 	}
1380 
1381 	BobSlot *pbs = _vm->graphics()->bob(0);
1382 	pbs->xflip = (joeFacing() == DIR_LEFT);
1383 	joePrevFacing(joeFacing());
1384 	joeCutFacing(joeFacing());
1385 	switch (joeFacing()) {
1386 	case DIR_FRONT:
1387 		pbs->frameNum = 36;
1388 		_vm->bankMan()->unpack(3, 31, 7);
1389 		break;
1390 	case DIR_BACK:
1391 		pbs->frameNum = 37;
1392 		_vm->bankMan()->unpack(5, 31, 7);
1393 		break;
1394 	default:
1395 		pbs->frameNum = 35;
1396 		_vm->bankMan()->unpack(1, 31, 7);
1397 		break;
1398 	}
1399 
1400 	_oldRoom = 0;
1401 	_newRoom = _currentRoom;
1402 	_entryObj = 0;
1403 
1404 	if (_vm->bam()->_flag != BamScene::F_STOP) {
1405 		_vm->bam()->prepareAnimation();
1406 	}
1407 
1408 	inventoryRefresh();
1409 }
1410 
sceneStart()1411 void Logic::sceneStart() {
1412 	debug(6, "[Logic::sceneStart] _scene = %i", _scene);
1413 	_scene++;
1414 
1415 	_vm->display()->showMouseCursor(false);
1416 
1417 	if (1 == _scene) {
1418 		_vm->display()->palGreyPanel();
1419 	}
1420 
1421 	_vm->update();
1422 }
1423 
sceneStop()1424 void Logic::sceneStop() {
1425 	debug(6, "[Logic::sceneStop] _scene = %i", _scene);
1426 	_scene--;
1427 
1428 	if (_scene > 0)
1429 		return;
1430 
1431 	_vm->display()->palSetAllDirty();
1432 	_vm->display()->showMouseCursor(true);
1433 	_vm->grid()->setupPanel();
1434 }
1435 
changeRoom()1436 void Logic::changeRoom() {
1437 	if (!changeToSpecialRoom())
1438 		displayRoom(currentRoom(), RDM_FADE_JOE, 100, 1, false);
1439 	_vm->display()->showMouseCursor(true);
1440 }
1441 
executeSpecialMove(uint16 sm)1442 void Logic::executeSpecialMove(uint16 sm) {
1443 	debug(6, "Special move: %d", sm);
1444 	if (sm < ARRAYSIZE(_specialMoves) && _specialMoves[sm] != 0) {
1445 		(this->*_specialMoves[sm])();
1446 	}
1447 }
1448 
asmMakeJoeUseDress()1449 void Logic::asmMakeJoeUseDress() {
1450 	joeUseDress(false);
1451 }
1452 
asmMakeJoeUseNormalClothes()1453 void Logic::asmMakeJoeUseNormalClothes() {
1454 	joeUseClothes(false);
1455 }
1456 
asmMakeJoeUseUnderwear()1457 void Logic::asmMakeJoeUseUnderwear() {
1458 	joeUseUnderwear();
1459 }
1460 
asmSwitchToDressPalette()1461 void Logic::asmSwitchToDressPalette() {
1462 	_vm->display()->palSetJoeDress();
1463 }
1464 
asmSwitchToNormalPalette()1465 void Logic::asmSwitchToNormalPalette() {
1466 	_vm->display()->palSetJoeNormal();
1467 }
1468 
asmStartCarAnimation()1469 void Logic::asmStartCarAnimation() {
1470 	_vm->bam()->_flag = BamScene::F_PLAY;
1471 	_vm->bam()->prepareAnimation();
1472 }
1473 
asmStopCarAnimation()1474 void Logic::asmStopCarAnimation() {
1475 	_vm->bam()->_flag = BamScene::F_STOP;
1476 	_vm->graphics()->bob(findBob(594))->active = false; // oil object
1477 	_vm->graphics()->bob(7)->active = false; // gun shots
1478 }
1479 
asmStartFightAnimation()1480 void Logic::asmStartFightAnimation() {
1481 	_vm->bam()->_flag = BamScene::F_PLAY;
1482 	_vm->bam()->prepareAnimation();
1483 	gameState(148, 1);
1484 }
1485 
asmWaitForFrankPosition()1486 void Logic::asmWaitForFrankPosition() {
1487 	_vm->bam()->_flag = BamScene::F_REQ_STOP;
1488 	while (_vm->bam()->_flag != BamScene::F_STOP) {
1489 		_vm->update();
1490 	}
1491 }
1492 
asmMakeFrankGrowing()1493 void Logic::asmMakeFrankGrowing() {
1494 	_vm->bankMan()->unpack(1, 38, 15);
1495 	BobSlot *bobFrank = _vm->graphics()->bob(5);
1496 	bobFrank->frameNum = 38;
1497 	if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) {
1498 		bobFrank->active = true;
1499 		bobFrank->x = 160;
1500 		bobFrank->scale = 100;
1501 		for (int i = 350; i >= 200; i -= 5) {
1502 			bobFrank->y = i;
1503 			_vm->update();
1504 		}
1505 	} else {
1506 		bobFrank->curPos(160, 200);
1507 		for (int i = 10; i <= 100; i += 4) {
1508 			bobFrank->scale = i;
1509 			_vm->update();
1510 		}
1511 	}
1512 	for (int i = 0; i <= 20; ++i) {
1513 		_vm->update();
1514 	}
1515 
1516 	objectData(521)->name =  ABS(objectData(521)->name); // Dinoray
1517 	objectData(526)->name =  ABS(objectData(526)->name); // Frank obj
1518 	objectData(522)->name = -ABS(objectData(522)->name); // TMPD object off
1519 	objectData(525)->name = -ABS(objectData(525)->name); // Floda guards off
1520 	objectData(523)->name = -ABS(objectData(523)->name); // Sparky object off
1521 	gameState(157, 1); // No more Ironstein
1522 }
1523 
asmMakeRobotGrowing()1524 void Logic::asmMakeRobotGrowing() {
1525 	_vm->bankMan()->unpack(1, 38, 15);
1526 	BobSlot *bobRobot = _vm->graphics()->bob(5);
1527 	bobRobot->frameNum = 38;
1528 	if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) {
1529 		bobRobot->active = true;
1530 		bobRobot->x = 160;
1531 		bobRobot->scale = 100;
1532 		for (int i = 350; i >= 200; i -= 5) {
1533 			bobRobot->y = i;
1534 			_vm->update();
1535 		}
1536 	} else {
1537 		bobRobot->curPos(160, 200);
1538 		for (int i = 10; i <= 100; i += 4) {
1539 			bobRobot->scale = i;
1540 			_vm->update();
1541 		}
1542 	}
1543 	for (int i = 0; i <= 20; ++i) {
1544 		_vm->update();
1545 	}
1546 
1547 	objectData(524)->name = -ABS(objectData(524)->name); // Azura object off
1548 	objectData(526)->name = -ABS(objectData(526)->name); // Frank object off
1549 }
1550 
asmShrinkRobot()1551 void Logic::asmShrinkRobot() {
1552 	int i;
1553 	BobSlot *robot = _vm->graphics()->bob(6);
1554 	for (i = 100; i >= 35; i -= 5) {
1555 		robot->scale = i;
1556 		_vm->update();
1557 	}
1558 }
1559 
asmEndGame()1560 void Logic::asmEndGame() {
1561 	int n = 40;
1562 	while (n--) {
1563 		_vm->update();
1564 	}
1565 //	debug("Game completed.");
1566 	_vm->quitGame();
1567 }
1568 
asmPutCameraOnDino()1569 void Logic::asmPutCameraOnDino() {
1570 	_vm->graphics()->putCameraOnBob(-1);
1571 	int16 scrollx = _vm->display()->horizontalScroll();
1572 	while (scrollx < 320) {
1573 		scrollx += 16;
1574 		if (scrollx > 320) {
1575 			scrollx = 320;
1576 		}
1577 		_vm->display()->horizontalScroll(scrollx);
1578 		_vm->update();
1579 	}
1580 	_vm->graphics()->putCameraOnBob(1);
1581 }
1582 
asmPutCameraOnJoe()1583 void Logic::asmPutCameraOnJoe() {
1584 	_vm->graphics()->putCameraOnBob(0);
1585 }
1586 
asmAltIntroPanRight()1587 void Logic::asmAltIntroPanRight() {
1588 	_vm->graphics()->putCameraOnBob(-1);
1589 	_vm->input()->fastMode(true);
1590 	_vm->update();
1591 	int16 scrollx = _vm->display()->horizontalScroll();
1592 	while (scrollx < 285 && !_vm->input()->cutawayQuit()) {
1593 		++scrollx;
1594 		if (scrollx > 285) {
1595 			scrollx = 285;
1596 		}
1597 		_vm->display()->horizontalScroll(scrollx);
1598 		_vm->update();
1599 	}
1600 	_vm->input()->fastMode(false);
1601 }
1602 
asmAltIntroPanLeft()1603 void Logic::asmAltIntroPanLeft() {
1604 	_vm->graphics()->putCameraOnBob(-1);
1605 	_vm->input()->fastMode(true);
1606 	int16 scrollx = _vm->display()->horizontalScroll();
1607 	while (scrollx > 0 && !_vm->input()->cutawayQuit()) {
1608 		scrollx -= 4;
1609 		if (scrollx < 0) {
1610 			scrollx = 0;
1611 		}
1612 		_vm->display()->horizontalScroll(scrollx);
1613 		_vm->update();
1614 	}
1615 	_vm->input()->fastMode(false);
1616 }
1617 
asmSetAzuraInLove()1618 void Logic::asmSetAzuraInLove() {
1619 	gameState(VAR_AZURA_IN_LOVE, 1);
1620 }
1621 
asmPanRightFromJoe()1622 void Logic::asmPanRightFromJoe() {
1623 	_vm->graphics()->putCameraOnBob(-1);
1624 	int16 scrollx = _vm->display()->horizontalScroll();
1625 	while (scrollx < 320) {
1626 		scrollx += 16;
1627 		if (scrollx > 320) {
1628 			scrollx = 320;
1629 		}
1630 		_vm->display()->horizontalScroll(scrollx);
1631 		_vm->update();
1632 	}
1633 }
1634 
asmSetLightsOff()1635 void Logic::asmSetLightsOff() {
1636 	_vm->display()->palCustomLightsOff(currentRoom());
1637 }
1638 
asmSetLightsOn()1639 void Logic::asmSetLightsOn() {
1640 	_vm->display()->palCustomLightsOn(currentRoom());
1641 }
1642 
asmSetManequinAreaOn()1643 void Logic::asmSetManequinAreaOn() {
1644 	Area *a = _vm->grid()->area(ROOM_FLODA_FRONTDESK, 7);
1645 	a->mapNeighbors = ABS(a->mapNeighbors);
1646 }
1647 
asmPanToJoe()1648 void Logic::asmPanToJoe() {
1649 	int i = _vm->graphics()->bob(0)->x - 160;
1650 	if (i < 0) {
1651 		i = 0;
1652 	} else if (i > 320) {
1653 		i = 320;
1654 	}
1655 	_vm->graphics()->putCameraOnBob(-1);
1656 	int16 scrollx = _vm->display()->horizontalScroll();
1657 	if (i < scrollx) {
1658 		while (scrollx > i) {
1659 			scrollx -= 16;
1660 			if (scrollx < i) {
1661 				scrollx = i;
1662 			}
1663 			_vm->display()->horizontalScroll(scrollx);
1664 			_vm->update();
1665 		}
1666 	} else {
1667 		while (scrollx < i) {
1668 			scrollx += 16;
1669 			if (scrollx > i) {
1670 				scrollx = i;
1671 			}
1672 			_vm->display()->horizontalScroll(scrollx);
1673 			_vm->update();
1674 		}
1675 		_vm->update();
1676 	}
1677 	_vm->graphics()->putCameraOnBob(0);
1678 }
1679 
asmTurnGuardOn()1680 void Logic::asmTurnGuardOn() {
1681 	gameState(VAR_GUARDS_TURNED_ON, 1);
1682 }
1683 
asmPanLeft320To144()1684 void Logic::asmPanLeft320To144() {
1685 	_vm->graphics()->putCameraOnBob(-1);
1686 	int16 scrollx = _vm->display()->horizontalScroll();
1687 	while (scrollx > 144) {
1688 		scrollx -= 8;
1689 		if (scrollx < 144) {
1690 			scrollx = 144;
1691 		}
1692 		_vm->display()->horizontalScroll(scrollx);
1693 		_vm->update();
1694 	}
1695 }
1696 
asmSmooch()1697 void Logic::asmSmooch() {
1698 	_vm->graphics()->putCameraOnBob(-1);
1699 	BobSlot *bobAzura = _vm->graphics()->bob(5);
1700 	BobSlot *bobJoe = _vm->graphics()->bob(6);
1701 	int16 scrollx = _vm->display()->horizontalScroll();
1702 	while (scrollx < 320) {
1703 		scrollx += 8;
1704 		_vm->display()->horizontalScroll(scrollx);
1705 		if (bobJoe->x - bobAzura->x > 128) {
1706 			bobAzura->x += 10;
1707 			bobJoe->x += 6;
1708 		} else {
1709 			bobAzura->x += 8;
1710 			bobJoe->x += 8;
1711 		}
1712 		_vm->update();
1713 	}
1714 }
1715 
asmSmoochNoScroll()1716 void Logic::asmSmoochNoScroll() {
1717 	_vm->graphics()->putCameraOnBob(-1);
1718 	BobSlot *bobAzura = _vm->graphics()->bob(5);
1719 	BobSlot *bobJoe = _vm->graphics()->bob(6);
1720 	for (int i = 0; i < 320; i += 8) {
1721 		if (bobJoe->x - bobAzura->x > 128) {
1722 			bobAzura->x += 2;
1723 			bobJoe->x -= 2;
1724 		}
1725 		_vm->update();
1726 	}
1727 }
1728 
asmMakeLightningHitPlane()1729 void Logic::asmMakeLightningHitPlane() {
1730 	_vm->graphics()->putCameraOnBob(-1);
1731 	short iy = 0, x, ydir = -1, j, k;
1732 
1733 	BobSlot *planeBob     = _vm->graphics()->bob(5);
1734 	BobSlot *lightningBob = _vm->graphics()->bob(20);
1735 
1736 	planeBob->y = 135;
1737 
1738 	if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) {
1739 		planeBob->scale = 100;
1740 	} else {
1741 		planeBob->scale = 20;
1742 	}
1743 
1744 	for (x = 660; x > 163; x -= 6) {
1745 		planeBob->x = x;
1746 		planeBob->y = 135 + iy;
1747 
1748 		iy -= ydir;
1749 		if (iy < -9 || iy > 9)
1750 			ydir = -ydir;
1751 
1752 		planeBob->scale++;
1753 		if (planeBob->scale > 100)
1754 			planeBob->scale = 100;
1755 
1756 		int scrollX = x - 163;
1757 		if (scrollX > 320)
1758 			scrollX = 320;
1759 		_vm->display()->horizontalScroll(scrollX);
1760 		_vm->update();
1761 	}
1762 
1763 	planeBob->scale = 100;
1764 	_vm->display()->horizontalScroll(0);
1765 
1766 	planeBob->x += 8;
1767 	planeBob->y += 6;
1768 
1769 	lightningBob->x = 160;
1770 	lightningBob->y = 0;
1771 
1772 	_vm->sound()->playSfx(currentRoomSfx());
1773 
1774 	_vm->bankMan()->unpack(18, lightningBob->frameNum, 15);
1775 	_vm->bankMan()->unpack(4,  planeBob    ->frameNum, 15);
1776 
1777 	// Plane plunges into the jungle!
1778 	BobSlot *fireBob = _vm->graphics()->bob(6);
1779 
1780 	fireBob->animating = true;
1781 	fireBob->x = planeBob->x;
1782 	fireBob->y = planeBob->y + 10;
1783 
1784 	_vm->bankMan()->unpack(19, fireBob->frameNum, 15);
1785 	_vm->update();
1786 
1787 	k = 20;
1788 	j = 1;
1789 
1790 	for (x = 163; x > -30; x -= 10) {
1791 		planeBob->y += 4;
1792 		fireBob->y += 4;
1793 		planeBob->x = fireBob->x = x;
1794 
1795 		if (k < 40) {
1796 			_vm->bankMan()->unpack(j, planeBob->frameNum, 15);
1797 			_vm->bankMan()->unpack(k, fireBob ->frameNum, 15);
1798 			k++;
1799 			j++;
1800 
1801 			if (j == 4)
1802 				j = 1;
1803 		}
1804 
1805 		_vm->update();
1806 	}
1807 
1808 	_vm->graphics()->putCameraOnBob(0);
1809 }
1810 
asmScaleBlimp()1811 void Logic::asmScaleBlimp() {
1812 	int16 z = 256;
1813 	BobSlot *bob = _vm->graphics()->bob(7);
1814 	int16 x = bob->x;
1815 	int16 y = bob->y;
1816 	bob->scale = 100;
1817 	while (bob->x > 150 && !_vm->shouldQuit()) {
1818 		bob->x = x * 256 / z + 150;
1819 		bob->y = y * 256 / z + 112;
1820 		if (_vm->resource()->getPlatform() != Common::kPlatformAmiga) {
1821 			bob->scale = 100 * 256 / z;
1822 		}
1823 		++z;
1824 		if (z % 6 == 0) {
1825 			--x;
1826 		}
1827 
1828 		_vm->update();
1829 	}
1830 }
1831 
asmScaleEnding()1832 void Logic::asmScaleEnding() {
1833 	_vm->graphics()->bob(7)->active = false; // Turn off blimp
1834 	BobSlot *b = _vm->graphics()->bob(20);
1835 	b->curPos(160, 100);
1836 	if (_vm->resource()->getPlatform() != Common::kPlatformAmiga) {
1837 		for (int i = 5; i <= 100; i += 5) {
1838 			b->scale = i;
1839 			_vm->update();
1840 		}
1841 	}
1842 	for (int i = 0; i < 50; ++i) {
1843 		_vm->update();
1844 	}
1845 	_vm->display()->palFadeOut(_currentRoom);
1846 }
1847 
asmWaitForCarPosition()1848 void Logic::asmWaitForCarPosition() {
1849 	// Wait for car to reach correct position before pouring oil
1850 	while (_vm->bam()->_index != 60) {
1851 		_vm->update();
1852 	}
1853 }
1854 
asmShakeScreen()1855 void Logic::asmShakeScreen() {
1856 	_vm->display()->shake(false);
1857 	_vm->update();
1858 	_vm->display()->shake(true);
1859 	_vm->update();
1860 }
1861 
asmAttemptPuzzle()1862 void Logic::asmAttemptPuzzle() {
1863 	++_puzzleAttemptCount;
1864 	if (_puzzleAttemptCount == 4) {
1865 		makeJoeSpeak(226, true);
1866 		_puzzleAttemptCount = 0;
1867 	}
1868 }
1869 
asmScaleTitle()1870 void Logic::asmScaleTitle() {
1871 	BobSlot *bob = _vm->graphics()->bob(5);
1872 	bob->animating = false;
1873 	bob->x = 161;
1874 	bob->y = 200;
1875 	bob->scale = 100;
1876 
1877 	int i;
1878 	for (i = 5; i <= 100; i +=5) {
1879 		bob->scale = i;
1880 		bob->y -= 4;
1881 		_vm->update();
1882 	}
1883 }
1884 
asmScrollTitle()1885 void Logic::asmScrollTitle() {
1886 	BobSlot *bob = _vm->graphics()->bob(5);
1887 	bob->animating = false;
1888 	bob->x = 161;
1889 	bob->y = 300;
1890 	bob->scale = 100;
1891 	while (bob->y >= 120) {
1892 		_vm->update();
1893 		bob->y -= 4;
1894 	}
1895 }
1896 
asmPanRightToHugh()1897 void Logic::asmPanRightToHugh() {
1898 	BobSlot *bob_thugA1 = _vm->graphics()->bob(20);
1899 	BobSlot *bob_thugA2 = _vm->graphics()->bob(21);
1900 	BobSlot *bob_thugA3 = _vm->graphics()->bob(22);
1901 	BobSlot *bob_hugh1  = _vm->graphics()->bob(1);
1902 	BobSlot *bob_hugh2  = _vm->graphics()->bob(23);
1903 	BobSlot *bob_hugh3  = _vm->graphics()->bob(24);
1904 	BobSlot *bob_thugB1 = _vm->graphics()->bob(25);
1905 	BobSlot *bob_thugB2 = _vm->graphics()->bob(26);
1906 
1907 	_vm->graphics()->putCameraOnBob(-1);
1908 	_vm->input()->fastMode(true);
1909 	_vm->update();
1910 
1911 	// Adjust thug1 gun so it matches rest of body
1912 	bob_thugA1->x += 160 - 45;
1913 	bob_thugA2->x += 160;
1914 	bob_thugA3->x += 160;
1915 
1916 	bob_hugh1->x += 160 * 2;
1917 	bob_hugh2->x += 160 * 2;
1918 	bob_hugh3->x += 160 * 2;
1919 
1920 	bob_thugB1->x += 160 * 3;
1921 	bob_thugB2->x += 160 * 3;
1922 
1923 	int horizontalScroll = 0;
1924 	while (horizontalScroll < 160 && !_vm->input()->cutawayQuit()) {
1925 
1926 		horizontalScroll += 8;
1927 		if (horizontalScroll > 160)
1928 			horizontalScroll = 160;
1929 
1930 		_vm->display()->horizontalScroll(horizontalScroll);
1931 
1932 		bob_thugA1->x -= 16;
1933 		bob_thugA2->x -= 16;
1934 		bob_thugA3->x -= 16;
1935 
1936 		bob_hugh1->x -= 24;
1937 		bob_hugh2->x -= 24;
1938 		bob_hugh3->x -= 24;
1939 
1940 		bob_thugB1->x -= 32;
1941 		bob_thugB2->x -= 32;
1942 
1943 		_vm->update();
1944 	}
1945 
1946 	_vm->input()->fastMode(false);
1947 }
1948 
asmMakeWhiteFlash()1949 void Logic::asmMakeWhiteFlash() {
1950 	_vm->display()->palCustomFlash();
1951 }
1952 
asmPanRightToJoeAndRita()1953 void Logic::asmPanRightToJoeAndRita() { // cdint.cut
1954 	BobSlot *bob_box   = _vm->graphics()->bob(20);
1955 	BobSlot *bob_beam  = _vm->graphics()->bob(21);
1956 	BobSlot *bob_crate = _vm->graphics()->bob(22);
1957 	BobSlot *bob_clock = _vm->graphics()->bob(23);
1958 	BobSlot *bob_hands = _vm->graphics()->bob(24);
1959 
1960 	_vm->graphics()->putCameraOnBob(-1);
1961 	_vm->input()->fastMode(true);
1962 
1963 	_vm->update();
1964 
1965 	bob_box  ->x += 280 * 2;
1966 	bob_beam ->x += 30;
1967 	bob_crate->x += 180 * 3;
1968 
1969 	int horizontalScroll = _vm->display()->horizontalScroll();
1970 
1971 	while (horizontalScroll < 290 && !_vm->input()->cutawayQuit()) {
1972 
1973 		++horizontalScroll;
1974 		if (horizontalScroll > 290)
1975 			horizontalScroll = 290;
1976 
1977 		_vm->display()->horizontalScroll(horizontalScroll);
1978 
1979 		bob_box  ->x -= 2;
1980 		bob_beam ->x -= 1;
1981 		bob_crate->x -= 3;
1982 		bob_clock->x -= 2;
1983 		bob_hands->x -= 2;
1984 
1985 		_vm->update();
1986 	}
1987 	_vm->input()->fastMode(false);
1988 }
1989 
asmPanLeftToBomb()1990 void Logic::asmPanLeftToBomb() {
1991 	BobSlot *bob21 = _vm->graphics()->bob(21);
1992 	BobSlot *bob22 = _vm->graphics()->bob(22);
1993 
1994 	_vm->graphics()->putCameraOnBob(-1);
1995 	_vm->input()->fastMode(true);
1996 
1997 	int horizontalScroll = _vm->display()->horizontalScroll();
1998 
1999 	while ((horizontalScroll > 0 || bob21->x < 136) && !_vm->input()->cutawayQuit()) {
2000 
2001 		horizontalScroll -= 5;
2002 		if (horizontalScroll < 0)
2003 			horizontalScroll = 0;
2004 
2005 		_vm->display()->horizontalScroll(horizontalScroll);
2006 
2007 		if (horizontalScroll < 272 && bob21->x < 136)
2008 			bob21->x += 2;
2009 
2010 		bob22->x += 5;
2011 
2012 		_vm->update();
2013 	}
2014 
2015 	_vm->input()->fastMode(false);
2016 }
2017 
asmEndDemo()2018 void Logic::asmEndDemo() {
2019 //	debug("Flight of the Amazon Queen, released January 95.");
2020 	_vm->quitGame();
2021 }
2022 
asmInterviewIntro()2023 void Logic::asmInterviewIntro() {
2024 	// put camera on airship
2025 	_vm->graphics()->putCameraOnBob(5);
2026 	BobSlot *bas = _vm->graphics()->bob(5);
2027 
2028 	bas->curPos(-30, 40);
2029 
2030 	bas->move(700, 10, 3);
2031 	int scale = 450;
2032 	while (bas->moving && !_vm->input()->cutawayQuit()) {
2033 		bas->scale = 256 * 100 / scale;
2034 		--scale;
2035 		if (scale < 256) {
2036 			scale = 256;
2037 		}
2038 		_vm->update();
2039 	}
2040 
2041 	bas->scale = 90;
2042 	bas->xflip = true;
2043 
2044 	bas->move(560, 25, 4);
2045 	while (bas->moving && !_vm->input()->cutawayQuit()) {
2046 		_vm->update();
2047 	}
2048 
2049 	bas->move(545, 65, 2);
2050 	while (bas->moving && !_vm->input()->cutawayQuit()) {
2051 		_vm->update();
2052 	}
2053 
2054 	bas->move(540, 75, 2);
2055 	while (bas->moving && !_vm->input()->cutawayQuit()) {
2056 		_vm->update();
2057 	}
2058 
2059 	// put camera on Joe
2060 	_vm->graphics()->putCameraOnBob(0);
2061 }
2062 
asmEndInterview()2063 void Logic::asmEndInterview() {
2064 //	debug("Interactive Interview copyright (c) 1995, IBI.");
2065 	_vm->quitGame();
2066 }
2067 
startCredits(const char * filename)2068 void Logic::startCredits(const char *filename) {
2069 	stopCredits();
2070 	_credits = new Credits(_vm, filename);
2071 }
2072 
stopCredits()2073 void Logic::stopCredits() {
2074 	if (_credits) {
2075 		_vm->display()->clearTexts(0, 199);
2076 		delete _credits;
2077 		_credits = NULL;
2078 	}
2079 }
2080 
useJournal()2081 void LogicDemo::useJournal() {
2082 	makePersonSpeak("This is a demo, so I can't load or save games*14", NULL, "");
2083 }
2084 
changeToSpecialRoom()2085 bool LogicDemo::changeToSpecialRoom() {
2086 	if (currentRoom() == FOTAQ_LOGO && gameState(VAR_INTRO_PLAYED) == 0) {
2087 		currentRoom(79);
2088 		displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
2089 		playCutaway("CLOGO.CUT");
2090 		sceneReset();
2091 		if (_vm->shouldQuit())
2092 			return true;
2093 		currentRoom(ROOM_HOTEL_LOBBY);
2094 		entryObj(584);
2095 		displayRoom(currentRoom(), RDM_FADE_JOE, 100, 2, true);
2096 		playCutaway("C70D.CUT");
2097 		gameState(VAR_INTRO_PLAYED, 1);
2098 		inventoryRefresh();
2099 		return true;
2100 	}
2101 	return false;
2102 }
2103 
setupSpecialMoveTable()2104 void LogicDemo::setupSpecialMoveTable() {
2105 	_specialMoves[4] = &LogicDemo::asmMakeJoeUseUnderwear;
2106 	_specialMoves[14] = &LogicDemo::asmEndDemo;
2107 	if (_vm->resource()->getPlatform() == Common::kPlatformDOS) {
2108 		_specialMoves[5]  = &LogicDemo::asmSwitchToDressPalette;
2109 	}
2110 }
2111 
useJournal()2112 void LogicInterview::useJournal() {
2113 	// no-op
2114 }
2115 
changeToSpecialRoom()2116 bool LogicInterview::changeToSpecialRoom() {
2117 	if (currentRoom() == 2 && gameState(2) == 0) {
2118 		currentRoom(6);
2119 		displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
2120 		playCutaway("START.CUT");
2121 		gameState(2, 1);
2122 		inventoryRefresh();
2123 		return true;
2124 	}
2125 	return false;
2126 }
2127 
setupSpecialMoveTable()2128 void LogicInterview::setupSpecialMoveTable() {
2129 	_specialMoves[1] = &LogicInterview::asmInterviewIntro;
2130 	_specialMoves[2] = &LogicInterview::asmEndInterview;
2131 }
2132 
useJournal()2133 void LogicGame::useJournal() {
2134 	_vm->input()->clearKeyVerb();
2135 	_vm->input()->clearMouseButton();
2136 
2137 	_vm->command()->clear(false);
2138 	_journal->use();
2139 	_vm->walk()->stopJoe();
2140 
2141 	_vm->input()->clearKeyVerb();
2142 	_vm->input()->clearMouseButton();
2143 }
2144 
changeToSpecialRoom()2145 bool LogicGame::changeToSpecialRoom() {
2146 	if (currentRoom() == ROOM_JUNGLE_PINNACLE) {
2147 		handlePinnacleRoom();
2148 		return true;
2149 	} else if (currentRoom() == FOTAQ_LOGO && gameState(VAR_INTRO_PLAYED) == 0) {
2150 		displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
2151 		playCutaway("COPY.CUT");
2152 		if (_vm->shouldQuit())
2153 			return true;
2154 		playCutaway("CLOGO.CUT");
2155 		if (_vm->shouldQuit())
2156 			return true;
2157 		if (_vm->resource()->getPlatform() != Common::kPlatformAmiga) {
2158 			if (ConfMan.getBool("alt_intro") && _vm->resource()->isCD()) {
2159 				playCutaway("CINTR.CUT");
2160 			} else {
2161 				playCutaway("CDINT.CUT");
2162 			}
2163 		}
2164 		if (_vm->shouldQuit())
2165 			return true;
2166 		playCutaway("CRED.CUT");
2167 		if (_vm->shouldQuit())
2168 			return true;
2169 		_vm->display()->palSetPanel();
2170 		sceneReset();
2171 		currentRoom(ROOM_HOTEL_LOBBY);
2172 		entryObj(584);
2173 		displayRoom(currentRoom(), RDM_FADE_JOE, 100, 2, true);
2174 		playCutaway("C70D.CUT");
2175 		gameState(VAR_INTRO_PLAYED, 1);
2176 		inventoryRefresh();
2177 		return true;
2178 	}
2179 	return false;
2180 }
2181 
setupSpecialMoveTable()2182 void LogicGame::setupSpecialMoveTable() {
2183 	_specialMoves[2] = &LogicGame::asmMakeJoeUseDress;
2184 	_specialMoves[3] = &LogicGame::asmMakeJoeUseNormalClothes;
2185 	_specialMoves[4] = &LogicGame::asmMakeJoeUseUnderwear;
2186 	_specialMoves[7] = &LogicGame::asmStartCarAnimation;       // room 74
2187 	_specialMoves[8] = &LogicGame::asmStopCarAnimation;        // room 74
2188 	_specialMoves[9] = &LogicGame::asmStartFightAnimation;     // room 69
2189 	_specialMoves[10] = &LogicGame::asmWaitForFrankPosition;   // c69e.cut
2190 	_specialMoves[11] = &LogicGame::asmMakeFrankGrowing;       // c69z.cut
2191 	_specialMoves[12] = &LogicGame::asmMakeRobotGrowing;       // c69z.cut
2192 	_specialMoves[14] = &LogicGame::asmEndGame;
2193 	_specialMoves[15] = &LogicGame::asmPutCameraOnDino;
2194 	_specialMoves[16] = &LogicGame::asmPutCameraOnJoe;
2195 	_specialMoves[19] = &LogicGame::asmSetAzuraInLove;
2196 	_specialMoves[20] = &LogicGame::asmPanRightFromJoe;
2197 	_specialMoves[21] = &LogicGame::asmSetLightsOff;
2198 	_specialMoves[22] = &LogicGame::asmSetLightsOn;
2199 	_specialMoves[23] = &LogicGame::asmSetManequinAreaOn;
2200 	_specialMoves[24] = &LogicGame::asmPanToJoe;
2201 	_specialMoves[25] = &LogicGame::asmTurnGuardOn;
2202 	_specialMoves[26] = &LogicGame::asmPanLeft320To144;
2203 	_specialMoves[27] = &LogicGame::asmSmoochNoScroll;
2204 	_specialMoves[28] = &LogicGame::asmMakeLightningHitPlane;
2205 	_specialMoves[29] = &LogicGame::asmScaleBlimp;
2206 	_specialMoves[30] = &LogicGame::asmScaleEnding;
2207 	_specialMoves[31] = &LogicGame::asmWaitForCarPosition;
2208 	_specialMoves[33] = &LogicGame::asmAttemptPuzzle;
2209 	_specialMoves[34] = &LogicGame::asmScrollTitle;
2210 	if (_vm->resource()->getPlatform() == Common::kPlatformDOS) {
2211 		_specialMoves[5]  = &LogicGame::asmSwitchToDressPalette;
2212 		_specialMoves[6]  = &LogicGame::asmSwitchToNormalPalette;
2213 		_specialMoves[13] = &LogicGame::asmShrinkRobot;
2214 		_specialMoves[17] = &LogicGame::asmAltIntroPanRight;      // cintr.cut
2215 		_specialMoves[18] = &LogicGame::asmAltIntroPanLeft;       // cintr.cut
2216 		_specialMoves[27] = &LogicGame::asmSmooch;
2217 		_specialMoves[32] = &LogicGame::asmShakeScreen;
2218 		_specialMoves[34] = &LogicGame::asmScaleTitle;
2219 		_specialMoves[36] = &LogicGame::asmPanRightToHugh;
2220 		_specialMoves[37] = &LogicGame::asmMakeWhiteFlash;
2221 		_specialMoves[38] = &LogicGame::asmPanRightToJoeAndRita;
2222 		_specialMoves[39] = &LogicGame::asmPanLeftToBomb;         // cdint.cut
2223 	}
2224 }
2225 
2226 } // End of namespace Queen
2227