Files
2026-02-02 04:50:13 +01:00

741 lines
24 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_WORLD_ITEM_H
#define ULTIMA8_WORLD_ITEM_H
#include "ultima/ultima8/kernel/object.h"
#include "ultima/ultima8/gfx/shape_info.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/box.h"
#include "ultima/ultima8/misc/point3.h"
#include "ultima/ultima8/misc/direction.h"
namespace Ultima {
namespace Ultima8 {
class Container;
class ShapeInfo;
class Shape;
class Gump;
class GravityProcess;
class Item : public Object {
friend class ItemFactory;
public:
Item();
~Item() override;
ENABLE_RUNTIME_CLASSTYPE()
//! Get the Container this Item is in, if any. (0 if not in a Container)
ObjId getParent() const {
return _parent;
}
//! Set the parent container of this item.
void setParent(ObjId p) {
_parent = p;
}
//! Get the Container this Item is in, if any. (NULL if not in a Container)
Container *getParentAsContainer() const;
//! Get the top-most Container this Item is in, if any. (NULL if not in a Container)
Container *getRootContainer() const;
//! Get the top-most Container this Item is in, or the Item itself if not
//! in a container
const Item *getTopItem() const;
//! Set item location. This strictly sets the location, and does not
//! even update CurrentMap
void setLocation(int32 x, int32 y, int32 z); // this only sets the loc.
void setLocation(const Point3 &pt); // this only sets the loc.
//! Move an item. This moves an item to the new location, and updates
//! CurrentMap and fastArea if necessary.
virtual void move(int32 x, int32 y, int32 z);
//! Move, but with a point struct.
void move(const Point3 &pt);
//! Move an item. This moves an item to a container and updates
//! CurrentMap and fastArea if necessary.
//! \param container The container this item should be placed in
//! \return true if item was moved, false if failed
bool moveToContainer(Container *container, bool checkwghtvol = false);
//! Move an item to the Ethereal Void
void moveToEtherealVoid();
//! Move an item out of the Ethereal Void to where it originally was
void returnFromEtherealVoid();
//! Check if moving this item is stealing; call AvatarStoleSomething if so
void movedByPlayer();
//! Get the location of the top-most container this Item is in, or
//! this Item's location if not in a container.
Point3 getLocationAbsolute() const;
//! Get this Item's location. Note that this does not return
//! 'usable' coordinates if the Item is contained or equipped.
inline Point3 getLocation() const {
return Point3(_x, _y, _z);
}
//! Get this Item's Z coordinate.
int32 getZ() const;
//! Set this Item's Z coordinate
void setZ(int32 z) {
_z = z;
}
//! Get this Item's location in a ContainerGump. Undefined if the Item
//! is not in a Container.
void getGumpLocation(int32 &x, int32 &y) const;
//! Set the Item's location in a ContainerGump. NOP if the Item
//! is not in a Container.
void setGumpLocation(int32 x, int32 y);
//! Randomize the Item's location in a ContainerGump. Effectively
//! this sets the coordinates to (255,255) and lets the ContainerGump
//! randomize the position when it is next opened.
void randomGumpLocation();
//! Get the world coordinates of the Item's centre. Undefined if the Item
//! is contained or equipped.
Point3 getCentre() const;
//! Get the size of this item's 3D bounding box, in world coordinates.
inline void getFootpadWorld(int32 &x, int32 &y, int32 &z) const;
//! Get the size of this item's 3D bounding box, scaled as in the datafiles
//! (i.e., the dimensions are not in the same unit as world coordinates!)
inline void getFootpadData(int32 &x, int32 &y, int32 &z) const;
//! Get the Box this item occupies in the world. Undef if item is contained
Box getWorldBox() const;
//! Get all flags
inline uint16 getFlags() const {
return _flags;
}
//! Does this item have any of the given flags mask set
inline bool hasFlags(uint16 flags) const {
return (_flags & flags) != 0;
}
//! Set the flags set in the given mask.
void setFlag(uint32 mask) {
_flags |= mask;
}
virtual void setFlagRecursively(uint32 mask) {
setFlag(mask);
}
//! Clear the flags set in the given mask.
void clearFlag(uint32 mask) {
_flags &= ~mask;
}
//! Set _extendedFlags
void setExtFlags(uint32 f) {
_extendedFlags = f;
}
//! Get _extendedFlags
inline uint32 getExtFlags() const {
return _extendedFlags;
}
//! Does item have any of the given extended flags
inline bool hasExtFlags(uint32 flags) const {
return (_extendedFlags & flags) != 0;
}
//! Set the _extendedFlags set in the given mask.
void setExtFlag(uint32 mask) {
_extendedFlags |= mask;
}
//! Clear the _extendedFlags set in the given mask.
void clearExtFlag(uint32 mask) {
_extendedFlags &= ~mask;
}
//! Get this Item's shape number
uint32 getShape() const {
return _shape;
}
//! Set this Item's shape number
void setShape(uint32 shape);
//! Get this Item's frame number
uint32 getFrame() const {
return _frame;
}
//! Set this Item's frame number
void setFrame(uint32 frame) {
_frame = frame;
}
//! Get this Item's quality (a.k.a. 'Q')
uint16 getQuality() const {
return _quality;
}
//! Set this Item's quality (a.k.a 'Q');
void setQuality(uint16 quality) {
_quality = quality;
}
//! Get the 'NpcNum' of this Item. Note that this can represent various
//! things depending on the family of this Item.
uint16 getNpcNum() const {
return _npcNum;
}
//! Set the 'NpcNum' of this Item. Note that this can represent various
//! things depending on the family of this Item.
void setNpcNum(uint16 npcnum) {
_npcNum = npcnum;
}
//! Get the 'MapNum' of this Item. Note that this can represent various
//! things depending on the family of this Item.
uint16 getMapNum() const {
return _mapNum;
}
//! Set the 'MapNum' of this Item. Note that this can represent various
//! things depending on the family of this Item.
void setMapNum(uint16 mapnum) {
_mapNum = mapnum;
}
//! Get the ShapeInfo object for this Item. (The pointer will be cached.)
inline const ShapeInfo *getShapeInfo() const;
//! Get the ShapeInfo object for this Item from the game instance.
virtual const ShapeInfo *getShapeInfoFromGameInstance() const;
//! Get the Shape object for this Item. (The pointer will be cached.)
const Shape *getShapeObject() const;
//! Get the family of the shape number of this Item. (This is a
//! member of the ShapeInfo object.)
uint16 getFamily() const;
//! Check if we can merge with another item.
bool canMergeWith(const Item *other) const;
//! Get the open ContainerGump for this Item, if any. (NULL if not open.)
ObjId getGump() const {
return _gump;
}
//! Call this to notify the Item's open Gump has closed.
void clearGump(); // set gump to 0 and clear the GUMP_OPEN flag
//! Open a gump with the given shape for this Item
ObjId openGump(uint32 gumpshape);
//! Close this Item's gump, if any
void closeGump();
ProcId bark(const Std::string &msg);
//! Call this to notify the Item's open bark has closed.
void clearBark(); // set bark to 0
//! Close this Item's bark, if any
void closeBark();
//! Destroy self.
virtual void destroy(bool delnow = false);
//! Check if this item overlaps another item in 3D world-space
bool overlaps(const Item &item2) const;
//! Check if this item overlaps another item in the xy dims in 3D space
bool overlapsxy(const Item &item2) const;
//! Check if this item is on top of another item
bool isOn(const Item &item2) const;
//! Check if this item is on completely on top of another item
bool isCompletelyOn(const Item &item2) const;
//! Check if the centre of this item is on top of another item
bool isCentreOn(const Item &item2) const;
//! Check if the item is currently entirely visible on screen
bool isOnScreen() const;
//! Check if the item is currently partly visible on screen
bool isPartlyOnScreen() const;
//! Check if this item can exist at the given coordinates
bool canExistAt(int32 x, int32 y, int32 z, bool needsupport = false) const;
bool canExistAt(const Point3 &pt, bool needsupport = false) const;
//! Get direction from centre to another item's centre.
//! Undefined if either item is contained or equipped.
Direction getDirToItemCentre(const Item &item2) const;
//! Same as above, but from a fixed point.
Direction getDirToItemCentre(const Point3 &pt) const;
//! get 'distance' to other item. This is the maximum of the differences
//! between the x, y (and possibly z) coordinates of the items.
int getRange(const Item &item2, bool checkz = false) const;
//! get 'distance' to other item if it's visible (ie, there's nothing blocking the path)
int getRangeIfVisible(const Item &item2) const;
//! Check if this item can reach another item. (This includes LoS.)
//! \param other item to be reached
//! \param range range
//! \param x x coordinate of other to use, If zero, use real coords.
//! \param y y coordinate of other to use
//! \param z z coordinate of other to use.
bool canReach(const Item *other, int range, int32 x = 0, int32 y = 0, int32 z = 0) const;
//! Move the object to (x,y,z) colliding with objects in the way.
//! \param teleport move without colliding with objects between source and
//! destination
//! \param force force the object to get to the destination without being
//! blocked by solid objects
//! \param hititem if non-NULL, this is set to (one of) the item(s)
//! blocking the movement, or to zero if nothing blocked it
//! \param dirs if non-NULL, this is set to a bitmask of the x/y/z
// directions in which movement was blocked (bit 0=x,1=y,2=z)
//! \returns 0-0x4000 representing how far it got.
//! 0 = didn't move
//! 0x4000 = reached destination
//! \note This can destroy the object
virtual int32 collideMove(int32 x, int32 y, int32 z, bool teleport, bool force,
ObjId *hititem = 0, uint8 *dirs = 0);
//! Make the item move up (delta>0) or down (delta<0),
//! including any items on top of it
//! \param delta distance in Z-direction to move
//! \returns 0-0x4000 representing how far it got.
//! 0 = didn't move
//! 0x4000 = reached destination
int32 ascend(int delta);
//! Make the item fall down.
//! This creates a GravityProcess to do the actual work if the Item
//! doesn't already have one.
void fall();
//! Make any items on top of this Item fall down and notify any supporting
//! items that we're gone by calling the 'release' event.
//! Note that this Item has to be moved away right after calling grab(),
//! since otherwise the items will immediately hit this Item again.
void grab();
//! Hurl the item in the given direction
void hurl(int xs, int ys, int zs, int grav);
//! Set the PID of the GravityProcess for this Item. There should be only one.
void setGravityPID(ProcId pid) {
assert(_gravityPid == 0 || pid == 0);
_gravityPid = pid;
}
//! Get the PID of the GravityProcess for this Item (or 0)
ProcId getGravityPID() const {
return _gravityPid;
}
//! Get the GravityProcess of this Item, creating it if necessary
virtual GravityProcess *ensureGravityProcess();
//! Get the weight of this Item
virtual uint32 getWeight() const;
//! Get the weight of this Item and its contents, if any
virtual uint32 getTotalWeight() const;
//! Get the volume this item takes up in a container
virtual uint32 getVolume() const;
//! explode with explosion type (0,1,2), whether to destroy the item,
//! and whether to cause splash damage.
void explode(int explosion_type, bool destroy_item, bool cause_damage = true);
//! get the damage type this object does when hitting something
virtual uint16 getDamageType() const;
//! receive a hit
//! \param other The item delivering the hit
//! \param dir The direction the hit is coming from (or inverse? CHECKME!)
//! \param damage The force of the hit. Zero for default
//! \param type The type of damage done. Zero for default
virtual void receiveHit(ObjId other, Direction dir, int damage, uint16 type);
//! fire the given weapon type in the given direction from location x, y, z.
uint16 fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype, bool findtarget);
//! get the distance (in map tiles) if we were to fire in this direction to "other"
//! and could hit, otherwise return 0.
uint16 fireDistance(const Item *other, Direction dir, int16 xoff, int16 yoff, int16 zoff) const;
//! get damage points, used in Crusader for item damage.
uint8 getDamagePoints() const {
return _damagePoints;
}
//! set damage points, used in Crusader for item damage.
void setDamagePoints(uint8 points) {
_damagePoints = points;
}
//! Get the right Z which an attacker should aim for, given the attacker's z.
//! (Crusader only)
int32 getTargetZRelativeToAttackerZ(int32 attackerz) const;
//! count nearby objects of a given shape
unsigned int countNearby(uint32 shape, uint16 range) const;
//! can this item be dragged?
bool canDrag() const;
//! how far can this item be thrown?
//! \return range, or 0 if item can't be thrown
int getThrowRange() const;
//! Check this Item against the given loopscript
//! \param script The loopscript to run
//! \param scriptsize The size (in bytes) of the loopscript
//! \return true if the item matches, false otherwise
bool checkLoopScript(const uint8 *script, uint32 scriptsize) const;
uint32 callUsecodeEvent_look(); // event 0
uint32 callUsecodeEvent_use(); // event 1
uint32 callUsecodeEvent_anim(); // event 2
uint32 callUsecodeEvent_cachein(); // event 4
uint32 callUsecodeEvent_hit(ObjId hitted, int16 hitforce); // event 5
uint32 callUsecodeEvent_gotHit(ObjId hitter, int16 hitforce);// event 6
uint32 callUsecodeEvent_hatch(); // event 7
uint32 callUsecodeEvent_schedule(uint32 time); // event 8
uint32 callUsecodeEvent_release(); // event 9
uint32 callUsecodeEvent_equip(); // event A
uint32 callUsecodeEvent_equipWithParam(ObjId param); // event A
uint32 callUsecodeEvent_unequip(); // event B
uint32 callUsecodeEvent_unequipWithParam(ObjId param); // event B
uint32 callUsecodeEvent_combine(); // event C
uint32 callUsecodeEvent_calledFromAnim(); // event E
uint32 callUsecodeEvent_enterFastArea(); // event F
uint32 callUsecodeEvent_leaveFastArea(); // event 10
uint32 callUsecodeEvent_cast(uint16 unk); // event 11
uint32 callUsecodeEvent_justMoved(); // event 12
uint32 callUsecodeEvent_AvatarStoleSomething(uint16 unk); // event 14
uint32 callUsecodeEvent_guardianBark(int16 unk); // event 15 (Ultima)
uint32 callUsecodeEvent_unhatch(); // event 15 (Crusader)
uint32 use();
//! Get lerped location.
inline Point3 getLerped() const {
return Point3(_ix, _iy, _iz);
}
//! Do lerping for an in between frame (0-256)
//! The result can be retrieved with getLerped(x,y,z)
//! \param factor The lerp factor: 0 is start of move, 256 is end of move
inline void doLerp(int32 factor) {
// Should be noted that this does indeed limit us to 'only' 24bit coords
// not that it matters because on disk they are unsigned 16 bit
if (factor == 256) {
_ix = _lNext._x;
_iy = _lNext._y;
_iz = _lNext._z;
} else if (factor == 0) {
_ix = _lPrev._x;
_iy = _lPrev._y;
_iz = _lPrev._z;
} else {
#if 1
// This way while possibly slower is more accurate
_ix = ((_lPrev._x * (256 - factor) + _lNext._x * factor) >> 8);
_iy = ((_lPrev._y * (256 - factor) + _lNext._y * factor) >> 8);
_iz = ((_lPrev._z * (256 - factor) + _lNext._z * factor) >> 8);
#else
_ix = _lPrev.x + (((_lNext.x - _lPrev.x) * factor) >> 8);
_iy = _lPrev.y + (((_lNext.y - _lPrev.y) * factor) >> 8);
_iz = _lPrev.z + (((_lNext.z - _lPrev.z) * factor) >> 8);
#endif
}
}
//! Setup the lerped info for this gametick and animate the item
void setupLerp(int32 gametick);
//! The item has entered the fast area
virtual uint32 enterFastArea();
//! The item has left the fast area
//! \note This can destroy the object
virtual void leaveFastArea();
//! dump some info about this item to a string
Common::String dumpInfo() const override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
// Intrinsics
INTRINSIC(I_touch);
INTRINSIC(I_getX);
INTRINSIC(I_getY);
INTRINSIC(I_getZ);
INTRINSIC(I_getCX);
INTRINSIC(I_getCY);
INTRINSIC(I_getCZ);
INTRINSIC(I_getPoint);
INTRINSIC(I_getShape);
INTRINSIC(I_setShape);
INTRINSIC(I_getFrame);
INTRINSIC(I_setFrame);
INTRINSIC(I_getQuality);
INTRINSIC(I_getUnkEggType);
INTRINSIC(I_setUnkEggType);
INTRINSIC(I_getQuantity);
INTRINSIC(I_getContainer);
INTRINSIC(I_getRootContainer);
INTRINSIC(I_getQ);
INTRINSIC(I_getQHi);
INTRINSIC(I_getQLo);
INTRINSIC(I_setQ);
INTRINSIC(I_setQHi);
INTRINSIC(I_setQLo);
INTRINSIC(I_setQuality);
INTRINSIC(I_setQuantity);
INTRINSIC(I_setQAndCombine);
INTRINSIC(I_getFamily);
INTRINSIC(I_getTypeFlag);
INTRINSIC(I_getStatus);
INTRINSIC(I_orStatus);
INTRINSIC(I_andStatus);
INTRINSIC(I_getFootpadData);
INTRINSIC(I_overlaps);
INTRINSIC(I_overlapsXY);
INTRINSIC(I_isOn);
INTRINSIC(I_isCompletelyOn);
INTRINSIC(I_isCentreOn);
INTRINSIC(I_isInNpc);
INTRINSIC(I_ascend);
INTRINSIC(I_getWeight);
INTRINSIC(I_getWeightIncludingContents);
INTRINSIC(I_getVolume);
INTRINSIC(I_bark);
INTRINSIC(I_getMapArray);
INTRINSIC(I_setMapArray);
INTRINSIC(I_getNpcNum);
INTRINSIC(I_setNpcNum);
INTRINSIC(I_getDirToCoords);
INTRINSIC(I_getDirFromCoords);
INTRINSIC(I_getDirToItem);
INTRINSIC(I_getDirFromItem);
INTRINSIC(I_getDirFromTo16);
INTRINSIC(I_getClosestDirectionInRange);
INTRINSIC(I_look);
INTRINSIC(I_use);
INTRINSIC(I_gotHit);
INTRINSIC(I_enterFastArea);
INTRINSIC(I_cast);
INTRINSIC(I_ask);
INTRINSIC(I_getSliderInput);
INTRINSIC(I_openGump);
INTRINSIC(I_closeGump);
INTRINSIC(I_create);
INTRINSIC(I_legalCreateAtPoint);
INTRINSIC(I_legalCreateAtCoords);
INTRINSIC(I_legalCreateInCont);
INTRINSIC(I_push);
INTRINSIC(I_pop);
INTRINSIC(I_popToCoords);
INTRINSIC(I_popToContainer);
INTRINSIC(I_popToEnd);
INTRINSIC(I_destroy);
INTRINSIC(I_move);
INTRINSIC(I_legalMoveToPoint);
INTRINSIC(I_legalMoveToContainer);
INTRINSIC(I_hurl);
INTRINSIC(I_shoot);
INTRINSIC(I_fall);
INTRINSIC(I_grab);
INTRINSIC(I_igniteChaos);
INTRINSIC(I_getFamilyOfType);
INTRINSIC(I_getEtherealTop);
INTRINSIC(I_guardianBark);
INTRINSIC(I_getSurfaceWeight);
INTRINSIC(I_isExplosive);
INTRINSIC(I_receiveHit);
INTRINSIC(I_explode);
INTRINSIC(I_canReach);
INTRINSIC(I_getRange);
INTRINSIC(I_getRangeIfVisible);
INTRINSIC(I_isCrusTypeNPC);
INTRINSIC(I_setBroken);
INTRINSIC(I_inFastArea);
INTRINSIC(I_equip);
INTRINSIC(I_unequip);
INTRINSIC(I_avatarStoleSomething);
INTRINSIC(I_isPartlyOnScreen);
INTRINSIC(I_fireWeapon);
INTRINSIC(I_fireDistance);
private:
uint32 _shape; // DO NOT modify this directly! Always use setShape()!
protected:
uint32 _frame;
int32 _x, _y, _z; // world coordinates
uint16 _flags;
uint16 _quality;
uint16 _npcNum;
uint16 _mapNum;
uint32 _extendedFlags; // pentagram's own flags
ObjId _parent; // objid container this item is in (or 0 for top-level items)
mutable const Shape *_cachedShape;
mutable const ShapeInfo *_cachedShapeInfo;
// This is stuff that is used for displaying and interpolation
struct Lerped {
Lerped() : _x(0), _y(0), _z(0), _shape(0), _frame(0) {};
int32 _x, _y, _z;
uint32 _shape, _frame;
};
Lerped _lPrev; // Previous state (relative to camera)
Lerped _lNext; // Next (current) state (relative to camera)
int32 _ix, _iy, _iz; // Interpolated position in camera space
ObjId _gump; // Item's container gump
ObjId _bark; // Item's bark gump
ProcId _gravityPid; // Item's GravityTracker (or 0)
uint8 _damagePoints; // Damage points, used for item damage in Crusader
//! True if this is a Robot shape (in a fixed list)
bool isRobotCru() const;
//! Scale a received damage value based on the current difficulty level
//! and the type of object this is.
int scaleReceivedDamageCru(int damage, uint16 type) const;
private:
//! Call a Usecode Event. Use the separate functions instead!
uint32 callUsecodeEvent(uint32 event, const uint8 *args = 0, int argsize = 0);
//! The gametick setupLerp was last called on
int32 _lastSetup;
//! Animate the item (called by setupLerp)
void animateItem();
//! The U8 version of receiveHit
void receiveHitU8(ObjId other, Direction dir, int damage, uint16 type);
//! The Crusader version of receiveHit
void receiveHitCru(ObjId other, Direction dir, int damage, uint16 type);
public:
enum statusflags {
FLG_DISPOSABLE = 0x0002, //!< Item is discarded on map change
FLG_OWNED = 0x0004, //!< Item is owned by avatar
FLG_CONTAINED = 0x0008, //!< Item is in a container
FLG_INVISIBLE = 0x0010, //!< Item is invisible
FLG_FLIPPED = 0x0020, //!< Item is flipped horizontally
FLG_IN_NPC_LIST = 0x0040, //!< Item is a NPC
FLG_FAST_ONLY = 0x0080, //!< Item is discarded when leaving fast area
FLG_GUMP_OPEN = 0x0100, //!< Item has a gump open
FLG_EQUIPPED = 0x0200, //!< Item is equipped
FLG_BOUNCING = 0x0400, //!< Item has bounced
FLG_ETHEREAL = 0x0800, //!< Item is in the ethereal list - confirmed same meaning in crusader
FLG_HANGING = 0x1000, //!< Item is suspended in the air
FLG_FASTAREA = 0x2000, //!< Item is in the fast area
FLG_LOW_FRICTION = 0x4000, //!< Item has low friction
FLG_BROKEN = 0x8000 //!< Item is broken - Crusader only - broken items are not targetable.
};
enum extflags {
EXT_FIXED = 0x0001, //!< Item came from FIXED
EXT_INCURMAP = 0x0002, //!< Item is in a CurrentMap display list
EXT_LERP_NOPREV = 0x0008, //!< Item can't be lerped this frame
EXT_HIGHLIGHT = 0x0010, //!< Item should be Painted highlighted
EXT_CAMERA = 0x0020, //!< Item is being followed by the camera
EXT_SPRITE = 0x0040, //!< Item is a sprite
EXT_TRANSPARENT = 0x0080, //!< Item should be painted transparent
EXT_PERMANENT_NPC = 0x0100, //!< Item is a permanent NPC
EXT_TARGET = 0x0200, //!< Item is the current reticle target in Crusader
EXT_FEMALE = 0x8000 //!< Item is Crusader Female NPC (controls sfx)
};
// easter egg as in original: items stack to max quantity of 666
static const int MAX_QUANTITY = 666;
};
inline const ShapeInfo *Item::getShapeInfo() const {
if (!_cachedShapeInfo)
_cachedShapeInfo = getShapeInfoFromGameInstance();
return _cachedShapeInfo;
}
inline void Item::getFootpadData(int32 &X, int32 &Y, int32 &Z) const {
const ShapeInfo *si = getShapeInfo();
Z = si->_z;
if (_flags & Item::FLG_FLIPPED) {
X = si->_y;
Y = si->_x;
} else {
X = si->_x;
Y = si->_y;
}
}
// like getFootpadData, but scaled to world coordinates
inline void Item::getFootpadWorld(int32 &X, int32 &Y, int32 &Z) const {
const ShapeInfo *si = getShapeInfo();
si->getFootpadWorld(X, Y, Z, _flags & Item::FLG_FLIPPED);
}
} // End of namespace Ultima8
} // End of namespace Ultima
#endif