/* 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 * aint32 with this program; if not, write to the Free Software * * * Based on the original sources * Faery Tale II -- The Halls of the Dead * (c) 1993-1996 The Wyrmkeep Entertainment Co. */ #include "saga2/saga2.h" #include "saga2/gdraw.h" #include "saga2/objects.h" #include "saga2/contain.h" #include "saga2/mouseimg.h" #include "saga2/grabinfo.h" namespace Saga2 { /* ===================================================================== * GrabInfo members * ===================================================================== */ GrabInfo::GrabInfo() { _pointerMap._size.x = 0; _pointerMap._size.y = 0; _pointerMap._data = nullptr; // null out the "held" object _grabId = Nothing; _grabObj = nullptr; _intentDoable = true; _intention = kIntWalkTo; _textBuf[0] = '\0'; _displayGauge = false; _gaugeNumerator = _gaugeDenominator = 0; _moveCount = 1; } GrabInfo::~GrabInfo() { if (_pointerMap._data != nullptr) delete[] _pointerMap._data; } // set the move count based on val and whether the object is // mergeable or not. void GrabInfo::setMoveCount(int16 val) { if (_grabObj) { if (_grabObj->proto()->flags & ResourceObjectPrototype::kObjPropMergeable) { _moveCount = val; } else { _moveCount = 1; } } } // Make the object ID given into the mouse pointer void GrabInfo::grabObject(ObjectID objid, Intent in, int16 count) { GameObject *obj = GameObject::objectAddress(objid); grabObject(obj, in, count); } // Make the object pointer into the mouse pointer void GrabInfo::grabObject(GameObject *obj, Intent in, int16 count) { char objText[kBufSize]; assert(!obj->isMoving()); // set the number of items setMoveCount(count); // Get address of object _grabObj = obj; _grabId = _grabObj->thisID(); // set the number of items setMoveCount(count); // get the original location _from = _grabObj->getLocation(); _from._context = _grabObj->IDParent(); // de-link the object _grabObj->move(Location(Nowhere, Nothing)); setIcon(); setIntent(in); // Display the name of the grabbed object under the mouse cursor _grabObj->objCursorText(objText, kBufSize, _moveCount); setMouseText(objText); clearMouseGauge(); } // this allows the mouse pointer to reference the original object // without removing it from the container/land // it does not actually copy it // Make the object ID given into the mouse pointer void GrabInfo::copyObject(ObjectID objid, Intent in, int16 count) { GameObject *obj = GameObject::objectAddress(objid); copyObject(obj, in, count); } // this allows the mouse pointer to reference the original object // without removing it from the container/land // it does not actually copy it // Make the object pointer into the mouse pointer void GrabInfo::copyObject(GameObject *obj, Intent in, int16 count) { char objText[kBufSize]; // set the number of items setMoveCount(count); // Get address of object, and address of object prototype _grabObj = obj; _grabId = _grabObj->thisID(); // set the number of items setMoveCount(count); _from = Nowhere; _from._context = Nothing; setIcon(); setIntent(in); // Display the name of the grabbed object under the mouse cursor _grabObj->objCursorText(objText, kBufSize, _moveCount); setMouseText(objText); clearMouseGauge(); } // Set a new intention. All changes to intention must use this function. uint8 GrabInfo::setIntent(uint8 in) { // If intention isn't being changed, return immediately if (_intention != (Intent)in) { // Intention has changed to None if (in == (uint8)kIntNone && _intention != kIntNone) g_vm->_pointer->hide(); // Intention has changed from None else if (in != (uint8)kIntNone && _intention == kIntNone) g_vm->_pointer->show(); _intention = (Intent)in; // Set new cursor setCursor(); } return in; } // Make the object given into the mouse pointer void GrabInfo::setIcon() { assert( _pointerMap._size.x == 0 && _pointerMap._size.y == 0 && _pointerMap._data == nullptr); assert(_grabObj != nullptr && isObject(_grabObj)); Sprite *spr; ProtoObj *proto; ColorTable mainColors; // colors for object uint8 *mapData; int32 mapBytes; // Get address of object, and address of object prototype proto = _grabObj->proto(); // Get address of sprite spr = proto->getSprite(_grabObj, ProtoObj::kObjAsMousePtr, _moveCount).sp; mapBytes = spr->size.x * spr->size.y; if ((mapData = new uint8[mapBytes]()) != nullptr) { // Clear out the image data memset(mapData, 0, mapBytes); // Build the current color table for the object _grabObj->getColorTranslation(mainColors); _pointerMap._size = spr->size; _pointerMap._data = mapData; _pointerOffset.x = - spr->size.x / 2; _pointerOffset.y = - spr->size.y / 2; // Render the sprite into the bitmap ExpandColorMappedSprite(_pointerMap, spr, mainColors); } else error("Unable to allocate mouse image buffer"); } void GrabInfo::clearIcon() { assert(_grabObj == nullptr); if (_pointerMap._data != nullptr) { delete[] _pointerMap._data; _pointerMap._size.x = 0; _pointerMap._size.y = 0; _pointerMap._data = nullptr; } } // Changes cursor image to reflect the current state of the cursor based // on the _intention and _intentDoable data members. void GrabInfo::setCursor() { if (_intentDoable) { switch (_intention) { case kIntNone: // If _intention has been changed to none then the // pointer has already been hidden. break; case kIntWalkTo: setMouseImage(kMouseArrowImage, 0, 0); break; case kIntOpen: setMouseImage(kMouseArrowImage, 0, 0); break; case kIntPickUp: setMouseImage(kMouseGrabPtrImage, -7, -7); break; case kIntDrop: setMouseImage(_pointerMap, _pointerOffset.x, _pointerOffset.y); break; case kIntUse: setMouseImage(kMouseUsePtrImage, -7, -7); break; case kIntAttack: setMouseImage(kMouseAttakPtrImage, -11, -11); break; case kIntCast: setMouseImage(kMouseAttakPtrImage, -11, -11); break; default: break; } } else { // indicate current _intention is not doable setMouseImage(kMouseXPointerImage, -7, -7); } } void GrabInfo::placeObject(const Location &loc) { _grabObj->move(loc); // Turn off state variables _grabObj = nullptr; _grabId = Nothing; _intentDoable = true; setIntent(kIntWalkTo); clearIcon(); // Display the saved text setMouseText(_textBuf[0] != '\0' ? _textBuf : nullptr); // Display the saved gauge data if (_displayGauge) setMouseGauge(_gaugeNumerator, _gaugeDenominator); else clearMouseGauge(); } // this should be use to return the object to the container // and/or remove the object from the cursor. void GrabInfo::replaceObject() { if (_grabObj == nullptr) return; // if actually attached to cursor, replace if (_grabObj->IDParent() == Nothing) { // ContainerView *updateView; _grabObj->move(_from); // Update the ContainerView from which the object came // updateView = ContainerView::findPane( _grabObj->parent() ); // if ( updateView != nullptr ) // ( updateView->getWindow() )->update( updateView->getExtent() ); } // Turn off state variables _grabObj = nullptr; _grabId = Nothing; _intentDoable = true; setIntent(kIntWalkTo); clearIcon(); // Display the saved text setMouseText(_textBuf[0] != '\0' ? _textBuf : nullptr); // Display the saved gauge data if (_displayGauge) setMouseGauge(_gaugeNumerator, _gaugeDenominator); else clearMouseGauge(); } // This function is called to request a new mouse text string. If there // is currently no grabbed object the text will be displayed, else the // text pointer will simply be saved. void GrabInfo::setText(const char *txt) { if ((txt != nullptr) && strlen(txt)) { Common::strlcpy(_textBuf, txt, kBufSize); if (_grabObj == nullptr) setMouseText(_textBuf); } else { _textBuf[0] = '\0'; if (_grabObj == nullptr) setMouseText(nullptr); } } // request a change to the mouse gauge void GrabInfo::setGauge(int16 numerator, int16 denominator) { _displayGauge = true; _gaugeNumerator = numerator; _gaugeDenominator = denominator; if (_grabObj == nullptr) setMouseGauge(_gaugeNumerator, _gaugeDenominator); } // clear the mouse gauge void GrabInfo::clearGauge() { _displayGauge = false; if (_grabObj == nullptr) clearMouseGauge(); } // FIXME: This code is specific to Dinotopia. Was disabled for some time and needs updating void cycleCursor() { #if 0 if (! g_vm->_mouseInfo->cursorCyclingEnabled) // FIXME: cursorCyclingEnabled not present in GrabInfo return; uint8 curIntent = g_vm->_mouseInfo->getIntent(); if (++curIntent == GrabInfo::IntentCounts) g_vm->_mouseInfo->setIntent(1); //Set Cursor First State Skip None else { if (!walkEnabled && curIntent == GrabInfo::WalkTo) // FIXME: walkEnabled not present ++curIntent; if (curIntent == GrabInfo::PickUp && g_vm->_mouseInfo->getObject() != nullptr) ++curIntent; if (curIntent == GrabInfo::Drop && g_vm->_mouseInfo->getObject() == nullptr) ++curIntent; if (!eyeEnabled && curIntent == GrabInfo::LookAt) // FIXME: eyeEnabled not present ++curIntent; if (curIntent == GrabInfo::Navigate) { if (walkEnabled) curIntent = 1; //Set Cursor First State Skip None else { curIntent = 2; if (curIntent == GrabInfo::PickUp && g_vm->_mouseInfo->getObject() != nullptr) ++curIntent; } } g_vm->_mouseInfo->setIntent(curIntent); //Set Cursor To Next State } #endif } } // end of namespace Saga2