mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
(INDY3 (FMTowns): Map lines are drawn incorrectly) This fix addresses the part concerning the line width of the travel marker. Turns out that SCUMM3 FM-Towns draws all boxes one pixel wider and higher on virtual screens other than 1. This also allows removal of a hack in ScummEngine_v5::o5_drawBox(). I also fixed the old style pause/restart banners, since they incorrectly used drawBox() for line drawing (which would be glitchy with the now verdrawing drawBox(). The positive outcome is that we no have original looking FM-Towns banners (only in Japanese, though, since - unlike the original - we don't run the English version in 640x480). The changed method of drawing and removing the banner also fixes the removal of the black country borders on the map. But it will still be removed below the pause banner and in the spots where the travel marker passes the border. So that has to be addressed separately.
1343 lines
37 KiB
C++
1343 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "scumm/actor.h"
|
|
#include "scumm/charset.h"
|
|
#include "scumm/he/intern_he.h"
|
|
#include "scumm/macgui/macgui.h"
|
|
#include "scumm/object.h"
|
|
#include "scumm/resource.h"
|
|
#include "scumm/scumm_v0.h"
|
|
#include "scumm/scumm_v7.h"
|
|
#include "scumm/string_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;
|
|
_hiLiteColorVerbArrow = _hiLiteColorInvSentence = 14;
|
|
if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderCGAComp)
|
|
_hiLiteColorInvSentence = 15;
|
|
else if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG || _renderMode == Common::kRenderCGA_BW)
|
|
_hiLiteColorVerbArrow = _hiLiteColorInvSentence = 15;
|
|
|
|
if (_game.platform == Common::kPlatformC64) {
|
|
color = 16;
|
|
_hiLiteColorVerbArrow = _hiLiteColorInvSentence = 7;
|
|
arrow_color = 6;
|
|
} else {
|
|
color = 13;
|
|
arrow_color = 1;
|
|
}
|
|
|
|
_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 = _hiLiteColorInvSentence;
|
|
|
|
_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 = _hiLiteColorInvSentence;
|
|
}
|
|
|
|
// 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 = _hiLiteColorVerbArrow;
|
|
|
|
_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 = _hiLiteColorVerbArrow;
|
|
|
|
// 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 = _hiLiteColorInvSentence;
|
|
}
|
|
|
|
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) {
|
|
bool ignoreVerbKeys = false;
|
|
|
|
// 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.
|
|
if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD)
|
|
ignoreVerbKeys = true;
|
|
|
|
// The Mac version of Last Crusade handles verb shortcut keys on
|
|
// its own, so that is also disabled here.
|
|
if (_macGui && _macGui->isVerbGuiActive())
|
|
ignoreVerbKeys = true;
|
|
|
|
/* Check keypresses */
|
|
if (!ignoreVerbKeys) {
|
|
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 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) {
|
|
const byte code = (_game.version > 3) ? (_mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2) : 0;
|
|
if (_game.id == GID_SAMNMAX) {
|
|
// This has been simplified for SAMNMAX while DOTT still has the "normal" implementation
|
|
// (which makes sense, since it still has the "normal" verb interface). Anyway, we need this,
|
|
// it fixes bug #13761 ("SAMNMAX: Can't shoot names during the credits").
|
|
runInputScript(kSceneClickArea, 0, code);
|
|
return;
|
|
}
|
|
|
|
VirtScreen *zone = findVirtScreen(_mouse.y);
|
|
// Fixes bug #2773: "MANIACNES: Crash on click in speechtext-area"
|
|
if (!zone)
|
|
return;
|
|
|
|
if (_macGui && _macGui->isVerbGuiActive() && zone->number == kVerbVirtScreen)
|
|
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;
|
|
|
|
// 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 (!zone || (_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;
|
|
|
|
TextStyleFlags flags = kStyleAlignLeft;
|
|
int xpos = vs->origLeft;
|
|
int ypos = vs->curRect.top;
|
|
|
|
if (vs->center) {
|
|
flags = kStyleAlignCenter;
|
|
} else if (_language == Common::HE_ISR) {
|
|
flags = kStyleAlignRight;
|
|
xpos = _screenWidth - 1 - vs->origLeft;
|
|
}
|
|
|
|
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
|
|
vs->curRect = _textV7->calcStringDimensions((const char*)msg, xpos, vs->curRect.top, flags);
|
|
|
|
const int maxWidth = _screenWidth - vs->curRect.left;
|
|
int finalWidth = maxWidth;
|
|
|
|
if (_game.version == 8 && _textV7->getStringWidth((const char*)buf) > maxWidth) {
|
|
byte tmpBuf[384];
|
|
int len = resStrLen(msg);
|
|
memcpy(tmpBuf, msg, len);
|
|
len--;
|
|
|
|
while (len >= 0) {
|
|
if (tmpBuf[len] == ' ') {
|
|
tmpBuf[len] = 0;
|
|
if ((finalWidth = _textV7->getStringWidth((const char*)tmpBuf)) <= maxWidth) {
|
|
break;
|
|
}
|
|
}
|
|
--len;
|
|
}
|
|
|
|
enqueueText(tmpBuf, xpos, ypos, color, vs->charset_nr, flags);
|
|
enqueueText(&msg[len + 1], xpos, ypos + _verbLineSpacing, color, vs->charset_nr, flags);
|
|
vs->curRect.right = vs->curRect.left + finalWidth;
|
|
vs->curRect.bottom += _verbLineSpacing;
|
|
} else {
|
|
enqueueText(msg, xpos, ypos, color, vs->charset_nr, flags);
|
|
}
|
|
|
|
vs->oldRect = vs->curRect;
|
|
vs->curRect.top = ypos;
|
|
_charset->setCurID(oldID);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void ScummEngine::drawVerb(int verb, int mode) {
|
|
VerbSlot *vs;
|
|
bool tmp;
|
|
|
|
if (_macGui && _game.id == GID_INDY3)
|
|
return;
|
|
|
|
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);
|
|
|
|
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) {
|
|
if (_macGui && _game.id == GID_INDY3)
|
|
return;
|
|
|
|
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) {
|
|
// Clip the dialog choices to a rectangle starting 35 pixels from the left
|
|
// for Japanese Monkey Island 1 SegaCD. _scummVars[451] is set by script 187,
|
|
// responsible for handling the dialog horizontal scrolling.
|
|
bool isSegaCDDialogChoice = _game.platform == Common::kPlatformSegaCD &&
|
|
_language == Common::JA_JPN && _scummVars[451] == 1;
|
|
if (isSegaCDDialogChoice && vs->oldRect.left < 35)
|
|
vs->oldRect.left = 35;
|
|
|
|
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 = nullptr;
|
|
int ydiff, xstrip;
|
|
int imgw, imgh;
|
|
int i, tmp;
|
|
byte *obim;
|
|
uint32 size;
|
|
|
|
if ((vs = findVirtScreen(y)) == nullptr)
|
|
return;
|
|
|
|
_gdi->disableZBuffer();
|
|
|
|
twobufs = vs->hasTwoBuffers;
|
|
vs->hasTwoBuffers = false;
|
|
|
|
xstrip = x / 8;
|
|
ydiff = y - vs->topline;
|
|
|
|
if (_game.version == 4)
|
|
ydiff &= ~7;
|
|
|
|
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");
|
|
|
|
// HACK: When the straw changes to gold, or the other way around, the
|
|
// object image changes, but the text is not undrawn. This causes the
|
|
// two object names to overlap each other. The text can be undrawn by
|
|
// script 8, but the logic for it is more convoluted in the Mac version
|
|
// than in the EGA DOS version, and I can't figure out how to bend it
|
|
// to my will. So hard-code the clearing here.
|
|
if (_game.id == GID_LOOM && verb == 53 && _game.platform == Common::kPlatformMacintosh)
|
|
drawBox(232, 152, 312, 192, 0);
|
|
|
|
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
|