scummvm/engines/scumm/verbs.cpp
Max Horn 0ce2ca4e00 COMMON: Replace MKID_BE by MKTAG
MKID_BE relied on unspecified behavior of the C++ compiler,
and as such was always a bit unsafe. The new MKTAG macro
is slightly less elegant, but does no longer depend on the
behavior of the compiler.
Inspired by FFmpeg, which has an almost identical macro.
2011-04-12 16:53:15 +02:00

1621 lines
42 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "scumm/actor.h"
#include "scumm/charset.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
#include "scumm/scumm_v0.h"
#include "scumm/scumm_v7.h"
#include "scumm/verbs.h"
namespace Scumm {
enum {
kInventoryUpArrow = 4,
kInventoryDownArrow = 5,
kSentenceLine = 6
};
struct VerbSettings {
int id;
int x_pos;
int y_pos;
int prep;
const char *name;
};
static const VerbSettings v0VerbTable_English[] = {
{ 1, 8, 0, 0, "Open"},
{ 2, 8, 1, 0, "Close"},
{ 3, 0, 2, 4, "Give"},
{ 4, 32, 0, 0, "Turn on"},
{ 5, 32, 1, 0, "Turn off"},
{ 6, 32, 2, 2, "Fix"},
{ 7, 24, 0, 0, "New Kid"},
{ 8, 24, 1, 2, "Unlock"},
{ 9, 0, 0, 0, "Push"},
{10, 0, 1, 0, "Pull"},
{11, 24, 2, 255, "Use"},
{12, 8, 2, 0, "Read"},
{13, 15, 0, 0, "Walk to"},
{14, 15, 1, 0, "Pick up"},
{15, 15, 2, 0, "What is"}
};
// FIXME: Replace * with the correct character
static const VerbSettings v0VerbTable_German[] = {
{ 1, 7, 0, 0, "$ffne"},
{ 2, 13, 1, 0, "Schlie*e"},
{ 3, 0, 2, 4, "Gebe"},
{ 4, 37, 1, 0, "Ein"},
{ 5, 37, 0, 0, "Aus"},
{ 6, 23, 1, 2, "Repariere"},
{ 7, 34, 2, 0, "Person"},
{ 8, 23, 0, 2, "Schlie*e auf"},
{ 9, 0, 0, 0, "Dr<cke"},
{10, 0, 1, 0, "Ziehe"},
{11, 23, 2, 255, "Benutz"},
{12, 7, 2, 0, "Lese"},
{13, 13, 0, 0, "Gehe zu"},
{14, 7, 1, 0, "Nimm"},
{15, 13, 2, 0, "Was ist"}
};
void ScummEngine_v0::resetVerbs() {
VirtScreen *virt = &_virtscr[kVerbVirtScreen];
VerbSlot *vs;
const VerbSettings *vtable;
int i;
switch (_language) {
case Common::DE_DEU:
vtable = (const VerbSettings*)v0VerbTable_German;
break;
default:
vtable = (const VerbSettings*)v0VerbTable_English;
}
for (i = 1; i < 16; i++)
killVerb(i);
for (i = 1; i < 16; i++) {
vs = &_verbs[i];
vs->verbid = vtable[i - 1].id;
vs->color = 5;
vs->hicolor = 7;
vs->dimcolor = 11;
vs->type = kTextVerbType;
vs->charset_nr = _string[0]._default.charset;
vs->curmode = 1;
vs->saveid = 0;
vs->key = 0;
vs->center = 0;
vs->imgindex = 0;
vs->prep = vtable[i - 1].prep;
vs->curRect.left = vtable[i - 1].x_pos * 8;
vs->curRect.top = vtable[i - 1].y_pos * 8 + virt->topline + 8;
loadPtrToResource(rtVerb, i, (const byte*)vtable[i - 1].name);
}
}
void ScummEngine_v0::setNewKidVerbs() {
VirtScreen *virt = &_virtscr[kVerbVirtScreen];
VerbSlot *vs;
int i;
for (i = 1; i < 16; i++)
killVerb(i);
for (i = 1; i < 4; i++) {
vs = &_verbs[i];
vs->verbid = i;
vs->color = 5;
vs->hicolor = 7;
vs->dimcolor = 11;
vs->type = kTextVerbType;
vs->charset_nr = _string[0]._default.charset;
vs->curmode = 1;
vs->saveid = 0;
vs->key = 0;
vs->center = 0;
vs->imgindex = 0;
vs->prep = 0;
vs->curRect.left = (i * 8) * 8;
vs->curRect.top = virt->topline + 8;
Actor *a = derefActor(VAR(96 + i), "setNewKidVerbs");
loadPtrToResource(rtVerb, i, (const byte*)a->getActorName());
}
setUserState(191);
}
void ScummEngine_v0::switchActor(int slot) {
resetSentence(false);
if (_currentRoom == 45)
return;
// radiation suit? don't let the player switch
if (VAR(VAR_EGO) == 8)
return;
// verbs disabled? or just new kid button?
if (_currentMode == 0 || _currentMode == 1 || _currentMode == 2)
return;
VAR(VAR_EGO) = VAR(97 + slot);
resetVerbs();
actorFollowCamera(VAR(VAR_EGO));
setUserState(247);
}
void ScummEngine_v2::initV2MouseOver() {
int i;
int arrow_color, color, hi_color;
if (_game.version == 2) {
color = 13;
hi_color = 14;
arrow_color = 1;
} else {
color = 16;
hi_color = 7;
arrow_color = 6;
}
_mouseOverBoxV2 = -1;
// Inventory items
for (i = 0; i < 2; i++) {
_mouseOverBoxesV2[2 * i].rect.left = 0;
_mouseOverBoxesV2[2 * i].rect.right = 144;
_mouseOverBoxesV2[2 * i].rect.top = 32 + 8 * i;
_mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;
_mouseOverBoxesV2[2 * i].color = color;
_mouseOverBoxesV2[2 * i].hicolor = hi_color;
_mouseOverBoxesV2[2 * i + 1].rect.left = 176;
_mouseOverBoxesV2[2 * i + 1].rect.right = 320;
_mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
_mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;
_mouseOverBoxesV2[2 * i + 1].color = color;
_mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
}
// Inventory arrows
_mouseOverBoxesV2[kInventoryUpArrow].rect.left = 144;
_mouseOverBoxesV2[kInventoryUpArrow].rect.right = 176;
_mouseOverBoxesV2[kInventoryUpArrow].rect.top = 32;
_mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 40;
_mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
_mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;
_mouseOverBoxesV2[kInventoryDownArrow].rect.left = 144;
_mouseOverBoxesV2[kInventoryDownArrow].rect.right = 176;
_mouseOverBoxesV2[kInventoryDownArrow].rect.top = 40;
_mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 48;
_mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
_mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;
// Sentence line
_mouseOverBoxesV2[kSentenceLine].rect.left = 0;
_mouseOverBoxesV2[kSentenceLine].rect.right = 320;
_mouseOverBoxesV2[kSentenceLine].rect.top = 0;
_mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;
_mouseOverBoxesV2[kSentenceLine].color = color;
_mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
}
void ScummEngine_v2::initNESMouseOver() {
int i;
int arrow_color, color, hi_color;
color = 0;
hi_color = 0;
arrow_color = 0;
_mouseOverBoxV2 = -1;
// Inventory items
for (i = 0; i < 2; i++) {
_mouseOverBoxesV2[2 * i].rect.left = 16;
_mouseOverBoxesV2[2 * i].rect.right = 120;
_mouseOverBoxesV2[2 * i].rect.top = 48 + 8 * i;
_mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;
_mouseOverBoxesV2[2 * i].color = color;
_mouseOverBoxesV2[2 * i].hicolor = hi_color;
_mouseOverBoxesV2[2 * i + 1].rect.left = 152;
_mouseOverBoxesV2[2 * i + 1].rect.right = 256;
_mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
_mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;
_mouseOverBoxesV2[2 * i + 1].color = color;
_mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
}
// Inventory arrows
_mouseOverBoxesV2[kInventoryUpArrow].rect.left = 128;
_mouseOverBoxesV2[kInventoryUpArrow].rect.right = 136;
_mouseOverBoxesV2[kInventoryUpArrow].rect.top = 48;
_mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 56;
_mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
_mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;
_mouseOverBoxesV2[kInventoryDownArrow].rect.left = 136;
_mouseOverBoxesV2[kInventoryDownArrow].rect.right = 144;
_mouseOverBoxesV2[kInventoryDownArrow].rect.top = 48;
_mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 56;
_mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
_mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;
// Sentence line
_mouseOverBoxesV2[kSentenceLine].rect.left = 16;
_mouseOverBoxesV2[kSentenceLine].rect.right = 256;
_mouseOverBoxesV2[kSentenceLine].rect.top = 0;
_mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;
_mouseOverBoxesV2[kSentenceLine].color = color;
_mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
}
void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
VirtScreen *vs = &_virtscr[kVerbVirtScreen];
Common::Rect rect;
byte *ptr, *dst;
int i, x, y, new_box = -1;
// Don't do anything unless the inventory is active
if (!(_userState & 64)) {
_mouseOverBoxV2 = -1;
return;
}
if (_cursor.state > 0) {
for (i = 0; i < ARRAYSIZE(_mouseOverBoxesV2); i++) {
if (_mouseOverBoxesV2[i].rect.contains(pos.x, pos.y - vs->topline)) {
new_box = i;
break;
}
}
}
if ((new_box != _mouseOverBoxV2) || (_game.version == 0)) {
if (_mouseOverBoxV2 != -1) {
rect = _mouseOverBoxesV2[_mouseOverBoxV2].rect;
dst = ptr = vs->getPixels(rect.left, rect.top);
// Remove highlight.
for (y = rect.height() - 1; y >= 0; y--) {
for (x = rect.width() - 1; x >= 0; x--) {
if (dst[x] == _mouseOverBoxesV2[_mouseOverBoxV2].hicolor)
dst[x] = _mouseOverBoxesV2[_mouseOverBoxV2].color;
}
dst += vs->pitch;
}
markRectAsDirty(kVerbVirtScreen, rect);
}
if (new_box != -1) {
rect = _mouseOverBoxesV2[new_box].rect;
dst = ptr = vs->getPixels(rect.left, rect.top);
// Apply highlight
for (y = rect.height() - 1; y >= 0; y--) {
for (x = rect.width() - 1; x >= 0; x--) {
if (dst[x] == _mouseOverBoxesV2[new_box].color)
dst[x] = _mouseOverBoxesV2[new_box].hicolor;
}
dst += vs->pitch;
}
markRectAsDirty(kVerbVirtScreen, rect);
}
_mouseOverBoxV2 = new_box;
}
}
void ScummEngine_v2::checkV2Inventory(int x, int y) {
int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
int object = 0;
y -= _virtscr[kVerbVirtScreen].topline;
if ((y < inventoryArea) || !(_mouseAndKeyboardStat & MBS_LEFT_CLICK))
return;
if (_mouseOverBoxesV2[kInventoryUpArrow].rect.contains(x, y)) {
if (_inventoryOffset >= 2) {
_inventoryOffset -= 2;
redrawV2Inventory();
}
} else if (_mouseOverBoxesV2[kInventoryDownArrow].rect.contains(x, y)) {
if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
_inventoryOffset += 2;
redrawV2Inventory();
}
}
for (object = 0; object < 4; object++) {
if (_mouseOverBoxesV2[object].rect.contains(x, y)) {
break;
}
}
if (object >= 4)
return;
object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
if (object > 0) {
if (_game.version == 0) {
_activeInventory = object;
} else {
runInputScript(kInventoryClickArea, object, 0);
}
}
}
void ScummEngine_v2::redrawV2Inventory() {
VirtScreen *vs = &_virtscr[kVerbVirtScreen];
int i;
int max_inv;
Common::Rect inventoryBox;
int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
int maxChars = (_game.platform == Common::kPlatformNES) ? 13: 18;
_mouseOverBoxV2 = -1;
if (!(_userState & 64)) // Don't draw inventory unless active
return;
// Clear on all invocations
inventoryBox.top = vs->topline + inventoryArea;
inventoryBox.bottom = vs->topline + vs->h;
inventoryBox.left = 0;
inventoryBox.right = vs->w;
restoreBackground(inventoryBox);
_string[1].charset = 1;
max_inv = getInventoryCount(_scummVars[VAR_EGO]) - _inventoryOffset;
if (max_inv > 4)
max_inv = 4;
for (i = 0; i < max_inv; i++) {
int obj = findInventory(_scummVars[VAR_EGO], i + 1 + _inventoryOffset);
if (obj == 0)
break;
_string[1].ypos = _mouseOverBoxesV2[i].rect.top + vs->topline;
_string[1].xpos = _mouseOverBoxesV2[i].rect.left;
_string[1].right = _mouseOverBoxesV2[i].rect.right - 1;
_string[1].color = _mouseOverBoxesV2[i].color;
_v0ObjectInInventory = true;
const byte *tmp = getObjOrActorName(obj);
_v0ObjectInInventory = false;
assert(tmp);
// Prevent inventory entries from overflowing by truncating the text
byte msg[20];
msg[maxChars] = 0;
strncpy((char *)msg, (const char *)tmp, maxChars);
// Draw it
drawString(1, msg);
}
// If necessary, draw "up" arrow
if (_inventoryOffset > 0) {
_string[1].xpos = _mouseOverBoxesV2[kInventoryUpArrow].rect.left;
_string[1].ypos = _mouseOverBoxesV2[kInventoryUpArrow].rect.top + vs->topline;
_string[1].right = _mouseOverBoxesV2[kInventoryUpArrow].rect.right - 1;
_string[1].color = _mouseOverBoxesV2[kInventoryUpArrow].color;
if (_game.platform == Common::kPlatformNES)
drawString(1, (const byte *)"\x7E");
else
drawString(1, (const byte *)" \1\2");
}
// If necessary, draw "down" arrow
if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
_string[1].xpos = _mouseOverBoxesV2[kInventoryDownArrow].rect.left;
_string[1].ypos = _mouseOverBoxesV2[kInventoryDownArrow].rect.top + vs->topline;
_string[1].right = _mouseOverBoxesV2[kInventoryDownArrow].rect.right - 1;
_string[1].color = _mouseOverBoxesV2[kInventoryDownArrow].color;
if (_game.platform == Common::kPlatformNES)
drawString(1, (const byte *)"\x7F");
else
drawString(1, (const byte *)" \3\4");
}
}
void ScummEngine::redrawVerbs() {
if (_game.version <= 2 && !(_userState & 128)) // Don't draw verbs unless active
return;
int i, verb = 0;
if (_cursor.state > 0)
verb = findVerbAtPos(_mouse.x, _mouse.y);
// Iterate over all verbs.
// Note: This is the correct order (at least for MI EGA, MI2, Full Throttle).
// Do not change it! If you discover, based on disasm, that some game uses
// another (e.g. the reverse) order here, you have to use an if/else construct
// to add it as a special case!
for (i = 0; i < _numVerbs; i++) {
if (i == verb && _verbs[verb].hicolor)
drawVerb(i, 1);
else
drawVerb(i, 0);
}
_verbMouseOver = verb;
}
void ScummEngine::handleMouseOver(bool updateInventory) {
if (_completeScreenRedraw) {
verbMouseOver(0);
} else {
if (_cursor.state > 0)
verbMouseOver(findVerbAtPos(_mouse.x, _mouse.y));
}
}
void ScummEngine_v2::handleMouseOver(bool updateInventory) {
ScummEngine::handleMouseOver(updateInventory);
if (updateInventory) {
// FIXME/TODO: Reset and redraw the sentence line
_inventoryOffset = 0;
}
if (_completeScreenRedraw || updateInventory) {
redrawV2Inventory();
}
checkV2MouseOver(_mouse);
}
void ScummEngine_v0::handleMouseOver(bool updateInventory) {
drawSentence();
ScummEngine_v2::handleMouseOver(updateInventory);
}
#ifdef ENABLE_HE
void ScummEngine_v72he::checkExecVerbs() {
VAR(VAR_MOUSE_STATE) = 0;
if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
return;
VAR(VAR_MOUSE_STATE) = _mouseAndKeyboardStat;
ScummEngine::checkExecVerbs();
}
#endif
void ScummEngine::checkExecVerbs() {
int i, over;
VerbSlot *vs;
if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
return;
if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
/* Check keypresses */
if (!(_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD)) {
// This is disabled in the SegaCD version as the "vs->key" values setup
// by script-17 conflict with the values expected by the generic keyboard
// input script. See tracker item #1193185.
vs = &_verbs[1];
for (i = 1; i < _numVerbs; i++, vs++) {
if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
if (_mouseAndKeyboardStat == vs->key) {
// Trigger verb as if the user clicked it
runInputScript(kVerbClickArea, vs->verbid, 1);
return;
}
}
}
}
if ((_game.id == GID_INDY4 || _game.id == GID_PASS) && _mouseAndKeyboardStat >= '0' && _mouseAndKeyboardStat <= '9') {
// To support keyboard fighting in FOA, we need to remap the number keys.
// FOA apparently expects PC scancode values (see script 46 if you want
// to know where I got these numbers from). Oddly enough, the The Indy 3
// part of the "Passport to Adventure" demo expects the same keyboard
// mapping, even though the full game doesn't.
static const int numpad[10] = {
'0',
335, 336, 337,
331, 332, 333,
327, 328, 329
};
_mouseAndKeyboardStat = numpad[_mouseAndKeyboardStat - '0'];
}
if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) {
// HACK: In the FM-TOWNS games Indy3, Loom and Zak the most significant bit is set for special keys
// like F5 (=0x8005) or joystick buttons (mask 0xFE00, e.g. SELECT=0xFE40 for the save/load menu).
// Hence the distinction with (_mouseAndKeyboardStat < MBS_MAX_KEY) between mouse- and key-events is not applicable
// to this games, so we have to remap the special keys here.
if (_mouseAndKeyboardStat == 319) {
_mouseAndKeyboardStat = 0x8005;
}
}
if ((_game.platform == Common::kPlatformFMTowns && _game.id == GID_ZAK) &&
(_mouseAndKeyboardStat >= 315 && _mouseAndKeyboardStat <= 318)) {
// Hack: Handle switching to a person via F1-F4 keys.
// This feature isn't available in the scripts of the FM-TOWNS version.
int fKey = _mouseAndKeyboardStat - 314;
int switchSlot = getVerbSlot(36, 0);
// check if switch-verb is enabled
if (_verbs[switchSlot].curmode == 1) {
// Check if person is available (see script 23 from ZAK_FM-TOWNS and script 4 from ZAK_PC).
// Zak: Var[144 Bit 15], Annie: Var[145 Bit 0], Melissa: Var[145 Bit 1], Leslie: Var[145 Bit 2]
if (!readVar(0x890E + fKey)) {
runInputScript(kVerbClickArea, 36 + fKey, 0);
}
}
return;
}
// Generic keyboard input
runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
VirtScreen *zone = findVirtScreen(_mouse.y);
const byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;
// This could be kUnkVirtScreen.
// Fixes bug #1536932: "MANIACNES: Crash on click in speechtext-area"
if (!zone)
return;
over = findVerbAtPos(_mouse.x, _mouse.y);
if (over != 0) {
// Verb was clicked
runInputScript(kVerbClickArea, _verbs[over].verbid, code);
} else {
// Scene was clicked
runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
}
}
}
void ScummEngine_v2::checkExecVerbs() {
int i, over;
VerbSlot *vs;
if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
return;
if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
/* Check keypresses */
vs = &_verbs[1];
for (i = 1; i < _numVerbs; i++, vs++) {
if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
if (_mouseAndKeyboardStat == vs->key) {
// Trigger verb as if the user clicked it
runInputScript(kVerbClickArea, vs->verbid, 1);
return;
}
}
}
// Simulate inventory picking and scrolling keys
int object = -1;
switch (_mouseAndKeyboardStat) {
case 'u': // arrow up
if (_inventoryOffset >= 2) {
_inventoryOffset -= 2;
redrawV2Inventory();
}
return;
case 'j': // arrow down
if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
_inventoryOffset += 2;
redrawV2Inventory();
}
return;
case 'i': // object
object = 0;
break;
case 'o':
object = 1;
break;
case 'k':
object = 2;
break;
case 'l':
object = 3;
break;
}
if (object != -1) {
object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
if (object > 0) {
if (_game.version == 0) {
_activeInventory = object;
} else {
runInputScript(kInventoryClickArea, object, 0);
}
}
return;
}
// Generic keyboard input
runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
VirtScreen *zone = findVirtScreen(_mouse.y);
const byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;
const int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
// This could be kUnkVirtScreen.
// Fixes bug #1536932: "MANIACNES: Crash on click in speechtext-area"
if (!zone)
return;
if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
// Click into V2 sentence line
runInputScript(kSentenceClickArea, 0, 0);
} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + inventoryArea) {
// Click into V2 inventory
checkV2Inventory(_mouse.x, _mouse.y);
} else {
over = findVerbAtPos(_mouse.x, _mouse.y);
if (over != 0) {
// Verb was clicked
runInputScript(kVerbClickArea, _verbs[over].verbid, code);
} else {
// Scene was clicked
runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
}
}
}
}
void ScummEngine_v0::runObject(int obj, int entry) {
bool prev = _v0ObjectInInventory;
if (getVerbEntrypoint(obj, entry) == 0) {
// If nothing was found, attempt to find the 'WHAT-IS' verb script
// (which is not really the what-is script, as this verb never actually executes
// it merely seems to be some type of fallback)
if (getVerbEntrypoint(obj, 0x0F) != 0) {
entry = 0x0F;
}
}
_v0ObjectInInventory = prev;
if (getVerbEntrypoint(obj, entry) != 0) {
_v0ObjectInInventory = prev;
runObjectScript(obj, entry, false, false, NULL);
} else if (entry != 13 && entry != 15) {
if (_activeVerb != 3) {
VAR(VAR_ACTIVE_VERB) = entry;
runScript(3, 0, 0, 0);
// For some reasons, certain objects don't have a "give" script
} else if (VAR(VAR_ACTIVE_ACTOR) > 0 && VAR(VAR_ACTIVE_ACTOR) < 8) {
if (_activeInventory)
setOwnerOf(_activeInventory, VAR(VAR_ACTIVE_ACTOR));
}
}
}
bool ScummEngine_v0::verbMoveToActor(int actor) {
Actor *a = derefActor(VAR(VAR_EGO), "verbMoveToActor");
Actor *a2 = derefActor(actor, "verbMoveToActor");
int dist = getDist(a->getRealPos().x, a->getRealPos().y, a2->getRealPos().x, a2->getRealPos().y);
if (!a->_moving && dist > 4) {
a->startWalkActor(a2->getRealPos().x, a2->getRealPos().y, -1);
} else {
if (dist <= 4) {
a->stopActorMoving();
return false;
}
}
return true;
}
bool ScummEngine_v0::verbMove(int object, int objectIndex, bool invObject) {
int x, y, dir;
Actor *a = derefActor(VAR(VAR_EGO), "verbMove");
if (_currentMode != 3 && _currentMode != 2)
return false;
_v0ObjectIndex = true;
getObjectXYPos(objectIndex, x, y, dir);
_v0ObjectIndex = false;
// Detect distance from target object
int dist = getDist(a->getRealPos().x, a->getRealPos().y, x, y);
if (a->_moving)
return true;
if (dist > 5) {
a->startWalkActor(x, y, dir);
VAR(6) = x;
VAR(7) = y;
return true;
} else {
// Finished walk, are we picking up the item?
if (_verbPickup) {
int oldActive = _activeObject, oldIndex = _activeObjectIndex;
_activeObject = object;
_activeObjectIndex = objectIndex;
_v0ObjectIndex = true;
// Execute pickup
runObject(objectIndex, 14);
_v0ObjectIndex = false;
_activeObject = oldActive;
_activeObjectIndex = oldIndex;
// Finished picking up
_verbPickup = false;
}
}
return false;
}
bool ScummEngine_v0::verbObtain(int obj, int objIndex) {
bool didPickup = false;
int prep, where = whereIsObjectInventory(obj);
if (objIndex == 0)
return false;
// Object in inventory ?
if (where != WIO_INVENTORY) {
_v0ObjectIndex = true;
prep = verbPrep(objIndex);
if (prep == 1 || prep == 4) {
if (_activeVerb != 13 && _activeVerb != 14) {
_verbPickup = true;
didPickup = true;
}
} else {
_verbPickup = false;
}
// Ignore verbs?
Actor *a = derefActor(VAR(VAR_EGO), "verbObtain");
if (((ActorC64 *)a)->_miscflags & 0x40) {
resetSentence(false);
return false;
}
//attempt move to object
if (verbMove(obj, objIndex, false))
return true;
if (didPickup && (prep == 1 || prep == 4))
if (_activeVerb != 13 && _activeVerb != 14) {
_v0ObjectInInventory = true;
if (whereIsObject(obj) == WIO_INVENTORY)
_activeInventory = obj;
else
resetSentence(false);
_v0ObjectInInventory = false;
}
}
return false;
}
int ScummEngine_v0::verbPrep(int object) {
if (!_v0ObjectInInventory)
_v0ObjectIndex = true;
else
_v0ObjectIndex = false;
byte *ptr = getOBCDFromObject(object);
_v0ObjectIndex = false;
assert(ptr);
return (*(ptr + 11) >> 5);
}
bool ScummEngine_v0::verbExecutes(int object, bool inventory) {
_v0ObjectInInventory = inventory;
int prep = verbPrep(object);
if (prep == 2 || prep == 0) {
return true;
}
return false;
}
bool ScummEngine_v0::verbExec() {
int prep = 0;
int entry = (_currentMode != 0 && _currentMode != 1) ? _activeVerb : 15;
if ((!_activeInvExecute && _activeObject && getObjectIndex(_activeObject) == -1)) {
resetSentence(false);
return false;
}
// Lets try walk to the object
if (_activeObject && _activeObjectIndex && !_activeObjectObtained && _currentMode != 0) {
prep = verbPrep(_activeObjectIndex);
if (verbObtain(_activeObject, _activeObjectIndex))
return true;
_activeObjectObtained = true;
}
// Attempt to obtain/reach object2
if (_activeObject2 && _activeObject2Index && !_activeObject2Obtained && _currentMode != 0) {
prep = verbPrep(_activeObject2Index);
_v0ObjectInInventory = false;
if (verbObtain(_activeObject2, _activeObject2Index))
return true;
if (prep != 1 && prep != 4) {
_activeInventory = _activeObject;
_activeObject = _activeObject2;
_activeObjectIndex = _activeObject2Index;
_activeObject2 = 0;
_activeObject2Index = 0;
}
_activeObject2Obtained = true;
}
// Give-To
if (_activeVerb == 3 && _activeInventory && _activeActor) {
// FIXME: Actors need to turn and face each other
if (verbMoveToActor(_activeActor)) {
// Ignore verbs?
Actor *a = derefActor(VAR(VAR_EGO), "verbExec");
if (((ActorC64 *)a)->_miscflags & 0x40) {
resetSentence(false);
return false;
}
return true;
}
_v0ObjectInInventory = true;
VAR(VAR_ACTIVE_ACTOR) = _activeActor;
runObject(_activeInventory , 3);
_v0ObjectInInventory = false;
resetSentence(false);
return false;
}
// Where we performing an action on an actor?
if (_activeActor) {
_v0ObjectIndex = true;
runObject(_activeActor, entry);
_v0ObjectIndex = false;
_verbExecuting = false;
resetSentence(false);
return false;
}
// If we've finished walking (now near target), execute the action
if (_activeObject && _activeObjectIndex && verbPrep(_activeObjectIndex) == 2) {
_v0ObjectIndex = true;
runObject(_activeObjectIndex, entry);
_v0ObjectIndex = false;
_verbExecuting = false;
if ((_currentMode == 3 || _currentMode == 2) && _activeVerb == 13)
return false;
resetSentence(false);
return false;
}
// We acted on an inventory item
if (_activeInventory && verbExecutes(_activeInventory, true) && _activeVerb != 3) {
_v0ObjectInInventory = true;
_activeObject = _activeInventory;
runObject(_activeInventory, _activeVerb);
_verbExecuting = false;
if (_currentMode == 3 && _activeVerb == 13) {
resetSentence(true);
return false;
}
resetSentence(false);
return false;
}
// Item not in inventory is executed
if (_activeObject) {
_v0ObjectIndex = true;
runObject(_activeObjectIndex, entry);
_v0ObjectIndex = false;
} else if (_activeInventory) {
// Not sure this is the correct way to do this,
// however its working for most situations - segra
if (verbExecutes(_activeInventory, true) == false) {
if (_activeObject2 && _activeObject2Inv && verbExecutes(_activeObject2, true)) {
_v0ObjectInInventory = true;
_activeObject = _activeInventory;
_activeInventory = _activeObject2;
runObject(_activeObject, _activeVerb);
} else {
_v0ObjectInInventory = true;
if (_activeObject2) {
_activeObject = _activeObject2;
runObject(_activeObject, _activeVerb);
} else
runObject(_activeInventory, _activeVerb);
}
} else {
_v0ObjectInInventory = true;
runObject(_activeInventory, _activeVerb);
}
}
_verbExecuting = false;
if (_activeVerb == 13) {
resetSentence(true);
return false;
}
resetSentence(false);
return false;
}
void ScummEngine_v0::checkExecVerbs() {
ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
VirtScreen *zone = findVirtScreen(_mouse.y);
// Is a verb currently executing
if (_verbExecuting) {
// Check if mouse click
if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
int over = findVerbAtPos(_mouse.x, _mouse.y);
int act = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
int obj = findObject(_virtualMouse.x, _virtualMouse.y);
if (over && over != _activeVerb) {
_activeVerb = over;
_verbExecuting = false;
return;
}
if (!obj && !act && !over) {
resetSentence(false);
} else {
a->stopActorMoving();
}
} else {
if (_verbExecuting && !verbExec())
return;
}
}
// What-Is selected, any object we hover over is selected, on mouse press we set to WalkTo
if (_activeVerb == 15) {
int obj = findObject(_virtualMouse.x, _virtualMouse.y);
int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y);
_activeObject = obj;
_activeObjectIndex = objIdx;
if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK))
_activeVerb = 13; // Walk-To
return;
}
if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
return;
if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
/* Check keypresses */
// TODO
} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
// TODO
} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) {
int prevInventory = _activeInventory;
int invOff = _inventoryOffset;
// Click into V2 inventory
checkV2Inventory(_mouse.x, _mouse.y);
// Did the Inventory position changed (arrows pressed, do nothing)
if (invOff != _inventoryOffset)
return;
// No inventory selected?
if (!_activeInventory)
return;
// Did we just change the selected inventory item?
if (prevInventory && prevInventory != _activeInventory && _activeInventory != _activeObject2) {
_v0ObjectInInventory = true;
int prep = verbPrep(_activeInventory);
_v0ObjectInInventory = true;
int prep2 = verbPrep(prevInventory);
// Should the new inventory object remain as the secondary selected object
// Or should the new inventory object become primary?
if (prep != prep2 || prep != 1) {
if (prep == 1 || prep == 3) {
int tmp = _activeInventory;
_activeInventory = prevInventory;
prevInventory = tmp;
}
}
// Setup object2
_activeObject = 0;
_activeInvExecute = true;
_activeObject2Inv = true;
_activeObject2 = _activeInventory;
_activeInventory = prevInventory;
return;
}
// is the new selected inventory the same as the last selected?, reset to previous if it is
if (_activeInventory == _activeObject2)
_activeInventory = prevInventory;
// Inventory Selected changed
if (prevInventory != _activeInventory)
if (!_activeObject2 || prevInventory != _activeObject2)
return;
if (_activeVerb == 11 && !(((_activeObject || _activeInventory)) || !_activeObject2))
return;
} else {
int over = findVerbAtPos(_mouse.x, _mouse.y);
int act = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
int obj = findObject(_virtualMouse.x, _virtualMouse.y);
int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y);
// If we already have an object selected, and we just clicked an actor
// Clear any object we may of also clicked on
if ((_activeObject || _activeInventory) && act) {
obj = 0;
objIdx = 0;
}
if (a->_miscflags & 0x80) {
if (_activeVerb != 7 && over != 7) {
_activeVerb = 0;
over = 0;
}
}
// Handle New Kid verb options
if (_activeVerb == 7 || over == 7) {
// Disable New-Kid (in the secret lab)
if (_currentMode == 2 || _currentMode == 0)
return;
if (_activeVerb == 7 && over) {
_activeVerb = 13;
switchActor(_verbs[over].verbid - 1);
return;
}
setNewKidVerbs();
_activeVerb = 7;
return;
}
// Clicked on nothing, walk here?
if (!over && !act && _activeVerb == 13 && !obj && _currentMode != 0) {
// Clear all selected
resetSentence(false);
// 0xB31
VAR(6) = _virtualMouse.x / V12_X_MULTIPLIER;
VAR(7) = _virtualMouse.y / V12_Y_MULTIPLIER;
if (zone->number == kMainVirtScreen) {
// Ignore verbs?
if (a->_miscflags & 0x40) {
resetSentence(false);
return;
}
a->stopActorMoving();
a->startWalkActor(VAR(6), VAR(7), -1);
_verbExecuting = true;
}
return;
}
// No new verb, use previous
if (over == 0)
over = _activeVerb;
// No verb selected, use walk-to
if (!_activeVerb)
_activeVerb = over = 13; // Walk-To
// New verb selected
if (_activeVerb != over) {
_activeVerb = over;
if (_activeVerb == 13) {
resetSentence(false);
}
return;
}
// Only allowing targetting actors if its the GIVE/USE verb
if (_activeVerb == 3 || _activeVerb == 11) {
// Different actor selected?
if (act) {
if (_activeActor != act) {
_activeActor = act;
return;
}
}
}
if (obj && obj != _activeObject) {
if (!_activeObject)
if (_activeInventory)
_activeInvExecute = true;
// USE
if (_activeVerb == 11 || _activeVerb == 8) {
if (obj != _activeObject || obj != _activeObject2) {
if (!_activeObject || _activeInventory) {
_activeObject = obj;
_activeObjectIndex = objIdx;
return;
} else {
if (_activeObject2 != obj) {
_activeObject2 = obj;
_activeObject2Index = objIdx;
return;
}
}
}
} else {
a->stopActorMoving();
_activeObject = obj;
_activeObjectIndex = objIdx;
if (_activeVerb != 13)
return;
//return;
}
}
}
_verbExecuting = true;
} // mouse k/b action
}
void ScummEngine::verbMouseOver(int verb) {
// Don't do anything unless verbs are active
if (_game.version <= 2 && !(_userState & 128))
return;
if (_game.id == GID_FT)
return;
if (_verbMouseOver != verb) {
if (_verbs[_verbMouseOver].type != kImageVerbType) {
drawVerb(_verbMouseOver, 0);
_verbMouseOver = verb;
}
if (_verbs[verb].type != kImageVerbType && _verbs[verb].hicolor) {
drawVerb(verb, 1);
_verbMouseOver = verb;
}
}
}
int ScummEngine::findVerbAtPos(int x, int y) const {
if (!_numVerbs)
return 0;
VerbSlot *vs;
int i = _numVerbs - 1;
vs = &_verbs[i];
do {
if (vs->curmode != 1 || !vs->verbid || vs->saveid || y < vs->curRect.top || y >= vs->curRect.bottom)
continue;
if (vs->center) {
if (x < -(vs->curRect.right - 2 * vs->curRect.left) || x >= vs->curRect.right)
continue;
} else {
if (x < vs->curRect.left || x >= vs->curRect.right)
continue;
}
return i;
} while (--vs, --i);
return 0;
}
#ifdef ENABLE_SCUMM_7_8
void ScummEngine_v7::drawVerb(int verb, int mode) {
VerbSlot *vs;
if (!verb)
return;
vs = &_verbs[verb];
if (!vs->saveid && vs->curmode && vs->verbid) {
if (vs->type == kImageVerbType) {
drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
return;
}
uint8 color = vs->color;
if (vs->curmode == 2)
color = vs->dimcolor;
else if (mode && vs->hicolor)
color = vs->hicolor;
const byte *msg = getResourceAddress(rtVerb, verb);
if (!msg)
return;
// Convert the message, and skip a few remaining 0xFF codes (they
// occur in FT; subtype 10, which is used for the speech associated
// with the string).
byte buf[384];
convertMessageToString(msg, buf, sizeof(buf));
msg = buf;
while (*msg == 0xFF)
msg += 4;
// Set the specified charset id
int oldID = _charset->getCurID();
_charset->setCurID(vs->charset_nr);
// Compute the text rect
vs->curRect.right = 0;
vs->curRect.bottom = 0;
const byte *msg2 = msg;
while (*msg2) {
const int charWidth = _charset->getCharWidth(*msg2);
const int charHeight = _charset->getCharHeight(*msg2);
vs->curRect.right += charWidth;
if (vs->curRect.bottom < charHeight)
vs->curRect.bottom = charHeight;
msg2++;
}
vs->curRect.right += vs->curRect.left;
vs->curRect.bottom += vs->curRect.top;
vs->oldRect = vs->curRect;
const int maxWidth = _screenWidth - vs->curRect.left;
if (_charset->getStringWidth(0, buf) > maxWidth && _game.version == 8) {
byte tmpBuf[384];
memcpy(tmpBuf, msg, 384);
int len = resStrLen(tmpBuf) - 1;
while (len >= 0) {
if (tmpBuf[len] == ' ') {
tmpBuf[len] = 0;
if (_charset->getStringWidth(0, tmpBuf) <= maxWidth) {
break;
}
}
--len;
}
enqueueText(tmpBuf, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);
if (len >= 0) {
enqueueText(&msg[len + 1], vs->curRect.left, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
vs->curRect.bottom += _verbLineSpacing;
}
} else {
enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);
}
_charset->setCurID(oldID);
}
}
#endif
void ScummEngine::drawVerb(int verb, int mode) {
VerbSlot *vs;
bool tmp;
if (!verb)
return;
vs = &_verbs[verb];
if (!vs->saveid && vs->curmode && vs->verbid) {
if (vs->type == kImageVerbType) {
drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
return;
}
restoreVerbBG(verb);
_string[4].charset = vs->charset_nr;
_string[4].xpos = vs->curRect.left;
_string[4].ypos = vs->curRect.top;
_string[4].right = _screenWidth - 1;
_string[4].center = vs->center;
if (vs->curmode == 2)
_string[4].color = vs->dimcolor;
else if (mode && vs->hicolor)
_string[4].color = vs->hicolor;
else
_string[4].color = vs->color;
// FIXME For the future: Indy3 and under inv scrolling
/*
if (verb >= 31 && verb <= 36)
verb += _inventoryOffset;
*/
const byte *msg = getResourceAddress(rtVerb, verb);
if (!msg)
return;
tmp = _charset->_center;
drawString(4, msg);
_charset->_center = tmp;
vs->curRect.right = _charset->_str.right;
vs->curRect.bottom = _charset->_str.bottom;
vs->oldRect = _charset->_str;
_charset->_str.left = _charset->_str.right;
} else if (_game.id != GID_FT) {
restoreVerbBG(verb);
}
}
void ScummEngine::restoreVerbBG(int verb) {
VerbSlot *vs;
vs = &_verbs[verb];
uint8 col =
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 :
#endif
vs->bkcolor;
if (vs->oldRect.left != -1) {
restoreBackground(vs->oldRect, col);
vs->oldRect.left = -1;
}
}
void ScummEngine::drawVerbBitmap(int verb, int x, int y) {
VerbSlot *vst = &_verbs[verb];
VirtScreen *vs;
bool twobufs;
const byte *imptr = 0;
int ydiff, xstrip;
int imgw, imgh;
int i, tmp;
byte *obim;
uint32 size;
if ((vs = findVirtScreen(y)) == NULL)
return;
_gdi->disableZBuffer();
twobufs = vs->hasTwoBuffers;
vs->hasTwoBuffers = false;
xstrip = x / 8;
ydiff = y - vs->topline;
obim = getResourceAddress(rtVerb, verb);
assert(obim);
if (_game.features & GF_OLD_BUNDLE) {
imgw = obim[0];
imgh = obim[1] / 8;
imptr = obim + 2;
} else if (_game.features & GF_SMALL_HEADER) {
size = READ_LE_UINT32(obim);
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
imgw = (*(obim + size + 10));
imgh = (*(obim + size + 15)) / 8;
} else {
imgw = (*(obim + size + 11));
imgh = (*(obim + size + 17)) / 8;
}
imptr = getObjectImage(obim, 1);
} else {
const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), obim);
if (_game.version >= 7) {
imgw = READ_LE_UINT16(&imhd->v7.width) / 8;
imgh = READ_LE_UINT16(&imhd->v7.height) / 8;
} else {
imgw = READ_LE_UINT16(&imhd->old.width) / 8;
imgh = READ_LE_UINT16(&imhd->old.height) / 8;
}
imptr = getObjectImage(obim, 1);
}
assert(imptr);
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
_gdi->_distaff = (vst->verbid != 54);
}
for (i = 0; i < imgw; i++) {
tmp = xstrip + i;
_gdi->drawBitmap(imptr, vs, tmp, ydiff, imgw * 8, imgh * 8, i, 1, Gdi::dbAllowMaskOr | Gdi::dbObjectMode);
}
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
_gdi->_distaff = false;
}
vst->curRect.right = vst->curRect.left + imgw * 8;
vst->curRect.bottom = vst->curRect.top + imgh * 8;
vst->oldRect = vst->curRect;
_gdi->enableZBuffer();
vs->hasTwoBuffers = twobufs;
}
int ScummEngine::getVerbSlot(int id, int mode) const {
int i;
for (i = 1; i < _numVerbs; i++) {
if (_verbs[i].verbid == id && _verbs[i].saveid == mode) {
return i;
}
}
return 0;
}
void ScummEngine::killVerb(int slot) {
VerbSlot *vs;
if (slot == 0)
return;
vs = &_verbs[slot];
vs->verbid = 0;
vs->curmode = 0;
_res->nukeResource(rtVerb, slot);
if (_game.version <= 6 && vs->saveid == 0) {
drawVerb(slot, 0);
verbMouseOver(0);
}
vs->saveid = 0;
}
void ScummEngine::setVerbObject(uint room, uint object, uint verb) {
const byte *obimptr;
const byte *obcdptr;
uint32 size, size2;
FindObjectInRoom foir;
int i;
if (_game.heversion >= 70) { // Windows titles. Here we always ignore room
room = getObjectRoom(object);
}
if (whereIsObject(object) == WIO_FLOBJECT)
error("Can't grab verb image from flobject");
if (_game.features & GF_OLD_BUNDLE) {
for (i = (_numLocalObjects-1); i > 0; i--) {
if (_objs[i].obj_nr == object) {
findObjectInRoom(&foir, foImageHeader, object, room);
size = READ_LE_UINT16(foir.obim);
byte *ptr = _res->createResource(rtVerb, verb, size + 2);
obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
ptr[0] = *(obcdptr + 9); // Width
ptr[1] = *(obcdptr + 15); // Height
memcpy(ptr + 2, foir.obim, size);
return;
}
}
} else if (_game.features & GF_SMALL_HEADER) {
for (i = (_numLocalObjects-1); i > 0; i--) {
if (_objs[i].obj_nr == object) {
// FIXME: the only thing we need from the OBCD is the image size!
// So we could use almost the same code (except for offsets)
// as in the GF_OLD_BUNDLE code. But of course that would break save games
// unless we insert special conversion code... <sigh>
findObjectInRoom(&foir, foImageHeader, object, room);
size = READ_LE_UINT32(foir.obim);
obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
size2 = READ_LE_UINT32(obcdptr);
_res->createResource(rtVerb, verb, size + size2);
obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
memcpy(getResourceAddress(rtVerb, verb) + size, obcdptr, size2);
return;
}
}
} else {
findObjectInRoom(&foir, foImageHeader, object, room);
size = READ_BE_UINT32(foir.obim + 4);
_res->createResource(rtVerb, verb, size);
obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
}
}
} // End of namespace Scumm