/* 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 . * */ #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(this)); } _movement->saveLoad(serializer); serializer->syncAsResourceReference(&_movementSuspendedScript); if (serializer->isLoading()) { _movement->start(); } } } Common::Array Item::listExitPositions() { return Common::Array(); } 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(false); _renderEntry->setClickable(_clickable); if (_subType != kItemModel) { setAnimActivity(Anim::kActionUsagePassive); } if (!_enabled) { setEnabled(false); } Location *location = findParent(); 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(hotspotIndex); return table && table->canPerformAction(action); } bool ItemVisual::doAction(uint32 action, uint32 hotspotIndex) { PATTable *table = findChildWithOrder(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(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 ItemVisual::listExitPositionsImpl() { Common::Array pattables = listChildrenRecursive(); Common::Array 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(false); if (bonesMesh) { _meshIndex = bonesMesh->getIndex(); } TextureSet *textureNormal = findChildWithSubtype(TextureSet::kTextureNormal, false); if (textureNormal) { _textureNormalIndex = textureNormal->getIndex(); } TextureSet *textureFace = findChildWithSubtype(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(); 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(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(_meshIndex); if (mesh && !StarkSettings->getBoolSetting(Settings::kHighModel)) { BonesMesh *lowMesh = findChildWithName(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(_textureNormalIndex); } } else if (textureType == TextureSet::kTextureFace) { if (_textureFaceIndex == -1) { return nullptr; } else { return findChildWithIndex(_textureFaceIndex); } } else { error("Unknown texture type %d", textureType); } } AnimHierarchy *GlobalItemTemplate::findStockAnimHierarchy() { if (_animHierarchyIndex == -1) { return nullptr; } else { return findChildWithIndex(_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(); if (_referencedItem) { _referencedItem->setInstanciatedItem(this); } } BonesMesh *LevelItemTemplate::findBonesMesh() { if (_meshIndex == -1) { return _referencedItem->findBonesMesh(); } else { BonesMesh *mesh = findChildWithIndex(_meshIndex); if (mesh && !StarkSettings->getBoolSetting(Settings::kHighModel)) { BonesMesh *lowMesh = findChildWithName(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(_textureNormalIndex); } } else if (textureType == TextureSet::kTextureFace) { if (_textureFaceIndex == -1) { if (_referencedItem) { return _referencedItem->findTextureSet(textureType); } else { return nullptr; } } else { return findChildWithIndex(_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(_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(); 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 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 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(false); if (bonesMesh) { _meshIndex = bonesMesh->getIndex(); } TextureSet *textureNormal = findChildWithSubtype(TextureSet::kTextureNormal, false); if (textureNormal) { _textureNormalIndex = textureNormal->getIndex(); } TextureSet *textureFace = findChildWithSubtype(TextureSet::kTextureFace, false); if (textureFace) { _textureFaceIndex = textureFace->getIndex(); } _referencedItem = _reference.resolve(); 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(_meshIndex); if (bonesMesh && !StarkSettings->getBoolSetting(Settings::kHighModel)) { BonesMesh *lowMesh = findChildWithName(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(_textureNormalIndex); } } else if (textureType == TextureSet::kTextureFace) { if (_textureFaceIndex == -1) { if (_referencedItem) { textureSet = _referencedItem->findTextureSet(textureType); } } else { textureSet = findChildWithIndex(_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