mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-04 09:56:30 +00:00
5f2bf42f84
It turns out that checking the room height causes verbs not to be redrawn after reading books. Let's just limit it to room 80, and hope there aren't any other cases.
1323 lines
37 KiB
C++
1323 lines
37 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.
|
|
*
|
|
*/
|
|
|
|
#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;
|
|
const char *name;
|
|
};
|
|
|
|
static const VerbSettings v0VerbTable_English[] = {
|
|
{kVerbOpen, 8, 0, "Open"},
|
|
{kVerbClose, 8, 1, "Close"},
|
|
{kVerbGive, 0, 2, "Give"},
|
|
{kVerbTurnOn, 32, 0, "Turn on"},
|
|
{kVerbTurnOff, 32, 1, "Turn off"},
|
|
{kVerbFix, 32, 2, "Fix"},
|
|
{kVerbNewKid, 24, 0, "New Kid"},
|
|
{kVerbUnlock, 24, 1, "Unlock"},
|
|
{kVerbPush, 0, 0, "Push"},
|
|
{kVerbPull, 0, 1, "Pull"},
|
|
{kVerbUse, 24, 2, "Use"},
|
|
{kVerbRead, 8, 2, "Read"},
|
|
{kVerbWalkTo, 15, 0, "Walk to"},
|
|
{kVerbPickUp, 15, 1, "Pick up"},
|
|
{kVerbWhatIs, 15, 2, "What is"}
|
|
};
|
|
|
|
static const VerbSettings v0VerbTable_German[] = {
|
|
{kVerbOpen, 7, 0, "$ffne"},
|
|
{kVerbClose, 13, 1, "Schlie*e"},
|
|
{kVerbGive, 0, 2, "Gebe"},
|
|
{kVerbTurnOn, 37, 1, "Ein"},
|
|
{kVerbTurnOff, 37, 0, "Aus"},
|
|
{kVerbFix, 23, 1, "Repariere"},
|
|
{kVerbNewKid, 34, 2, "Person"},
|
|
{kVerbUnlock, 23, 0, "Schlie*e auf"},
|
|
{kVerbPush, 0, 0, "Dr<cke"},
|
|
{kVerbPull, 0, 1, "Ziehe"},
|
|
{kVerbUse, 23, 2, "Benutz"},
|
|
{kVerbRead, 7, 2, "Lese"},
|
|
{kVerbWalkTo, 13, 0, "Gehe zu"},
|
|
{kVerbPickUp, 7, 1, "Nimm"},
|
|
{kVerbWhatIs, 13, 2, "Was ist"}
|
|
};
|
|
|
|
struct VerbDemo {
|
|
int color;
|
|
const char *str;
|
|
};
|
|
static VerbDemo v0DemoStr[] = {
|
|
{7, " MANIAC MANSION DEMO DISK "},
|
|
{5, " from Lucasfilm Games "},
|
|
{5, " Copyright = 1987 by Lucasfilm Ltd. "},
|
|
{5, " All Rights Reserved. "},
|
|
{0, " "},
|
|
{16, " Press F7 to return to menu. "}
|
|
};
|
|
|
|
int ScummEngine_v0::verbPrepIdType(int verbid) {
|
|
switch (verbid) {
|
|
case kVerbUse: // depends on object1
|
|
return kVerbPrepObject;
|
|
case kVerbGive:
|
|
return kVerbPrepTo;
|
|
case kVerbUnlock: case kVerbFix:
|
|
return kVerbPrepWith;
|
|
default:
|
|
return kVerbPrepNone;
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v0::verbDemoMode() {
|
|
int i;
|
|
|
|
for (i = 1; i < 16; i++)
|
|
killVerb(i);
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
verbDrawDemoString(i);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v0::verbDrawDemoString(int VerbDemoNumber) {
|
|
byte string[80];
|
|
const char *ptr = v0DemoStr[VerbDemoNumber].str;
|
|
int i = 0, len = 0;
|
|
|
|
// Maximum length of printable characters
|
|
int maxChars = 40;
|
|
while (*ptr) {
|
|
if (*ptr != '@')
|
|
len++;
|
|
if (len > maxChars) {
|
|
break;
|
|
}
|
|
|
|
string[i++] = *ptr++;
|
|
|
|
}
|
|
string[i] = 0;
|
|
|
|
_string[2].charset = 1;
|
|
_string[2].ypos = _virtscr[kVerbVirtScreen].topline + (8 * VerbDemoNumber);
|
|
_string[2].xpos = 0;
|
|
_string[2].right = _virtscr[kVerbVirtScreen].w - 1;
|
|
_string[2].color = v0DemoStr[VerbDemoNumber].color;
|
|
drawString(2, (byte *)string);
|
|
}
|
|
|
|
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 = verbPrepIdType(vtable[i - 1].id);
|
|
vs->curRect.left = vs->origLeft = 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::switchActor(int slot) {
|
|
resetSentence();
|
|
|
|
// actor switching only allowed during normal gamplay (not cutscene, ...)
|
|
if (_currentMode != kModeNormal)
|
|
return;
|
|
|
|
VAR(VAR_EGO) = VAR(97 + slot);
|
|
actorFollowCamera(VAR(VAR_EGO));
|
|
}
|
|
|
|
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 & USERSTATE_IFACE_INVENTORY)) {
|
|
_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;
|
|
}
|
|
}
|
|
|
|
int 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 0;
|
|
|
|
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 0;
|
|
|
|
return findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
|
|
}
|
|
|
|
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 & USERSTATE_IFACE_INVENTORY)) // 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;
|
|
|
|
const byte *tmp = getObjOrActorName(obj);
|
|
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 & USERSTATE_IFACE_VERBS)) // 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) {
|
|
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 #2013.
|
|
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 #2773: "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;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (object != -1) {
|
|
object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
|
|
if (object > 0)
|
|
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 #2773: "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
|
|
int object = checkV2Inventory(_mouse.x, _mouse.y);
|
|
if (object > 0)
|
|
runInputScript(kInventoryClickArea, object, 0);
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int ScummEngine_v0::getVerbPrepId() {
|
|
if (_verbs[_activeVerb].prep != 0xFF) {
|
|
return _verbs[_activeVerb].prep;
|
|
} else {
|
|
byte *ptr = getOBCDFromObject(_activeObject, true);
|
|
assert(ptr);
|
|
return (*(ptr + 11) >> 5);
|
|
}
|
|
}
|
|
|
|
int ScummEngine_v0::activeVerbPrep() {
|
|
if (!_activeVerb || !_activeObject)
|
|
return 0;
|
|
return getVerbPrepId();
|
|
}
|
|
|
|
void ScummEngine_v0::verbExec() {
|
|
_sentenceNum = 0;
|
|
_sentenceNestedCount = 0;
|
|
|
|
if (_activeVerb == kVerbWhatIs)
|
|
return;
|
|
|
|
if (!(_activeVerb == kVerbWalkTo && _activeObject == 0)) {
|
|
doSentence(_activeVerb, _activeObject, _activeObject2);
|
|
if (_activeVerb != kVerbWalkTo) {
|
|
_activeVerb = kVerbWalkTo;
|
|
_activeObject = 0;
|
|
_activeObject2 = 0;
|
|
}
|
|
_walkToObjectState = kWalkToObjectStateDone;
|
|
return;
|
|
}
|
|
|
|
Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "verbExec");
|
|
int x = _virtualMouse.x / V12_X_MULTIPLIER;
|
|
int y = _virtualMouse.y / V12_Y_MULTIPLIER;
|
|
|
|
// 0xB31
|
|
VAR(6) = x;
|
|
VAR(7) = y;
|
|
|
|
if (a->_miscflags & kActorMiscFlagFreeze)
|
|
return;
|
|
|
|
a->startWalkActor(VAR(6), VAR(7), -1);
|
|
}
|
|
|
|
bool ScummEngine_v0::checkSentenceComplete() {
|
|
if (_activeVerb && _activeVerb != kVerbWalkTo && _activeVerb != kVerbWhatIs) {
|
|
if (_activeObject && (!activeVerbPrep() || _activeObject2))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ScummEngine_v0::checkExecVerbs() {
|
|
Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
|
|
VirtScreen *zone = findVirtScreen(_mouse.y);
|
|
|
|
bool execute = false;
|
|
|
|
if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
|
|
int over = findVerbAtPos(_mouse.x, _mouse.y);
|
|
// click region: verbs
|
|
if (over) {
|
|
if (_activeVerb != over) { // new verb
|
|
// keep first object if no preposition is used yet
|
|
if (activeVerbPrep())
|
|
_activeObject = 0;
|
|
_activeObject2 = 0;
|
|
_activeVerb = over;
|
|
_redrawSentenceLine = true;
|
|
} else {
|
|
// execute sentence if complete
|
|
if (checkSentenceComplete())
|
|
execute = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (a->_miscflags & kActorMiscFlagHide) {
|
|
if (_activeVerb != kVerbNewKid) {
|
|
_activeVerb = kVerbNone;
|
|
}
|
|
}
|
|
|
|
if (_currentMode != kModeCutscene) {
|
|
if (_currentMode == kModeKeypad) {
|
|
_activeVerb = kVerbPush;
|
|
}
|
|
|
|
if (_mouseAndKeyboardStat > 0 && _mouseAndKeyboardStat < MBS_MAX_KEY) {
|
|
// keys already checked by input handler
|
|
} else if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK) || _activeVerb == kVerbWhatIs) {
|
|
// click region: sentence line
|
|
if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
|
|
if (_activeVerb == kVerbNewKid) {
|
|
if (_currentMode == kModeNormal) {
|
|
int kid;
|
|
int lineX = _mouse.x >> V12_X_SHIFT;
|
|
if (lineX < 11)
|
|
kid = 0;
|
|
else if (lineX < 25)
|
|
kid = 1;
|
|
else
|
|
kid = 2;
|
|
_activeVerb = kVerbWalkTo;
|
|
_redrawSentenceLine = true;
|
|
drawSentenceLine();
|
|
switchActor(kid);
|
|
}
|
|
_activeVerb = kVerbWalkTo;
|
|
_redrawSentenceLine = true;
|
|
return;
|
|
} else {
|
|
// execute sentence if complete
|
|
if (checkSentenceComplete())
|
|
execute = true;
|
|
}
|
|
// click region: inventory or main screen
|
|
} else if ((zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) ||
|
|
(zone->number == kMainVirtScreen))
|
|
{
|
|
int obj = 0;
|
|
|
|
// click region: inventory
|
|
if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) {
|
|
// click into inventory
|
|
int invOff = _inventoryOffset;
|
|
obj = checkV2Inventory(_mouse.x, _mouse.y);
|
|
if (invOff != _inventoryOffset) {
|
|
// inventory position changed (arrows pressed, do nothing)
|
|
return;
|
|
}
|
|
// the second object of a give-to command has to be an actor
|
|
if (_activeVerb == kVerbGive && _activeObject)
|
|
obj = 0;
|
|
// click region: main screen
|
|
} else if (zone->number == kMainVirtScreen) {
|
|
// click into main screen
|
|
if (_activeVerb == kVerbGive && _activeObject) {
|
|
int actor = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
|
|
if (actor != 0)
|
|
obj = OBJECT_V0(actor, kObjectV0TypeActor);
|
|
} else {
|
|
obj = findObject(_virtualMouse.x, _virtualMouse.y);
|
|
}
|
|
}
|
|
|
|
if (!obj) {
|
|
if (_activeVerb == kVerbWalkTo) {
|
|
_activeObject = 0;
|
|
_activeObject2 = 0;
|
|
}
|
|
} else {
|
|
if (activeVerbPrep() == kVerbPrepNone) {
|
|
if (obj == _activeObject)
|
|
execute = true;
|
|
else
|
|
_activeObject = obj;
|
|
// immediately execute action in keypad/selection mode
|
|
if (_currentMode == kModeKeypad)
|
|
execute = true;
|
|
} else {
|
|
if (obj == _activeObject2)
|
|
execute = true;
|
|
if (obj != _activeObject) {
|
|
_activeObject2 = obj;
|
|
if (_currentMode == kModeKeypad)
|
|
execute = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
_redrawSentenceLine = true;
|
|
if (_activeVerb == kVerbWalkTo && zone->number == kMainVirtScreen) {
|
|
_walkToObjectState = kWalkToObjectStateDone;
|
|
execute = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_drawDemo && _game.features & GF_DEMO) {
|
|
verbDemoMode();
|
|
}
|
|
|
|
if (_redrawSentenceLine)
|
|
drawSentenceLine();
|
|
|
|
if (!execute || !_activeVerb)
|
|
return;
|
|
|
|
if (_activeVerb == kVerbWalkTo)
|
|
verbExec();
|
|
else if (_activeObject) {
|
|
// execute if we have a 1st object and either have or do not need a 2nd
|
|
if (activeVerbPrep() == kVerbPrepNone || _activeObject2)
|
|
verbExec();
|
|
}
|
|
}
|
|
|
|
void ScummEngine::verbMouseOver(int verb) {
|
|
// Don't do anything unless verbs are active
|
|
if (_game.version <= 2 && !(_userState & USERSTATE_IFACE_VERBS))
|
|
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];
|
|
memset(buf, 0, sizeof(buf));
|
|
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
|
|
int textWidth = 0;
|
|
vs->curRect.bottom = 0;
|
|
const byte *msg2 = msg;
|
|
while (*msg2) {
|
|
const int charWidth = _charset->getCharWidth(*msg2);
|
|
const int charHeight = _charset->getCharHeight(*msg2);
|
|
textWidth += charWidth;
|
|
if (vs->curRect.bottom < charHeight)
|
|
vs->curRect.bottom = charHeight;
|
|
msg2++;
|
|
}
|
|
vs->curRect.bottom += vs->curRect.top;
|
|
vs->oldRect = vs->curRect;
|
|
|
|
const int maxWidth = _language == Common::HE_ISR ? vs->curRect.right + 1 : _screenWidth - vs->curRect.left;
|
|
if (_game.version == 8 && _charset->getStringWidth(0, buf) > maxWidth) {
|
|
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;
|
|
}
|
|
int16 leftPos = vs->curRect.left;
|
|
if (_language == Common::HE_ISR)
|
|
vs->curRect.left = vs->origLeft = leftPos = vs->curRect.right - _charset->getStringWidth(0, tmpBuf);
|
|
else
|
|
vs->curRect.right = vs->curRect.left + _charset->getStringWidth(0, tmpBuf);
|
|
enqueueText(tmpBuf, leftPos, vs->curRect.top, color, vs->charset_nr, vs->center);
|
|
if (len >= 0) {
|
|
if (_language == Common::HE_ISR)
|
|
leftPos = vs->curRect.right - _charset->getStringWidth(0, &msg[len + 1]);
|
|
enqueueText(&msg[len + 1], leftPos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
|
|
vs->curRect.bottom += _verbLineSpacing;
|
|
}
|
|
} else {
|
|
if (_language == Common::HE_ISR)
|
|
vs->curRect.left = vs->origLeft = vs->curRect.right - textWidth;
|
|
else
|
|
vs->curRect.right = vs->curRect.left + textWidth;
|
|
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;
|
|
|
|
// The way we implement high-resolution font on a scaled low-resolution
|
|
// background requires there to always be a text surface telling which
|
|
// pixels have been drawn on. This means that the "has mask" feature is
|
|
// not correctly implemented, and in most cases this works just fine
|
|
// for both Loom and Indiana Jones and the Last Crusade.
|
|
//
|
|
// However, there is at least one scene in Indiana Jones - room 80,
|
|
// where you escape from the zeppelin on a biplane - where the game
|
|
// (sloppily, in my opinion) draws two disabled verbs (Travel and Talk)
|
|
// and then counts on the background to draw over them. Obviously that
|
|
// won't work here.
|
|
//
|
|
// I thought it would be possible to base this workaround on room
|
|
// height, but then verbs aren't redrawn after reading books. So I
|
|
// guess the safest path is to limit it to this particular room.
|
|
//
|
|
// Note that with the low-resolution font, which does implement the
|
|
// "has mask" feature, the Macintosh version still needs a hack in
|
|
// printChar() for black text to work properly. This version of the
|
|
// game is weird.
|
|
if (_game.id == GID_INDY3 && _macScreen && _currentRoom == 80)
|
|
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);
|
|
|
|
const bool isRtl = _language == Common::HE_ISR && !vs->center;
|
|
_string[4].charset = vs->charset_nr;
|
|
_string[4].xpos = isRtl ? vs->origLeft : 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;
|
|
|
|
if (isRtl)
|
|
vs->curRect.left = _charset->_str.left;
|
|
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
|