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