scummvm/engines/saga2/contain.cpp

1915 lines
55 KiB
C++
Raw Normal View History

2021-05-17 20:47:39 +02:00
/* 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.
2021-05-17 20:47:39 +02:00
*
* 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/>.
2021-05-17 20:47:39 +02:00
*
*
* Based on the original sources
* Faery Tale II -- The Halls of the Dead
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
*/
2021-06-23 14:41:01 +02:00
#include "saga2/saga2.h"
2021-10-17 23:13:36 +03:00
#include "saga2/detection.h"
2021-05-17 20:47:39 +02:00
#include "saga2/objects.h"
#include "saga2/contain.h"
#include "saga2/grabinfo.h"
#include "saga2/motion.h"
#include "saga2/uimetrcs.h"
#include "saga2/localize.h"
#include "saga2/intrface.h"
#include "saga2/spellbuk.h"
#include "saga2/imagcach.h"
2021-06-02 00:16:29 +02:00
#include "saga2/hresmgr.h"
#include "saga2/fontlib.h"
2021-05-17 20:47:39 +02:00
#include "saga2/pclass.r"
2021-05-17 20:47:39 +02:00
namespace Saga2 {
2021-07-06 21:39:48 +02:00
enum {
kMaxOpenDistance = 32
};
2021-05-17 20:47:39 +02:00
/* ===================================================================== *
Imports
* ===================================================================== */
2021-06-07 18:19:10 +02:00
extern ReadyContainerView *TrioCviews[kNumViews];
2021-05-17 20:47:39 +02:00
extern ReadyContainerView *indivCviewTop, *indivCviewBot;
2021-06-03 18:47:26 +09:00
extern SpriteSet *objectSprites; // object sprites
2021-05-17 20:47:39 +02:00
extern Alarm containerObjTextAlarm;
extern bool gameSetupComplete;
extern hResContext *imageRes; // image resource handle
hResContext *containerRes; // image resource handle
extern APPFUNC(cmdWindowFunc);
// Temporary...
void grabObject(ObjectID pickedObject); // turn object into mouse ptr
2021-09-11 12:13:35 +03:00
void releaseObject(); // restore mouse pointer
2021-05-17 20:47:39 +02:00
/* Reference Types
2022-10-29 13:35:19 +02:00
ProtoObj::kIsTangible
ProtoObj::kIsContainer
ProtoObj::kIsBottle
ProtoObj::kIsFood
ProtoObj::kIsWearable
ProtoObj::kIsWeapon
ProtoObj::kIsDocument
ProtoObj::kIsIntangible
ProtoObj::kIsConcept
ProtoObj::kIsMemory
ProtoObj::kIsPsych
ProtoObj::kIsSpell
ProtoObj::kIsEnchantment
2021-05-17 20:47:39 +02:00
*/
//-----------------------------------------------------------------------
// Physical container appearance
static ContainerAppearanceDef physicalContainerAppearance = {
2021-07-01 20:41:01 +09:00
{250, 60, 268, 304 + 16},
{17 + 4, 87, 268 - 2, 304 - 87},
{13 + 8, 37, 44, 42},
{13 + 8 + 44, 37, 44, 42},
{13 + 118, 50, 36, 36},
{13 + 139, 37, 88, 43},
{ MKTAG('P', 'C', 'L', 0), MKTAG('P', 'C', 'L', 1) },
{ MKTAG('P', 'S', 'L', 0), MKTAG('P', 'S', 'L', 1) },
2021-07-01 20:41:01 +09:00
{13, 8},
{22, 22},
2021-07-01 06:32:53 +09:00
0, 0,
0
};
2021-05-17 20:47:39 +02:00
static const StaticWindow brassDecorations[] = {
{{0, 0, 268, 86}, nullptr, 3},
{{13, 86, 242, 109}, nullptr, 4},
{{13, 195, 242, 121}, nullptr, 5}
2021-05-17 20:47:39 +02:00
};
static const StaticWindow clothDecorations[] = {
{{0, 0, 268, 86}, nullptr, 6},
{{13, 86, 242, 109}, nullptr, 7},
{{13, 195, 242, 121}, nullptr, 8}
2021-05-17 20:47:39 +02:00
};
static const StaticWindow steelDecorations[] = {
{{0, 0, 268, 86}, nullptr, 9},
{{13, 86, 242, 109}, nullptr, 10},
{{13, 195, 242, 121}, nullptr, 11}
2021-05-17 20:47:39 +02:00
};
static const StaticWindow woodDecorations[] = {
{{0, 0, 268, 86}, nullptr, 12},
{{13, 86, 242, 109}, nullptr, 13},
{{13, 195, 242, 121}, nullptr, 14}
2021-05-17 20:47:39 +02:00
};
//-----------------------------------------------------------------------
// Death container appearance
2021-07-01 20:41:01 +09:00
static ContainerAppearanceDef deathContainerAppearance = {
{260, 60, 206, 250},
{2, 87, 206 - 22, 250 - 87 - 32},
{16, 24, 44, 42},
{120 + 18, 24, 44, 42},
{0, 0, 0, 0},
{0, 0, 0, 0},
{ MKTAG('D', 'C', 'L', 0), MKTAG('D', 'C', 'L', 1) },
{ MKTAG('D', 'S', 'L', 0), MKTAG('D', 'S', 'L', 1) },
2021-07-01 20:41:01 +09:00
{27, -4},
{22, 22},
2021-07-01 06:32:53 +09:00
0, 0,
0
};
2021-05-17 20:47:39 +02:00
// physal dialog window decorations
static const StaticWindow deathDecorations[] = {
{{0, 0, 206, 250}, nullptr, 15}
};
2021-05-17 20:47:39 +02:00
//-----------------------------------------------------------------------
// ReadyContainer appearance
static const ContainerAppearanceDef readyContainerAppearance = {
2021-07-01 20:41:01 +09:00
{0, 0, 0, 0},
{476, 105, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{ 0, 0 },
{ 0, 0 },
2022-10-29 22:43:38 +02:00
{kIconOriginX - 1, kIconOriginY - 1 - 8},
{kIconSpacingX, kIconSpacingY},
2021-07-01 06:32:53 +09:00
1, 3,
3
};
2021-05-17 20:47:39 +02:00
//-----------------------------------------------------------------------
// Mental Container appearance
static const ContainerAppearanceDef mentalContainerAppearance = {
2021-07-01 20:41:01 +09:00
{478, 168 - 54, 158, 215},
{2, 86 - 18 - 4, 158 - 2, 215 - 66},
{2, 19, 44, 44},
{103, 40 - 18 - 4, 44, 44},
{0, 0, 0, 0},
{0, 0, 0, 0},
{ MKTAG('C', 'L', 'S', 0), MKTAG('C', 'L', 'S', 1) },
{ MKTAG('S', 'E', 'L', 0), MKTAG('S', 'E', 'L', 1) },
2021-07-01 20:41:01 +09:00
{3, 0},
{4, 4},
2021-07-01 06:32:53 +09:00
4, 4,
20
};
2021-05-17 20:47:39 +02:00
static const StaticWindow mentalDecorations[] = {
{{0, 0, 158, 215}, nullptr, 0} // Bottom decoration panel
};
2021-05-17 20:47:39 +02:00
//-----------------------------------------------------------------------
// Enchantment container appearance
static const ContainerAppearanceDef enchantmentContainerAppearance = {
2021-07-01 20:41:01 +09:00
{262, 92, 116, 202},
{2, 87, 116 - 2, 202 - 87},
{7, 50, 44, 43},
{57, 50, 44, 43},
{38, 7, 32, 32},
{0, 0, 0, 0},
{ MKTAG('A', 'A', 'A', 0), MKTAG('A', 'A', 'A', 0) },
{ MKTAG('A', 'A', 'A', 0), MKTAG('A', 'A', 'A', 0) },
2021-07-01 20:41:01 +09:00
{12, 98},
{16, 13},
2021-07-01 06:32:53 +09:00
2, 2,
2
};
2021-05-17 20:47:39 +02:00
//-----------------------------------------------------------------------
// ContainerView class
ContainerView::ContainerView(
gPanelList &list,
const Rect16 &rect,
ContainerNode &nd,
const ContainerAppearanceDef &app,
2021-05-17 20:47:39 +02:00
AppFunc *cmd)
: gControl(list, rect, nullptr, 0, cmd),
_iconOrigin(app.iconOrigin),
_iconSpacing(app.iconSpacing),
_visibleRows(app.rows),
_visibleCols(app.cols),
_node(nd) {
_containerObject = GameObject::objectAddress(nd._object);
_scrollPosition = 0;
_totalRows = app.totRows;
2021-06-13 16:56:52 +02:00
setMousePoll(true);
_totalMass = 0;
_totalBulk = 0;
_numObjects = 0;
2021-05-17 20:47:39 +02:00
}
// Destructor
ContainerView::~ContainerView() {
}
2021-06-13 16:56:52 +02:00
// returns true if the object is visible for this type of
2021-05-17 20:47:39 +02:00
// container.
bool ContainerView::isVisible(GameObject *item) {
ProtoObj *proto = item->proto();
2022-10-29 13:35:19 +02:00
if (proto->containmentSet() & ProtoObj::kIsEnchantment)
2021-06-13 16:56:52 +02:00
return false;
2021-05-17 20:47:39 +02:00
// If Intangible Container then don't show it.
2022-10-29 13:35:19 +02:00
if ((proto->containmentSet() & (ProtoObj::kIsContainer | ProtoObj::kIsIntangible)) == (ProtoObj::kIsContainer | ProtoObj::kIsIntangible))
2021-06-13 16:56:52 +02:00
return true;
2021-05-17 20:47:39 +02:00
2021-06-13 16:56:52 +02:00
return true;
2021-05-17 20:47:39 +02:00
}
// total the mass, bulk, and number of all objects in container.
2021-09-11 12:13:35 +03:00
void ContainerView::totalObjects() {
ObjectID objID;
GameObject *item = nullptr;
2021-05-17 20:47:39 +02:00
_totalMass = 0;
_totalBulk = 0;
_numObjects = 0;
2021-05-17 20:47:39 +02:00
if (_containerObject == nullptr) return;
2021-05-17 20:47:39 +02:00
RecursiveContainerIterator iter(_containerObject);
2021-05-17 20:47:39 +02:00
// See if he has enough currency
for (objID = iter.first(&item); objID != Nothing; objID = iter.next(&item)) {
// If the object is visible, then add to total mass and
// bulk.
if (isVisible(item)) {
uint16 numItems;
ProtoObj *proto = item->proto();
_numObjects++;
2021-05-17 20:47:39 +02:00
// if it's mergeable calc using the getExtra method of
// quantity calculation
// if not, then use the objLoc.z method
2022-10-29 13:35:19 +02:00
if (proto->flags & ResourceObjectPrototype::kObjPropMergeable)
2021-05-17 20:47:39 +02:00
numItems = item->getExtra();
else numItems = 1;
_totalMass += proto->mass * numItems;
_totalBulk += proto->bulk * numItems;
2021-05-17 20:47:39 +02:00
}
}
}
// Return the Nth visible object from this container.
ObjectID ContainerView::getObject(int16 slotNum) {
ObjectID objID;
GameObject *item;
if (_containerObject == nullptr) return Nothing;
2021-05-17 20:47:39 +02:00
ContainerIterator iter(_containerObject);
2021-05-17 20:47:39 +02:00
// Iterate through all the objects in the container.
while ((objID = iter.next(&item)) != Nothing) {
// If the object is visible, then check which # it is,
// and return the object if slotnum has been decremented
// to zero.
if (isVisible(item)) {
if (slotNum == 0) return objID;
slotNum--;
}
}
return Nothing;
}
// REM: We need to handle the case of a NULL container here...
// Draw the contents of the container.
void ContainerView::drawClipped(
gPort &port,
const Point16 &offset,
const Rect16 &r) {
// Coordinates to draw the inventory icon at.
int16 x,
y;
// Coordinates for slot 0,0.
int16 originX = _extent.x - offset.x + _iconOrigin.x,
originY = _extent.y - offset.y + _iconOrigin.y;
2021-05-17 20:47:39 +02:00
ObjectID objID;
GameObject *item;
// Iterator class for the container.
ContainerIterator iter(_containerObject);
2021-05-17 20:47:39 +02:00
// Color set to draw the object.
ColorTable objColors;
// If there's no overlap between extent and clip, then return.
if (!_extent.overlap(r)) return;
2021-05-17 20:47:39 +02:00
// Iterate through each item within the container.
while ((objID = iter.next(&item)) != Nothing) {
TilePoint objLoc;
ProtoObj *objProto = item->proto();
objLoc = item->getLocation();
if (objLoc.z == 0) continue;
// Draw object only if visible and in a visible row & col.
if (objLoc.u >= _scrollPosition
&& objLoc.u < _scrollPosition + _visibleRows
&& objLoc.v < _visibleCols
2021-05-17 20:47:39 +02:00
&& isVisible(item)) {
Sprite *spr;
ProtoObj *proto = item->proto();
Point16 sprPos;
y = originY
+ (objLoc.u - _scrollPosition)
2022-10-29 22:43:38 +02:00
* (_iconSpacing.y + kIconHeight);
2021-05-17 20:47:39 +02:00
x = originX
+ objLoc.v
2022-10-29 22:43:38 +02:00
* (_iconSpacing.x + kIconWidth);
2021-05-17 20:47:39 +02:00
// Get the address of the sprite.
2022-10-29 13:35:19 +02:00
spr = proto->getSprite(item, ProtoObj::kObjInContainerView).sp;
2021-05-17 20:47:39 +02:00
2022-10-29 22:43:38 +02:00
sprPos.x = x + ((kIconWidth - spr->size.x) >> 1)
2021-05-17 20:47:39 +02:00
- spr->offset.x;
2022-10-29 22:43:38 +02:00
sprPos.y = y + ((kIconHeight - spr->size.y) >> 1)
2021-05-17 20:47:39 +02:00
- spr->offset.y;
// Build the color table.
item->getColorTranslation(objColors);
// Draw the sprite into the port
DrawColorMappedSprite(
port,
sprPos,
spr,
objColors);
// check to see if selecting amount for this objec
2021-09-13 09:27:20 +09:00
if (g_vm->_cnm->_objToGet == item) {
2022-10-29 22:43:38 +02:00
Point16 selectorPos = Point16(x + ((kIconWidth - kSelectorX) >> 1),
y + ((kIconHeight - kSelectorY) >> 1));
2021-05-17 20:47:39 +02:00
// draw the selector thingy
drawSelector(port, selectorPos);
// set the position of the inc center
2022-10-27 14:06:11 +02:00
g_vm->_cnm->_amountIndY = y - (kSelectorY >> 1) - 12;
2021-05-17 20:47:39 +02:00
} else drawQuantity(port, item, objProto, x, y);
}
}
}
// draws the mereged object multi-item selector
void ContainerView::drawSelector(gPort &port, Point16 &pos) {
2021-06-24 00:03:14 +02:00
char buf[20];
2021-05-17 20:47:39 +02:00
uint8 num;
SAVE_GPORT_STATE(port);
// draw the arrow images
2021-09-13 09:27:20 +09:00
drawCompressedImage(port, pos, g_vm->_cnm->_selImage);
2021-05-17 20:47:39 +02:00
// draw the number of items selected thus far
num = Common::sprintf_s(buf, " %d ", g_vm->_cnm->_numPicked);
2021-05-17 20:47:39 +02:00
port.moveTo(Point16(pos.x - ((3 * (num - 3)) + 1), pos.y + 7));
port.setFont(&Helv11Font);
port.setColor(11); // set color to white
2022-10-28 23:43:53 +02:00
port.setStyle(kTextStyleThickOutline);
2021-05-17 20:47:39 +02:00
port.setOutlineColor(24); // set outline color to black
2022-10-28 23:43:53 +02:00
port.setMode(kDrawModeMatte);
2021-05-17 20:47:39 +02:00
port.drawText(buf);
}
void ContainerView::drawQuantity(
gPort &port,
GameObject *item,
ProtoObj *objProto,
int16 x,
int16 y) {
int16 quantity;
2022-10-29 13:35:19 +02:00
quantity = (objProto->flags & ResourceObjectPrototype::kObjPropMergeable)
2021-05-17 20:47:39 +02:00
? item->getExtra()
: item->getLocation().z;
if (quantity > 1) {
SAVE_GPORT_STATE(port);
2021-06-07 18:19:10 +02:00
char buf[8];
2021-05-17 20:47:39 +02:00
// draw the number of items selected thus far
Common::sprintf_s(buf, "%d", quantity);
2021-05-17 20:47:39 +02:00
port.moveTo(x - 1, y + 22);
port.setFont(&Helv11Font);
port.setColor(11); // set color to white
2022-10-28 23:43:53 +02:00
port.setStyle(kTextStyleThickOutline);
2021-05-17 20:47:39 +02:00
port.setOutlineColor(24); // set outline color to black
2022-10-28 23:43:53 +02:00
port.setMode(kDrawModeMatte);
2021-05-17 20:47:39 +02:00
port.drawText(buf);
}
}
void ContainerView::setContainer(GameObject *containerObj) {
_containerObject = containerObj;
2021-05-17 20:47:39 +02:00
totalObjects();
}
// Get the slot that the point is over
TilePoint ContainerView::pickObjectSlot(const Point16 &pickPos) {
TilePoint slot;
Point16 temp;
// Compute the u/v of the slot that they clicked on.
temp = pickPos + _iconSpacing / 2 - _iconOrigin;
2022-10-29 22:43:38 +02:00
slot.v = clamp(0, temp.x / (kIconWidth + _iconSpacing.x), _visibleCols - 1);
slot.u = clamp(0, temp.y / (kIconHeight + _iconSpacing.y), _visibleRows - 1) + _scrollPosition;
2021-05-17 20:47:39 +02:00
slot.z = 1;
return slot;
}
// Get the object that the pointer is over
GameObject *ContainerView::getObject(const TilePoint &slot) {
GameObject *item;
TilePoint loc;
item = _containerObject->child();
2021-05-17 20:47:39 +02:00
while (item != nullptr) {
2021-05-17 20:47:39 +02:00
// Skip objects that are stacked behind other objects
if (item->getLocation().z != 0) {
ProtoObj *proto = item->proto();
loc = item->getLocation();
if (
(loc.u == slot.u) &&
(loc.v == slot.v) &&
//Skip The Enchantments
2022-10-29 13:35:19 +02:00
(!(proto->containmentSet() & ProtoObj::kIsEnchantment))
2021-05-17 20:47:39 +02:00
) {
return item;
}
}
item = item->next();
}
return nullptr;
2021-05-17 20:47:39 +02:00
}
// Get the object ID that the point is over
ObjectID ContainerView::pickObjectID(const Point16 &pickPos) {
TilePoint slot;
GameObject *item;
slot = pickObjectSlot(pickPos);
item = getObject(slot);
if (item != nullptr) {
2021-05-17 20:47:39 +02:00
return item->thisID();
} else {
return 0;
}
}
// Get the object ID that the point is over
GameObject *ContainerView::pickObject(const Point16 &pickPos) {
TilePoint slot;
GameObject *item;
slot = pickObjectSlot(pickPos);
item = getObject(slot);
return item;
}
bool ContainerView::activate(gEventType why) {
gPanel::activate(why);
2021-06-13 16:56:52 +02:00
return true;
2021-05-17 20:47:39 +02:00
}
2021-09-11 12:13:35 +03:00
void ContainerView::deactivate() {
2021-05-17 20:47:39 +02:00
}
void ContainerView::pointerMove(gPanelMessage &msg) {
if (msg._pointerLeave) {
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_lastPickedObjectID = Nothing;
g_vm->_cnm->_lastPickedObjectQuantity = -1;
g_vm->_mouseInfo->setText(nullptr);
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_mouseText[0] = 0;
2021-05-17 20:47:39 +02:00
// static bool that tells if the mouse cursor
// is in a panel
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_mouseInView = false;
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->setDoable(true);
2021-05-17 20:47:39 +02:00
} else {
// if( msg.pointerEnter )
{
// static bool that tells if the mouse cursor
// is in a panel
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_mouseInView = true;
2021-05-17 20:47:39 +02:00
GameObject *mouseObject;
2021-07-01 05:05:03 +09:00
mouseObject = g_vm->_mouseInfo->getObject();
2021-05-17 20:47:39 +02:00
if (!_node.isAccessable(getCenterActorID())) {
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->setDoable(false);
} else if (mouseObject == nullptr) {
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->setDoable(true);
2021-05-17 20:47:39 +02:00
} else {
g_vm->_mouseInfo->setDoable(_containerObject->canContain(mouseObject->thisID()));
2021-05-17 20:47:39 +02:00
}
}
// Determine if mouse is pointing at a new object
updateMouseText(msg._pickPos);
2021-05-17 20:47:39 +02:00
}
}
bool ContainerView::pointerHit(gPanelMessage &msg) {
GameObject *mouseObject;
GameObject *slotObject;
uint16 mouseSet;
slotObject = pickObject(msg._pickPos);
2021-07-01 05:05:03 +09:00
mouseObject = g_vm->_mouseInfo->getObject();
2021-05-17 20:47:39 +02:00
mouseSet = mouseObject ? mouseObject->containmentSet() : 0;
2021-07-01 05:05:03 +09:00
if (!g_vm->_mouseInfo->getDoable()) return false;
2021-05-17 20:47:39 +02:00
if (msg._doubleClick && !g_vm->_cnm->_alreadyDone) {
2021-05-17 20:47:39 +02:00
dblClick(mouseObject, slotObject, msg);
} else { // single click
if (mouseObject != nullptr) {
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_alreadyDone = true; // if object then no doubleClick
2021-05-17 20:47:39 +02:00
2022-10-28 23:52:24 +02:00
if (g_vm->_mouseInfo->getIntent() == GrabInfo::kIntDrop) {
2022-10-29 13:35:19 +02:00
if (mouseSet & ProtoObj::kIsTangible) {
2021-07-01 05:05:03 +09:00
dropPhysical(msg, mouseObject, slotObject, g_vm->_mouseInfo->getMoveCount());
2021-05-17 20:47:39 +02:00
}
// intangibles are used by dropping them
2022-10-29 13:35:19 +02:00
else if ((mouseSet & ProtoObj::kIsConcept) ||
(mouseSet & ProtoObj::kIsPsych) ||
(mouseSet & ProtoObj::kIsSpell) ||
(mouseSet & ProtoObj::kIsSkill)) {
2021-05-17 20:47:39 +02:00
useConcept(msg, mouseObject, slotObject);
} else {
// !!!! bad state, reset cursor
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->replaceObject();
2021-05-17 20:47:39 +02:00
}
2022-10-28 23:52:24 +02:00
} else if (g_vm->_mouseInfo->getIntent() == GrabInfo::kIntUse) {
2022-10-29 13:35:19 +02:00
if (mouseSet & ProtoObj::kIsTangible) {
2021-05-17 20:47:39 +02:00
usePhysical(msg, mouseObject, slotObject);
2022-10-29 13:35:19 +02:00
} else if ((mouseSet & ProtoObj::kIsSpell) ||
(mouseSet & ProtoObj::kIsSkill)) {
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->replaceObject();
2021-05-17 20:47:39 +02:00
} else {
useConcept(msg, mouseObject, slotObject);
}
} else {
// !!!! bad state, reset cursor
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->replaceObject();
2021-05-17 20:47:39 +02:00
}
} else {
// default to doubleClick active
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_alreadyDone = false;
2021-05-17 20:47:39 +02:00
clickOn(msg, mouseObject, slotObject);
}
}
// total the mass and bulk of all the objects in this container
totalObjects();
_window.update(_extent);
2021-05-17 20:47:39 +02:00
2022-10-29 13:41:51 +02:00
return activate(kEventMouseDown);
2021-05-17 20:47:39 +02:00
}
void ContainerView::pointerRelease(gPanelMessage &) {
// see if in multi-item get mode
2021-09-13 09:27:20 +09:00
if (g_vm->_cnm->_objToGet) {
g_vm->_cnm->_objToGet->take(getCenterActorID(), g_vm->_cnm->_numPicked);
2021-05-17 20:47:39 +02:00
// reset the flags and pointer dealing with merged object movement
g_vm->_cnm->_objToGet = nullptr;
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_numPicked = 1;
g_vm->_cnm->_amountIndY = -1;
2021-05-17 20:47:39 +02:00
}
gPanel::deactivate();
}
void ContainerView::timerTick(gPanelMessage &msg) {
// validate objToGet and make sure that the number selected for move
// is less then or equal to the number of items present in the merged object
2021-09-13 09:27:20 +09:00
if (g_vm->_cnm->_objToGet && g_vm->_cnm->_amountIndY != -1) {
int32 rate = (g_vm->_cnm->_amountIndY - msg._pickAbsPos.y);
2021-05-17 20:47:39 +02:00
rate = rate * ((rate > 0) ? rate : -rate);
// Add to the amount accumulator based on the mouse position
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_amountAccumulator += rate / 4;
2021-05-17 20:47:39 +02:00
// Take the top bits of the amount accumulator and add to
// the mergeable amount.
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_numPicked = clamp(1,
g_vm->_cnm->_numPicked + (g_vm->_cnm->_amountAccumulator >> 8),
g_vm->_cnm->_objToGet->getExtra());
2021-05-17 20:47:39 +02:00
// Now remove the bits that we added to the grab amount
// keep the remaining bits to accumulate for next time
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_amountAccumulator &= 0x00ff;
2021-05-17 20:47:39 +02:00
}
}
void ContainerView::dblClick(GameObject *mouseObject, GameObject *slotObject, gPanelMessage &msg) {
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_alreadyDone = true;
2021-05-17 20:47:39 +02:00
// double click stuff
dblClickOn(msg, mouseObject, slotObject);
}
// activate the click function
void ContainerView::clickOn(
gPanelMessage &,
GameObject *,
GameObject *cObj) {
if (cObj != nullptr) {
2022-10-29 13:35:19 +02:00
if (cObj->proto()->flags & ResourceObjectPrototype::kObjPropMergeable) {
2021-05-17 20:47:39 +02:00
if (!rightButtonState()) {
// just get the object into the cursor
cObj->take(getCenterActorID(), cObj->getExtra());
} else {
// activate multi-object get interface if a mergeable object
getMerged(cObj);
g_vm->_mouseInfo->setText(nullptr);
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_mouseText[0] = 0;
2021-05-17 20:47:39 +02:00
}
} else {
// just get the object into the cursor
2021-09-13 09:27:20 +09:00
cObj->take(getCenterActorID(), g_vm->_cnm->_numPicked);
2021-05-17 20:47:39 +02:00
}
}
}
void ContainerView::getMerged(GameObject *obj) {
// reset the number picked.
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_numPicked = 1;
2021-05-17 20:47:39 +02:00
// set the object to be gotten
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_objToGet = obj;
2021-05-17 20:47:39 +02:00
}
// Activate the double click function
void ContainerView::dblClickOn(
gPanelMessage &,
GameObject *mObj,
GameObject *) {
if (mObj != nullptr) {
2021-05-17 20:47:39 +02:00
ObjectID possessor = mObj->possessor();
ProtoObj *proto = mObj->proto();
PlayerActorID pID;
// Only player actors can be possessors as far as the UI is concerned
2021-06-13 16:56:52 +02:00
if (actorIDToPlayerID(possessor, pID) == false) possessor = Nothing;
2021-05-17 20:47:39 +02:00
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->replaceObject(); //Put Object Back
2021-05-17 20:47:39 +02:00
if (!(proto->setUseCursor(mObj->thisID()))) {
MotionTask::useObject(
possessor == Nothing ? *getCenterActor() : * (Actor *)GameObject::objectAddress(possessor),
*mObj);
}
}
}
// drop a physical object
void ContainerView::dropPhysical(
gPanelMessage &msg,
GameObject *mObj,
GameObject *cObj,
int16 num) {
2021-07-01 05:05:03 +09:00
assert(g_vm->_mouseInfo->getObject() == mObj);
2022-10-29 13:35:19 +02:00
assert(mObj->containmentSet() & ProtoObj::kIsTangible);
2021-05-17 20:47:39 +02:00
// Place object back where it came from, temporarily
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->replaceObject();
2021-05-17 20:47:39 +02:00
// test to check if item is accepted by container
if (_containerObject->canContain(mObj->thisID())) {
2021-05-17 20:47:39 +02:00
Actor *centerActor = getCenterActor();
Location loc(pickObjectSlot(msg._pickPos),
_containerObject->thisID());
2021-05-17 20:47:39 +02:00
// check if no object in the current slot
if (cObj == nullptr) {
2021-05-17 20:47:39 +02:00
MotionTask::dropObject(*centerActor, *mObj, loc, num);
WriteStatusF(6, "No object state");
} else {
// Try dropping this object on the object in the container
MotionTask::dropObjectOnObject(*centerActor, *mObj, *cObj, num);
}
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_alreadyDone = true;
2021-05-17 20:47:39 +02:00
}
}
// use a physical object
void ContainerView::usePhysical(
gPanelMessage &msg,
GameObject *mObj,
GameObject *cObj) {
2021-07-01 05:05:03 +09:00
assert(g_vm->_mouseInfo->getObject() == mObj);
2022-10-29 13:35:19 +02:00
assert(mObj->containmentSet() & ProtoObj::kIsTangible);
2021-05-17 20:47:39 +02:00
if (cObj == nullptr) {
2021-05-17 20:47:39 +02:00
dropPhysical(msg, mObj, cObj);
} else {
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->replaceObject();
2021-05-17 20:47:39 +02:00
// Use mouse object on container object
MotionTask::useObjectOnObject(*getCenterActor(), *mObj, *cObj);
}
}
// Use Concept or Psycological state
void ContainerView::useConcept(
gPanelMessage &msg,
GameObject *mObj,
GameObject *cObj) {
2021-07-01 05:05:03 +09:00
assert(g_vm->_mouseInfo->getObject() == mObj);
2022-10-29 13:35:19 +02:00
assert(mObj->containmentSet() & ProtoObj::kIsIntangible);
2021-05-17 20:47:39 +02:00
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->replaceObject();
2021-05-17 20:47:39 +02:00
// Determine if this object can go into this container
if (_containerObject->canContain(mObj->thisID())) {
2021-05-17 20:47:39 +02:00
ObjectID centerActorID = getCenterActorID();
if (cObj == nullptr) {
2021-05-17 20:47:39 +02:00
// If there is no object already in this slot drop the
// mouse object here
Location loc(pickObjectSlot(msg._pickPos),
_containerObject->thisID());
2021-05-17 20:47:39 +02:00
mObj->drop(centerActorID, loc);
} else
// If there is an object here drop the mouse object onto it
mObj->dropOn(centerActorID, cObj->thisID());
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_alreadyDone = true;
2021-05-17 20:47:39 +02:00
}
}
// Use Spell or Skill
void ContainerView::useSpell(
gPanelMessage &msg,
GameObject *mObj,
GameObject *cObj) {
useConcept(msg, mObj, cObj);
}
// Determine if the mouse is pointing at a new object, and if so,
// adjust the mouse text
void ContainerView::updateMouseText(Point16 &pickPos) {
ObjectID slotID = pickObjectID(pickPos);
// set the mouse text to null if there is no object to get hints about
if (slotID == Nothing) {
// clear out the mouse text
g_vm->_mouseInfo->setText(nullptr);
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_mouseText[0] = 0;
2021-05-17 20:47:39 +02:00
// reset the last picked thingy
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_lastPickedObjectID = Nothing;
g_vm->_cnm->_lastPickedObjectQuantity = -1;
2021-05-17 20:47:39 +02:00
2021-06-13 16:56:52 +02:00
// set the display alarm to false
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_objTextAlarm = false;
2021-05-17 20:47:39 +02:00
return;
}
// get handles to the object in question
GameObject *slotObject = GameObject::objectAddress(slotID);
2021-09-13 09:27:20 +09:00
if (slotID == g_vm->_cnm->_lastPickedObjectID && slotObject->getExtra() == g_vm->_cnm->_lastPickedObjectQuantity) {
2021-05-17 20:47:39 +02:00
return; // same object, bug out
} else {
// was not same, but is now.
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_lastPickedObjectID = slotID;
g_vm->_cnm->_lastPickedObjectQuantity = slotObject->getExtra();
2021-05-17 20:47:39 +02:00
// clear out the mouse text
g_vm->_mouseInfo->setText(nullptr);
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_mouseText[0] = 0;
2021-05-17 20:47:39 +02:00
// reset the alarm flag
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_objTextAlarm = false;
2021-05-17 20:47:39 +02:00
// set the hint alarm
2022-10-29 23:06:17 +02:00
containerObjTextAlarm.set(kTicksPerSecond / 2);
2021-05-17 20:47:39 +02:00
// put the normalized text into mouseText
2021-09-13 09:27:20 +09:00
slotObject->objCursorText(g_vm->_cnm->_mouseText, ContainerManager::kBufSize);
2021-05-17 20:47:39 +02:00
}
}
/* ===================================================================== *
EnchantmentContainerView member functions
* ===================================================================== */
EnchantmentContainerView::EnchantmentContainerView(
gPanelList &list,
ContainerNode &nd,
const ContainerAppearanceDef &app,
2021-05-17 20:47:39 +02:00
AppFunc *cmd)
: ContainerView(list, app.viewRect, nd, app, cmd) {
}
// Check If Sprite Should Be Shown
bool EnchantmentContainerView::pointerHit(gPanelMessage &) {
2021-06-13 16:56:52 +02:00
return true;
2021-05-17 20:47:39 +02:00
}
void EnchantmentContainerView::pointerMove(gPanelMessage &) {}
/* ===================================================================== *
ReadyContainerView member functions
* ===================================================================== */
ReadyContainerView::ReadyContainerView(
gPanelList &list,
const Rect16 &box,
ContainerNode &nd,
void **backgrounds,
int16 numRes,
int16 numRows,
int16 numCols,
int16 totRows,
AppFunc *cmd)
: ContainerView(list, box, nd, readyContainerAppearance, cmd) {
// Over-ride row and column info in appearance record.
_visibleRows = numRows;
_visibleCols = numCols;
_totalRows = totRows;
2021-05-17 20:47:39 +02:00
if (backgrounds) {
_backImages = backgrounds;
_numIm = numRes;
2021-05-17 20:47:39 +02:00
} else {
_backImages = nullptr;
_numIm = 0;
2021-05-17 20:47:39 +02:00
}
}
void ReadyContainerView::setScrollOffset(int8 num) {
if (num > 0) {
_scrollPosition = num;
2021-05-17 20:47:39 +02:00
}
}
void ReadyContainerView::timerTick(gPanelMessage &msg) {
// validate objToGet and make sure that the number selected for move
// is less then or equal to the number of items present in the merged object
2021-09-13 09:27:20 +09:00
if (g_vm->_cnm->_objToGet && g_vm->_cnm->_amountIndY != -1) {
2021-05-17 20:47:39 +02:00
ContainerView::timerTick(msg);
// redraw the container to draw the amount indicator
draw();
}
}
void ReadyContainerView::drawClipped(
gPort &port,
const Point16 &offset,
const Rect16 &r) {
// Coordinates to draw the inventory icon at.
int16 x,
y;
// Coordinates for slot 0,0.
int16 originX = _extent.x - offset.x + _iconOrigin.x,
originY = _extent.y - offset.y + _iconOrigin.y;
2021-05-17 20:47:39 +02:00
// Row and column number of the inventory slot.
2021-05-17 20:47:39 +02:00
int16 col,
row;
ObjectID objID;
GameObject *item;
// Iterator class for the container.
ContainerIterator iter(_containerObject);
2021-05-17 20:47:39 +02:00
// Color set to draw the object.
ColorTable objColors;
// If there's no overlap between extent and clip, then return.
if (!_extent.overlap(r)) return;
2021-05-17 20:47:39 +02:00
// Draw the boxes for visible rows and cols.
if (_backImages) {
2021-05-17 20:47:39 +02:00
int16 i;
2022-10-29 22:43:38 +02:00
Point16 drawOrg(_extent.x - offset.x + kBackOriginX,
_extent.y - offset.y + kBackOriginY);
2021-05-17 20:47:39 +02:00
for (y = drawOrg.y, row = 0;
row < _visibleRows;
2022-10-29 22:43:38 +02:00
row++, y += _iconSpacing.y + kIconHeight) {
2021-05-17 20:47:39 +02:00
// reset y for background image stuff
//y = drawOrg.y;
for (i = 0, x = drawOrg.x, col = 0;
col < _visibleCols;
2022-10-29 22:43:38 +02:00
i++, col++, x += _iconSpacing.x + kIconWidth) {
2021-05-17 20:47:39 +02:00
Point16 pos(x, y);
if (isGhosted()) drawCompressedImageGhosted(port, pos, _backImages[i % _numIm]);
else drawCompressedImage(port, pos, _backImages[i % _numIm]);
2021-05-17 20:47:39 +02:00
}
}
} else {
for (y = originY, row = 0;
row < _visibleRows;
2022-10-29 22:43:38 +02:00
row++, y += _iconSpacing.y + kIconHeight) {
2021-05-17 20:47:39 +02:00
for (x = originX, col = 0;
col < _visibleCols;
2022-10-29 22:43:38 +02:00
col++, x += _iconSpacing.x + kIconWidth) {
2021-05-17 20:47:39 +02:00
// REM: We need to come up with some way of
// indicating how to render the pattern data which
// is behind the object...
port.setColor(14);
2022-10-29 22:43:38 +02:00
port.fillRect(x, y, kIconWidth, kIconHeight);
2021-05-17 20:47:39 +02:00
}
}
}
// Iterate through each item within the container.
while ((objID = iter.next(&item)) != Nothing) {
TilePoint objLoc;
ProtoObj *objProto = item->proto();
// If Intangible Container then don't show it.
2022-10-29 13:35:19 +02:00
if ((objProto->containmentSet() & (ProtoObj::kIsContainer | ProtoObj::kIsIntangible)) == (ProtoObj::kIsContainer | ProtoObj::kIsIntangible))
2021-05-17 20:47:39 +02:00
continue;
objLoc = item->getLocation();
if (objLoc.z == 0) continue;
// Draw object only if visible and in a visible row & col.
if (objLoc.u >= _scrollPosition &&
objLoc.u < _scrollPosition + _visibleRows &&
objLoc.v < _visibleCols &&
2021-05-17 20:47:39 +02:00
isVisible(item)) {
Sprite *spr;
ProtoObj *proto = item->proto();
Point16 sprPos;
2022-10-29 22:43:38 +02:00
y = originY + (objLoc.u - _scrollPosition) * (_iconSpacing.y + kIconHeight);
x = originX + objLoc.v * (_iconSpacing.x + kIconWidth);
2021-05-17 20:47:39 +02:00
// Get the address of the sprite.
2022-10-29 13:35:19 +02:00
spr = proto->getSprite(item, ProtoObj::kObjInContainerView).sp;
2021-05-17 20:47:39 +02:00
2022-10-29 22:43:38 +02:00
sprPos.x = x + ((kIconWidth - spr->size.x) >> 1)
2021-05-17 20:47:39 +02:00
- spr->offset.x;
2022-10-29 22:43:38 +02:00
sprPos.y = y + ((kIconHeight - spr->size.y) >> 1)
2021-05-17 20:47:39 +02:00
- spr->offset.y;
if (isGhosted()) return;
// Draw the "in use" indicator.
if (_backImages && proto->isObjectBeingUsed(item)) {
2021-05-17 20:47:39 +02:00
drawCompressedImage(port,
Point16(x - 4, y - 4), _backImages[3]);
2021-05-17 20:47:39 +02:00
}
// Build the color table.
item->getColorTranslation(objColors);
// Draw the sprite into the port
DrawColorMappedSprite(
port,
sprPos,
spr,
objColors);
// check to see if selecting amount for this objec
2021-09-13 09:27:20 +09:00
if (g_vm->_cnm->_objToGet == item) {
2022-10-29 22:43:38 +02:00
Point16 selectorPos = Point16(x + ((kIconWidth - kSelectorX) >> 1),
y + ((kIconHeight - kSelectorY) >> 1));
2021-05-17 20:47:39 +02:00
// draw the selector thingy
drawSelector(port, selectorPos);
// set the position of the inc center
2022-10-27 14:06:11 +02:00
g_vm->_cnm->_amountIndY = y - (kSelectorY >> 1) + 28; // extent.y;
2021-05-17 20:47:39 +02:00
} else drawQuantity(port, item, objProto, x, y);
}
}
}
/* ===================================================================== *
ContainerWindow member functions
* ===================================================================== */
// ContainerWindow class
ContainerWindow::ContainerWindow(ContainerNode &nd,
const ContainerAppearanceDef &app,
2021-05-17 20:47:39 +02:00
const char saveas[])
: FloatingWindow(nd._position, 0, saveas, cmdWindowFunc) {
2021-05-17 20:47:39 +02:00
// Initialize view to NULL.
_view = nullptr;
2021-05-17 20:47:39 +02:00
// create the close button for this window
_closeCompButton = new GfxCompButton(
2021-05-17 20:47:39 +02:00
*this,
app.closeRect, // rect for button
containerRes, // resource context
2021-06-07 18:19:10 +02:00
app.closeResID[0],
app.closeResID[1],
2021-05-17 20:47:39 +02:00
0,
cmdCloseButtonFunc); // mind app func
}
// Virtual destructor (base does nothing)
2021-09-11 12:13:35 +03:00
ContainerWindow::~ContainerWindow() {}
2021-05-17 20:47:39 +02:00
2021-09-11 12:13:35 +03:00
ContainerView &ContainerWindow::getView() {
return *_view;
2021-05-17 20:47:39 +02:00
}
/* ===================================================================== *
ScrollableContainerWindow member functions
* ===================================================================== */
ScrollableContainerWindow::ScrollableContainerWindow(
ContainerNode &nd, const ContainerAppearanceDef &app, const char saveas[])
2021-05-17 20:47:39 +02:00
: ContainerWindow(nd, app, saveas) {
_view = new ContainerView(*this, app.viewRect, nd, app);
2021-05-17 20:47:39 +02:00
// make the button conected to this window
_scrollCompButton = new GfxCompButton(
2021-05-17 20:47:39 +02:00
*this,
app.scrollRect, // rect for button
containerRes, // resource context
2021-06-07 18:19:10 +02:00
app.scrollResID[0], // resource handle name
app.scrollResID[1],
2021-05-17 20:47:39 +02:00
0,
cmdScrollFunc); // mind app func
assert(_view != nullptr);
assert(_scrollCompButton != nullptr);
2021-05-17 20:47:39 +02:00
}
/* ===================================================================== *
TangibleContainerWindow member functions
* ===================================================================== */
TangibleContainerWindow::TangibleContainerWindow(
ContainerNode &nd, const ContainerAppearanceDef &app)
2021-05-17 20:47:39 +02:00
: ScrollableContainerWindow(nd, app, "ObjectWindow") {
const int weightIndicatorType = 2;
_objRect = app.iconRect;
2022-10-27 14:06:11 +02:00
_deathFlag = nd.getType() == ContainerNode::kDeadType;
_containerSpriteImg = nullptr;
2021-05-17 20:47:39 +02:00
// setup the mass and weight indicator
if (_deathFlag) {
2021-05-17 20:47:39 +02:00
// set the decorations for this window
setDecorations(deathDecorations,
ARRAYSIZE(deathDecorations),
2021-05-17 20:47:39 +02:00
containerRes, 'F', 'R', 'M');
_massWeightIndicator = nullptr;
2021-05-17 20:47:39 +02:00
} else {
const StaticWindow *winDecs[] = {
brassDecorations,
clothDecorations,
steelDecorations,
woodDecorations
};
uint16 bgndType = _view->_containerObject->proto()->appearanceType;
2021-05-17 20:47:39 +02:00
2021-05-27 20:05:08 +02:00
assert(bgndType < 4);
2021-05-17 20:47:39 +02:00
setContainerSprite(); // show at the top of the box
// set the decorations for this window
2021-06-07 18:19:10 +02:00
setDecorations(winDecs[bgndType],
ARRAYSIZE(brassDecorations), // brass was arb, all should have same
2021-05-17 20:47:39 +02:00
containerRes, 'F', 'R', 'M');
// set the userdata such that we can extract the container object later
// through an appfunc.
this->_userData = _view->_containerObject;
2021-05-17 20:47:39 +02:00
_massWeightIndicator = new CMassWeightIndicator(
2021-05-17 20:47:39 +02:00
this,
Point16(app.massRect.x, app.massRect.y),
weightIndicatorType,
_deathFlag);
2021-05-17 20:47:39 +02:00
}
}
2021-09-11 12:13:35 +03:00
TangibleContainerWindow::~TangibleContainerWindow() {
if (_massWeightIndicator) delete _massWeightIndicator;
if (_containerSpriteImg) delete _containerSpriteImg;
2021-05-17 20:47:39 +02:00
}
2021-09-11 12:13:35 +03:00
void TangibleContainerWindow::setContainerSprite() {
2021-05-17 20:47:39 +02:00
// pointer to sprite data that will be drawn
Sprite *spr;
ProtoObj *proto = _view->_containerObject->proto();
2021-05-17 20:47:39 +02:00
Point16 sprPos;
char dummy = '\0';
// Get the address of the sprite.
2022-10-29 13:35:19 +02:00
spr = proto->getSprite(_view->_containerObject, ProtoObj::kObjInContainerView).sp;
2021-05-17 20:47:39 +02:00
sprPos.x = _objRect.x - (spr->size.x >> 1); //_objRect.x + ( spr->size.x >> 1 );
sprPos.y = _objRect.y - (spr->size.y >> 1);
2021-05-17 20:47:39 +02:00
_containerSpriteImg = new GfxSpriteImage(
2021-05-17 20:47:39 +02:00
*this,
Rect16(sprPos.x,
sprPos.y,
_objRect.height,
_objRect.width),
_view->_containerObject,
2021-05-17 20:47:39 +02:00
dummy,
0,
nullptr);
2021-05-17 20:47:39 +02:00
}
void TangibleContainerWindow::massBulkUpdate() {
if (_massWeightIndicator) { // Death container doesn't have MW indicator
2021-05-17 20:47:39 +02:00
// set the indicators to the correct mass and bulk
_massWeightIndicator->setMassPie(_view->_totalMass);
_massWeightIndicator->setBulkPie(_view->_totalBulk);
2021-05-17 20:47:39 +02:00
}
}
void TangibleContainerWindow::drawClipped(
gPort &port,
const Point16 &offset,
const Rect16 &clip) {
if (!_extent.overlap(clip)) return;
2021-05-17 20:47:39 +02:00
// draw the decorations
ScrollableContainerWindow::drawClipped(port, offset, clip);
}
/* ===================================================================== *
IntangibleContainerWindow member functions
* ===================================================================== */
IntangibleContainerWindow::IntangibleContainerWindow(
ContainerNode &nd, const ContainerAppearanceDef &app)
2021-05-17 20:47:39 +02:00
: ScrollableContainerWindow(nd, app, "MentalWindow") {
// make the button conected to this window
_mindSelectorCompButton = new GfxMultCompButton(
2021-05-17 20:47:39 +02:00
*this,
Rect16(49, 15 - 13, 52, 67),
containerRes,
'H', 'E', 'D', 1, 3, 1,
0,
cmdMindContainerFunc); // mind app func
assert(_mindSelectorCompButton != nullptr);
2021-05-17 20:47:39 +02:00
_mindSelectorCompButton->setResponse(false);
2021-05-17 20:47:39 +02:00
// set the decorations for this window
setDecorations(mentalDecorations,
ARRAYSIZE(mentalDecorations),
2021-05-17 20:47:39 +02:00
containerRes, 'F', 'R', 'M');
setMindContainer(nd._mindType, *this);
2021-05-17 20:47:39 +02:00
}
/* ===================================================================== *
EnchantmentContainerWindow member functions
* ===================================================================== */
EnchantmentContainerWindow::EnchantmentContainerWindow(
ContainerNode &nd, const ContainerAppearanceDef &app)
2021-05-17 20:47:39 +02:00
: ContainerWindow(nd, app, "EnchantmentWindow") {
_view = new EnchantmentContainerView(*this, nd, app);
2021-05-17 20:47:39 +02:00
// make the button conected to this window
_scrollCompButton = new GfxCompButton(
2021-05-17 20:47:39 +02:00
*this,
app.scrollRect, // rect for button
containerRes, // resource context
2021-06-07 18:19:10 +02:00
app.scrollResID[0], // resource handle name
app.scrollResID[1],
2021-05-17 20:47:39 +02:00
0,
cmdScrollFunc); // mind app func
assert(_view != nullptr);
assert(_scrollCompButton != nullptr);
2021-05-17 20:47:39 +02:00
}
/* ===================================================================== *
ContainerNode functions
* ===================================================================== */
2021-08-28 17:58:53 +09:00
ContainerNode::ContainerNode(ContainerManager &cl, ObjectID id, int typ) {
2021-05-17 20:47:39 +02:00
GameObject *obj = GameObject::objectAddress(id);
PlayerActorID ownerID;
// Convert the possessor() of the object to a player actor ID,
// if it is indeed a player actor; Else set to "nobody".
if (isActor(id)) {
2021-06-13 16:56:52 +02:00
if (actorIDToPlayerID(id, ownerID) == false)
2022-10-27 14:06:11 +02:00
ownerID = ContainerNode::kNobody;
2021-05-17 20:47:39 +02:00
} else {
ObjectID possessor = obj->possessor();
2021-06-13 16:56:52 +02:00
if (possessor == Nothing || actorIDToPlayerID(possessor, ownerID) == false)
2022-10-27 14:06:11 +02:00
ownerID = ContainerNode::kNobody;
2021-05-17 20:47:39 +02:00
}
// Compute the initial position of the container window
switch (typ) {
2022-10-27 14:06:11 +02:00
case kReadyType:
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kDeadType:
_position = deathContainerAppearance.defaultWindowPos;
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kMentalType:
_mindType = 0; //protoClassIdeaContainer;
_position = mentalContainerAppearance.defaultWindowPos;
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kPhysicalType:
_position = physicalContainerAppearance.defaultWindowPos;
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kEnchantType:
_position = enchantmentContainerAppearance.defaultWindowPos;
2021-05-17 20:47:39 +02:00
break;
}
// Fill in the initial values.
_window = nullptr;
_type = typ;
_object = id;
_owner = ownerID;
_action = 0;
_mindType = 0;
2021-05-17 20:47:39 +02:00
// Add to container list.
2021-07-03 07:44:13 +09:00
cl.add(this);
2021-05-17 20:47:39 +02:00
}
// Return the container window for a container node, if it is visible
2021-09-11 12:13:35 +03:00
ContainerWindow *ContainerNode::getWindow() {
return _window;
2021-05-17 20:47:39 +02:00
}
// Return the container view for a container node, if it is visible
2021-09-11 12:13:35 +03:00
ContainerView *ContainerNode::getView() {
return _window ? &_window->getView() : nullptr;
2021-05-17 20:47:39 +02:00
}
// Destructor
ContainerNode::~ContainerNode() {
// Close the container window.
hide();
// Remove from container list
2021-08-28 17:58:53 +09:00
g_vm->_cnm->remove(this);
2021-05-17 20:47:39 +02:00
}
void ContainerNode::read(Common::InSaveFile *in) {
// Restore fields
_object = in->readUint16LE();
_type = in->readByte();
_owner = in->readByte();
_position.read(in);
_mindType = in->readByte();
_window = nullptr;
_action = 0;
2021-07-15 21:07:09 +09:00
bool shown = in->readUint16LE();
// If this container was shown, re-show it
if (shown)
markForShow();
debugC(4, kDebugSaveload, "... object = %d", _object);
debugC(4, kDebugSaveload, "... type = %d", _type);
debugC(4, kDebugSaveload, "... owner = %d", _owner);
debugC(4, kDebugSaveload, "... position = (%d, %d, %d, %d)", _position.x, _position.y, _position.width, _position.height);
debugC(4, kDebugSaveload, "... _mindType = %d", _mindType);
debugC(4, kDebugSaveload, "... shown = %d", shown);
}
void ContainerNode::write(Common::MemoryWriteStreamDynamic *out) {
// Store fields
out->writeUint16LE(_object);
out->writeByte(_type);
out->writeByte(_owner);
_position.write(out);
out->writeByte(_mindType);
out->writeUint16LE(_window != nullptr);
debugC(4, kDebugSaveload, "... object = %d", _object);
debugC(4, kDebugSaveload, "... type = %d", _type);
debugC(4, kDebugSaveload, "... owner = %d", _owner);
debugC(4, kDebugSaveload, "... position = (%d, %d, %d, %d)", _position.x, _position.y, _position.width, _position.height);
debugC(4, kDebugSaveload, "... _mindType = %d", _mindType);
debugC(4, kDebugSaveload, "... shown = %d", _window != nullptr);
}
2021-05-17 20:47:39 +02:00
// Close the container window, but leave the node.
2021-09-11 12:13:35 +03:00
void ContainerNode::hide() {
2021-05-17 20:47:39 +02:00
// close the window, but don't close the object.
2022-10-27 14:06:11 +02:00
if (_type != kReadyType && _window != nullptr) {
_position = _window->getExtent(); // Save old window position
_window->close();
delete _window;
_window = nullptr;
2021-05-17 20:47:39 +02:00
}
}
// Open the cotainer window, given the node info.
2021-09-11 12:13:35 +03:00
void ContainerNode::show() {
ProtoObj *proto = GameObject::protoAddress(_object);
2021-05-17 20:47:39 +02:00
2021-05-27 20:05:08 +02:00
assert(proto);
2021-05-17 20:47:39 +02:00
// open the window; Object should already be "open"
if (_window == nullptr) {
switch (_type) {
2022-10-27 14:06:11 +02:00
case kPhysicalType:
2021-05-17 20:47:39 +02:00
physicalContainerAppearance.rows = proto->getViewableRows();
physicalContainerAppearance.cols = proto->getViewableCols();
physicalContainerAppearance.totRows = proto->getMaxRows();
_window = new TangibleContainerWindow(*this, physicalContainerAppearance);
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kDeadType:
2021-05-17 20:47:39 +02:00
deathContainerAppearance.rows = proto->getViewableRows();
deathContainerAppearance.cols = proto->getViewableCols();
deathContainerAppearance.totRows = proto->getMaxRows();
_window = new TangibleContainerWindow(*this, deathContainerAppearance);
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kMentalType:
_window = new IntangibleContainerWindow(*this, mentalContainerAppearance);
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kEnchantType:
_window = new EnchantmentContainerWindow(*this, enchantmentContainerAppearance);
2021-05-17 20:47:39 +02:00
break;
2022-10-27 14:06:11 +02:00
case kReadyType:
default:
return;
2021-05-17 20:47:39 +02:00
}
}
_window->open();
2021-05-17 20:47:39 +02:00
}
2021-09-11 12:13:35 +03:00
void ContainerNode::update() {
2022-10-27 14:06:11 +02:00
if (_type == kReadyType) {
2021-05-17 20:47:39 +02:00
// Update ready containers if they are enabled
if (TrioCviews[_owner]->getEnabled()) TrioCviews[_owner]->invalidate();
2021-05-17 20:47:39 +02:00
if (indivCviewTop->getEnabled()) indivCviewTop->invalidate();
if (indivCviewBot->getEnabled()) indivCviewBot->invalidate();
// If the container to update is the center brother's ready container.
if (isIndivMode() && _owner == getCenterActorPlayerID()) {
2021-05-17 20:47:39 +02:00
// Update player's mass & weight indicator...
MassWeightIndicator->update();
}
} else if (_window) {
2021-05-17 20:47:39 +02:00
getView()->invalidate();
_window->massBulkUpdate();
2021-05-17 20:47:39 +02:00
}
}
// Find a container node, given a specific object
2021-08-28 17:58:53 +09:00
ContainerNode *ContainerManager::find(ObjectID id) {
2021-07-03 07:44:13 +09:00
for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it)
if ((*it)->_object == id)
2021-07-03 07:44:13 +09:00
return *it;
return nullptr;
2021-05-17 20:47:39 +02:00
}
2021-08-28 17:58:53 +09:00
ContainerNode *ContainerManager::find(ObjectID id, int16 type) {
2021-07-03 07:44:13 +09:00
for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it)
if ((*it)->_object == id && (*it)->_type == type)
2021-07-03 07:44:13 +09:00
return *it;
return nullptr;
2021-05-17 20:47:39 +02:00
}
2021-06-13 16:56:52 +02:00
// returns true if the object represented by the container can be
2021-05-17 20:47:39 +02:00
// accessed by the player.
bool ContainerNode::isAccessable(ObjectID enactor) {
Actor *a = (Actor *)GameObject::objectAddress(enactor);
ObjectID holder;
GameObject *obj = GameObject::objectAddress(_object);
2021-05-17 20:47:39 +02:00
int32 dist;
// REM: We really ought to do a line-of-sight test here.
// Calculate distance between actor and container.
dist = (a->getLocation() - obj->getWorldLocation()).quickHDistance();
// If the container object is too far away we can't access any containers.
// Note: Actors are not considered to be in possession of themselves...
holder = obj->possessor();
if (holder != Nothing || isActor(_object)) {
2021-05-17 20:47:39 +02:00
// "Reach" for other players is further than for other objects
2021-07-06 21:39:48 +02:00
if (holder != a->thisID() && dist > 96)
return false;
} else if (dist > kMaxOpenDistance)
return false;
2021-05-17 20:47:39 +02:00
2021-06-13 16:56:52 +02:00
return true;
2021-05-17 20:47:39 +02:00
}
// Change the owner of a ready container (for indiv mode)
void ContainerNode::changeOwner(int16 newOwner) {
_owner = newOwner;
_object = getPlayerActorAddress(newOwner)->getActorID();
2021-05-17 20:47:39 +02:00
}
/* ===================================================================== *
2021-08-28 17:58:53 +09:00
ContainerManager functions
2021-05-17 20:47:39 +02:00
* ===================================================================== */
2021-08-28 17:58:53 +09:00
void ContainerManager::setPlayerNum(PlayerActorID playerNum) {
2021-05-17 20:47:39 +02:00
// Close all containers which are not on the ground and not owned
// by the current protagonist.
2021-07-03 07:44:13 +09:00
for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it) {
ContainerNode *n = *it;
2021-05-17 20:47:39 +02:00
2022-10-27 14:06:11 +02:00
if (n->_owner != ContainerNode::kNobody && n->_owner != playerNum)
2021-07-03 07:44:13 +09:00
n->hide();
2021-05-17 20:47:39 +02:00
}
// Open any containers which belong to the new protagonist.
2021-07-03 07:44:13 +09:00
for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it) {
ContainerNode *n = *it;
if (n->_owner == playerNum)
2021-07-03 07:44:13 +09:00
n->markForShow();
2021-05-17 20:47:39 +02:00
}
}
2021-09-11 12:13:35 +03:00
void ContainerManager::doDeferredActions() {
2021-07-03 07:44:13 +09:00
Common::List<ContainerNode *>::iterator nextIt;
2021-05-17 20:47:39 +02:00
Actor *a = getCenterActor();
TilePoint tp = a->getLocation();
GameObject *world = a->parent();
// Close all containers which are not on the ground and not owned
// by the current protagonist.
2021-07-03 07:44:13 +09:00
for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); it = nextIt) {
nextIt = it;
nextIt++;
ContainerNode *n = *it;
2021-05-17 20:47:39 +02:00
// If the object is not in a player inventory (i.e. on the ground)
2022-10-27 14:06:11 +02:00
if (n->_owner == ContainerNode::kNobody) {
2021-05-17 20:47:39 +02:00
// If the object is in a different world, or too far away
// from the protagonist, then quietly close the object.
GameObject *obj = GameObject::objectAddress(n->_object);
2021-05-17 20:47:39 +02:00
if (obj->world() != world
2021-07-06 21:39:48 +02:00
|| (obj->getWorldLocation() - tp).quickHDistance() > kMaxOpenDistance) {
2021-05-17 20:47:39 +02:00
// Close object image and window (silently)
2022-10-27 14:35:54 +02:00
obj->setFlags(0, kObjectOpen);
2021-05-17 20:47:39 +02:00
delete n;
continue;
}
}
2022-10-27 14:06:11 +02:00
if (n->_action & ContainerNode::kActionDelete) {
2021-05-17 20:47:39 +02:00
delete n;
continue;
}
2022-10-27 14:06:11 +02:00
if (n->_action & ContainerNode::kActionHide) {
2021-05-17 20:47:39 +02:00
n->hide();
} else {
2022-10-27 14:06:11 +02:00
if (n->_action & ContainerNode::kActionShow) n->show();
if (n->_action & ContainerNode::kActionUpdate) n->update();
2021-05-17 20:47:39 +02:00
}
n->_action = 0;
2021-05-17 20:47:39 +02:00
}
}
2021-08-28 17:58:53 +09:00
void ContainerManager::setUpdate(ObjectID id) {
2021-05-17 20:47:39 +02:00
// Close all containers which are not on the ground and not owned
// by the current protagonist.
2021-07-03 07:44:13 +09:00
for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it) {
ContainerNode *n = *it;
if (n->_object == id)
2021-07-03 07:44:13 +09:00
n->update();
2022-10-27 14:06:11 +02:00
else if (n->_type == ContainerNode::kMentalType // Special case for mind containers
&& n->_object == GameObject::objectAddress(id)->IDParent())
2021-05-17 20:47:39 +02:00
n->update();
}
}
extern int16 openMindType;
// General function to create a container "node". This determines what
// kind of container is appropriate, and also if a container of that
// type is already open.
ContainerNode *CreateContainerNode(ObjectID id, bool open, int16) {
ContainerNode *cn = nullptr;
2021-05-17 20:47:39 +02:00
GameObject *obj = GameObject::objectAddress(id);
PlayerActorID owner;
if (isActor(id)) {
2021-06-13 16:56:52 +02:00
if (actorIDToPlayerID(id, owner) == false)
2022-10-27 14:06:11 +02:00
owner = ContainerNode::kNobody;
2021-05-17 20:47:39 +02:00
if (((Actor *)obj)->isDead()) {
// Open dead container for dead actor
2022-10-27 14:06:11 +02:00
if (!(cn = g_vm->_cnm->find(owner, ContainerNode::kDeadType)))
cn = new ContainerNode(*g_vm->_cnm, id, ContainerNode::kDeadType);
} else if (owner != ContainerNode::kNobody) {
2021-05-17 20:47:39 +02:00
return OpenMindContainer(owner, open, /*mType*/ openMindType);
} else {
error("Attempt to open non-dead actor as a container");
2021-05-17 20:47:39 +02:00
}
} else {
2021-06-13 16:56:52 +02:00
if (actorIDToPlayerID(obj->possessor(), owner) == false)
2022-10-27 14:06:11 +02:00
owner = ContainerNode::kNobody;
2021-05-17 20:47:39 +02:00
2022-10-27 14:06:11 +02:00
if (!(cn = g_vm->_cnm->find(id, ContainerNode::kPhysicalType)))
cn = new ContainerNode(*g_vm->_cnm, id, ContainerNode::kPhysicalType);
2021-05-17 20:47:39 +02:00
}
// If node was successfully created, and we wanted it open, and the owner
2021-05-17 20:47:39 +02:00
// is the center actor or no-actor then make the container window visible.
if (cn != nullptr
2021-05-17 20:47:39 +02:00
&& open
2022-10-27 14:06:11 +02:00
&& (owner == getCenterActorID() || owner == ContainerNode::kNobody)) {
2021-05-17 20:47:39 +02:00
cn->show();
}
return cn;
}
ContainerNode *CreateReadyContainerNode(PlayerActorID player) {
2021-08-28 17:58:53 +09:00
return new ContainerNode(*g_vm->_cnm,
2021-05-17 20:47:39 +02:00
getPlayerActorAddress(player)->getActorID(),
2022-10-27 14:06:11 +02:00
ContainerNode::kReadyType);
2021-05-17 20:47:39 +02:00
}
ContainerNode *OpenMindContainer(PlayerActorID player, int16 open, int16 type) {
ContainerNode *cn;
ObjectID id = getPlayerActorAddress(player)->getActorID();
2022-10-27 14:06:11 +02:00
if (!(cn = g_vm->_cnm->find(id, ContainerNode::kMentalType))) {
cn = new ContainerNode(*g_vm->_cnm, id, ContainerNode::kMentalType);
cn->_mindType = type;
2021-05-17 20:47:39 +02:00
// If node was successfully created, and we wanted it open, and the owner
2021-05-17 20:47:39 +02:00
// is the center actor or no-actor then make the container window visible.
if (open && id == getCenterActorID()) {
2021-05-17 20:47:39 +02:00
cn->show();
}
} else {
IntangibleContainerWindow *cw = (IntangibleContainerWindow *)cn->getWindow();
if (cw && (type != cn->_mindType)) {
cn->_mindType = type;
setMindContainer(cn->_mindType, *cw);
2021-05-17 20:47:39 +02:00
cw->update(cw->getView().getExtent());
}
}
return cn;
}
/* ===================================================================== *
Misc. functions
* ===================================================================== */
2021-09-11 12:13:35 +03:00
void initContainers() {
2021-10-17 23:13:36 +03:00
if (g_vm->getGameId() == GID_DINO) {
warning("TODO: initContainers() for Dino");
return;
}
if (containerRes == nullptr)
containerRes = resFile->newContext(MKTAG('C', 'O', 'N', 'T'), "cont.resources");
2021-05-17 20:47:39 +02:00
2021-09-13 09:27:20 +09:00
g_vm->_cnm->_selImage = g_vm->_imageCache->requestImage(imageRes, MKTAG('A', 'M', 'N', 'T'));
2021-05-17 20:47:39 +02:00
}
2021-09-11 12:13:35 +03:00
void cleanupContainers() {
2021-09-13 09:27:20 +09:00
if (g_vm->_cnm->_selImage)
g_vm->_imageCache->releaseImage(g_vm->_cnm->_selImage);
if (containerRes)
resFile->disposeContext(containerRes);
2021-05-17 20:47:39 +02:00
g_vm->_cnm->_selImage = nullptr;
containerRes = nullptr;
2021-05-17 20:47:39 +02:00
}
2021-09-11 12:13:35 +03:00
void initContainerNodes() {
2021-05-17 20:47:39 +02:00
// Verify the globalContainerList only has ready ContainerNodes
Common::List<ContainerNode *>::iterator it;
2021-05-17 20:47:39 +02:00
for (it = g_vm->_cnm->_list.begin(); it != g_vm->_cnm->_list.end(); ++it) {
2022-10-27 14:06:11 +02:00
if ((*it)->getType() != ContainerNode::kReadyType) {
error("initContainerNodes: ContainerNode type not readyType (%d != %d)", (*it)->getType(), ContainerNode::kReadyType);
2021-05-17 20:47:39 +02:00
}
}
}
void saveContainerNodes(Common::OutSaveFile *outS) {
debugC(2, kDebugSaveload, "Saving Container Nodes");
int i = 0;
int16 numNodes = 0;
// Make sure there are no pending container view actions
2021-08-28 17:58:53 +09:00
g_vm->_cnm->doDeferredActions();
// Count the number of nodes to save
2021-08-28 17:58:53 +09:00
for (Common::List<ContainerNode *>::iterator it = g_vm->_cnm->_list.begin(); it != g_vm->_cnm->_list.end(); ++it) {
ContainerNode *n = *it;
2022-10-27 14:06:11 +02:00
if (n->getType() != ContainerNode::kReadyType)
numNodes++;
}
outS->write("CONT", 4);
CHUNK_BEGIN;
// Store the number of nodes to save
out->writeSint16LE(numNodes);
debugC(3, kDebugSaveload, "... numNodes = %d", numNodes);
// Store the nodes
2021-08-28 17:58:53 +09:00
for (Common::List<ContainerNode *>::iterator it = g_vm->_cnm->_list.begin(); it != g_vm->_cnm->_list.end(); ++it) {
ContainerNode *n = *it;
2022-10-27 14:06:11 +02:00
if (n->getType() != ContainerNode::kReadyType) {
2021-07-12 16:31:23 +09:00
debugC(3, kDebugSaveload, "Saving ContainerNode %d", i++);
n->write(out);
2021-07-12 16:31:23 +09:00
}
}
CHUNK_END;
}
void loadContainerNodes(Common::InSaveFile *in) {
debugC(2, kDebugSaveload, "Loading Container Nodes");
ContainerNode *node;
Common::List<ContainerNode *> tempList;
int16 numNodes;
// Read in the number of container nodes to restore
numNodes = in->readSint16LE();
debugC(3, kDebugSaveload, "... numNodes = %d", numNodes);
for (int i = 0; i < numNodes; i++) {
debugC(3, kDebugSaveload, "Loading ContainerNode %d", i);
node = new ContainerNode;
// Restore the state of the node
node->read(in);
// Add it back to the container list
2021-08-28 17:58:53 +09:00
g_vm->_cnm->add(node);
}
assert(tempList.empty());
}
2021-09-11 12:13:35 +03:00
void cleanupContainerNodes() {
2021-08-28 17:58:53 +09:00
if (g_vm->_cnm == nullptr)
return;
2021-07-15 23:34:13 +09:00
Common::Array<ContainerNode *> deletionArray;
2021-08-28 17:58:53 +09:00
for (Common::List<ContainerNode *>::iterator it = g_vm->_cnm->_list.begin(); it != g_vm->_cnm->_list.end(); ++it) {
2021-07-03 07:44:13 +09:00
ContainerNode *n = *it;
2021-05-17 20:47:39 +02:00
2022-10-27 14:06:11 +02:00
if (n->getType() != ContainerNode::kReadyType)
2021-07-15 23:34:13 +09:00
deletionArray.push_back(*it);
2021-05-17 20:47:39 +02:00
}
2021-07-15 23:34:13 +09:00
for (uint i = 0; i < deletionArray.size(); ++i)
delete deletionArray[i];
2021-05-17 20:47:39 +02:00
}
2021-09-11 12:13:35 +03:00
void updateContainerWindows() {
2021-10-17 23:13:36 +03:00
// TODO: updateContainerWindows() for Dino
if (g_vm->getGameId() == GID_DINO)
return;
2021-08-28 17:58:53 +09:00
g_vm->_cnm->doDeferredActions();
2021-05-17 20:47:39 +02:00
}
void setMindContainer(int index, IntangibleContainerWindow &cw) {
const int classTable[] = {
protoClassIdeaContainer,
protoClassSkillContainer,
protoClassMemoryContainer,
protoClassPsychContainer // Not used anymore
};
2021-05-17 20:47:39 +02:00
ObjectID ownerID = cw.getView()._node.getObject();
2021-05-17 20:47:39 +02:00
GameObject *object = GameObject::objectAddress(ownerID);
ContainerIterator iter(object);
GameObject *item;
ObjectID id;
2021-05-27 20:05:08 +02:00
assert(index >= 0);
assert(index < ARRAYSIZE(classTable));
2021-05-17 20:47:39 +02:00
2021-06-07 18:19:10 +02:00
int containerClass = classTable[index];
2021-05-17 20:47:39 +02:00
cw._mindSelectorCompButton->setCurrent(index);
cw._mindSelectorCompButton->invalidate();
2021-05-17 20:47:39 +02:00
while ((id = iter.next(&item)) != Nothing) {
if (item->proto()->classType == containerClass) {
cw._view->setContainer(item);
2021-05-17 20:47:39 +02:00
return;
}
}
}
APPFUNC(cmdMindContainerFunc) {
2022-10-29 13:41:51 +02:00
if (ev.panel && ev.eventType == kEventNewValue /* && ev.value */) {
2021-05-17 20:47:39 +02:00
IntangibleContainerWindow *cw = (IntangibleContainerWindow *)ev.window;
ContainerNode &nd = cw->getView()._node;
int newMindType = nd._mindType;
2021-05-17 20:47:39 +02:00
const Rect16 idea(0, 0, 22, 67), // idea button click area
skill(22, 0, 11, 67), // skill area
memory(33, 0, 9, 67), // memory area
psych(42, 0, 10, 67); // psych(ic?) area
2021-05-17 20:47:39 +02:00
if (idea.ptInside(ev.mouse)) newMindType = 0; //protoClassIdeaContainer;
if (skill.ptInside(ev.mouse)) newMindType = 1; //protoClassSkillContainer;
if (memory.ptInside(ev.mouse)) newMindType = 2; //protoClassMemoryContainer;
// if (psych.ptInside(ev.mouse)) newMindType = protoClassPsychContainer;
if (newMindType != nd._mindType) {
nd._mindType = newMindType;
setMindContainer(nd._mindType, *cw);
2021-05-17 20:47:39 +02:00
cw->update(cw->getView().getExtent());
}
2022-10-29 13:41:51 +02:00
} else if (ev.eventType == kEventMouseMove) {
2021-05-17 20:47:39 +02:00
//if (ev.value == gCompImage::enter)
{
const Rect16 idea(0, 0, 22, 67), // idea button click area
skill(22, 0, 11, 67), // skill area
memory(33, 0, 9, 67); // memory area
2021-05-17 20:47:39 +02:00
const int BUF_SIZE = 64;
2021-06-07 18:19:10 +02:00
char textBuffer[BUF_SIZE];
int _mindType = -1;
2021-05-17 20:47:39 +02:00
if (idea.ptInside(ev.mouse)) _mindType = 0; //protoClassIdeaContainer;
if (skill.ptInside(ev.mouse)) _mindType = 1; //protoClassSkillContainer;
if (memory.ptInside(ev.mouse)) _mindType = 2; //protoClassMemoryContainer;
2021-05-17 20:47:39 +02:00
switch (_mindType) {
2021-05-17 20:47:39 +02:00
case 0:
Common::sprintf_s(textBuffer, IDEAS_MENTAL);
2021-05-17 20:47:39 +02:00
break;
case 1:
Common::sprintf_s(textBuffer, SPELL_MENTAL);
2021-05-17 20:47:39 +02:00
break;
case 2:
Common::sprintf_s(textBuffer, SKILL_MENTAL);
2021-05-17 20:47:39 +02:00
break;
case -1:
2021-06-23 12:46:02 +02:00
textBuffer[0] = 0;
2021-05-17 20:47:39 +02:00
break;
default:
2021-06-13 16:56:52 +02:00
assert(false); // should never get here
2021-05-17 20:47:39 +02:00
break;
}
// set the text in the cursor
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->setText(textBuffer);
2021-05-17 20:47:39 +02:00
}
2022-10-27 13:37:08 +02:00
if (ev.value == GfxCompImage::kLeave) {
g_vm->_mouseInfo->setText(nullptr);
2021-05-17 20:47:39 +02:00
}
}
}
APPFUNC(cmdCloseButtonFunc) {
2022-10-29 13:41:51 +02:00
if (ev.eventType == kEventNewValue && ev.value == 1) {
2021-05-17 20:47:39 +02:00
ContainerWindow *win = (ContainerWindow *)ev.window;
2022-10-27 14:06:11 +02:00
if (win->getView()._node.getType() == ContainerNode::kMentalType) {
win->getView()._node.markForDelete();
2021-05-17 20:47:39 +02:00
} else {
win->containerObject()->close(getCenterActorID());
}
updateContainerWindows();
// make sure the hint text goes away
if (g_vm->_mouseInfo->getObject() == nullptr) {
g_vm->_mouseInfo->setText(nullptr);
2021-05-17 20:47:39 +02:00
}
2022-10-29 13:41:51 +02:00
} else if (ev.eventType == kEventMouseMove) {
2022-10-27 13:37:08 +02:00
if (ev.value == GfxCompImage::kEnter) {
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->setText(CLOSE_MOUSE);
2022-10-27 13:37:08 +02:00
} else if (ev.value == GfxCompImage::kLeave) {
g_vm->_mouseInfo->setText(nullptr);
2021-05-17 20:47:39 +02:00
}
}
}
APPFUNC(cmdScrollFunc) {
2022-10-29 13:41:51 +02:00
if (ev.panel && ev.eventType == kEventNewValue && ev.value) {
ScrollableContainerWindow *cw;
const Rect16 upArea(0, 0, 44, 22);
2021-05-17 20:47:39 +02:00
cw = (ScrollableContainerWindow *)ev.window;
if (upArea.ptInside(ev.mouse))
cw->scrollUp();
else
cw->scrollDown();
ev.window->update(cw->getView().getExtent());
2022-10-29 13:41:51 +02:00
} else if (ev.eventType == kEventMouseMove) {
2022-10-27 13:37:08 +02:00
if (ev.value == GfxCompImage::kEnter) {
2021-07-01 05:05:03 +09:00
g_vm->_mouseInfo->setText(SCROLL_MOUSE);
2022-10-27 13:37:08 +02:00
} else if (ev.value == GfxCompImage::kLeave) {
g_vm->_mouseInfo->setText(nullptr);
2021-05-17 20:47:39 +02:00
}
}
}
} // end of namespace Saga2