scummvm/engines/stark/resources/item.cpp
2021-12-26 21:19:38 +01:00

1125 lines
29 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/>.
*
*/
#include "engines/stark/resources/item.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/model/animhandler.h"
#include "engines/stark/movement/movement.h"
#include "engines/stark/visual/actor.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animhierarchy.h"
#include "engines/stark/resources/bonesmesh.h"
#include "engines/stark/resources/bookmark.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/resources/floorface.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/pattable.h"
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/textureset.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h"
#include "engines/stark/services/userinterface.h"
#include "engines/stark/services/settings.h"
namespace Stark {
namespace Resources {
Object *Item::construct(Object *parent, byte subType, uint16 index, const Common::String &name) {
switch (subType) {
case kItemGlobalTemplate:
return new GlobalItemTemplate(parent, subType, index, name);
case kItemInventory:
return new InventoryItem(parent, subType, index, name);
case kItemLevelTemplate:
return new LevelItemTemplate(parent, subType, index, name);
case kItemStaticProp:
case kItemAnimatedProp:
return new FloorPositionedImageItem(parent, subType, index, name);
case kItemBackgroundElement:
case kItemBackground:
return new ImageItem(parent, subType, index, name);
case kItemModel:
return new ModelItem(parent, subType, index, name);
default:
error("Unknown item subtype %d", subType);
}
}
Item::Item(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_enabled(true),
_characterIndex(0),
_movement(nullptr),
_movementSuspendedScript(nullptr) {
_type = TYPE;
}
Item::~Item() {
delete _movement;
}
void Item::readData(Formats::XRCReadStream *stream) {
_enabled = stream->readBool();
_characterIndex = stream->readSint32LE();
}
void Item::onGameLoop() {
Object::onGameLoop();
if (_enabled && _movement) {
_movement->onGameLoop();
if (_movement && _movement->hasEnded()) {
setMovement(nullptr);
}
}
}
bool Item::isEnabled() const {
return _enabled;
}
void Item::setEnabled(bool enabled) {
if (_enabled && !enabled) {
setMovement(nullptr);
}
_enabled = enabled;
}
int32 Item::getCharacterIndex() const {
return _characterIndex;
}
Gfx::RenderEntry *Item::getRenderEntry(const Common::Point &positionOffset) {
return nullptr;
}
Movement *Item::getMovement() const {
return _movement;
}
void Item::setMovement(Movement *movement) {
if (_movementSuspendedScript) {
if (_movement && _movement->hasReachedDestination()) {
_movementSuspendedScript->setResumeStatus(Script::kResumeComplete);
} else {
_movementSuspendedScript->setResumeStatus(Script::kResumeAbort);
}
_movementSuspendedScript = nullptr;
}
if (_movement && !_movement->hasEnded()) {
_movement->stop(true);
}
delete _movement;
_movement = movement;
}
void Item::setMovementSuspendedScript(Script *script) {
_movementSuspendedScript = script;
}
void Item::printData() {
debug("enabled: %d", _enabled);
debug("character: %d", _characterIndex);
}
void Item::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_enabled);
}
void Item::saveLoadCurrent(ResourceSerializer *serializer) {
bool hasMovement = _movement != nullptr && !_movement->hasEnded();
serializer->syncAsUint32LE(hasMovement, 8);
if (hasMovement) {
uint32 movementType = _movement != nullptr ? _movement->getType() : 0;
serializer->syncAsUint32LE(movementType);
if (serializer->isLoading()) {
_movement = Movement::construct(movementType, Object::cast<ItemVisual>(this));
}
_movement->saveLoad(serializer);
serializer->syncAsResourceReference(&_movementSuspendedScript);
if (serializer->isLoading()) {
_movement->start();
}
}
}
Common::Array<Common::Point> Item::listExitPositions() {
return Common::Array<Common::Point>();
}
ItemVisual::~ItemVisual() {
delete _renderEntry;
}
ItemVisual::ItemVisual(Object *parent, byte subType, uint16 index, const Common::String &name) :
Item(parent, subType, index, name),
_renderEntry(nullptr),
_actionAnim(nullptr),
_animHierarchy(nullptr),
_currentAnimActivity(-1),
_clickable(true) {
_renderEntry = new Gfx::RenderEntry(this, getName());
}
void ItemVisual::readData(Formats::XRCReadStream *stream) {
Item::readData(stream);
_clickable = stream->readBool();
}
void ItemVisual::onAllLoaded() {
Item::onAllLoaded();
_animHierarchy = findChild<AnimHierarchy>(false);
_renderEntry->setClickable(_clickable);
if (_subType != kItemModel) {
setAnimActivity(Anim::kActionUsagePassive);
}
if (!_enabled) {
setEnabled(false);
}
Location *location = findParent<Location>();
if (location) {
location->registerCharacterItem(_characterIndex, this);
}
}
void ItemVisual::saveLoad(ResourceSerializer *serializer) {
Item::saveLoad(serializer);
serializer->syncAsSint32LE(_currentAnimActivity);
serializer->syncAsResourceReference(&_animHierarchy);
if (serializer->isLoading() && _animHierarchy) {
setAnimHierarchy(_animHierarchy);
}
serializer->syncAsResourceReference(&_actionAnim);
if (serializer->isLoading()) {
if (_actionAnim) {
_actionAnim->applyToItem(this);
} else {
setAnimActivity(_currentAnimActivity);
}
}
}
void ItemVisual::saveLoadCurrent(ResourceSerializer *serializer) {
Item::saveLoadCurrent(serializer);
// Apply the animation once again now the data from the item templates has been loaded.
// This ensures template level textures and models are applied when loading.
serializer->syncAsSint32LE(_currentAnimActivity, 11);
serializer->syncAsResourceReference(&_animHierarchy, 11);
if (serializer->isLoading()) {
if (_animHierarchy) {
setAnimHierarchy(_animHierarchy);
}
}
serializer->syncAsResourceReference(&_actionAnim, 11);
if (serializer->isLoading()) {
if (_actionAnim) {
_actionAnim->applyToItem(this);
} else {
setAnimActivity(_currentAnimActivity);
}
}
}
void ItemVisual::setEnabled(bool enabled) {
Item::setEnabled(enabled);
if (enabled) {
// If an action animation was selected while the item was disabled,
// honor it. This is not what the original engine did, but seems
// logical and fixes the Gribbler hurting animation in the forest.
if (!_actionAnim) {
_animHierarchy->selectItemAnim(this);
}
} else {
resetActionAnim();
_animHierarchy->unselectItemAnim(this);
}
}
ItemVisual *ItemVisual::getSceneInstance() {
return this;
}
int32 ItemVisual::getAnimActivity() const {
return _currentAnimActivity;
}
void ItemVisual::setAnimActivity(int32 activity) {
bool animNeedsUpdate = activity != _currentAnimActivity || _actionAnim != nullptr || _animHierarchy->getCurrentAnim() == nullptr;
resetActionAnim();
_currentAnimActivity = activity;
if (animNeedsUpdate && _animHierarchy) {
_animHierarchy->setItemAnim(this, activity);
}
}
void ItemVisual::printData() {
Item::printData();
debug("clickable: %d", _clickable);
}
Anim *ItemVisual::getAnim() const {
if (_actionAnim) {
return _actionAnim;
}
return _animHierarchy->getCurrentAnim();
}
Anim *ItemVisual::getActionAnim() const {
return _actionAnim;
}
void ItemVisual::setAnimHierarchy(AnimHierarchy *animHierarchy) {
resetActionAnim();
if (_animHierarchy) {
_animHierarchy->unselectItemAnim(this);
}
_animHierarchy = animHierarchy;
}
Visual *ItemVisual::getVisual() {
Anim *anim = getAnim();
if (!anim) {
return nullptr;
}
return anim->getVisual();
}
int ItemVisual::getHotspotIndexForPoint(const Common::Point &point) {
Anim *anim = getAnim();
if (anim) {
return anim->getPointHotspotIndex(point);
}
return -1;
}
bool ItemVisual::canPerformAction(uint32 action, uint32 hotspotIndex) {
PATTable *table = findChildWithOrder<PATTable>(hotspotIndex);
return table && table->canPerformAction(action);
}
bool ItemVisual::doAction(uint32 action, uint32 hotspotIndex) {
PATTable *table = findChildWithOrder<PATTable>(hotspotIndex);
if (table && table->canPerformAction(action)) {
return table->runScriptForAction(action);
}
return false;
}
void ItemVisual::playActionAnim(Anim *anim) {
resetActionAnim();
_animHierarchy->unselectItemAnim(this);
_actionAnim = anim;
anim->applyToItem(this);
anim->playAsAction(this);
}
void ItemVisual::resetActionAnim() {
if (_actionAnim) {
_actionAnim->removeFromItem(this);
_actionAnim = nullptr;
// TODO: Add a condition to this?
_animHierarchy->selectItemAnim(this);
if (_subType == kItemModel) {
_animHierarchy->setItemAnim(this, Anim::kActorActivityIdle);
}
}
}
void ItemVisual::setPosition2D(const Common::Point &position) {
warning("ItemVisual::setPosition2D is not implemented for this item type: %d (%s)", _subType, _name.c_str());
}
Common::String ItemVisual::getHotspotTitle(uint32 hotspotIndex) {
PATTable *table = findChildWithOrder<PATTable>(hotspotIndex);
Common::String title;
if (table) {
title = table->getName();
} else {
title = getName();
}
if (title.equalsIgnoreCase("Default April PAT")) {
return "April"; // The same hack exists in the original
}
return title;
}
Common::Array<Common::Point> ItemVisual::listExitPositionsImpl() {
Common::Array<PATTable *> pattables = listChildrenRecursive<PATTable>();
Common::Array<Common::Point> positions;
Common::Point invalidPosition(-1, -1);
for (uint i = 0; i < pattables.size(); ++i) {
if (pattables[i]->getDefaultAction() != PATTable::kActionExit) continue;
Anim *anim = getAnim();
if (!anim) continue;
Common::Point hotspot = anim->getHotspotPosition(i);
if (hotspot != invalidPosition) {
hotspot += _renderEntry->getPosition();
positions.push_back(hotspot);
}
}
return positions;
}
ItemTemplate::~ItemTemplate() {
}
ItemTemplate::ItemTemplate(Object *parent, byte subType, uint16 index, const Common::String &name) :
Item(parent, subType, index, name),
_meshIndex(-1),
_textureNormalIndex(-1),
_textureFaceIndex(-1),
_animHierarchyIndex(-1),
_referencedItem(nullptr),
_instanciatedItem(nullptr) {
}
void ItemTemplate::onAllLoaded() {
Item::onAllLoaded();
BonesMesh *bonesMesh = findChild<BonesMesh>(false);
if (bonesMesh) {
_meshIndex = bonesMesh->getIndex();
}
TextureSet *textureNormal = findChildWithSubtype<TextureSet>(TextureSet::kTextureNormal, false);
if (textureNormal) {
_textureNormalIndex = textureNormal->getIndex();
}
TextureSet *textureFace = findChildWithSubtype<TextureSet>(TextureSet::kTextureFace, false);
if (textureFace) {
_textureFaceIndex = textureFace->getIndex();
}
}
void ItemTemplate::saveLoadCurrent(ResourceSerializer *serializer) {
Item::saveLoadCurrent(serializer);
serializer->syncAsSint32LE(_meshIndex);
serializer->syncAsSint32LE(_textureNormalIndex);
serializer->syncAsSint32LE(_textureFaceIndex);
serializer->syncAsSint32LE(_animHierarchyIndex);
}
void ItemTemplate::setInstanciatedItem(Item *instance) {
_instanciatedItem = instance;
}
ItemVisual *ItemTemplate::getSceneInstance() {
if (_instanciatedItem) {
return _instanciatedItem->getSceneInstance();
}
return nullptr;
}
void ItemTemplate::setStockAnimHierachy(AnimHierarchy *animHierarchy) {
Object *animHierarchyParent = animHierarchy->findParent<Object>();
if (animHierarchyParent == this) {
_animHierarchyIndex = animHierarchy->getIndex();
} else {
_animHierarchyIndex = -1;
}
}
void ItemTemplate::setBonesMesh(int32 index) {
_meshIndex = index;
}
void ItemTemplate::setTexture(int32 index, uint32 textureType) {
if (textureType == TextureSet::kTextureNormal) {
_textureNormalIndex = index;
} else if (textureType == TextureSet::kTextureFace) {
_textureFaceIndex = index;
} else {
error("Unknown texture type %d", textureType);
}
// Reset the animation to apply the changes
ModelItem *sceneInstance = Resources::Object::cast<Resources::ModelItem>(getSceneInstance());
sceneInstance->updateAnim();
}
void ItemTemplate::setAnimHierarchy(AnimHierarchy *animHierarchy) {
setStockAnimHierachy(animHierarchy);
if (_instanciatedItem) {
_instanciatedItem->setAnimHierarchy(animHierarchy);
}
}
GlobalItemTemplate::~GlobalItemTemplate() {
}
GlobalItemTemplate::GlobalItemTemplate(Object *parent, byte subType, uint16 index, const Common::String &name) :
ItemTemplate(parent, subType, index, name) {
_animHierarchyIndex = 0;
}
BonesMesh *GlobalItemTemplate::findBonesMesh() {
if (_meshIndex == -1) {
return nullptr;
} else {
BonesMesh *mesh = findChildWithIndex<BonesMesh>(_meshIndex);
if (mesh && !StarkSettings->getBoolSetting(Settings::kHighModel)) {
BonesMesh *lowMesh = findChildWithName<BonesMesh>(mesh->getName() + "_LO_RES");
if (lowMesh) {
mesh = lowMesh;
}
}
return mesh;
}
}
TextureSet *GlobalItemTemplate::findTextureSet(uint32 textureType) {
if (textureType == TextureSet::kTextureNormal) {
if (_textureNormalIndex == -1) {
return nullptr;
} else {
return findChildWithIndex<TextureSet>(_textureNormalIndex);
}
} else if (textureType == TextureSet::kTextureFace) {
if (_textureFaceIndex == -1) {
return nullptr;
} else {
return findChildWithIndex<TextureSet>(_textureFaceIndex);
}
} else {
error("Unknown texture type %d", textureType);
}
}
AnimHierarchy *GlobalItemTemplate::findStockAnimHierarchy() {
if (_animHierarchyIndex == -1) {
return nullptr;
} else {
return findChildWithIndex<AnimHierarchy>(_animHierarchyIndex);
}
}
InventoryItem::~InventoryItem() {
}
InventoryItem::InventoryItem(Object *parent, byte subType, uint16 index, const Common::String &name) :
ItemVisual(parent, subType, index, name) {
}
Gfx::RenderEntry *InventoryItem::getRenderEntry(const Common::Point &positionOffset) {
if (_enabled) {
setAnimActivity(Anim::kUIUsageInventory);
Visual *visual = getVisual();
_renderEntry->setVisual(visual);
_renderEntry->setPosition(Common::Point());
} else {
_renderEntry->setVisual(nullptr);
}
return _renderEntry;
}
void InventoryItem::setEnabled(bool enabled) {
ItemVisual::setEnabled(enabled);
// Deselect the item in the inventory when removing it
int16 selectedInventoryItem = StarkUserInterface->getSelectedInventoryItem();
if (!enabled && selectedInventoryItem == getIndex()) {
StarkUserInterface->selectInventoryItem(-1);
}
KnowledgeSet *inventory = StarkGlobal->getInventory();
if (inventory) {
if (enabled) {
inventory->addItem(this);
} else {
inventory->removeItem(this);
}
}
}
Visual *InventoryItem::getActionVisual(bool active) const {
if (active) {
return _animHierarchy->getVisualForUsage(Anim::kActionUsageActive);
} else {
return _animHierarchy->getVisualForUsage(Anim::kActionUsagePassive);
}
}
Visual *InventoryItem::getCursorVisual() const {
Visual *visual = _animHierarchy->getVisualForUsage(Anim::kUIUsageUseCursorPassive);
if (!visual) {
visual = _animHierarchy->getVisualForUsage(Anim::kUIUsageUseCursorActive);
}
if (!visual) {
visual = _animHierarchy->getVisualForUsage(Anim::kUIUsageInventory);
}
return visual;
}
LevelItemTemplate::~LevelItemTemplate() {
}
LevelItemTemplate::LevelItemTemplate(Object *parent, byte subType, uint16 index, const Common::String &name) :
ItemTemplate(parent, subType, index, name) {
}
void LevelItemTemplate::readData(Formats::XRCReadStream *stream) {
ItemTemplate::readData(stream);
_reference = stream->readResourceReference();
}
void LevelItemTemplate::onAllLoaded() {
ItemTemplate::onAllLoaded();
_referencedItem = _reference.resolve<ItemTemplate>();
if (_referencedItem) {
_referencedItem->setInstanciatedItem(this);
}
}
BonesMesh *LevelItemTemplate::findBonesMesh() {
if (_meshIndex == -1) {
return _referencedItem->findBonesMesh();
} else {
BonesMesh *mesh = findChildWithIndex<BonesMesh>(_meshIndex);
if (mesh && !StarkSettings->getBoolSetting(Settings::kHighModel)) {
BonesMesh *lowMesh = findChildWithName<BonesMesh>(mesh->getName() + "_LO_RES");
if (lowMesh) {
mesh = lowMesh;
}
}
return mesh;
}
}
TextureSet *LevelItemTemplate::findTextureSet(uint32 textureType) {
if (textureType == TextureSet::kTextureNormal) {
if (_textureNormalIndex == -1) {
return _referencedItem->findTextureSet(textureType);
} else {
return findChildWithIndex<TextureSet>(_textureNormalIndex);
}
} else if (textureType == TextureSet::kTextureFace) {
if (_textureFaceIndex == -1) {
if (_referencedItem) {
return _referencedItem->findTextureSet(textureType);
} else {
return nullptr;
}
} else {
return findChildWithIndex<TextureSet>(_textureFaceIndex);
}
} else {
error("Unknown texture type %d", textureType);
}
}
AnimHierarchy *LevelItemTemplate::findStockAnimHierarchy() {
if (_animHierarchyIndex == -1 && !_referencedItem) {
_animHierarchyIndex = 0; // Prefer referenced anim to local
}
if (_animHierarchyIndex == -1) {
return _referencedItem->findStockAnimHierarchy();
} else {
return findChildWithIndex<AnimHierarchy>(_animHierarchyIndex);
}
}
ItemTemplate *LevelItemTemplate::getItemTemplate() const {
return _referencedItem;
}
void LevelItemTemplate::printData() {
ItemTemplate::printData();
debug("reference: %s", _reference.describe().c_str());
}
FloorPositionedItem::~FloorPositionedItem() {
}
FloorPositionedItem::FloorPositionedItem(Object *parent, byte subType, uint16 index, const Common::String &name) :
ItemVisual(parent, subType, index, name),
_direction3D(0.0),
_floorFaceIndex(-1),
_sortKeyOverride(false),
_sortKeyOverridenValue(0.0) {
}
Math::Vector3d FloorPositionedItem::getPosition3D() const {
return _position3D;
}
void FloorPositionedItem::setPosition3D(const Math::Vector3d &position) {
_position3D = position;
}
int32 FloorPositionedItem::getFloorFaceIndex() const {
return _floorFaceIndex;
}
void FloorPositionedItem::setFloorFaceIndex(int32 faceIndex) {
_floorFaceIndex = faceIndex;
_sortKeyOverride = false;
}
void FloorPositionedItem::placeOnBookmark(Bookmark *target) {
Floor *floor = StarkGlobal->getCurrent()->getFloor();
_position3D = target->getPosition();
// Find the floor face index the item is on
setFloorFaceIndex(floor->findFaceContainingPoint(_position3D));
// Set the z coordinate using the floor height at that position
if (_floorFaceIndex < 0) {
warning("Item '%s' has been placed out of the floor field", getName().c_str());
}
}
void FloorPositionedItem::placeDefaultPosition() {
Floor *floor = StarkGlobal->getCurrent()->getFloor();
FloorFace *face = floor->getFace(0);
_position3D = face->getCenter();
// Find the floor face index the item is on
setFloorFaceIndex(0);
// Set the z coordinate using the floor height at that position
floor->computePointHeightInFace(_position3D, 0);
}
Math::Vector3d FloorPositionedItem::getDirectionVector() const {
Math::Matrix3 rot;
rot.buildAroundZ(-_direction3D);
Math::Vector3d direction(1.0, 0.0, 0.0);
rot.transformVector(&direction);
return direction;
}
void FloorPositionedItem::setDirection(const Math::Angle &direction) {
_direction3D = direction.getDegrees(0.0);
}
void FloorPositionedItem::overrideSortKey(float sortKey) {
_sortKeyOverride = true;
_sortKeyOverridenValue = sortKey;
}
float FloorPositionedItem::getSortKey() const {
if (_sortKeyOverride) {
return _sortKeyOverridenValue;
}
Floor *floor = StarkGlobal->getCurrent()->getFloor();
if (_floorFaceIndex == -1) {
// warning("Undefined floor face index for item '%s'", getName().c_str());
return floor->getDistanceFromCamera(0);
}
return floor->getDistanceFromCamera(_floorFaceIndex);
}
void FloorPositionedItem::saveLoad(ResourceSerializer *serializer) {
ItemVisual::saveLoad(serializer);
serializer->syncAsVector3d(_position3D);
serializer->syncAsFloat(_direction3D);
serializer->syncAsSint32LE(_floorFaceIndex);
}
FloorPositionedImageItem::~FloorPositionedImageItem() {
}
FloorPositionedImageItem::FloorPositionedImageItem(Object *parent, byte subType, uint16 index, const Common::String &name) :
FloorPositionedItem(parent, subType, index, name) {
}
void FloorPositionedImageItem::readData(Formats::XRCReadStream *stream) {
FloorPositionedItem::readData(stream);
setFloorFaceIndex(stream->readSint32LE());
_position = stream->readPoint();
// WORKAROUND: Fix the position of various items being incorrect in the game datafiles
Location *location = findParent<Location>();
if (_name == "Shelves" && location && location->getName() == "April's Room") {
_position = Common::Point(543, 77);
} else if (_name == "Door" && location && location->getName() == "Hallway") {
_position = Common::Point(328, 44);
} else if (_name == "Bench" && location && location->getName() == "Outside Border House") {
_position = Common::Point(707, 255);
} else if (_name == "Printer" && location && location->getName() == "Archives") {
_position = Common::Point(260, 119);
} else if (_name == "Prop04_chair01" && location && location->getName() == "Inn Night") {
_position = Common::Point(185, 324);
} else if (_name == "Prop05_chair02" && location && location->getName() == "Inn Night") {
_position = Common::Point(381, 329);
} else if (_name == "Gargoyle" && location && location->getName() == "Below Floating Mountain") {
_position = Common::Point(352, 0);
} else if (_name == "Computer" && location && location->getName() == "Vanguard Laboratory") {
_position = Common::Point(411, 141);
}
}
Gfx::RenderEntry *FloorPositionedImageItem::getRenderEntry(const Common::Point &positionOffset) {
if (_enabled) {
Visual *visual = getVisual();
_renderEntry->setVisual(visual);
if (getAnim() && getAnim()->getSubType() == Anim::kAnimVideo) {
// AnimVideos override the _position, but still need to move according to scroll-offsets
_renderEntry->setPosition(positionOffset);
} else {
_renderEntry->setPosition(_position - positionOffset);
}
_renderEntry->setSortKey(getSortKey());
} else {
_renderEntry->setVisual(nullptr);
}
return _renderEntry;
}
Common::Array<Common::Point> FloorPositionedImageItem::listExitPositions() {
return listExitPositionsImpl();
}
void FloorPositionedImageItem::setPosition2D(const Common::Point &position) {
_position = position;
}
void FloorPositionedImageItem::printData() {
FloorPositionedItem::printData();
debug("floorFaceIndex: %d", _floorFaceIndex);
debug("position: x %d, y %d", _position.x, _position.y);
}
ImageItem::~ImageItem() {
}
ImageItem::ImageItem(Object *parent, byte subType, uint16 index, const Common::String &name) :
ItemVisual(parent, subType, index, name) {
}
void ImageItem::readData(Formats::XRCReadStream *stream) {
ItemVisual::readData(stream);
_position = stream->readPoint();
_reference = stream->readResourceReference();
}
Gfx::RenderEntry *ImageItem::getRenderEntry(const Common::Point &positionOffset) {
if (_enabled) {
Visual *visual = getVisual();
_renderEntry->setVisual(visual);
if (getAnim() && getAnim()->getSubType() == Anim::kAnimVideo) {
// AnimVideos override the _position, but still need to move according to scroll-offsets
_renderEntry->setPosition(positionOffset);
} else {
_renderEntry->setPosition(_position - positionOffset);
}
} else {
_renderEntry->setVisual(nullptr);
}
return _renderEntry;
}
void ImageItem::setPosition2D(const Common::Point &position) {
_position = position;
}
void ImageItem::printData() {
ItemVisual::printData();
debug("reference: %s", _reference.describe().c_str());
debug("position: x %d, y %d", _position.x, _position.y);
}
Common::Array<Common::Point> ImageItem::listExitPositions() {
return listExitPositionsImpl();
}
ModelItem::~ModelItem() {
delete _animHandler;
}
ModelItem::ModelItem(Object *parent, byte subType, uint16 index, const Common::String &name) :
FloorPositionedItem(parent, subType, index, name),
_meshIndex(-1),
_textureNormalIndex(-1),
_textureFaceIndex(-1),
_referencedItem(nullptr),
_animHandler(nullptr) {
}
void ModelItem::readData(Formats::XRCReadStream *stream) {
FloorPositionedItem::readData(stream);
_reference = stream->readResourceReference();
}
void ModelItem::onAllLoaded() {
FloorPositionedItem::onAllLoaded();
BonesMesh *bonesMesh = findChild<BonesMesh>(false);
if (bonesMesh) {
_meshIndex = bonesMesh->getIndex();
}
TextureSet *textureNormal = findChildWithSubtype<TextureSet>(TextureSet::kTextureNormal, false);
if (textureNormal) {
_textureNormalIndex = textureNormal->getIndex();
}
TextureSet *textureFace = findChildWithSubtype<TextureSet>(TextureSet::kTextureFace, false);
if (textureFace) {
_textureFaceIndex = textureFace->getIndex();
}
_referencedItem = _reference.resolve<ItemTemplate>();
if (_referencedItem) {
_referencedItem->setInstanciatedItem(this);
}
_animHandler = new AnimHandler();
}
void ModelItem::onEnterLocation() {
FloorPositionedItem::onEnterLocation();
if (_referencedItem) {
_referencedItem->setInstanciatedItem(this);
}
if (_referencedItem) {
_animHierarchy = _referencedItem->findStockAnimHierarchy();
}
setAnimActivity(Anim::kActorActivityIdle);
}
void ModelItem::onExitLocation() {
FloorPositionedItem::onExitLocation();
resetActionAnim();
if (_animHierarchy) {
_animHierarchy->unselectItemAnim(this);
}
}
void ModelItem::setBonesMesh(int32 index) {
_meshIndex = index;
if (_meshIndex != -1) {
updateAnim();
}
}
BonesMesh *ModelItem::findBonesMesh() {
// Prefer retrieving the mesh from the anim hierarchy
BonesMesh *bonesMesh = _animHierarchy->findBonesMesh();
// Otherwise, use a children mesh, or a referenced mesh
if (!bonesMesh) {
if (_meshIndex == -1) {
bonesMesh = _referencedItem->findBonesMesh();
} else {
bonesMesh = findChildWithIndex<BonesMesh>(_meshIndex);
if (bonesMesh && !StarkSettings->getBoolSetting(Settings::kHighModel)) {
BonesMesh *lowMesh = findChildWithName<BonesMesh>(bonesMesh->getName() + "_LO_RES");
if (lowMesh) {
bonesMesh = lowMesh;
}
}
}
}
return bonesMesh;
}
void ModelItem::setTexture(int32 index, uint32 textureType) {
if (textureType == TextureSet::kTextureNormal) {
_textureNormalIndex = index;
} else if (textureType == TextureSet::kTextureFace) {
_textureFaceIndex = index;
} else {
error("Unknown texture type %d", textureType);
}
}
TextureSet *ModelItem::findTextureSet(uint32 textureType) {
// Prefer retrieving the mesh from the anim hierarchy
TextureSet *textureSet = _animHierarchy->findTextureSet(textureType);
// Otherwise, use a children mesh, or a referenced mesh
if (!textureSet) {
if (textureType == TextureSet::kTextureNormal) {
if (_textureNormalIndex == -1) {
textureSet = _referencedItem->findTextureSet(textureType);
} else {
textureSet = findChildWithIndex<TextureSet>(_textureNormalIndex);
}
} else if (textureType == TextureSet::kTextureFace) {
if (_textureFaceIndex == -1) {
if (_referencedItem) {
textureSet = _referencedItem->findTextureSet(textureType);
}
} else {
textureSet = findChildWithIndex<TextureSet>(_textureFaceIndex);
}
} else {
error("Unknown texture type %d", textureType);
}
}
return textureSet;
}
void ModelItem::updateAnim() {
Anim *anim = getAnim();
if (anim && anim->getSubType() == Anim::kAnimSkeleton) {
anim->removeFromItem(this);
anim->applyToItem(this);
}
}
Gfx::RenderEntry *ModelItem::getRenderEntry(const Common::Point &positionOffset) {
if (_enabled) {
Visual *visual = getVisual();
if (!visual) {
_animHierarchy->selectItemAnim(this);
visual = getVisual();
}
_renderEntry->setVisual(visual);
_renderEntry->setPosition3D(_position3D, _direction3D);
_renderEntry->setSortKey(getSortKey());
} else {
_renderEntry->setVisual(nullptr);
}
return _renderEntry;
}
ItemTemplate *ModelItem::getItemTemplate() const {
return _referencedItem;
}
Anim *ModelItem::getIdleActionAnim() const {
if (_animHierarchy) {
return _animHierarchy->getIdleActionAnim();
}
return nullptr;
}
void ModelItem::saveLoadCurrent(ResourceSerializer *serializer) {
FloorPositionedItem::saveLoadCurrent(serializer);
serializer->syncAsSint32LE(_meshIndex);
serializer->syncAsSint32LE(_textureNormalIndex);
serializer->syncAsSint32LE(_textureFaceIndex);
}
void ModelItem::printData() {
FloorPositionedItem::printData();
debug("reference: %s", _reference.describe().c_str());
}
void ModelItem::resetAnimationBlending() {
_animHandler->resetBlending();
}
AnimHandler *ModelItem::getAnimHandler() const {
return _animHandler;
}
void ItemTemplate::onEnterLocation() {
Object::onEnterLocation();
if (_referencedItem) {
_referencedItem->setInstanciatedItem(this);
}
}
} // End of namespace Resources
} // End of namespace Stark