/* 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 .
*
*/
/*
* This code is based on original Hugo Trilogy source code
*
* Copyright (c) 1989-1995 David P. Gray
*
*/
#include "common/debug.h"
#include "hugo/hugo.h"
#include "hugo/game.h"
#include "hugo/object.h"
#include "hugo/display.h"
#include "hugo/file.h"
#include "hugo/route.h"
#include "hugo/util.h"
#include "hugo/parser.h"
#include "hugo/schedule.h"
#include "hugo/text.h"
#include "hugo/inventory.h"
#include "hugo/mouse.h"
namespace Hugo {
ObjectHandler::ObjectHandler(HugoEngine *vm) : _vm(vm) {
_uses = nullptr;
_objects = nullptr;
_numObj = 0;
_objCount = 0;
_usesSize = 0;
memset(_objBound, '\0', sizeof(Overlay));
memset(_boundary, '\0', sizeof(Overlay));
memset(_overlay, '\0', sizeof(Overlay));
memset(_ovlBase, '\0', sizeof(Overlay));
}
ObjectHandler::~ObjectHandler() {
}
byte ObjectHandler::getBoundaryOverlay(uint16 index) const {
return _boundary[index];
}
byte ObjectHandler::getObjectBoundary(uint16 index) const {
return _objBound[index];
}
byte ObjectHandler::getBaseBoundary(uint16 index) const {
return _ovlBase[index];
}
byte ObjectHandler::getFirstOverlay(uint16 index) const {
return _overlay[index];
}
bool ObjectHandler::isCarried(int objIndex) const {
return _objects[objIndex]._carriedFl;
}
void ObjectHandler::setCarry(int objIndex, bool val) {
_objects[objIndex]._carriedFl = val;
}
void ObjectHandler::setVelocity(int objIndex, int8 vx, int8 vy) {
_objects[objIndex]._vx = vx;
_objects[objIndex]._vy = vy;
}
void ObjectHandler::setPath(int objIndex, Path pathType, int16 vxPath, int16 vyPath) {
_objects[objIndex]._pathType = pathType;
_objects[objIndex]._vxPath = vxPath;
_objects[objIndex]._vyPath = vyPath;
}
/**
* Save sequence number and image number in given object
*/
void ObjectHandler::saveSeq(Object *obj) {
debugC(1, kDebugObject, "saveSeq");
bool found = false;
for (int i = 0; !found && (i < obj->_seqNumb); i++) {
Seq *q = obj->_seqList[i]._seqPtr;
for (int j = 0; !found && (j < obj->_seqList[i]._imageNbr); j++) {
if (obj->_currImagePtr == q) {
found = true;
obj->_curSeqNum = i;
obj->_curImageNum = j;
} else {
q = q->_nextSeqPtr;
}
}
}
}
/**
* Set up cur_seqPtr from stored sequence and image number in object
*/
void ObjectHandler::restoreSeq(Object *obj) {
debugC(1, kDebugObject, "restoreSeq");
Seq *q = obj->_seqList[obj->_curSeqNum]._seqPtr;
for (int j = 0; j < obj->_curImageNum; j++)
q = q->_nextSeqPtr;
obj->_currImagePtr = q;
}
/**
* If status.objid = -1, pick up objid, else use status.objid on objid,
* if objid can't be picked up, use it directly
*/
void ObjectHandler::useObject(int16 objId) {
debugC(1, kDebugObject, "useObject(%d)", objId);
int16 inventObjId = _vm->_inventory->getInventoryObjId();
Object *obj = &_objects[objId]; // Ptr to object
if (inventObjId == -1) {
const char *verb; // Background verb to use directly
// Get or use objid directly
if ((obj->_genericCmd & TAKE) || obj->_objValue) // Get collectible item
Common::sprintf_s(_vm->_line, "%s %s", _vm->_text->getVerb(_vm->_take, 0), _vm->_text->getNoun(obj->_nounIndex, 0));
else if (obj->_cmdIndex != 0) // Use non-collectible item if able
Common::sprintf_s(_vm->_line, "%s %s", _vm->_text->getVerb(_vm->_parser->getCmdDefaultVerbIdx(obj->_cmdIndex), 0), _vm->_text->getNoun(obj->_nounIndex, 0));
else if ((verb = _vm->_parser->useBG(_vm->_text->getNoun(obj->_nounIndex, 0))) != nullptr)
Common::sprintf_s(_vm->_line, "%s %s", verb, _vm->_text->getNoun(obj->_nounIndex, 0));
else
return; // Can't use object directly
} else {
// Use status.objid on objid
// Default to first cmd verb
Common::sprintf_s(_vm->_line, "%s %s %s", _vm->_text->getVerb(_vm->_parser->getCmdDefaultVerbIdx(_objects[inventObjId]._cmdIndex), 0),
_vm->_text->getNoun(_objects[inventObjId]._nounIndex, 0),
_vm->_text->getNoun(obj->_nounIndex, 0));
// Check valid use of objects and override verb if necessary
for (Uses *use = _uses; use->_objId != _numObj; use++) {
if (inventObjId == use->_objId) {
// Look for secondary object, if found use matching verb
bool foundFl = false;
for (Target *target = use->_targets; target->_nounIndex != 0; target++)
if (target->_nounIndex == obj->_nounIndex) {
foundFl = true;
Common::sprintf_s(_vm->_line, "%s %s %s", _vm->_text->getVerb(target->_verbIndex, 0),
_vm->_text->getNoun(_objects[inventObjId]._nounIndex, 0),
_vm->_text->getNoun(obj->_nounIndex, 0));
}
// No valid use of objects found, print failure string
if (!foundFl) {
// Deselect dragged icon if inventory not active
if (_vm->_inventory->getInventoryState() != kInventoryActive)
_vm->_screen->resetInventoryObjId();
Utils::notifyBox(_vm->_text->getTextData(use->_dataIndex));
return;
}
}
}
}
if (_vm->_inventory->getInventoryState() == kInventoryActive) // If inventory active, remove it
_vm->_inventory->setInventoryState(kInventoryUp);
_vm->_screen->resetInventoryObjId();
_vm->_parser->lineHandler(); // and process command
}
/**
* Return object index of the topmost object under the cursor, or -1 if none
* Objects are filtered if not "useful"
*/
int16 ObjectHandler::findObject(uint16 x, uint16 y) {
debugC(3, kDebugObject, "findObject(%d, %d)", x, y);
int16 objIndex = -1; // Index of found object
uint16 y2Max = 0; // Greatest y2
Object *obj = _objects;
// Check objects on screen
for (int i = 0; i < _numObj; i++, obj++) {
// Object must be in current screen and "useful"
if (obj->_screenIndex == *_vm->_screenPtr && (obj->_genericCmd || obj->_objValue || obj->_cmdIndex)) {
Seq *curImage = obj->_currImagePtr;
// Object must have a visible image...
if (curImage != nullptr && obj->_cycling != kCycleInvisible) {
// If cursor inside object
if (x >= (uint16)obj->_x && x <= obj->_x + curImage->_x2 && y >= (uint16)obj->_y && y <= obj->_y + curImage->_y2) {
// If object is closest so far
if (obj->_y + curImage->_y2 > y2Max) {
y2Max = obj->_y + curImage->_y2;
objIndex = i; // Found an object!
}
}
} else {
// ...or a dummy object that has a hotspot rectangle
if (curImage == nullptr && obj->_vxPath != 0 && !obj->_carriedFl) {
// If cursor inside special rectangle
if ((int16)x >= obj->_oldx && (int16)x < obj->_oldx + obj->_vxPath && (int16)y >= obj->_oldy && (int16)y < obj->_oldy + obj->_vyPath) {
// If object is closest so far
if (obj->_oldy + obj->_vyPath - 1 > (int16)y2Max) {
y2Max = obj->_oldy + obj->_vyPath - 1;
objIndex = i; // Found an object!
}
}
}
}
}
}
return objIndex;
}
/**
* Issue "Look at