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 "scumm/actor.h"
24 #include "scumm/charset.h"
25 #include "scumm/he/intern_he.h"
26 #include "scumm/object.h"
27 #include "scumm/resource.h"
28 #include "scumm/scumm_v0.h"
29 #include "scumm/scumm_v7.h"
30 #include "scumm/verbs.h"
31 
32 namespace Scumm {
33 
34 enum {
35 	kInventoryUpArrow = 4,
36 	kInventoryDownArrow = 5,
37 	kSentenceLine = 6
38 };
39 
40 struct VerbSettings {
41 	int id;
42 	int x_pos;
43 	int y_pos;
44 	const char *name;
45 };
46 
47 static const VerbSettings v0VerbTable_English[] = {
48 	{kVerbOpen,     8, 0, "Open"},
49 	{kVerbClose,    8, 1, "Close"},
50 	{kVerbGive,     0, 2, "Give"},
51 	{kVerbTurnOn,  32, 0, "Turn on"},
52 	{kVerbTurnOff, 32, 1, "Turn off"},
53 	{kVerbFix,     32, 2, "Fix"},
54 	{kVerbNewKid,  24, 0, "New Kid"},
55 	{kVerbUnlock,  24, 1, "Unlock"},
56 	{kVerbPush,     0, 0, "Push"},
57 	{kVerbPull,     0, 1, "Pull"},
58 	{kVerbUse,     24, 2, "Use"},
59 	{kVerbRead,     8, 2, "Read"},
60 	{kVerbWalkTo,  15, 0, "Walk to"},
61 	{kVerbPickUp,  15, 1, "Pick up"},
62 	{kVerbWhatIs,  15, 2, "What is"}
63 };
64 
65 static const VerbSettings v0VerbTable_German[] = {
66 	{kVerbOpen,     7, 0, "$ffne"},
67 	{kVerbClose,   13, 1, "Schlie*e"},
68 	{kVerbGive,     0, 2, "Gebe"},
69 	{kVerbTurnOn,  37, 1, "Ein"},
70 	{kVerbTurnOff, 37, 0, "Aus"},
71 	{kVerbFix,     23, 1, "Repariere"},
72 	{kVerbNewKid,  34, 2, "Person"},
73 	{kVerbUnlock,  23, 0, "Schlie*e auf"},
74 	{kVerbPush,     0, 0, "Dr<cke"},
75 	{kVerbPull,     0, 1, "Ziehe"},
76 	{kVerbUse,     23, 2, "Benutz"},
77 	{kVerbRead,     7, 2, "Lese"},
78 	{kVerbWalkTo,  13, 0, "Gehe zu"},
79 	{kVerbPickUp,   7, 1, "Nimm"},
80 	{kVerbWhatIs,  13, 2, "Was ist"}
81 };
82 
83 struct VerbDemo {
84 	int color;
85 	const char *str;
86 };
87 static VerbDemo v0DemoStr[] = {
88 	{7,  "        MANIAC MANSION DEMO DISK        "},
89 	{5,  "          from Lucasfilm Games          "},
90 	{5,  "    Copyright = 1987 by Lucasfilm Ltd.  "},
91 	{5,  "           All Rights Reserved.         "},
92 	{0,  "                                        "},
93 	{16, "       Press F7 to return to menu.      "}
94 };
95 
verbPrepIdType(int verbid)96 int ScummEngine_v0::verbPrepIdType(int verbid) {
97 	switch (verbid) {
98 	case kVerbUse: // depends on object1
99 		return kVerbPrepObject;
100 	case kVerbGive:
101 		return kVerbPrepTo;
102 	case kVerbUnlock: case kVerbFix:
103 		return kVerbPrepWith;
104 	default:
105 		return kVerbPrepNone;
106 	}
107 }
108 
verbDemoMode()109 void ScummEngine_v0::verbDemoMode() {
110 	int i;
111 
112 	for (i = 1; i < 16; i++)
113 		killVerb(i);
114 
115 	for (i = 0; i < 6; i++) {
116 		verbDrawDemoString(i);
117 	}
118 }
119 
verbDrawDemoString(int VerbDemoNumber)120 void ScummEngine_v0::verbDrawDemoString(int VerbDemoNumber) {
121 	byte string[80];
122 	const char *ptr = v0DemoStr[VerbDemoNumber].str;
123 	int i = 0, len = 0;
124 
125 	// Maximum length of printable characters
126 	int maxChars = 40;
127 	while (*ptr) {
128 		if (*ptr != '@')
129 			len++;
130 		if (len > maxChars) {
131 			break;
132 		}
133 
134 		string[i++] = *ptr++;
135 
136 	}
137 	string[i] = 0;
138 
139 	_string[2].charset = 1;
140 	_string[2].ypos = _virtscr[kVerbVirtScreen].topline + (8 * VerbDemoNumber);
141 	_string[2].xpos = 0;
142 	_string[2].right = _virtscr[kVerbVirtScreen].w - 1;
143 	_string[2].color = v0DemoStr[VerbDemoNumber].color;
144 	drawString(2, (byte *)string);
145 }
146 
resetVerbs()147 void ScummEngine_v0::resetVerbs() {
148 	VirtScreen *virt = &_virtscr[kVerbVirtScreen];
149 	VerbSlot *vs;
150 	const VerbSettings *vtable;
151 	int i;
152 
153 	switch (_language) {
154 	case Common::DE_DEU:
155 		vtable = (const VerbSettings*)v0VerbTable_German;
156 		break;
157 	default:
158 		vtable = (const VerbSettings*)v0VerbTable_English;
159 	}
160 
161 	for (i = 1; i < 16; i++)
162 		killVerb(i);
163 
164 	for (i = 1; i < 16; i++) {
165 		vs = &_verbs[i];
166 		vs->verbid = vtable[i - 1].id;
167 		vs->color = 5;
168 		vs->hicolor = 7;
169 		vs->dimcolor = 11;
170 		vs->type = kTextVerbType;
171 		vs->charset_nr = _string[0]._default.charset;
172 		vs->curmode = 1;
173 		vs->saveid = 0;
174 		vs->key = 0;
175 		vs->center = 0;
176 		vs->imgindex = 0;
177 		vs->prep = verbPrepIdType(vtable[i - 1].id);
178 		vs->curRect.left = vs->origLeft = vtable[i - 1].x_pos * 8;
179 		vs->curRect.top = vtable[i - 1].y_pos * 8 + virt->topline + 8;
180 		loadPtrToResource(rtVerb, i, (const byte*)vtable[i - 1].name);
181 	}
182 }
183 
switchActor(int slot)184 void ScummEngine_v0::switchActor(int slot) {
185 	resetSentence();
186 
187 	// actor switching only allowed during normal gamplay (not cutscene, ...)
188 	if (_currentMode != kModeNormal)
189 		return;
190 
191 	VAR(VAR_EGO) = VAR(97 + slot);
192 	actorFollowCamera(VAR(VAR_EGO));
193 }
194 
initV2MouseOver()195 void ScummEngine_v2::initV2MouseOver() {
196 	int i;
197 	int arrow_color, color, hi_color;
198 
199 	if (_game.version == 2) {
200 		color = 13;
201 		hi_color = 14;
202 		arrow_color = 1;
203 	} else {
204 		color = 16;
205 		hi_color = 7;
206 		arrow_color = 6;
207 	}
208 
209 	_mouseOverBoxV2 = -1;
210 
211 	// Inventory items
212 
213 	for (i = 0; i < 2; i++) {
214 		_mouseOverBoxesV2[2 * i].rect.left = 0;
215 		_mouseOverBoxesV2[2 * i].rect.right = 144;
216 		_mouseOverBoxesV2[2 * i].rect.top = 32 + 8 * i;
217 		_mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;
218 
219 		_mouseOverBoxesV2[2 * i].color = color;
220 		_mouseOverBoxesV2[2 * i].hicolor = hi_color;
221 
222 		_mouseOverBoxesV2[2 * i + 1].rect.left = 176;
223 		_mouseOverBoxesV2[2 * i + 1].rect.right = 320;
224 		_mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
225 		_mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;
226 
227 		_mouseOverBoxesV2[2 * i + 1].color = color;
228 		_mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
229 	}
230 
231 	// Inventory arrows
232 
233 	_mouseOverBoxesV2[kInventoryUpArrow].rect.left = 144;
234 	_mouseOverBoxesV2[kInventoryUpArrow].rect.right = 176;
235 	_mouseOverBoxesV2[kInventoryUpArrow].rect.top = 32;
236 	_mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 40;
237 
238 	_mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
239 	_mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;
240 
241 	_mouseOverBoxesV2[kInventoryDownArrow].rect.left = 144;
242 	_mouseOverBoxesV2[kInventoryDownArrow].rect.right = 176;
243 	_mouseOverBoxesV2[kInventoryDownArrow].rect.top = 40;
244 	_mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 48;
245 
246 	_mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
247 	_mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;
248 
249 	// Sentence line
250 
251 	_mouseOverBoxesV2[kSentenceLine].rect.left = 0;
252 	_mouseOverBoxesV2[kSentenceLine].rect.right = 320;
253 	_mouseOverBoxesV2[kSentenceLine].rect.top = 0;
254 	_mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;
255 
256 	_mouseOverBoxesV2[kSentenceLine].color = color;
257 	_mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
258 }
259 
initNESMouseOver()260 void ScummEngine_v2::initNESMouseOver() {
261 	int i;
262 	int arrow_color, color, hi_color;
263 
264 	color = 0;
265 	hi_color = 0;
266 	arrow_color = 0;
267 
268 	_mouseOverBoxV2 = -1;
269 
270 	// Inventory items
271 
272 	for (i = 0; i < 2; i++) {
273 		_mouseOverBoxesV2[2 * i].rect.left = 16;
274 		_mouseOverBoxesV2[2 * i].rect.right = 120;
275 		_mouseOverBoxesV2[2 * i].rect.top = 48 + 8 * i;
276 		_mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;
277 
278 		_mouseOverBoxesV2[2 * i].color = color;
279 		_mouseOverBoxesV2[2 * i].hicolor = hi_color;
280 
281 		_mouseOverBoxesV2[2 * i + 1].rect.left = 152;
282 		_mouseOverBoxesV2[2 * i + 1].rect.right = 256;
283 		_mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
284 		_mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;
285 
286 		_mouseOverBoxesV2[2 * i + 1].color = color;
287 		_mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
288 	}
289 
290 	// Inventory arrows
291 
292 	_mouseOverBoxesV2[kInventoryUpArrow].rect.left = 128;
293 	_mouseOverBoxesV2[kInventoryUpArrow].rect.right = 136;
294 	_mouseOverBoxesV2[kInventoryUpArrow].rect.top = 48;
295 	_mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 56;
296 
297 	_mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
298 	_mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;
299 
300 	_mouseOverBoxesV2[kInventoryDownArrow].rect.left = 136;
301 	_mouseOverBoxesV2[kInventoryDownArrow].rect.right = 144;
302 	_mouseOverBoxesV2[kInventoryDownArrow].rect.top = 48;
303 	_mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 56;
304 
305 	_mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
306 	_mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;
307 
308 	// Sentence line
309 
310 	_mouseOverBoxesV2[kSentenceLine].rect.left = 16;
311 	_mouseOverBoxesV2[kSentenceLine].rect.right = 256;
312 	_mouseOverBoxesV2[kSentenceLine].rect.top = 0;
313 	_mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;
314 
315 	_mouseOverBoxesV2[kSentenceLine].color = color;
316 	_mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
317 }
318 
checkV2MouseOver(Common::Point pos)319 void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
320 	VirtScreen *vs = &_virtscr[kVerbVirtScreen];
321 	Common::Rect rect;
322 	byte *ptr, *dst;
323 	int i, x, y, new_box = -1;
324 
325 	// Don't do anything unless the inventory is active
326 	if (!(_userState & USERSTATE_IFACE_INVENTORY)) {
327 		_mouseOverBoxV2 = -1;
328 		return;
329 	}
330 
331 	if (_cursor.state > 0) {
332 		for (i = 0; i < ARRAYSIZE(_mouseOverBoxesV2); i++) {
333 			if (_mouseOverBoxesV2[i].rect.contains(pos.x, pos.y - vs->topline)) {
334 				new_box = i;
335 				break;
336 			}
337 		}
338 	}
339 
340 	if ((new_box != _mouseOverBoxV2) || (_game.version == 0)) {
341 		if (_mouseOverBoxV2 != -1) {
342 			rect = _mouseOverBoxesV2[_mouseOverBoxV2].rect;
343 
344 			dst = ptr = vs->getPixels(rect.left, rect.top);
345 
346 			// Remove highlight.
347 			for (y = rect.height() - 1; y >= 0; y--) {
348 				for (x = rect.width() - 1; x >= 0; x--) {
349 					if (dst[x] == _mouseOverBoxesV2[_mouseOverBoxV2].hicolor)
350 						dst[x] = _mouseOverBoxesV2[_mouseOverBoxV2].color;
351 				}
352 				dst += vs->pitch;
353 			}
354 
355 			markRectAsDirty(kVerbVirtScreen, rect);
356 		}
357 
358 		if (new_box != -1) {
359 			rect = _mouseOverBoxesV2[new_box].rect;
360 
361 			dst = ptr = vs->getPixels(rect.left, rect.top);
362 
363 			// Apply highlight
364 			for (y = rect.height() - 1; y >= 0; y--) {
365 				for (x = rect.width() - 1; x >= 0; x--) {
366 					if (dst[x] == _mouseOverBoxesV2[new_box].color)
367 						dst[x] = _mouseOverBoxesV2[new_box].hicolor;
368 				}
369 				dst += vs->pitch;
370 			}
371 
372 			markRectAsDirty(kVerbVirtScreen, rect);
373 		}
374 
375 		_mouseOverBoxV2 = new_box;
376 	}
377 }
378 
checkV2Inventory(int x,int y)379 int ScummEngine_v2::checkV2Inventory(int x, int y) {
380 	int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
381 	int object = 0;
382 
383 	y -= _virtscr[kVerbVirtScreen].topline;
384 
385 	if ((y < inventoryArea) || !(_mouseAndKeyboardStat & MBS_LEFT_CLICK))
386 		return 0;
387 
388 	if (_mouseOverBoxesV2[kInventoryUpArrow].rect.contains(x, y)) {
389 		if (_inventoryOffset >= 2) {
390 			_inventoryOffset -= 2;
391 			redrawV2Inventory();
392 		}
393 	} else if (_mouseOverBoxesV2[kInventoryDownArrow].rect.contains(x, y)) {
394 		if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
395 			_inventoryOffset += 2;
396 			redrawV2Inventory();
397 		}
398 	}
399 
400 	for (object = 0; object < 4; object++) {
401 		if (_mouseOverBoxesV2[object].rect.contains(x, y)) {
402 			break;
403 		}
404 	}
405 
406 	if (object >= 4)
407 		return 0;
408 
409 	return findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
410 }
411 
redrawV2Inventory()412 void ScummEngine_v2::redrawV2Inventory() {
413 	VirtScreen *vs = &_virtscr[kVerbVirtScreen];
414 	int i;
415 	int max_inv;
416 	Common::Rect inventoryBox;
417 	int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
418 	int maxChars = (_game.platform == Common::kPlatformNES) ? 13: 18;
419 
420 	_mouseOverBoxV2 = -1;
421 
422 	if (!(_userState & USERSTATE_IFACE_INVENTORY))	// Don't draw inventory unless active
423 		return;
424 
425 	// Clear on all invocations
426 	inventoryBox.top = vs->topline + inventoryArea;
427 	inventoryBox.bottom = vs->topline + vs->h;
428 	inventoryBox.left = 0;
429 	inventoryBox.right = vs->w;
430 	restoreBackground(inventoryBox);
431 
432 	_string[1].charset = 1;
433 
434 	max_inv = getInventoryCount(_scummVars[VAR_EGO]) - _inventoryOffset;
435 	if (max_inv > 4)
436 		max_inv = 4;
437 	for (i = 0; i < max_inv; i++) {
438 		int obj = findInventory(_scummVars[VAR_EGO], i + 1 + _inventoryOffset);
439 		if (obj == 0)
440 			break;
441 
442 		_string[1].ypos = _mouseOverBoxesV2[i].rect.top + vs->topline;
443 		_string[1].xpos = _mouseOverBoxesV2[i].rect.left;
444 		_string[1].right = _mouseOverBoxesV2[i].rect.right - 1;
445 		_string[1].color = _mouseOverBoxesV2[i].color;
446 
447 		const byte *tmp = getObjOrActorName(obj);
448 		assert(tmp);
449 
450 		// Prevent inventory entries from overflowing by truncating the text
451 		byte msg[20];
452 		msg[maxChars] = 0;
453 		strncpy((char *)msg, (const char *)tmp, maxChars);
454 
455 		// Draw it
456 		drawString(1, msg);
457 	}
458 
459 
460 	// If necessary, draw "up" arrow
461 	if (_inventoryOffset > 0) {
462 		_string[1].xpos = _mouseOverBoxesV2[kInventoryUpArrow].rect.left;
463 		_string[1].ypos = _mouseOverBoxesV2[kInventoryUpArrow].rect.top + vs->topline;
464 		_string[1].right = _mouseOverBoxesV2[kInventoryUpArrow].rect.right - 1;
465 		_string[1].color = _mouseOverBoxesV2[kInventoryUpArrow].color;
466 		if (_game.platform == Common::kPlatformNES)
467 			drawString(1, (const byte *)"\x7E");
468 		else
469 			drawString(1, (const byte *)" \1\2");
470 	}
471 
472 	// If necessary, draw "down" arrow
473 	if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
474 		_string[1].xpos = _mouseOverBoxesV2[kInventoryDownArrow].rect.left;
475 		_string[1].ypos = _mouseOverBoxesV2[kInventoryDownArrow].rect.top + vs->topline;
476 		_string[1].right = _mouseOverBoxesV2[kInventoryDownArrow].rect.right - 1;
477 		_string[1].color = _mouseOverBoxesV2[kInventoryDownArrow].color;
478 		if (_game.platform == Common::kPlatformNES)
479 			drawString(1, (const byte *)"\x7F");
480 		else
481 			drawString(1, (const byte *)" \3\4");
482 	}
483 }
484 
redrawVerbs()485 void ScummEngine::redrawVerbs() {
486 	if (_game.version <= 2 && !(_userState & USERSTATE_IFACE_VERBS)) // Don't draw verbs unless active
487 		return;
488 
489 	int i, verb = 0;
490 	if (_cursor.state > 0)
491 		verb = findVerbAtPos(_mouse.x, _mouse.y);
492 
493 	// Iterate over all verbs.
494 	// Note: This is the correct order (at least for MI EGA, MI2, Full Throttle).
495 	// Do not change it! If you discover, based on disasm, that some game uses
496 	// another (e.g. the reverse) order here, you have to use an if/else construct
497 	// to add it as a special case!
498 	for (i = 0; i < _numVerbs; i++) {
499 		if (i == verb && _verbs[verb].hicolor)
500 			drawVerb(i, 1);
501 		else
502 			drawVerb(i, 0);
503 	}
504 	_verbMouseOver = verb;
505 }
506 
handleMouseOver(bool updateInventory)507 void ScummEngine::handleMouseOver(bool updateInventory) {
508 	if (_completeScreenRedraw) {
509 		verbMouseOver(0);
510 	} else {
511 		if (_cursor.state > 0)
512 			verbMouseOver(findVerbAtPos(_mouse.x, _mouse.y));
513 	}
514 }
515 
handleMouseOver(bool updateInventory)516 void ScummEngine_v2::handleMouseOver(bool updateInventory) {
517 	ScummEngine::handleMouseOver(updateInventory);
518 
519 	if (updateInventory) {
520 		// FIXME/TODO: Reset and redraw the sentence line
521 		_inventoryOffset = 0;
522 	}
523 	if (_completeScreenRedraw || updateInventory) {
524 		redrawV2Inventory();
525 	}
526 	checkV2MouseOver(_mouse);
527 }
528 
handleMouseOver(bool updateInventory)529 void ScummEngine_v0::handleMouseOver(bool updateInventory) {
530 	ScummEngine_v2::handleMouseOver(updateInventory);
531 }
532 
533 #ifdef ENABLE_HE
checkExecVerbs()534 void ScummEngine_v72he::checkExecVerbs() {
535 	VAR(VAR_MOUSE_STATE) = 0;
536 
537 	if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
538 		return;
539 
540 	VAR(VAR_MOUSE_STATE) = _mouseAndKeyboardStat;
541 
542 	ScummEngine::checkExecVerbs();
543 }
544 #endif
545 
checkExecVerbs()546 void ScummEngine::checkExecVerbs() {
547 	int i, over;
548 	VerbSlot *vs;
549 
550 	if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
551 		return;
552 
553 	if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
554 		/* Check keypresses */
555 		if (!(_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD)) {
556 			// This is disabled in the SegaCD version as the "vs->key" values setup
557 			// by script-17 conflict with the values expected by the generic keyboard
558 			// input script. See tracker item #2013.
559 			vs = &_verbs[1];
560 			for (i = 1; i < _numVerbs; i++, vs++) {
561 				if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
562 					if (_mouseAndKeyboardStat == vs->key) {
563 						// Trigger verb as if the user clicked it
564 						runInputScript(kVerbClickArea, vs->verbid, 1);
565 						return;
566 					}
567 				}
568 			}
569 		}
570 
571 		if ((_game.id == GID_INDY4 || _game.id == GID_PASS) && _mouseAndKeyboardStat >= '0' && _mouseAndKeyboardStat <= '9') {
572 			// To support keyboard fighting in FOA, we need to remap the number keys.
573 			// FOA apparently expects PC scancode values (see script 46 if you want
574 			// to know where I got these numbers from). Oddly enough, the The Indy 3
575 			// part of the "Passport to Adventure" demo expects the same keyboard
576 			// mapping, even though the full game doesn't.
577 			static const int numpad[10] = {
578 				'0',
579 				335, 336, 337,
580 				331, 332, 333,
581 				327, 328, 329
582 			};
583 			_mouseAndKeyboardStat = numpad[_mouseAndKeyboardStat - '0'];
584 		}
585 
586 		if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) {
587 			// HACK: In the FM-TOWNS games Indy3, Loom and Zak the most significant bit is set for special keys
588 			// like F5 (=0x8005) or joystick buttons (mask 0xFE00, e.g. SELECT=0xFE40 for the save/load menu).
589 			// Hence the distinction with (_mouseAndKeyboardStat < MBS_MAX_KEY) between mouse- and key-events is not applicable
590 			// to this games, so we have to remap the special keys here.
591 			if (_mouseAndKeyboardStat == 319) {
592 				_mouseAndKeyboardStat = 0x8005;
593 			}
594 		}
595 
596 		if ((_game.platform == Common::kPlatformFMTowns && _game.id == GID_ZAK) &&
597 			(_mouseAndKeyboardStat >= 315 && _mouseAndKeyboardStat <= 318)) {
598 			// Hack: Handle switching to a person via F1-F4 keys.
599 			// This feature isn't available in the scripts of the FM-TOWNS version.
600 			int fKey = _mouseAndKeyboardStat - 314;
601 			int switchSlot = getVerbSlot(36, 0);
602 			// check if switch-verb is enabled
603 			if (_verbs[switchSlot].curmode == 1) {
604 				// Check if person is available (see script 23 from ZAK_FM-TOWNS and script 4 from ZAK_PC).
605 				// Zak: Var[144 Bit 15], Annie: Var[145 Bit 0], Melissa: Var[145 Bit 1], Leslie: Var[145 Bit 2]
606 				if (!readVar(0x890E + fKey)) {
607 					runInputScript(kVerbClickArea, 36 + fKey, 0);
608 				}
609 			}
610 			return;
611 		}
612 
613 		// Generic keyboard input
614 		runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
615 	} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
616 		VirtScreen *zone = findVirtScreen(_mouse.y);
617 		const byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;
618 
619 		// This could be kUnkVirtScreen.
620 		// Fixes bug #2773: "MANIACNES: Crash on click in speechtext-area"
621 		if (!zone)
622 			return;
623 
624 		over = findVerbAtPos(_mouse.x, _mouse.y);
625 		if (over != 0) {
626 			// Verb was clicked
627 			runInputScript(kVerbClickArea, _verbs[over].verbid, code);
628 		} else {
629 			// Scene was clicked
630 			runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
631 		}
632 	}
633 }
634 
checkExecVerbs()635 void ScummEngine_v2::checkExecVerbs() {
636 	int i, over;
637 	VerbSlot *vs;
638 
639 	if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
640 		return;
641 
642 	if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
643 		/* Check keypresses */
644 		vs = &_verbs[1];
645 		for (i = 1; i < _numVerbs; i++, vs++) {
646 			if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
647 				if (_mouseAndKeyboardStat == vs->key) {
648 					// Trigger verb as if the user clicked it
649 					runInputScript(kVerbClickArea, vs->verbid, 1);
650 					return;
651 				}
652 			}
653 		}
654 
655 		// Simulate inventory picking and scrolling keys
656 		int object = -1;
657 
658 		switch (_mouseAndKeyboardStat) {
659 		case 'u': // arrow up
660 			if (_inventoryOffset >= 2) {
661 				_inventoryOffset -= 2;
662 				redrawV2Inventory();
663 			}
664 			return;
665 		case 'j': // arrow down
666 			if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
667 				_inventoryOffset += 2;
668 				redrawV2Inventory();
669 			}
670 			return;
671 		case 'i': // object
672 			object = 0;
673 			break;
674 		case 'o':
675 			object = 1;
676 			break;
677 		case 'k':
678 			object = 2;
679 			break;
680 		case 'l':
681 			object = 3;
682 			break;
683 		default:
684 			break;
685 		}
686 
687 		if (object != -1) {
688 			object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
689 			if (object > 0)
690 				runInputScript(kInventoryClickArea, object, 0);
691 			return;
692 		}
693 
694 		// Generic keyboard input
695 		runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
696 	} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
697 		VirtScreen *zone = findVirtScreen(_mouse.y);
698 		const byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;
699 		const int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
700 
701 		// This could be kUnkVirtScreen.
702 		// Fixes bug #2773: "MANIACNES: Crash on click in speechtext-area"
703 		if (!zone)
704 			return;
705 
706 		if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
707 			// Click into V2 sentence line
708 			runInputScript(kSentenceClickArea, 0, 0);
709 		} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + inventoryArea) {
710 			// Click into V2 inventory
711 			int object = checkV2Inventory(_mouse.x, _mouse.y);
712 			if (object > 0)
713 				runInputScript(kInventoryClickArea, object, 0);
714 		} else {
715 			over = findVerbAtPos(_mouse.x, _mouse.y);
716 			if (over != 0) {
717 				// Verb was clicked
718 				runInputScript(kVerbClickArea, _verbs[over].verbid, code);
719 			} else {
720 				// Scene was clicked
721 				runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
722 			}
723 		}
724 	}
725 }
726 
getVerbPrepId()727 int ScummEngine_v0::getVerbPrepId() {
728 	if (_verbs[_activeVerb].prep != 0xFF) {
729 		return _verbs[_activeVerb].prep;
730 	} else {
731 		byte *ptr = getOBCDFromObject(_activeObject, true);
732 		assert(ptr);
733 		return (*(ptr + 11) >> 5);
734 	}
735 }
736 
activeVerbPrep()737 int ScummEngine_v0::activeVerbPrep() {
738 	if (!_activeVerb || !_activeObject)
739 		return 0;
740 	return getVerbPrepId();
741 }
742 
verbExec()743 void ScummEngine_v0::verbExec() {
744 	_sentenceNum = 0;
745 	_sentenceNestedCount = 0;
746 
747 	if (_activeVerb == kVerbWhatIs)
748 		return;
749 
750 	if (!(_activeVerb == kVerbWalkTo && _activeObject == 0)) {
751 		doSentence(_activeVerb, _activeObject, _activeObject2);
752 		if (_activeVerb != kVerbWalkTo) {
753 			_activeVerb = kVerbWalkTo;
754 			_activeObject = 0;
755 			_activeObject2 = 0;
756 		}
757 		_walkToObjectState = kWalkToObjectStateDone;
758 		return;
759 	}
760 
761 	Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "verbExec");
762 	int x = _virtualMouse.x / V12_X_MULTIPLIER;
763 	int y = _virtualMouse.y / V12_Y_MULTIPLIER;
764 
765 	// 0xB31
766 	VAR(6) = x;
767 	VAR(7) = y;
768 
769 	if (a->_miscflags & kActorMiscFlagFreeze)
770 		return;
771 
772 	a->startWalkActor(VAR(6), VAR(7), -1);
773 }
774 
checkSentenceComplete()775 bool ScummEngine_v0::checkSentenceComplete() {
776 	if (_activeVerb && _activeVerb != kVerbWalkTo && _activeVerb != kVerbWhatIs) {
777 		if (_activeObject && (!activeVerbPrep() || _activeObject2))
778 			return true;
779 	}
780 	return false;
781 }
782 
checkExecVerbs()783 void ScummEngine_v0::checkExecVerbs() {
784 	Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
785 	VirtScreen *zone = findVirtScreen(_mouse.y);
786 
787 	bool execute = false;
788 
789 	if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
790 		int over = findVerbAtPos(_mouse.x, _mouse.y);
791 		// click region: verbs
792 		if (over) {
793 			if (_activeVerb != over) { // new verb
794 				// keep first object if no preposition is used yet
795 				if (activeVerbPrep())
796 					_activeObject = 0;
797 				_activeObject2 = 0;
798 				_activeVerb = over;
799 				_redrawSentenceLine = true;
800 			} else {
801 				// execute sentence if complete
802 				if (checkSentenceComplete())
803 					execute = true;
804 			}
805 		}
806 	}
807 
808 	if (a->_miscflags & kActorMiscFlagHide) {
809 		if (_activeVerb != kVerbNewKid) {
810 			_activeVerb = kVerbNone;
811 		}
812 	}
813 
814 	if (_currentMode != kModeCutscene) {
815 		if (_currentMode == kModeKeypad) {
816 			_activeVerb = kVerbPush;
817 		}
818 
819 		if (_mouseAndKeyboardStat > 0 && _mouseAndKeyboardStat < MBS_MAX_KEY) {
820 			// keys already checked by input handler
821 		} else if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK) || _activeVerb == kVerbWhatIs) {
822 			// click region: sentence line
823 			if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
824 				if (_activeVerb == kVerbNewKid) {
825 					if (_currentMode == kModeNormal) {
826 						int kid;
827 						int lineX = _mouse.x >> V12_X_SHIFT;
828 						if (lineX < 11)
829 							kid = 0;
830 						else if (lineX < 25)
831 							kid = 1;
832 						else
833 							kid = 2;
834 						_activeVerb = kVerbWalkTo;
835 						_redrawSentenceLine = true;
836 						drawSentenceLine();
837 						switchActor(kid);
838 					}
839 					_activeVerb = kVerbWalkTo;
840 					_redrawSentenceLine = true;
841 					return;
842 				} else {
843 					// execute sentence if complete
844 					if (checkSentenceComplete())
845 						execute = true;
846 				}
847 			// click region: inventory or main screen
848 			} else if ((zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) ||
849 			           (zone->number == kMainVirtScreen))
850 			{
851 				int obj = 0;
852 
853 				// click region: inventory
854 				if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) {
855 					// click into inventory
856 					int invOff = _inventoryOffset;
857 					obj = checkV2Inventory(_mouse.x, _mouse.y);
858 					if (invOff != _inventoryOffset) {
859 						// inventory position changed (arrows pressed, do nothing)
860 						return;
861 					}
862 					// the second object of a give-to command has to be an actor
863 					if (_activeVerb == kVerbGive && _activeObject)
864 						obj = 0;
865 				// click region: main screen
866 				} else if (zone->number == kMainVirtScreen) {
867 					// click into main screen
868 					if (_activeVerb == kVerbGive && _activeObject) {
869 						int actor = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
870 						if (actor != 0)
871 							obj = OBJECT_V0(actor, kObjectV0TypeActor);
872 					} else {
873 						obj = findObject(_virtualMouse.x, _virtualMouse.y);
874 					}
875 				}
876 
877 				if (!obj) {
878 					if (_activeVerb == kVerbWalkTo) {
879 						_activeObject = 0;
880 						_activeObject2 = 0;
881 					}
882 				} else {
883 					if (activeVerbPrep() == kVerbPrepNone) {
884 						if (obj == _activeObject)
885 							execute = true;
886 						else
887 							_activeObject = obj;
888 						// immediately execute action in keypad/selection mode
889 						if (_currentMode == kModeKeypad)
890 							execute = true;
891 					} else {
892 						if (obj == _activeObject2)
893 							execute = true;
894 						if (obj != _activeObject) {
895 							_activeObject2 = obj;
896 							if (_currentMode == kModeKeypad)
897 								execute = true;
898 						}
899 					}
900 				}
901 
902 				_redrawSentenceLine = true;
903 				if (_activeVerb == kVerbWalkTo && zone->number == kMainVirtScreen) {
904 					_walkToObjectState = kWalkToObjectStateDone;
905 					execute = true;
906 				}
907 			}
908 		}
909 	}
910 
911 	if (_drawDemo && _game.features & GF_DEMO) {
912 		verbDemoMode();
913 	}
914 
915 	if (_redrawSentenceLine)
916 		drawSentenceLine();
917 
918 	if (!execute || !_activeVerb)
919 		return;
920 
921 	if (_activeVerb == kVerbWalkTo)
922 		verbExec();
923 	else if (_activeObject) {
924 		// execute if we have a 1st object and either have or do not need a 2nd
925 		if (activeVerbPrep() == kVerbPrepNone || _activeObject2)
926 			verbExec();
927 	}
928 }
929 
verbMouseOver(int verb)930 void ScummEngine::verbMouseOver(int verb) {
931 	// Don't do anything unless verbs are active
932 	if (_game.version <= 2 && !(_userState & USERSTATE_IFACE_VERBS))
933 		return;
934 
935 	if (_game.id == GID_FT)
936 		return;
937 
938 	if (_verbMouseOver != verb) {
939 		if (_verbs[_verbMouseOver].type != kImageVerbType) {
940 			drawVerb(_verbMouseOver, 0);
941 			_verbMouseOver = verb;
942 		}
943 
944 		if (_verbs[verb].type != kImageVerbType && _verbs[verb].hicolor) {
945 			drawVerb(verb, 1);
946 			_verbMouseOver = verb;
947 		}
948 	}
949 }
950 
findVerbAtPos(int x,int y) const951 int ScummEngine::findVerbAtPos(int x, int y) const {
952 	if (!_numVerbs)
953 		return 0;
954 
955 	VerbSlot *vs;
956 	int i = _numVerbs - 1;
957 
958 	vs = &_verbs[i];
959 	do {
960 		if (vs->curmode != 1 || !vs->verbid || vs->saveid || y < vs->curRect.top || y >= vs->curRect.bottom)
961 			continue;
962 		if (vs->center) {
963 			if (x < -(vs->curRect.right - 2 * vs->curRect.left) || x >= vs->curRect.right)
964 				continue;
965 		} else {
966 			if (x < vs->curRect.left || x >= vs->curRect.right)
967 				continue;
968 		}
969 
970 		return i;
971 	} while (--vs, --i);
972 
973 	return 0;
974 }
975 
976 #ifdef ENABLE_SCUMM_7_8
drawVerb(int verb,int mode)977 void ScummEngine_v7::drawVerb(int verb, int mode) {
978 	VerbSlot *vs;
979 
980 	if (!verb)
981 		return;
982 
983 	vs = &_verbs[verb];
984 
985 	if (!vs->saveid && vs->curmode && vs->verbid) {
986 		if (vs->type == kImageVerbType) {
987 			drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
988 			return;
989 		}
990 
991 		uint8 color = vs->color;
992 		if (vs->curmode == 2)
993 			color = vs->dimcolor;
994 		else if (mode && vs->hicolor)
995 			color = vs->hicolor;
996 
997 		const byte *msg = getResourceAddress(rtVerb, verb);
998 		if (!msg)
999 			return;
1000 
1001 		// Convert the message, and skip a few remaining 0xFF codes (they
1002 		// occur in FT; subtype 10, which is used for the speech associated
1003 		// with the string).
1004 		byte buf[384];
1005 		memset(buf, 0, sizeof(buf));
1006 		convertMessageToString(msg, buf, sizeof(buf));
1007 		msg = buf;
1008 		while (*msg == 0xFF)
1009 			msg += 4;
1010 
1011 		// Set the specified charset id
1012 		int oldID = _charset->getCurID();
1013 		_charset->setCurID(vs->charset_nr);
1014 
1015 		// Compute the text rect
1016 		int textWidth = 0;
1017 		vs->curRect.bottom = 0;
1018 		const byte *msg2 = msg;
1019 		while (*msg2) {
1020 			const int charWidth = _charset->getCharWidth(*msg2);
1021 			const int charHeight = _charset->getCharHeight(*msg2);
1022 			textWidth += charWidth;
1023 			if (vs->curRect.bottom < charHeight)
1024 				vs->curRect.bottom = charHeight;
1025 			msg2++;
1026 		}
1027 		vs->curRect.bottom += vs->curRect.top;
1028 		vs->oldRect = vs->curRect;
1029 
1030 		const int maxWidth = _language == Common::HE_ISR ? vs->curRect.right + 1 : _screenWidth - vs->curRect.left;
1031 		if (_game.version == 8 && _charset->getStringWidth(0, buf) > maxWidth) {
1032 			byte tmpBuf[384];
1033 			memcpy(tmpBuf, msg, 384);
1034 
1035 			int len = resStrLen(tmpBuf) - 1;
1036 			while (len >= 0) {
1037 				if (tmpBuf[len] == ' ') {
1038 					tmpBuf[len] = 0;
1039 					if (_charset->getStringWidth(0, tmpBuf) <= maxWidth) {
1040 						break;
1041 					}
1042 				}
1043 				--len;
1044 			}
1045 			int16 leftPos = vs->curRect.left;
1046 			if (_language == Common::HE_ISR)
1047 				vs->curRect.left = vs->origLeft = leftPos = vs->curRect.right - _charset->getStringWidth(0, tmpBuf);
1048 			else
1049 				vs->curRect.right = vs->curRect.left + _charset->getStringWidth(0, tmpBuf);
1050 			enqueueText(tmpBuf, leftPos, vs->curRect.top, color, vs->charset_nr, vs->center);
1051 			if (len >= 0) {
1052 				if (_language == Common::HE_ISR)
1053 					leftPos = vs->curRect.right - _charset->getStringWidth(0, &msg[len + 1]);
1054 				enqueueText(&msg[len + 1], leftPos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
1055 				vs->curRect.bottom += _verbLineSpacing;
1056 			}
1057 		} else {
1058 			if (_language == Common::HE_ISR)
1059 				vs->curRect.left = vs->origLeft = vs->curRect.right - textWidth;
1060 			else
1061 				vs->curRect.right = vs->curRect.left + textWidth;
1062 			enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);
1063 		}
1064 		_charset->setCurID(oldID);
1065 	}
1066 }
1067 #endif
1068 
drawVerb(int verb,int mode)1069 void ScummEngine::drawVerb(int verb, int mode) {
1070 	VerbSlot *vs;
1071 	bool tmp;
1072 
1073 	if (!verb)
1074 		return;
1075 
1076 	// The way we implement high-resolution font on a scaled low-resolution
1077 	// background requires there to always be a text surface telling which
1078 	// pixels have been drawn on. This means that the "has mask" feature is
1079 	// not correctly implemented, and in most cases this works just fine
1080 	// for both Loom and Indiana Jones and the Last Crusade.
1081 	//
1082 	// However, there is at least one scene in Indiana Jones - room 80,
1083 	// where you escape from the zeppelin on a biplane - where the game
1084 	// (sloppily, in my opinion) draws two disabled verbs (Travel and Talk)
1085 	// and then counts on the background to draw over them. Obviously that
1086 	// won't work here.
1087 	//
1088 	// I thought it would be possible to base this workaround on room
1089 	// height, but then verbs aren't redrawn after reading books. So I
1090 	// guess the safest path is to limit it to this particular room.
1091 	//
1092 	// Note that with the low-resolution font, which does implement the
1093 	// "has mask" feature, the Macintosh version still needs a hack in
1094 	// printChar() for black text to work properly. This version of the
1095 	// game is weird.
1096 	if (_game.id == GID_INDY3 && _macScreen && _currentRoom == 80)
1097 		return;
1098 
1099 	vs = &_verbs[verb];
1100 
1101 	if (!vs->saveid && vs->curmode && vs->verbid) {
1102 		if (vs->type == kImageVerbType) {
1103 			drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
1104 			return;
1105 		}
1106 
1107 		restoreVerbBG(verb);
1108 
1109 		const bool isRtl = _language == Common::HE_ISR && !vs->center;
1110 		_string[4].charset = vs->charset_nr;
1111 		_string[4].xpos = isRtl ? vs->origLeft : vs->curRect.left;
1112 		_string[4].ypos = vs->curRect.top;
1113 		_string[4].right = _screenWidth - 1;
1114 		_string[4].center = vs->center;
1115 
1116 		if (vs->curmode == 2)
1117 			_string[4].color = vs->dimcolor;
1118 		else if (mode && vs->hicolor)
1119 			_string[4].color = vs->hicolor;
1120 		else
1121 			_string[4].color = vs->color;
1122 
1123 		// FIXME For the future: Indy3 and under inv scrolling
1124 		/*
1125 		   if (verb >= 31 && verb <= 36)
1126 		   verb += _inventoryOffset;
1127 		 */
1128 
1129 		const byte *msg = getResourceAddress(rtVerb, verb);
1130 		if (!msg)
1131 			return;
1132 
1133 		tmp = _charset->_center;
1134 		drawString(4, msg);
1135 		_charset->_center = tmp;
1136 
1137 		if (isRtl)
1138 			vs->curRect.left = _charset->_str.left;
1139 		vs->curRect.right = _charset->_str.right;
1140 		vs->curRect.bottom = _charset->_str.bottom;
1141 		vs->oldRect = _charset->_str;
1142 		_charset->_str.left = _charset->_str.right;
1143 	} else if (_game.id != GID_FT) {
1144 		restoreVerbBG(verb);
1145 	}
1146 }
1147 
restoreVerbBG(int verb)1148 void ScummEngine::restoreVerbBG(int verb) {
1149 	VerbSlot *vs;
1150 
1151 	vs = &_verbs[verb];
1152 	uint8 col =
1153 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
1154 		((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 :
1155 #endif
1156 		vs->bkcolor;
1157 
1158 	if (vs->oldRect.left != -1) {
1159 		restoreBackground(vs->oldRect, col);
1160 		vs->oldRect.left = -1;
1161 	}
1162 }
1163 
drawVerbBitmap(int verb,int x,int y)1164 void ScummEngine::drawVerbBitmap(int verb, int x, int y) {
1165 	VerbSlot *vst = &_verbs[verb];
1166 	VirtScreen *vs;
1167 	bool twobufs;
1168 	const byte *imptr = 0;
1169 	int ydiff, xstrip;
1170 	int imgw, imgh;
1171 	int i, tmp;
1172 	byte *obim;
1173 	uint32 size;
1174 
1175 	if ((vs = findVirtScreen(y)) == NULL)
1176 		return;
1177 
1178 	_gdi->disableZBuffer();
1179 
1180 	twobufs = vs->hasTwoBuffers;
1181 	vs->hasTwoBuffers = false;
1182 
1183 	xstrip = x / 8;
1184 	ydiff = y - vs->topline;
1185 
1186 	obim = getResourceAddress(rtVerb, verb);
1187 	assert(obim);
1188 	if (_game.features & GF_OLD_BUNDLE) {
1189 		imgw = obim[0];
1190 		imgh = obim[1] / 8;
1191 		imptr = obim + 2;
1192 	} else if (_game.features & GF_SMALL_HEADER) {
1193 		size = READ_LE_UINT32(obim);
1194 
1195 		if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
1196 			imgw = (*(obim + size + 10));
1197 			imgh = (*(obim + size + 15)) / 8;
1198 		} else {
1199 			imgw = (*(obim + size + 11));
1200 			imgh = (*(obim + size + 17)) / 8;
1201 		}
1202 		imptr = getObjectImage(obim, 1);
1203 	} else {
1204 		const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), obim);
1205 		if (_game.version >= 7) {
1206 			imgw = READ_LE_UINT16(&imhd->v7.width) / 8;
1207 			imgh = READ_LE_UINT16(&imhd->v7.height) / 8;
1208 		} else {
1209 			imgw = READ_LE_UINT16(&imhd->old.width) / 8;
1210 			imgh = READ_LE_UINT16(&imhd->old.height) / 8;
1211 		}
1212 		imptr = getObjectImage(obim, 1);
1213 	}
1214 	assert(imptr);
1215 
1216 	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
1217 		_gdi->_distaff = (vst->verbid != 54);
1218 	}
1219 
1220 	for (i = 0; i < imgw; i++) {
1221 		tmp = xstrip + i;
1222 		_gdi->drawBitmap(imptr, vs, tmp, ydiff, imgw * 8, imgh * 8, i, 1, Gdi::dbAllowMaskOr | Gdi::dbObjectMode);
1223 	}
1224 
1225 	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
1226 		_gdi->_distaff = false;
1227 	}
1228 
1229 	vst->curRect.right = vst->curRect.left + imgw * 8;
1230 	vst->curRect.bottom = vst->curRect.top + imgh * 8;
1231 	vst->oldRect = vst->curRect;
1232 
1233 	_gdi->enableZBuffer();
1234 
1235 	vs->hasTwoBuffers = twobufs;
1236 }
1237 
getVerbSlot(int id,int mode) const1238 int ScummEngine::getVerbSlot(int id, int mode) const {
1239 	int i;
1240 	for (i = 1; i < _numVerbs; i++) {
1241 		if (_verbs[i].verbid == id && _verbs[i].saveid == mode) {
1242 			return i;
1243 		}
1244 	}
1245 	return 0;
1246 }
1247 
killVerb(int slot)1248 void ScummEngine::killVerb(int slot) {
1249 	VerbSlot *vs;
1250 
1251 	if (slot == 0)
1252 		return;
1253 
1254 	vs = &_verbs[slot];
1255 	vs->verbid = 0;
1256 	vs->curmode = 0;
1257 
1258 	_res->nukeResource(rtVerb, slot);
1259 
1260 	if (_game.version <= 6 && vs->saveid == 0) {
1261 		drawVerb(slot, 0);
1262 		verbMouseOver(0);
1263 	}
1264 	vs->saveid = 0;
1265 }
1266 
setVerbObject(uint room,uint object,uint verb)1267 void ScummEngine::setVerbObject(uint room, uint object, uint verb) {
1268 	const byte *obimptr;
1269 	const byte *obcdptr;
1270 	uint32 size, size2;
1271 	FindObjectInRoom foir;
1272 	int i;
1273 
1274 	if (_game.heversion >= 70) { // Windows titles. Here we always ignore room
1275 		room = getObjectRoom(object);
1276 	}
1277 
1278 	if (whereIsObject(object) == WIO_FLOBJECT)
1279 		error("Can't grab verb image from flobject");
1280 
1281 	if (_game.features & GF_OLD_BUNDLE) {
1282 		for (i = (_numLocalObjects-1); i > 0; i--) {
1283 			if (_objs[i].obj_nr == object) {
1284 				findObjectInRoom(&foir, foImageHeader, object, room);
1285 				size = READ_LE_UINT16(foir.obim);
1286 				byte *ptr = _res->createResource(rtVerb, verb, size + 2);
1287 				obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
1288 				ptr[0] = *(obcdptr + 9);	// Width
1289 				ptr[1] = *(obcdptr + 15);	// Height
1290 				memcpy(ptr + 2, foir.obim, size);
1291 				return;
1292 			}
1293 		}
1294 	} else if (_game.features & GF_SMALL_HEADER) {
1295 		for (i = (_numLocalObjects-1); i > 0; i--) {
1296 			if (_objs[i].obj_nr == object) {
1297 				// FIXME: the only thing we need from the OBCD is the image size!
1298 				// So we could use almost the same code (except for offsets)
1299 				// as in the GF_OLD_BUNDLE code. But of course that would break save games
1300 				// unless we insert special conversion code... <sigh>
1301 				findObjectInRoom(&foir, foImageHeader, object, room);
1302 				size = READ_LE_UINT32(foir.obim);
1303 				obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
1304 				size2 = READ_LE_UINT32(obcdptr);
1305 				_res->createResource(rtVerb, verb, size + size2);
1306 				obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
1307 				obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
1308 				memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
1309 				memcpy(getResourceAddress(rtVerb, verb) + size, obcdptr, size2);
1310 				return;
1311 			}
1312 		}
1313 	} else {
1314 		findObjectInRoom(&foir, foImageHeader, object, room);
1315 		size = READ_BE_UINT32(foir.obim + 4);
1316 		_res->createResource(rtVerb, verb, size);
1317 		obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
1318 		memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
1319 	}
1320 }
1321 
1322 } // End of namespace Scumm
1323