Initial commit
This commit is contained in:
418
engines/hugo/mouse.cpp
Normal file
418
engines/hugo/mouse.cpp
Normal file
@@ -0,0 +1,418 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Hugo Trilogy source code
|
||||
*
|
||||
* Copyright (c) 1989-1995 David P. Gray
|
||||
*
|
||||
*/
|
||||
|
||||
// mouse.cpp : Handle all mouse activity
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "hugo/hugo.h"
|
||||
#include "hugo/dialogs.h"
|
||||
#include "hugo/game.h"
|
||||
#include "hugo/mouse.h"
|
||||
#include "hugo/schedule.h"
|
||||
#include "hugo/display.h"
|
||||
#include "hugo/inventory.h"
|
||||
#include "hugo/route.h"
|
||||
#include "hugo/util.h"
|
||||
#include "hugo/object.h"
|
||||
#include "hugo/text.h"
|
||||
|
||||
namespace Hugo {
|
||||
|
||||
MouseHandler::MouseHandler(HugoEngine *vm) : _vm(vm) {
|
||||
_hotspots = nullptr;
|
||||
_leftButtonFl = false;
|
||||
_rightButtonFl = false;
|
||||
_jumpExitFl = false; // Can't jump to a screen exit
|
||||
_mouseX = kXPix / 2;
|
||||
_mouseY = kYPix / 2;
|
||||
}
|
||||
|
||||
void MouseHandler::resetLeftButton() {
|
||||
_leftButtonFl = false;
|
||||
}
|
||||
|
||||
void MouseHandler::resetRightButton() {
|
||||
_rightButtonFl = false;
|
||||
}
|
||||
|
||||
void MouseHandler::setLeftButton() {
|
||||
_leftButtonFl = true;
|
||||
}
|
||||
|
||||
void MouseHandler::setRightButton() {
|
||||
_rightButtonFl = true;
|
||||
}
|
||||
|
||||
void MouseHandler::setJumpExitFl(bool fl) {
|
||||
_jumpExitFl = fl;
|
||||
}
|
||||
|
||||
void MouseHandler::setMouseX(int x) {
|
||||
_mouseX = x;
|
||||
}
|
||||
|
||||
void MouseHandler::setMouseY(int y) {
|
||||
_mouseY = y;
|
||||
}
|
||||
|
||||
void MouseHandler::freeHotspots() {
|
||||
free(_hotspots);
|
||||
_hotspots = nullptr;
|
||||
}
|
||||
|
||||
bool MouseHandler::getJumpExitFl() const {
|
||||
return _jumpExitFl;
|
||||
}
|
||||
|
||||
int MouseHandler::getMouseX() const {
|
||||
return _mouseX;
|
||||
}
|
||||
|
||||
int MouseHandler::getMouseY() const {
|
||||
return _mouseY;
|
||||
}
|
||||
|
||||
int16 MouseHandler::getDirection(const int16 hotspotId) const {
|
||||
return _hotspots[hotspotId]._direction;
|
||||
}
|
||||
|
||||
int16 MouseHandler::getHotspotActIndex(const int16 hotspotId) const {
|
||||
return _hotspots[hotspotId]._actIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow-blit supplied string into dib_a at cx,cy and add to display list
|
||||
*/
|
||||
void MouseHandler::cursorText(const char *buffer, const int16 cx, const int16 cy, const Uif fontId, const int16 color) {
|
||||
debugC(1, kDebugMouse, "cursorText(%s, %d, %d, %d, %d)", buffer, cx, cy, fontId, color);
|
||||
|
||||
_vm->_screen->loadFont(fontId);
|
||||
|
||||
// Find bounding rect for string
|
||||
int16 sdx = _vm->_screen->stringLength(buffer);
|
||||
int16 sdy = _vm->_screen->fontHeight() + 1; // + 1 for shadow
|
||||
int16 sx, sy;
|
||||
if (cx < kXPix / 2) {
|
||||
sx = cx + kCursorNameOffX;
|
||||
if (_vm->_inventory->getInventoryObjId() == -1) {
|
||||
sy = cy + kCursorNameOffY;
|
||||
} else {
|
||||
sy = cy + kCursorNameOffY - (_vm->_screen->fontHeight() + 1);
|
||||
if (sy < 0) {
|
||||
sx = cx + kCursorNameOffX + 25;
|
||||
sy = cy + kCursorNameOffY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sx = cx - sdx - kCursorNameOffX / 2;
|
||||
sy = cy + kCursorNameOffY;
|
||||
}
|
||||
|
||||
if (sy < 0) {
|
||||
sy = 0;
|
||||
}
|
||||
|
||||
// Display the string and add rect to display list
|
||||
_vm->_screen->shadowStr(sx, sy, buffer, _TBRIGHTWHITE);
|
||||
_vm->_screen->displayList(kDisplayAdd, sx, sy, sdx, sdy);
|
||||
|
||||
#ifdef USE_TTS
|
||||
_vm->sayText(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the exit hotspot containing cx, cy.
|
||||
* Return hotspot index or -1 if not found.
|
||||
*/
|
||||
int16 MouseHandler::findExit(const int16 cx, const int16 cy, byte screenId) {
|
||||
debugC(2, kDebugMouse, "findExit(%d, %d, %d)", cx, cy, screenId);
|
||||
|
||||
for (int i = 0; _hotspots[i]._screenIndex >= 0; i++) {
|
||||
if (_hotspots[i]._screenIndex == screenId) {
|
||||
if (cx >= _hotspots[i]._x1 && cx <= _hotspots[i]._x2 && cy >= _hotspots[i]._y1 && cy <= _hotspots[i]._y2)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a mouse right click at coord cx, cy over object objid
|
||||
*/
|
||||
void MouseHandler::processRightClick(const int16 objId, const int16 cx, const int16 cy) {
|
||||
debugC(1, kDebugMouse, "ProcessRightClick(%d, %d, %d)", objId, cx, cy);
|
||||
|
||||
Status &gameStatus = _vm->getGameStatus();
|
||||
|
||||
if (gameStatus._storyModeFl || _vm->_hero->_pathType == kPathQuiet) // Make sure user has control
|
||||
return;
|
||||
|
||||
int16 inventObjId = _vm->_inventory->getInventoryObjId();
|
||||
// Check if this was over iconbar
|
||||
if ((_vm->_inventory->getInventoryState() == kInventoryActive) && (cy < kInvDy + kDibOffY)) { // Clicked over iconbar object
|
||||
if (inventObjId == -1)
|
||||
_vm->_screen->selectInventoryObjId(objId);
|
||||
else if (inventObjId == objId)
|
||||
_vm->_screen->resetInventoryObjId();
|
||||
else
|
||||
_vm->_object->useObject(objId); // Use status.objid on object
|
||||
} else { // Clicked over viewport object
|
||||
Object *obj = &_vm->_object->_objects[objId];
|
||||
int16 x, y;
|
||||
switch (obj->_viewx) { // Where to walk to
|
||||
case -1: { // Walk to object position
|
||||
bool foundFl = false;
|
||||
if (_vm->_object->findObjectSpace(obj, &x, &y))
|
||||
foundFl = _vm->_route->startRoute(kRouteGet, objId, x, y); // TRUE if route found to object
|
||||
if (!foundFl) // Can't get there, try to use from here
|
||||
_vm->_object->useObject(objId);
|
||||
}
|
||||
break;
|
||||
case 0: // Immediate use
|
||||
_vm->_object->useObject(objId); // Pick up or use object
|
||||
break;
|
||||
default: // Walk to view point if possible
|
||||
if (!_vm->_route->startRoute(kRouteGet, objId, obj->_viewx, obj->_viewy)) {
|
||||
if (_vm->_hero->_cycling == kCycleInvisible) // If invisible do
|
||||
_vm->_object->useObject(objId); // immediate use
|
||||
else
|
||||
Utils::notifyBox(_vm->_text->getTextMouse(kMsNoWayText)); // Can't get there
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Process a left mouse click over:
|
||||
* 1. An icon - show description
|
||||
* 2. An object - walk to and show description
|
||||
* 3. An icon scroll arrow - scroll the iconbar
|
||||
* 4. Nothing - attempt to walk there
|
||||
* 5. Exit - walk to exit hotspot
|
||||
*/
|
||||
void MouseHandler::processLeftClick(const int16 objId, const int16 cx, const int16 cy) {
|
||||
debugC(1, kDebugMouse, "ProcessLeftClick(%d, %d, %d)", objId, cx, cy);
|
||||
|
||||
int16 i, x, y;
|
||||
Object *obj;
|
||||
|
||||
Status &gameStatus = _vm->getGameStatus();
|
||||
|
||||
if (gameStatus._storyModeFl || _vm->_hero->_pathType == kPathQuiet) // Make sure user has control
|
||||
return;
|
||||
|
||||
switch (objId) {
|
||||
case -1: // Empty space - attempt to walk there
|
||||
_vm->_route->startRoute(kRouteSpace, 0, cx, cy);
|
||||
break;
|
||||
case kLeftArrow: // A scroll arrow - scroll the iconbar
|
||||
case kRightArrow:
|
||||
// Scroll the iconbar and display results
|
||||
_vm->_inventory->processInventory((objId == kLeftArrow) ? kInventoryActionLeft : kInventoryActionRight);
|
||||
_vm->_screen->moveImage(_vm->_screen->getIconBuffer(), 0, 0, kXPix, kInvDy, kXPix, _vm->_screen->getFrontBuffer(), 0, kDibOffY, kXPix);
|
||||
_vm->_screen->moveImage(_vm->_screen->getIconBuffer(), 0, 0, kXPix, kInvDy, kXPix, _vm->_screen->getBackBuffer(), 0, kDibOffY, kXPix);
|
||||
_vm->_screen->displayList(kDisplayAdd, 0, kDibOffY, kXPix, kInvDy);
|
||||
break;
|
||||
case kExitHotspot: // Walk to exit hotspot
|
||||
i = findExit(cx, cy, *_vm->_screenPtr);
|
||||
x = _hotspots[i]._viewx;
|
||||
y = _hotspots[i]._viewy;
|
||||
if (x >= 0) { // Hotspot refers to an exit
|
||||
// Special case of immediate exit
|
||||
if (_jumpExitFl) {
|
||||
// Get rid of iconbar if necessary
|
||||
if (_vm->_inventory->getInventoryState() != kInventoryOff)
|
||||
_vm->_inventory->setInventoryState(kInventoryUp);
|
||||
_vm->_scheduler->insertActionList(_hotspots[i]._actIndex);
|
||||
} else { // Set up route to exit spot
|
||||
if (_hotspots[i]._direction == Common::KEYCODE_RIGHT)
|
||||
x -= kHeroMaxWidth;
|
||||
else if (_hotspots[i]._direction == Common::KEYCODE_LEFT)
|
||||
x += kHeroMaxWidth;
|
||||
if (!_vm->_route->startRoute(kRouteExit, i, x, y))
|
||||
Utils::notifyBox(_vm->_text->getTextMouse(kMsNoWayText)); // Can't get there
|
||||
}
|
||||
|
||||
// Get rid of any attached icon
|
||||
_vm->_screen->resetInventoryObjId();
|
||||
}
|
||||
break;
|
||||
default: // Look at an icon or object
|
||||
obj = &_vm->_object->_objects[objId];
|
||||
|
||||
// Over iconbar - immediate description
|
||||
if ((_vm->_inventory->getInventoryState() == kInventoryActive) && (cy < kInvDy + kDibOffY)) {
|
||||
_vm->_object->lookObject(obj);
|
||||
} else {
|
||||
bool foundFl = false; // TRUE if route found to object
|
||||
switch (obj->_viewx) { // Clicked over viewport object
|
||||
case -1: // Walk to object position
|
||||
if (_vm->_object->findObjectSpace(obj, &x, &y))
|
||||
foundFl = _vm->_route->startRoute(kRouteLook, objId, x, y);
|
||||
if (!foundFl) // Can't get there, immediate description
|
||||
_vm->_object->lookObject(obj);
|
||||
break;
|
||||
case 0: // Immediate description
|
||||
_vm->_object->lookObject(obj);
|
||||
break;
|
||||
default: // Walk to view point if possible
|
||||
if (!_vm->_route->startRoute(kRouteLook, objId, obj->_viewx, obj->_viewy)) {
|
||||
if (_vm->_hero->_cycling == kCycleInvisible) // If invisible do
|
||||
_vm->_object->lookObject(obj); // immediate decription
|
||||
else
|
||||
Utils::notifyBox(_vm->_text->getTextMouse(kMsNoWayText)); // Can't get there
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process mouse activity
|
||||
*/
|
||||
void MouseHandler::mouseHandler() {
|
||||
debugC(2, kDebugMouse, "mouseHandler");
|
||||
|
||||
Status &gameStatus = _vm->getGameStatus();
|
||||
Istate inventState = _vm->_inventory->getInventoryState();
|
||||
if ((gameStatus._viewState != kViewPlay) && (inventState != kInventoryActive))
|
||||
return;
|
||||
|
||||
int16 cx = getMouseX();
|
||||
int16 cy = getMouseY();
|
||||
|
||||
// gameStatus._cx = cx; // Save cursor coords
|
||||
// gameStatus._cy = cy;
|
||||
|
||||
// Don't process if outside client area
|
||||
if ((cx < 0) || (cx > kXPix) || (cy < kDibOffY) || (cy > kViewSizeY + kDibOffY))
|
||||
return;
|
||||
|
||||
int16 objId = -1; // Current source object
|
||||
// Process cursor over an object or icon
|
||||
if (inventState == kInventoryActive) { // Check inventory icon bar first
|
||||
objId = _vm->_inventory->processInventory(kInventoryActionGet, cx, cy);
|
||||
} else {
|
||||
if (cy < 5 && cy > 0) {
|
||||
_vm->_topMenu->runModal();
|
||||
// When the top menu is shown, it eats all the events, including mouse move, which means the
|
||||
// getMouseX() and getMouseY() have not been updated and the topMenu will be shown immediately
|
||||
// again. We do not know where the cursor is currently, but move it outside of the ]0, 5[ range.
|
||||
setMouseY(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameStatus._gameOverFl) {
|
||||
if (objId == -1) // No match, check rest of view
|
||||
objId = _vm->_object->findObject(cx, cy);
|
||||
|
||||
if (objId >= 0) { // Got a match
|
||||
// Display object name next to cursor (unless CURSOR_NOCHAR)
|
||||
// Note test for swapped hero name
|
||||
const char *name = _vm->_text->getNoun(_vm->_object->_objects[(objId == kHeroIndex) ? _vm->_heroImage : objId]._nounIndex, kCursorNameIndex);
|
||||
if (name[0] != kCursorNochar)
|
||||
cursorText(name, cx, cy, U_FONT8, _TBRIGHTWHITE);
|
||||
#ifdef USE_TTS
|
||||
else {
|
||||
_vm->_previousSaid.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Process right click over object in view or iconbar
|
||||
if (_rightButtonFl)
|
||||
processRightClick(objId, cx, cy);
|
||||
}
|
||||
|
||||
// Process cursor over an exit hotspot
|
||||
if (objId == -1) {
|
||||
int i = findExit(cx, cy, *_vm->_screenPtr);
|
||||
if (i != -1 && _hotspots[i]._viewx >= 0) {
|
||||
objId = kExitHotspot;
|
||||
cursorText(_vm->_text->getTextMouse(kMsExit), cx, cy, U_FONT8, _TBRIGHTWHITE);
|
||||
}
|
||||
#ifdef USE_TTS
|
||||
else {
|
||||
_vm->_previousSaid.clear();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// Left click over icon, object or to move somewhere
|
||||
if (_leftButtonFl)
|
||||
processLeftClick(objId, cx, cy);
|
||||
|
||||
// Clear mouse click states
|
||||
resetLeftButton();
|
||||
resetRightButton();
|
||||
}
|
||||
|
||||
void MouseHandler::readHotspot(Common::ReadStream &in, Hotspot &hotspot) {
|
||||
hotspot._screenIndex = in.readSint16BE();
|
||||
hotspot._x1 = in.readSint16BE();
|
||||
hotspot._y1 = in.readSint16BE();
|
||||
hotspot._x2 = in.readSint16BE();
|
||||
hotspot._y2 = in.readSint16BE();
|
||||
hotspot._actIndex = in.readUint16BE();
|
||||
hotspot._viewx = in.readSint16BE();
|
||||
hotspot._viewy = in.readSint16BE();
|
||||
hotspot._direction = in.readSint16BE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load hotspots data from hugo.dat
|
||||
*/
|
||||
void MouseHandler::loadHotspots(Common::ReadStream &in) {
|
||||
Hotspot *wrkHotspots = nullptr;
|
||||
Hotspot tmp;
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
|
||||
int numRows = in.readUint16BE();
|
||||
if (varnt == _vm->_gameVariant)
|
||||
_hotspots = wrkHotspots = (Hotspot *)malloc(sizeof(Hotspot) * numRows);
|
||||
|
||||
for (int i = 0; i < numRows; i++)
|
||||
readHotspot(in, (varnt == _vm->_gameVariant) ? wrkHotspots[i] : tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display hotspot boundaries for the current screen
|
||||
*/
|
||||
void MouseHandler::drawHotspots() const {
|
||||
for (int i = 0; _hotspots[i]._screenIndex >= 0; i++) {
|
||||
Hotspot *hotspot = &_hotspots[i];
|
||||
if (hotspot->_screenIndex == _vm->_hero->_screenIndex)
|
||||
_vm->_screen->drawRectangle(false, hotspot->_x1, hotspot->_y1, hotspot->_x2, hotspot->_y2, _TLIGHTRED);
|
||||
}
|
||||
}
|
||||
} // End of namespace Hugo
|
||||
Reference in New Issue
Block a user