Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,677 @@
/* 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 "common/debug.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/biffmesh.h"
#include "engines/stark/formats/tm.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animscript.h"
#include "engines/stark/resources/bonesmesh.h"
#include "engines/stark/resources/direction.h"
#include "engines/stark/resources/image.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/textureset.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/settings.h"
#include "engines/stark/services/stateprovider.h"
#include "engines/stark/model/animhandler.h"
#include "engines/stark/model/skeleton_anim.h"
#include "engines/stark/visual/actor.h"
#include "engines/stark/visual/prop.h"
#include "engines/stark/visual/smacker.h"
namespace Stark {
namespace Resources {
Object *Anim::construct(Object *parent, byte subType, uint16 index, const Common::String &name) {
switch (subType) {
case kAnimImages:
return new AnimImages(parent, subType, index, name);
case kAnimProp:
return new AnimProp(parent, subType, index, name);
case kAnimVideo:
return new AnimVideo(parent, subType, index, name);
case kAnimSkeleton:
return new AnimSkeleton(parent, subType, index, name);
default:
error("Unknown anim subtype %d", subType);
}
}
Anim::~Anim() {
}
Anim::Anim(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_activity(0),
_currentFrame(0),
_numFrames(0),
_refCount(0) {
_type = TYPE;
}
void Anim::readData(Formats::XRCReadStream *stream) {
_activity = stream->readUint32LE();
_numFrames = stream->readUint32LE();
}
void Anim::selectFrame(uint32 frameIndex) {
}
uint32 Anim::getActivity() const {
return _activity;
}
void Anim::applyToItem(Item *item) {
_refCount++;
}
void Anim::removeFromItem(Item *item) {
_refCount--;
}
bool Anim::isInUse() const {
return _refCount > 0;
}
int Anim::getPointHotspotIndex(const Common::Point &point) const {
// Most anim types only have one hotspot
return 0;
}
void Anim::playAsAction(ItemVisual *item) {
AnimScript *animScript = findChild<AnimScript>();
animScript->goToScriptItem(0);
}
bool Anim::isAtTime(uint32 time) const {
warning("Anim::isAtTime is not implemented");
return true;
}
void Anim::shouldResetItem(bool resetItem) {
// Script animations don't keep track of the item
}
void Anim::resetItem() {
// Script animations don't keep track of the item
}
bool Anim::isDone() const {
AnimScript *animScript = findChild<AnimScript>();
return animScript->isDone();
}
uint32 Anim::getMovementSpeed() const {
return 100;
}
uint32 Anim::getIdleActionFrequency() const {
return 1;
}
void Anim::printData() {
debug("activity: %d", _activity);
debug("numFrames: %d", _numFrames);
}
AnimImages::~AnimImages() {
}
AnimImages::AnimImages(Object *parent, byte subType, uint16 index, const Common::String &name) :
Anim(parent, subType, index, name),
_field_3C(0),
_currentDirection(0),
_currentFrameImage(nullptr) {
}
void AnimImages::readData(Formats::XRCReadStream *stream) {
Anim::readData(stream);
_field_3C = stream->readFloatLE();
}
void AnimImages::onAllLoaded() {
Anim::onAllLoaded();
_directions = listChildren<Direction>();
}
void AnimImages::selectFrame(uint32 frameIndex) {
if (frameIndex > _numFrames) {
// The original silently ignores this as well
warning("Request for frame %d for anim '%s' has been ignored, it is above max frame %d", frameIndex, getName().c_str(), _numFrames);
_currentFrame = 0;
}
_currentFrame = frameIndex;
}
Visual *AnimImages::getVisual() {
Direction *direction = _directions[_currentDirection];
_currentFrameImage = direction->findChildWithIndex<Image>(_currentFrame);
return _currentFrameImage->getVisual();
}
void AnimImages::printData() {
Anim::printData();
debug("field_3C: %f", _field_3C);
}
int AnimImages::getPointHotspotIndex(const Common::Point &point) const {
if (_currentFrameImage) {
return _currentFrameImage->indexForPoint(point);
}
return -1;
}
Common::Point AnimImages::getHotspotPosition(uint index) const {
if (_currentFrameImage) {
return _currentFrameImage->getHotspotPosition(index);
}
return Common::Point(-1, -1);
}
void AnimImages::saveLoad(ResourceSerializer *serializer) {
Anim::saveLoad(serializer);
serializer->syncAsUint32LE(_currentFrame);
if (serializer->isLoading()) {
selectFrame(_currentFrame);
}
}
AnimProp::~AnimProp() {
delete _visual;
}
AnimProp::AnimProp(Object *parent, byte subType, uint16 index, const Common::String &name) :
Anim(parent, subType, index, name),
_movementSpeed(100) {
_visual = StarkGfx->createPropRenderer();
}
Visual *AnimProp::getVisual() {
return _visual;
}
uint32 AnimProp::getMovementSpeed() const {
return _movementSpeed;
}
void AnimProp::readData(Formats::XRCReadStream *stream) {
Anim::readData(stream);
_field_3C = stream->readString();
uint32 meshCount = stream->readUint32LE();
for (uint i = 0; i < meshCount; i++) {
_meshFilenames.push_back(Common::Path(stream->readString()));
}
_textureFilename = stream->readString();
_movementSpeed = stream->readUint32LE();
_archiveName = stream->getArchiveName();
}
void AnimProp::onPostRead() {
if (_meshFilenames.size() != 1) {
error("Unexpected mesh count in prop anim: '%d'", _meshFilenames.size());
}
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_meshFilenames[0], _archiveName);
_visual->setModel(Formats::BiffMeshReader::read(stream));
delete stream;
stream = StarkArchiveLoader->getFile(_textureFilename, _archiveName);
_visual->setTexture(Formats::TextureSetReader::read(stream));
delete stream;
}
void AnimProp::printData() {
Anim::printData();
debug("field_3C: %s", _field_3C.c_str());
Common::String description;
for (uint32 i = 0; i < _meshFilenames.size(); i++) {
debug("meshFilename[%d]: %s", i, _meshFilenames[i].toString().c_str());
}
debug("textureFilename: %s", _textureFilename.toString().c_str());
debug("movementSpeed: %d", _movementSpeed);
}
AnimVideo::~AnimVideo() {
delete _smacker;
}
AnimVideo::AnimVideo(Object *parent, byte subType, uint16 index, const Common::String &name) :
Anim(parent, subType, index, name),
_width(0),
_height(0),
_smacker(nullptr),
_frameRateOverride(-1),
_preload(false),
_loop(false),
_actionItem(nullptr),
_shouldResetItem(true),
_done(false) {
}
void AnimVideo::readData(Formats::XRCReadStream *stream) {
Anim::readData(stream);
_smackerFile = stream->readString();
_width = stream->readUint32LE();
_height = stream->readUint32LE();
_positions.clear();
_sizes.clear();
uint32 size = stream->readUint32LE();
for (uint i = 0; i < size; i++) {
_positions.push_back(stream->readPoint());
_sizes.push_back(stream->readRect());
}
_loop = stream->readBool();
_frameRateOverride = stream->readUint32LE();
if (stream->isDataLeft()) {
_preload = stream->readBool();
}
_archiveName = stream->getArchiveName();
// WORKAROUND: Fix the position of various items being incorrect in the game datafiles
Location *location = findParent<Location>();
if (_name == "Mountain comes down" && location && location->getName() == "Below Floating Mountain") {
for (uint i = 0; i < _sizes.size(); i++) {
_positions[i].x = 352;
}
}
}
void AnimVideo::onAllLoaded() {
if (!_smacker) {
_smacker = new VisualSmacker(StarkGfx);
Common::SeekableReadStream *overrideStreamBink = nullptr;
Common::SeekableReadStream *overrideStreamSmacker = nullptr;
if (StarkSettings->isAssetsModEnabled() && StarkGfx->supportsModdedAssets()) {
overrideStreamBink = openOverrideFile(".bik");
if (!overrideStreamBink) {
overrideStreamSmacker = openOverrideFile(".smk");
}
}
Common::SeekableReadStream *stream = StarkArchiveLoader->getExternalFile(_smackerFile, _archiveName);
if (overrideStreamBink) {
_smacker->loadBink(overrideStreamBink);
_smacker->readOriginalSize(stream);
} else if (overrideStreamSmacker) {
_smacker->loadSmacker(overrideStreamSmacker);
_smacker->readOriginalSize(stream);
} else {
_smacker->loadSmacker(stream);
}
_smacker->overrideFrameRate(_frameRateOverride);
updateSmackerPosition();
}
}
Common::SeekableReadStream *AnimVideo::openOverrideFile(const Common::String &extension) const {
Common::String baseName(_smackerFile.baseName());
if (!baseName.hasSuffixIgnoreCase(".sss")) {
return nullptr;
}
baseName = Common::String(baseName.c_str(), baseName.size() - 4) + extension;
Common::Path filePath(_smackerFile.getParent().appendComponent(baseName));
filePath = StarkArchiveLoader->getExternalFilePath(filePath, _archiveName);
debugC(kDebugModding, "Attempting to load %s", filePath.toString(Common::Path::kNativeSeparator).c_str());
Common::SeekableReadStream *smkStream = SearchMan.createReadStreamForMember(filePath);
if (!smkStream) {
return nullptr;
}
debugC(kDebugModding, "Loaded %s", filePath.toString(Common::Path::kNativeSeparator).c_str());
return smkStream;
}
void AnimVideo::onGameLoop() {
if (!_smacker || !isInUse()) {
return; // Animation not in use, no need to update the movie
}
if (_smacker->isDone()) {
// The last frame has been reached
_done = true;
if (_shouldResetItem) {
resetItem();
}
if (_loop) {
_smacker->rewind();
}
}
if (!_smacker->isDone()) {
_smacker->update();
updateSmackerPosition();
}
}
void AnimVideo::resetItem() {
if (!_loop && _actionItem) {
// Reset our item if needed
if (_actionItem->getActionAnim() == this) {
_actionItem->resetActionAnim();
}
_actionItem = nullptr;
}
}
void AnimVideo::onEnginePause(bool pause) {
Object::onEnginePause(pause);
if (_smacker && isInUse()) {
_smacker->pause(pause);
}
}
Visual *AnimVideo::getVisual() {
return _smacker;
}
void AnimVideo::updateSmackerPosition() {
int frame = _smacker->getFrameNumber();
if (frame == -1) {
return;
}
if (frame < (int) _positions.size()) {
_smacker->setPosition(_positions[frame]);
}
}
void AnimVideo::shouldResetItem(bool resetItem) {
_shouldResetItem = resetItem;
}
void AnimVideo::playAsAction(ItemVisual *item) {
_actionItem = item;
_shouldResetItem = true;
_done = false;
if (!_loop) {
_smacker->rewind();
}
// Update here so we have something up to date to show when rendering this frame
_smacker->update();
}
bool AnimVideo::isAtTime(uint32 time) const {
uint32 currentTime = _smacker->getCurrentTime();
return currentTime >= time;
}
void AnimVideo::saveLoadCurrent(ResourceSerializer *serializer) {
Anim::saveLoadCurrent(serializer);
int32 frameNumber = _smacker->getFrameNumber();
serializer->syncAsSint32LE(frameNumber);
serializer->syncAsSint32LE(_refCount);
// TODO: Seek to the saved frame number when loading
}
void AnimVideo::printData() {
Anim::printData();
debug("smackerFile: %s", _smackerFile.toString().c_str());
debug("size: x %d, y %d", _width, _height);
Common::String description;
for (uint32 i = 0; i < _positions.size(); i++) {
description += Common::String::format("(x %d, y %d) ", _positions[i].x, _positions[i].y);
}
debug("positions: %s", description.c_str());
description.clear();
for (uint32 i = 0; i < _sizes.size(); i++) {
description += Common::String::format("(l %d, t %d, r %d, b %d) ",
_sizes[i].left, _sizes[i].top, _sizes[i].right, _sizes[i].bottom);
}
debug("sizes: %s", description.c_str());
debug("frameRateOverride: %d", _frameRateOverride);
debug("preload: %d", _preload);
debug("loop: %d", _loop);
}
AnimSkeleton::~AnimSkeleton() {
delete _visual;
delete _skeletonAnim;
}
AnimSkeleton::AnimSkeleton(Object *parent, byte subType, uint16 index, const Common::String &name) :
Anim(parent, subType, index, name),
_castsShadow(true),
_loop(false),
_movementSpeed(100),
_idleActionFrequency(1),
_skeletonAnim(nullptr),
_currentTime(0),
_totalTime(0),
_done(false),
_actionItem(nullptr),
_shouldResetItem(true) {
_visual = StarkGfx->createActorRenderer();
}
void AnimSkeleton::applyToItem(Item *item) {
Anim::applyToItem(item);
if (!_loop) {
_currentTime = 0;
}
if (_currentTime > _totalTime) {
_currentTime = 0;
}
debugC(kDebugAnimation, "%s: add %s", item->getName().c_str(), getName().c_str());
ModelItem *modelItem = Object::cast<ModelItem>(item);
BonesMesh *mesh = modelItem->findBonesMesh();
TextureSet *texture = modelItem->findTextureSet(TextureSet::kTextureNormal);
AnimHandler *animHandler = modelItem->getAnimHandler();
animHandler->setModel(mesh->getModel());
animHandler->setAnim(_skeletonAnim);
_visual->setModel(mesh->getModel());
_visual->setAnimHandler(animHandler);
_visual->setTexture(texture->getTexture());
_visual->setTextureFacial(nullptr);
_visual->setTime(_currentTime);
_visual->setCastShadow(_castsShadow);
}
void AnimSkeleton::removeFromItem(Item *item) {
Anim::removeFromItem(item);
debugC(kDebugAnimation, "%s: remove %s", item->getName().c_str(), getName().c_str());
_actionItem = nullptr;
}
Visual *AnimSkeleton::getVisual() {
return _visual;
}
void AnimSkeleton::readData(Formats::XRCReadStream *stream) {
Anim::readData(stream);
_animFilename = stream->readString();
stream->readString(); // Skipped in the original
stream->readString(); // Skipped in the original
stream->readString(); // Skipped in the original
_loop = stream->readBool();
_movementSpeed = stream->readUint32LE();
if (_movementSpeed < 1) {
_movementSpeed = 100;
}
if (stream->isDataLeft()) {
_castsShadow = stream->readBool();
} else {
_castsShadow = true;
}
if (stream->isDataLeft()) {
_idleActionFrequency = stream->readUint32LE();
} else {
_idleActionFrequency = 1;
}
_archiveName = stream->getArchiveName();
}
void AnimSkeleton::onPostRead() {
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_animFilename, _archiveName);
_skeletonAnim = new SkeletonAnim();
_skeletonAnim->createFromStream(stream);
delete stream;
}
void AnimSkeleton::onAllLoaded() {
Anim::onAllLoaded();
_totalTime = _skeletonAnim->getLength();
_currentTime = 0;
}
void AnimSkeleton::onGameLoop() {
Anim::onGameLoop();
if (isInUse() && _totalTime) {
uint32 newTime = _currentTime + StarkGlobal->getMillisecondsPerGameloop();
if (!_loop && newTime >= _totalTime) {
_done = true;
if (_shouldResetItem) {
resetItem();
}
} else {
_currentTime = newTime % _totalTime;
_visual->setTime(_currentTime);
}
}
}
void AnimSkeleton::resetItem() {
if (_actionItem) {
if (_actionItem->getActionAnim() == this) {
_actionItem->resetActionAnim();
}
_actionItem = nullptr;
}
}
void AnimSkeleton::onPreDestroy() {
resetItem();
Anim::onPreDestroy();
}
uint32 AnimSkeleton::getMovementSpeed() const {
return _movementSpeed;
}
uint32 AnimSkeleton::getCurrentTime() const {
return _currentTime;
}
uint32 AnimSkeleton::getRemainingTime() const {
int32 remainingTime = _totalTime - _currentTime;
return CLIP<int32>(remainingTime, 0, _totalTime);
}
void AnimSkeleton::shouldResetItem(bool resetItem) {
_shouldResetItem = resetItem;
}
void AnimSkeleton::playAsAction(ItemVisual *item) {
_actionItem = item;
_done = false;
_shouldResetItem = true;
if (!_loop) {
_currentTime = 0;
}
}
bool AnimSkeleton::isAtTime(uint32 time) const {
return _currentTime >= time;
}
uint32 AnimSkeleton::getIdleActionFrequency() const {
return _idleActionFrequency;
}
void AnimSkeleton::printData() {
Anim::printData();
debug("filename: %s", _animFilename.toString().c_str());
debug("castsShadow: %d", _castsShadow);
debug("loop: %d", _loop);
debug("movementSpeed: %d", _movementSpeed);
debug("idleActionFrequency: %d", _idleActionFrequency);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,337 @@
/* 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 STARK_RESOURCES_ANIM_H
#define STARK_RESOURCES_ANIM_H
#include "common/path.h"
#include "common/rect.h"
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Common {
class SeekableReadStream;
}
namespace Stark {
class SkeletonAnim;
class VisualActor;
class VisualProp;
class VisualSmacker;
class Visual;
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Direction;
class Image;
class Item;
class ItemVisual;
/**
* Animation base class
*
* Animations provide a time dependent visual state to Items
*/
class Anim : public Object {
public:
static const Type::ResourceType TYPE = Type::kAnim;
enum SubType {
kAnimImages = 1,
kAnimProp = 2,
kAnimVideo = 3,
kAnimSkeleton = 4
};
enum ActionUsage {
kActionUsagePassive = 1,
kActionUsageActive = 2
};
enum UIUsage {
kUIUsageInventory = 1,
kUIUsageUseCursorPassive = 4,
kUIUsageUseCursorActive = 5
};
enum ActorActivity {
kActorActivityIdle = 1,
kActorActivityWalk = 2,
kActorActivityTalk = 3,
kActorActivityRun = 6,
kActorActivityIdleAction = 10
};
/** Anim factory */
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name);
Anim(Object *parent, byte subType, uint16 index, const Common::String &name);
~Anim() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
/** Get current displayed frame */
uint32 getCurrentFrame() { return _currentFrame; }
/** Sets the animation frame to be displayed */
virtual void selectFrame(uint32 frameIndex);
/** Obtain the Visual to be used to render the animation */
virtual Visual *getVisual() = 0;
/** Associate the animation to an Item */
virtual void applyToItem(Item *item);
/** Dissociate the animation from an item */
virtual void removeFromItem(Item *item);
/** Check is the animation is being used by an item */
bool isInUse() const;
/** Obtain the purpose of this anim */
uint32 getActivity() const;
/** Return the hotspot index for a point given in relative coordinates */
virtual int getPointHotspotIndex(const Common::Point &point) const;
/** Get the hotspot position for a given index of a pat-table */
virtual Common::Point getHotspotPosition(uint index) const { return Common::Point(-1, -1); }
/**
* Play the animation as an action for an item.
*
* This sets up a callback to the item for when the animation completes.
*/
virtual void playAsAction(ItemVisual *item);
/** Checks if the elapsed time since the animation start is greater than a specified duration */
virtual bool isAtTime(uint32 time) const;
/** Get the anim movement speed in units per seconds */
virtual uint32 getMovementSpeed() const;
/** Get the chance the animation has to play among other idle actions from the same anim hierarchy */
virtual uint32 getIdleActionFrequency() const;
/**
* When this animation is playing as an action should a new animation
* be chosen for the item as soon as this one completes based on
* the item's activity?
* This is true by default, but setting it to false allows scripts
* to chose precisely the new animation to play, and to start it
* in the same frame as this one is removed.
*/
virtual void shouldResetItem(bool resetItem);
/**
* Remove this action animation for the item and select a new animation
* based on the item's current activity.
*/
virtual void resetItem();
/**
* Is this animation done playing.
*
* Only valid for animations started with playAsAction.
*/
virtual bool isDone() const;
protected:
void printData() override;
uint32 _activity;
uint32 _currentFrame;
uint32 _numFrames;
int32 _refCount;
};
/**
* Displays still images controlled by an AnimScript
*/
class AnimImages : public Anim {
public:
AnimImages(Object *parent, byte subType, uint16 index, const Common::String &name);
~AnimImages() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
void saveLoad(ResourceSerializer *serializer) override;
// Anim API
void selectFrame(uint32 frameIndex) override;
Visual *getVisual() override;
int getPointHotspotIndex(const Common::Point &point) const override;
Common::Point getHotspotPosition(uint index) const override;
protected:
void printData() override;
float _field_3C;
uint32 _currentDirection;
Common::Array<Direction *> _directions;
Image *_currentFrameImage;
};
class AnimProp : public Anim {
public:
AnimProp(Object *parent, byte subType, uint16 index, const Common::String &name);
~AnimProp() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onPostRead() override;
// Anim API
Visual *getVisual() override;
uint32 getMovementSpeed() const override;
protected:
void printData() override;
Common::String _field_3C;
Common::Array<Common::Path> _meshFilenames;
Common::Path _textureFilename;
uint32 _movementSpeed;
Common::Path _archiveName;
VisualProp *_visual;
};
/**
* Displays a Smacker video
*/
class AnimVideo : public Anim {
public:
AnimVideo(Object *parent, byte subType, uint16 index, const Common::String &name);
~AnimVideo() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
void onGameLoop() override;
void onEnginePause(bool pause) override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
// Anim API
Visual *getVisual() override;
void playAsAction(ItemVisual *item) override;
void shouldResetItem(bool resetItem) override;
void resetItem() override;
bool isAtTime(uint32 time) const override;
bool isDone() const override { return _done || !isInUse(); }
protected:
typedef Common::Array<Common::Point> PointArray;
typedef Common::Array<Common::Rect> RectArray;
void printData() override;
Common::SeekableReadStream *openOverrideFile(const Common::String &extension) const;
/** Update the position of the video for the current frame */
void updateSmackerPosition();
Common::Path _smackerFile;
Common::Path _archiveName;
VisualSmacker *_smacker;
uint32 _width;
uint32 _height;
PointArray _positions;
RectArray _sizes;
int32 _frameRateOverride;
bool _preload;
bool _loop;
bool _done;
ItemVisual *_actionItem;
bool _shouldResetItem;
};
/**
* Animates a 3D mesh skeleton
*/
class AnimSkeleton : public Anim {
public:
AnimSkeleton(Object *parent, byte subType, uint16 index, const Common::String &name);
~AnimSkeleton() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onPostRead() override;
void onAllLoaded() override;
void onGameLoop() override;
void onPreDestroy() override;
// Anim API
void applyToItem(Item *item) override;
void removeFromItem(Item *item) override;
Visual *getVisual() override;
void playAsAction(ItemVisual *item) override;
bool isAtTime(uint32 time) const override;
bool isDone() const override { return _done || !isInUse(); }
uint32 getMovementSpeed() const override;
uint32 getIdleActionFrequency() const override;
void shouldResetItem(bool resetItem) override;
void resetItem() override;
/** Get the duration in milliseconds before the animation loops ends */
uint32 getRemainingTime() const;
/** Get the position in the animation loop in milliseconds */
uint32 getCurrentTime() const;
protected:
void printData() override;
bool _castsShadow;
Common::Path _archiveName;
Common::Path _animFilename;
bool _loop;
uint32 _movementSpeed;
uint32 _idleActionFrequency;
uint32 _totalTime;
uint32 _currentTime;
bool _done;
SkeletonAnim *_skeletonAnim;
VisualActor *_visual;
ItemVisual *_actionItem;
bool _shouldResetItem;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_ANIM_H

View File

@@ -0,0 +1,209 @@
/* 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/animhierarchy.h"
#include "common/debug.h"
#include "common/random.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/bonesmesh.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/textureset.h"
#include "engines/stark/services/services.h"
namespace Stark {
namespace Resources {
AnimHierarchy::~AnimHierarchy() {
}
AnimHierarchy::AnimHierarchy(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_currentActivity(0),
_currentAnim(nullptr),
_field_5C(0),
_idleActionsFrequencySum(0) {
_type = TYPE;
}
void AnimHierarchy::readData(Formats::XRCReadStream *stream) {
_animationReferences.clear();
uint32 refCount = stream->readUint32LE();
for (uint32 i = 0; i < refCount; i++) {
_animationReferences.push_back(stream->readResourceReference());
}
_parentAnimHierarchyReference = stream->readResourceReference();
_field_5C = stream->readFloatLE();
}
void AnimHierarchy::onAllLoaded() {
Object::onAllLoaded();
loadActivityAnimations();
loadIdleAnimations();
}
void AnimHierarchy::loadActivityAnimations() {
AnimHierarchy *parentHierarchy = _parentAnimHierarchyReference.resolve<AnimHierarchy>();
// Activity animations are inherited from the parent ...
if (parentHierarchy) {
_activityAnimations = parentHierarchy->_activityAnimations;
}
// ... but can be overridden
for (uint i = 0; i < _animationReferences.size(); i++) {
Anim *anim = _animationReferences[i].resolve<Anim>();
bool inserted = false;
for (uint j = 0; j < _activityAnimations.size(); j++) {
if (_activityAnimations[j]->getActivity() == anim->getActivity()) {
_activityAnimations[j] = anim;
inserted = true;
}
}
if (!inserted) {
_activityAnimations.push_back(anim);
}
}
}
void AnimHierarchy::loadIdleAnimations() {
AnimHierarchy *parentHierarchy = _parentAnimHierarchyReference.resolve<AnimHierarchy>();
if (parentHierarchy) {
_idleAnimations = parentHierarchy->_idleAnimations;
}
for (uint i = 0; i < _animationReferences.size(); i++) {
Anim *anim = _animationReferences[i].resolve<Anim>();
if (anim->getActivity() == Anim::kActorActivityIdleAction) {
_idleAnimations.push_back(anim);
}
}
_idleActionsFrequencySum = 0;
for (uint i = 0; i < _idleAnimations.size(); i++) {
_idleActionsFrequencySum += _idleAnimations[i]->getIdleActionFrequency();
}
}
void AnimHierarchy::setItemAnim(ItemVisual *item, int32 activity) {
unselectItemAnim(item);
_currentActivity = activity;
selectItemAnim(item);
}
void AnimHierarchy::unselectItemAnim(ItemVisual *item) {
if (_currentAnim && _currentAnim->isInUse()) {
_currentAnim->removeFromItem(item);
}
_currentAnim = nullptr;
}
void AnimHierarchy::selectItemAnim(ItemVisual *item) {
// Search for an animation with the appropriate index
for (uint i = 0; i < _activityAnimations.size(); i++) {
if (_activityAnimations[i]->getActivity() == _currentActivity) {
_currentAnim = _activityAnimations[i];
break;
}
}
// Default to the first animation
if (!_currentAnim && !_activityAnimations.empty()) {
_currentAnim = _activityAnimations[0];
}
if (!_currentAnim) {
error("Failed to set an animation for item %s", item->getName().c_str());
}
if (!_currentAnim->isInUse()) {
_currentAnim->applyToItem(item);
}
}
Anim *AnimHierarchy::getCurrentAnim() {
return _currentAnim;
}
BonesMesh *AnimHierarchy::findBonesMesh() {
return findChild<BonesMesh>();
}
TextureSet *AnimHierarchy::findTextureSet(uint32 textureType) {
return findChildWithSubtype<TextureSet>(textureType);
}
Anim *AnimHierarchy::getAnimForActivity(uint32 activity) {
// Search for an animation with the appropriate use
for (uint i = 0; i < _activityAnimations.size(); i++) {
if (_activityAnimations[i]->getActivity() == activity) {
return _activityAnimations[i];
}
}
return nullptr;
}
Visual *AnimHierarchy::getVisualForUsage(uint32 usage) {
Anim *anim = getAnimForActivity(usage);
if (anim) {
return anim->getVisual();
}
return nullptr;
}
Anim *AnimHierarchy::getIdleActionAnim() const {
if (_idleActionsFrequencySum == 0) {
return nullptr; // There are no idle animations
}
int pick = StarkRandomSource->getRandomNumber(_idleActionsFrequencySum - 1);
for (uint i = 0; i < _idleAnimations.size(); i++) {
pick -= _idleAnimations[i]->getIdleActionFrequency();
if (pick < 0) {
return _idleAnimations[i];
}
}
return nullptr;
}
void AnimHierarchy::printData() {
for (uint i = 0; i < _animationReferences.size(); i++) {
debug("anim %d: %s", i, _animationReferences[i].describe().c_str());
}
debug("animHierarchy: %s", _parentAnimHierarchyReference.describe().c_str());
debug("field_5C: %f", _field_5C);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,110 @@
/* 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 STARK_RESOURCES_ANIM_HIERARCHY_H
#define STARK_RESOURCES_ANIM_HIERARCHY_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
class Visual;
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Anim;
class BonesMesh;
class ItemVisual;
class TextureSet;
/**
* An animation hierarchy is a container resource referencing the available
* animations for an item.
*
* This resource keeps track of the currently selected animation.
*/
class AnimHierarchy : public Object {
public:
static const Type::ResourceType TYPE = Type::kAnimHierarchy;
AnimHierarchy(Object *parent, byte subType, uint16 index, const Common::String &name);
~AnimHierarchy() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
/** Set and apply the current animation kind for an item */
void setItemAnim(ItemVisual *item, int32 activity);
/** Unselect the current animation and remove it from an item */
void unselectItemAnim(ItemVisual *item);
/** Apply the current animation to an item */
void selectItemAnim(ItemVisual *item);
/** Obtain the currently selected animation */
Anim *getCurrentAnim();
/** Retrieve the first bone mesh from the anim hierarchy children, if any */
BonesMesh *findBonesMesh();
/**
* Retrieve the first texture of the appropriate type from the anim
* hierarchy children, if any
*/
TextureSet *findTextureSet(uint32 textureType);
Visual *getVisualForUsage(uint32 usage);
/** Randomize an idle action animation */
Anim *getIdleActionAnim() const;
protected:
void loadActivityAnimations();
void loadIdleAnimations();
Anim *getAnimForActivity(uint32 activity);
void printData() override;
Common::Array<ResourceReference> _animationReferences;
Common::Array<Anim *> _activityAnimations;
Common::Array<Anim *> _idleAnimations;
ResourceReference _parentAnimHierarchyReference;
float _field_5C;
uint32 _currentActivity;
Anim *_currentAnim;
uint32 _idleActionsFrequencySum;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_ANIM_HIERARCHY_H

View File

@@ -0,0 +1,211 @@
/* 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/animscript.h"
#include "common/debug.h"
#include "common/random.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/container.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
AnimScript::~AnimScript() {
}
AnimScript::AnimScript(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_anim(nullptr),
_nextItemIndex(-1),
_msecsToNextUpdate(0),
_done(false) {
_type = TYPE;
}
void AnimScript::onAllLoaded() {
Object::onAllLoaded();
_anim = Object::cast<Anim>(_parent);
_items = listChildren<AnimScriptItem>();
if (!_items.empty()) {
// Setup the next item to the first
_nextItemIndex = 0;
}
}
void AnimScript::onGameLoop() {
Object::onGameLoop();
if (!_anim || !_anim->isInUse() || _nextItemIndex == -1) {
// The script is disabled, do nothing
return;
}
int executedCommandCount = 0;
while (_msecsToNextUpdate <= (int32)StarkGlobal->getMillisecondsPerGameloop()) {
bool goingBackwards = false;
AnimScriptItem *item = _items[_nextItemIndex];
_msecsToNextUpdate += item->getDuration();
switch (item->getOpcode()) {
case AnimScriptItem::kDisplayFrame:
_anim->selectFrame(item->getOperand());
goToNextItem();
break;
case AnimScriptItem::kPlayAnimSound: {
Container *sounds = _parent->findChildWithSubtype<Container>(Container::kSounds);
Sound *sound = sounds->findChildWithOrder<Sound>(item->getOperand());
sound->play();
goToNextItem();
break;
}
case AnimScriptItem::kGoToItem:
if (item->getOperand() <= (uint32) _nextItemIndex) {
goingBackwards = true;
}
_nextItemIndex = item->getOperand();
break;
case AnimScriptItem::kDisplayRandomFrame: {
uint32 startFrame = item->getOperand() >> 16;
uint32 endFrame = item->getOperand() & 0xFFFF;
uint32 frame = StarkRandomSource->getRandomNumberRng(startFrame, endFrame);
_anim->selectFrame(frame);
goToNextItem();
break;
}
case AnimScriptItem::kSleepRandomDuration: {
uint duration = StarkRandomSource->getRandomNumber(item->getOperand());
_msecsToNextUpdate += duration;
goToNextItem();
break;
}
case AnimScriptItem::kPlayStockSound: {
Location *location = StarkGlobal->getCurrent()->getLocation();
Sound *sound = location->findStockSound(item->getOperand());
if (sound) {
sound->play();
}
goToNextItem();
break;
}
default:
error("Unknown anim script type %d", item->getOpcode());
}
if (_nextItemIndex == 0 || goingBackwards) {
_done = true;
}
executedCommandCount++;
if (executedCommandCount >= 10) {
debugC(kDebugAnimation, "Potential infinite loop in anim script %s", getName().c_str());
break;
}
}
_msecsToNextUpdate -= StarkGlobal->getMillisecondsPerGameloop();
}
void AnimScript::goToNextItem() {
_nextItemIndex += 1;
_nextItemIndex %= _items.size();
}
void AnimScript::goToScriptItem(AnimScriptItem *item) {
_nextItemIndex = findItemIndex(item);
_msecsToNextUpdate = 0;
_done = false;
if (item && item->getOpcode() == AnimScriptItem::kDisplayFrame) {
_anim->selectFrame(item->getOperand());
}
}
bool AnimScript::hasReached(AnimScriptItem *item) {
int32 index = findItemIndex(item);
return _nextItemIndex >= index;
}
bool AnimScript::isDone() const {
return _done;
}
int32 AnimScript::findItemIndex(AnimScriptItem *item) {
if (!item) {
return 0;
}
for (uint i = 0; i < _items.size(); i++) {
if (_items[i] == item) {
return i;
}
}
return 0;
}
void AnimScript::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_nextItemIndex);
if (serializer->isLoading()) {
_msecsToNextUpdate = 0;
}
}
AnimScriptItem::~AnimScriptItem() {
}
AnimScriptItem::AnimScriptItem(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_opcode(0),
_duration(0),
_operand(0) {
_type = TYPE;
}
void AnimScriptItem::readData(Formats::XRCReadStream *stream) {
_opcode = stream->readUint32LE();
_duration = stream->readUint32LE();
_operand = stream->readUint32LE();
}
void AnimScriptItem::printData() {
debug("op: %d, duration: %d ms, operand: %d", _opcode, _duration, _operand);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,127 @@
/* 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 STARK_RESOURCES_ANIM_SCRIPT_H
#define STARK_RESOURCES_ANIM_SCRIPT_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Anim;
class AnimScriptItem;
/**
* Animation scripts control the currently displayed frame for images animation
* resources.
*
* Animation scripts contain animation script items defining which frames
* should be displayed and when.
*
* Animation scripts also allow to play sounds.
*/
class AnimScript : public Object {
public:
static const Type::ResourceType TYPE = Type::kAnimScript;
AnimScript(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~AnimScript();
// Resource API
void onAllLoaded() override;
void onGameLoop() override;
void saveLoad(ResourceSerializer *serializer) override;
/** Go to a script item. Cancel any delay so that it is shown immediately. */
void goToScriptItem(AnimScriptItem *item);
/** Is the current script item later in the script when compared to the specified one? */
bool hasReached(AnimScriptItem *item);
/** Has the script completed playing the last script item at least once since started? */
bool isDone() const;
protected:
void goToNextItem();
int32 findItemIndex(AnimScriptItem *item);
Anim *_anim;
Common::Array<AnimScriptItem *> _items;
int32 _nextItemIndex;
int32 _msecsToNextUpdate;
bool _done;
};
/**
* Animation script element
*
* Has a type defining the operation to perform,
* an argument and a duration.
*/
class AnimScriptItem : public Object {
public:
static const Type::ResourceType TYPE = Type::kAnimScriptItem;
enum Opcodes {
kDisplayFrame = 0,
kPlayAnimSound = 1,
kGoToItem = 2,
kDisplayRandomFrame = 3,
kSleepRandomDuration = 4,
kPlayStockSound = 5
};
AnimScriptItem(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~AnimScriptItem();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
/** Obtain the operation code */
uint32 getOpcode() const { return _opcode; }
/** Obtain the operation parameter */
uint32 getOperand() const { return _operand; }
/** Obtain the operation duration */
uint32 getDuration() const { return _duration; }
protected:
void printData() override;
uint32 _opcode;
uint32 _operand;
uint32 _duration;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_ANIM_SCRIPT_H

View File

@@ -0,0 +1,101 @@
/* 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/animsoundtrigger.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
namespace Stark {
namespace Resources {
AnimSoundTrigger::~AnimSoundTrigger() {
}
AnimSoundTrigger::AnimSoundTrigger(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_soundStockType(0),
_soundTriggerTime(0),
_anim(nullptr),
_alreadyPlayed(false),
_timeRemainingBeforeLoop(34) {
_type = TYPE;
}
void AnimSoundTrigger::onAllLoaded() {
Object::onAllLoaded();
_anim = Object::cast<AnimSkeleton>(_parent);
}
void AnimSoundTrigger::onGameLoop() {
Object::onGameLoop();
if (!_anim || !_anim->isInUse()) {
return;
}
if (_alreadyPlayed && _anim->getCurrentTime() < 33) {
// Animation loop detected, reset
_alreadyPlayed = false;
}
if ((!_alreadyPlayed && _anim->getCurrentTime() >= _soundTriggerTime) || _timeRemainingBeforeLoop < 33) {
if (_timeRemainingBeforeLoop >= 33) {
_alreadyPlayed = true;
}
if (_subType == kAnimTriggerSound) {
Location *location = StarkGlobal->getCurrent()->getLocation();
Sound *sound = location->findStockSound(_soundStockType);
if (sound && !StarkGlobal->isFastForward()) {
// TODO: If the location has a 3D layer set the source position of the sound to the item position
sound->stop();
sound->play();
}
} else {
warning("Unknown animation trigger subtype '%d'", _subType);
}
}
// Special handling for trigger times right before the animation loop point
if (!_alreadyPlayed && _soundTriggerTime - _anim->getCurrentTime() < 33) {
_timeRemainingBeforeLoop = _anim->getRemainingTime();
} else {
_timeRemainingBeforeLoop = 34;
}
}
void AnimSoundTrigger::readData(Formats::XRCReadStream *stream) {
_soundTriggerTime = stream->readUint32LE();
_soundStockType = stream->readUint32LE();
}
void AnimSoundTrigger::printData() {
debug("triggerTime: %d", _soundTriggerTime);
debug("soundStockType: %d", _soundStockType);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,73 @@
/* 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 STARK_RESOURCES_ANIM_SOUND_TRIGGER_H
#define STARK_RESOURCES_ANIM_SOUND_TRIGGER_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class AnimSkeleton;
/**
* An AnimSoundTrigger plays a sound when a certain time of an animation is reached
*
* The sound is played at most once per animation loop.
*/
class AnimSoundTrigger : public Object {
public:
static const Type::ResourceType TYPE = Type::kAnimSoundTrigger;
enum SubType {
kAnimTriggerSound = 1
};
AnimSoundTrigger(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~AnimSoundTrigger();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void printData() override;
void onAllLoaded() override;
void onGameLoop() override;
private:
uint32 _soundStockType;
uint32 _soundTriggerTime;
AnimSkeleton *_anim;
bool _alreadyPlayed;
uint _timeRemainingBeforeLoop;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_ANIM_SOUND_TRIGGER_H

View File

@@ -0,0 +1,66 @@
/* 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/bonesmesh.h"
#include "engines/stark/model/animhandler.h"
#include "engines/stark/model/model.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/services.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
BonesMesh::~BonesMesh() {
delete _model;
}
BonesMesh::BonesMesh(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_model(nullptr) {
_type = TYPE;
}
void BonesMesh::readData(Formats::XRCReadStream *stream) {
_filename = Common::Path(stream->readString());
_archiveName = stream->getArchiveName();
}
void BonesMesh::onPostRead() {
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_filename, _archiveName);
_model = new Model();
_model->readFromStream(stream);
delete stream;
}
Model *BonesMesh::getModel() {
return _model;
}
void BonesMesh::printData() {
debug("filename: %s", _filename.toString().c_str());
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,69 @@
/* 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 STARK_RESOURCES_BONES_MESH_H
#define STARK_RESOURCES_BONES_MESH_H
#include "common/path.h"
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
class Model;
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* Bone mesh resources reference a mesh usable by actor resources
*/
class BonesMesh : public Object {
public:
static const Type::ResourceType TYPE = Type::kBonesMesh;
BonesMesh(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~BonesMesh();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onPostRead() override;
/** Obtain the mesh object */
Model *getModel();
protected:
void printData() override;
Common::Path _filename;
Common::Path _archiveName;
Model *_model;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_BONES_MESH_H

View File

@@ -0,0 +1,65 @@
/* 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/bookmark.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
namespace Stark {
namespace Resources {
Bookmark::~Bookmark() {
}
Bookmark::Bookmark(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name) {
_type = TYPE;
}
Math::Vector3d Bookmark::getPosition() const {
Floor *floor = StarkGlobal->getCurrent()->getFloor();
Math::Vector3d position = _position;
int32 floorFaceIndex = floor->findFaceContainingPoint(position);
floor->computePointHeightInFace(position, floorFaceIndex);
return position;
}
void Bookmark::readData(Formats::XRCReadStream *stream) {
_position.x() = stream->readFloatLE();
_position.y() = stream->readFloatLE();
_position.z() = 0;
}
void Bookmark::printData() {
Common::StreamDebug debug = streamDbg();
debug << "position: " << _position << "\n";
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,66 @@
/* 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 STARK_RESOURCES_BOOKMARK_H
#define STARK_RESOURCES_BOOKMARK_H
#include "common/str.h"
#include "math/vector3d.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* Bookmark resources are handles for a position on the floor field.
*
* The height value is not set, it needs to be retrieved by interpolation
* from the floor field.
*/
class Bookmark : public Object {
public:
static const Type::ResourceType TYPE = Type::kBookmark;
Bookmark(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Bookmark();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
/** Obtain the position */
Math::Vector3d getPosition() const;
protected:
void printData() override;
Math::Vector3d _position;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_BOOKMARK_H

View File

@@ -0,0 +1,103 @@
/* 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/camera.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/scene.h"
#include "engines/stark/services/services.h"
namespace Stark {
namespace Resources {
Camera::~Camera() {
}
Camera::Camera(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_f1(0),
_fov(45),
_nearClipPlane(100.0),
_farClipPlane(64000.0) {
_type = TYPE;
}
void Camera::setClipPlanes(float near, float far) {
_nearClipPlane = near;
_farClipPlane = far;
}
void Camera::readData(Formats::XRCReadStream *stream) {
_position = stream->readVector3();
_lookDirection = stream->readVector3();
_f1 = stream->readFloatLE();
_fov = stream->readFloatLE();
_viewSize = stream->readRect();
_v4 = stream->readVector3();
}
void Camera::onAllLoaded() {
Object::onAllLoaded();
// Compute scroll coordinates bounds
Common::Point maxScroll;
maxScroll.x = _viewSize.width() - 640;
maxScroll.y = _viewSize.height() - 365;
Location *location = findParent<Location>();
location->initScroll(maxScroll);
}
void Camera::onEnterLocation() {
Object::onEnterLocation();
// Setup the camera
StarkScene->initCamera(_position, _lookDirection, _fov, _viewSize, _nearClipPlane, _farClipPlane);
// Scroll the camera to its initial position
Location *location = findParent<Location>();
location->setScrollPosition(location->getScrollPosition());
}
Math::Angle Camera::getHorizontalAngle() const {
Math::Angle lookDirectionAngle = Math::Vector3d::angle(_lookDirection, Math::Vector3d(1.0, 0.0, 0.0));
Math::Vector3d cross = Math::Vector3d::crossProduct(_lookDirection, Math::Vector3d(1.0, 0.0, 0.0));
if (cross.z() < 0) {
return -lookDirectionAngle;
} else {
return lookDirectionAngle;
}
}
void Camera::printData() {
Common::StreamDebug debug = streamDbg();
debug << "position: " << _position << "\n";
debug << "lookDirection: " << _lookDirection << "\n";
debug << "f1: " << _f1 << "\n";
debug << "fov: " << _fov << "\n";
debug << "viewSize:" << _viewSize.left << _viewSize.top << _viewSize.right << _viewSize.bottom << "\n";
debug << "v4: " << _v4 << "\n";
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,82 @@
/* 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 STARK_RESOURCES_CAMERA_H
#define STARK_RESOURCES_CAMERA_H
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
#include "math/angle.h"
#include "math/vector3d.h"
#include "math/vector4d.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* Camera resources define the camera position, perspective parameters,
* and look at direction.
*/
class Camera : public Object {
public:
static const Type::ResourceType TYPE = Type::kCamera;
Camera(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Camera();
// Resource API
void onAllLoaded() override;
void onEnterLocation() override;
/** Define the near and far clip planes distances */
void setClipPlanes(float near, float far);
/** Compute the angle between the X vector and the look at direction in the horizontal plane */
Math::Angle getHorizontalAngle() const;
protected:
void readData(Formats::XRCReadStream *stream) override;
void printData() override;
Math::Vector3d _position;
Math::Vector3d _lookDirection;
float _f1;
float _fov;
Common::Rect _viewSize;
Math::Vector3d _v4;
float _nearClipPlane;
float _farClipPlane;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_CAMERA_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,294 @@
/* 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 STARK_RESOURCES_COMMAND_H
#define STARK_RESOURCES_COMMAND_H
#include "common/array.h"
#include "common/str.h"
#include "math/vector3d.h"
#include "engines/stark/resources/object.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
class ResourceReference;
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Script;
/**
* Command resources are script operations.
*
* The operation code is the resource subtype.
*
* The operation arguments can be integers, strings or resource references.
*/
class Command : public Object {
public:
static const Type::ResourceType TYPE = Type::kCommand;
Command(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Command();
enum SubType {
kCommandBegin = 0,
kCommandEnd = 1,
kScriptCall = 2,
kDialogCall = 3,
kSetInteractiveMode = 4,
kLocationGoTo = 5,
kWalkTo = 7,
kGameLoop = 8,
kScriptPause = 9,
kScriptPauseRandom = 10,
kScriptPauseSkippable = 11,
kScriptAbort = 13,
kExit2DLocation = 16,
kGoto2DLocation = 17,
kRumbleScene = 19,
kFadeScene = 20,
kSwayScene = 21,
kLocationGoToNewCD = 22,
kGameEnd = 23,
kInventoryOpen = 24,
kFloatScene = 25,
kBookOfSecretsOpen = 26,
kDoNothing = 80,
kItem3DPlaceOn = 81,
kItem3DWalkTo = 82,
kItem3DFollowPath = 83,
kItemLookAt = 84,
kItem2DFollowPath = 86,
kItemEnable = 87,
kItemSetActivity = 88,
kItemSelectInInventory = 89,
kUseAnimHierarchy = 92,
kPlayAnimation = 93,
kScriptEnable = 94,
kShowPlay = 95,
kKnowledgeSetBoolean = 96,
kKnowledgeSetInteger = 100,
kKnowledgeAddInteger = 101,
kEnableFloorField = 103,
kPlayAnimScriptItem = 104,
kItemAnimFollowPath = 105,
kKnowledgeAssignBool = 107,
kKnowledgeAssignInteger = 110,
kLocationScrollTo = 111,
kSoundPlay = 112,
kKnowledgeSetIntRandom = 115,
kKnowledgeSubValue = 117,
kItemLookDirection = 118,
kStopPlayingSound = 119,
kLayerGoTo = 120,
kLayerEnable = 121,
kLocationScrollSet = 122,
kFullMotionVideoPlay = 123,
kAnimSetFrame = 125,
kKnowledgeAssignNegatedBool = 126,
kDiaryEnableEntry = 127,
kPATChangeTooltip = 128,
kSoundChange = 129,
kLightSetColor = 130,
kLightFollowPath = 131,
kItem3DRunTo = 132,
kItemPlaceDirection = 133,
kItemRotateDirection = 134,
kActivateTexture = 135,
kActivateMesh = 136,
kItem3DSetWalkTarget = 137,
kSpeakWithoutTalking = 139,
kIsOnFloorField = 162,
kIsItemEnabled = 163,
kIsScriptEnabled = 165,
kIsKnowledgeBooleanSet = 166,
kIsKnowledgeIntegerInRange = 170,
kIsKnowledgeIntegerAbove = 171,
kIsKnowledgeIntegerEqual = 172,
kIsKnowledgeIntegerLower = 173,
kIsScriptActive = 174,
kIsRandom = 175,
kIsAnimScriptItemReached = 176,
kIsItemOnPlace = 177,
kIsAnimPlaying = 179,
kIsItemActivity = 180,
kIsItemNearPlace = 183,
kIsAnimAtTime = 185,
kIsLocation2D = 186,
kIsInventoryOpen = 187
};
struct Argument {
enum Type {
kTypeInteger1 = 1,
kTypeInteger2 = 2,
kTypeResourceReference = 3,
kTypeString = 4
};
uint32 type;
uint32 intValue;
Common::String stringValue;
ResourceReference referenceValue;
};
/** Execute the command */
Command *execute(uint32 callMode, Script *script);
/** Obtain the next command to be executed */
Command *nextCommand();
/** Obtain the next command to be executed, depending on a predicate */
Command *nextCommandIf(bool predicate);
/** Get the command's list of arguments */
Common::Array<Argument> getArguments() const;
/** Resume the opcode ItemSetActivity after it has stopped waiting for the action anim to complete */
void resumeItemSetActivity();
protected:
void readData(Formats::XRCReadStream *stream) override;
Command *resolveArgumentSiblingReference(const Argument &argument);
Math::Vector3d getObjectPosition(const ResourceReference &targetRef, int32 *floorFace = nullptr);
Command *opScriptBegin();
Command *opScriptCall(Script *script, const ResourceReference &scriptRef, int32 synchronous);
Command *opDialogCall(Script *script, const ResourceReference &dialogRef, int32 suspend);
Command *opSetInteractiveMode(bool enabled);
Command *opLocationGoTo(const Common::String &level, const Common::String &location, const ResourceReference &bookmarkRef, int32 direction);
Command *opWalkTo(Script *script, const ResourceReference &objectRef, int32 suspend);
Command *opScriptPauseGameLoop(Script *script, int32 count);
Command *opScriptPause(Script *script, const ResourceReference &durationRef);
Command *opScriptPauseRandom(Script *script, const ResourceReference &itemRef);
Command *opScriptPauseSkippable(Script *script, const ResourceReference &durationRef);
Command *opScriptAbort(ResourceReference scriptRef, bool disable);
Command *opExit2DLocation();
Command *opGoto2DLocation(const Common::String &level, const Common::String &location);
Command *opRumbleScene(Script *script, int32 rumbleDuration, int32 pause);
Command *opFadeScene(Script *script, bool fadeOut, int32 fadeDuration, bool pause);
Command *opSwayScene(int32 periodMs, int32 angleIn, int32 amplitudeIn, int32 offsetIn);
Command *opGameEnd();
Command *opInventoryOpen(bool open);
Command *opFloatScene(int32 periodMs, int32 amplitudeIn, int32 offsetIn);
Command *opBookOfSecretsOpen();
Command *opDoNothing();
Command *opItem3DPlaceOn(const ResourceReference &itemRef, const ResourceReference &targetRef);
Command *opItem3DWalkTo(Script *script, const ResourceReference &itemRef, const ResourceReference &targetRef, bool suspend);
Command *opItemFollowPath(Script *script, ResourceReference itemRef, ResourceReference pathRef, uint32 speed, uint32 suspend);
Command *opItemLookAt(Script *script, const ResourceReference &itemRef, const ResourceReference &objRef, bool suspend, int32 unknown);
Command *opItemEnable(const ResourceReference &itemRef, int32 enable);
Command *opItemSetActivity(Script *script, const ResourceReference &itemRef, int32 animActivity, bool wait);
Command *opItemSelectInInventory(const ResourceReference &itemRef);
Command *opUseAnimHierachy(const ResourceReference &animHierRef);
Command *opPlayAnimation(Script *script, const ResourceReference &animRef, bool suspend);
Command *opScriptEnable(const ResourceReference &scriptRef, int32 enable);
Command *opShowPlay(Script *script, const ResourceReference &ref, int32 suspend);
Command *opKnowledgeSetBoolean(const ResourceReference &knowledgeRef, int32 enable);
Command *opKnowledgeSetInteger(const ResourceReference &knowledgeRef, int32 value);
Command *opKnowledgeSetIntRandom(const ResourceReference &knowledgeRef, uint32 min, uint32 max);
Command *opKnowledgeAddInteger(const ResourceReference &knowledgeRef, int32 increment);
Command *opKnowledgeSubValue(const ResourceReference &knowledgeRef, const ResourceReference &valueRef);
Command *opEnableFloorField(const ResourceReference &floorFieldRef, bool enable);
Command *opPlayAnimScriptItem(Script *script, const ResourceReference &animScriptItemRef, int32 suspend);
Command *opItemAnimFollowPath(Script *script, const ResourceReference &animRef, const ResourceReference &pathRef, int32 speed, bool suspend);
Command *opKnowledgeAssignBool(const ResourceReference &knowledgeRef1, const ResourceReference &knowledgeRef2);
Command *opKnowledgeAssignNegatedBool(const ResourceReference &knowledgeRef1, const ResourceReference &knowledgeRef2);
Command *opKnowledgeAssignInteger(const ResourceReference &knowledgeRef1, const ResourceReference &knowledgeRef2);
Command *opLocationScrollTo(Script *script, const ResourceReference &scrollRef, bool suspend);
Command *opSoundPlay(Script *script, const ResourceReference &soundRef, int32 suspend);
Command *opItemLookDirection(Script *script, const ResourceReference &itemRef, int32 direction, bool suspend);
Command *opStopPlayingSound(const ResourceReference &soundRef);
Command *opLayerGoTo(const ResourceReference &layerRef);
Command *opLayerEnable(const ResourceReference &layerRef, int32 enable);
Command *opLocationScrollSet(const ResourceReference &scrollRef);
Command *opFullMotionVideoPlay(Script *script, const ResourceReference &movieRef, int32 unknown);
Command *opAnimSetFrame(const ResourceReference &animRef, const ResourceReference &knowledgeRef);
Command *opDiaryEnableEntry(const ResourceReference &knowledgeRef);
Command *opPATChangeTooltip(const ResourceReference &patRef, const ResourceReference &stringRef);
Command *opSoundChange(Script *script, const ResourceReference &soundRef, int32 volume, int32 pan, int32 duration, bool pause);
Command *opLightSetColor(const ResourceReference &lightRef, int32 red, int32 green, int32 blue);
Command *opLightFollowPath(Script *script, const ResourceReference &itemRef, const ResourceReference &lightRef, const ResourceReference &pathRef, int32 speed, bool suspend);
Command *opItem3DRunTo(Script *script, const ResourceReference &itemRef, const ResourceReference &targetRef, int32 suspend);
Command *opItemPlaceDirection(const ResourceReference &itemRef, int32 direction);
Command *opItemRotateDirection(Script *script, const ResourceReference &itemRef, int32 direction, int32 speed, bool suspend);
Command *opActivateTexture(const ResourceReference &textureRef);
Command *opActivateMesh(const ResourceReference &meshRef);
Command *opItem3DSetWalkTarget(const ResourceReference &itemRef, const ResourceReference &targetRef);
Command *opSpeakWithoutTalking(Script *script, const ResourceReference &speechRef, int32 unknown);
Command *opIsOnFloorField(const ResourceReference &itemRef, const ResourceReference &floorFieldRef);
Command *opIsItemEnabled(const ResourceReference &itemRef);
Command *opIsScriptEnabled(const ResourceReference &scriptRef);
Command *opIsKnowledgeBooleanSet(const ResourceReference &knowledgeRef);
Command *opIsKnowledgeIntegerInRange(const ResourceReference &knowledgeRef, int32 min, int32 max);
Command *opIsKnowledgeIntegerAbove(const ResourceReference &knowledgeRef, int32 value);
Command *opIsKnowledgeIntegerEqual(const ResourceReference &knowledgeRef, int32 value);
Command *opIsKnowledgeIntegerLower(const ResourceReference &knowledgeRef, int32 value);
Command *opIsScriptActive(const ResourceReference &scriptRef);
Command *opIsRandom(int32 chance);
Command *opIsAnimScriptItemReached(const ResourceReference &animScriptItemRef);
Command *opIsItemNearPlace(const ResourceReference &itemRef, const ResourceReference &positionRef, int32 testDistance);
Command *opIsItemOnPlace(const ResourceReference &itemRef, const ResourceReference &positionRef);
Command *opIsAnimPlaying(const ResourceReference &animRef);
Command *opIsItemActivity(const ResourceReference &itemRef, int32 value);
Command *opIsAnimAtTime(const ResourceReference &animRef, int32 time);
Command *opIsLocation2D();
Command *opIsInventoryOpen();
Common::Array<Argument> _arguments;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_COMMAND_H

View File

@@ -0,0 +1,38 @@
/* 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/container.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
Container::~Container() {
}
Container::Container(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name) {
_type = TYPE;
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,52 @@
/* 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 STARK_RESOURCES_CONTAINER_H
#define STARK_RESOURCES_CONTAINER_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Resources {
/**
* Containers are holder resources for other resources of various kinds
*/
class Container : public Object {
public:
static const Type::ResourceType TYPE = Type::kContainer;
enum SubType {
kSounds = 5,
kStockSounds = 8
};
Container(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Container();
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_CONTAINER_H

View File

@@ -0,0 +1,334 @@
/* 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/dialog.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/knowledge.h"
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/speech.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
Dialog::~Dialog() {
}
Dialog::Dialog(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_character(0),
_hasAskAbout(0) {
_type = TYPE;
}
void Dialog::readData(Formats::XRCReadStream *stream) {
Object::readData(stream);
_hasAskAbout = stream->readUint32LE();
_character = stream->readUint32LE();
uint32 numTopics = stream->readUint32LE();
for (uint32 i = 0; i < numTopics; i++) {
Topic topic;
topic._removeOnceDepleted = stream->readBool();
uint32 numReplies = stream->readUint32LE();
for (uint j = 0; j < numReplies; j++) {
Reply reply;
reply._conditionType = stream->readUint32LE();
reply._conditionReference = stream->readResourceReference();
reply._conditionScriptReference = stream->readResourceReference();
reply._conditionReversed = stream->readUint32LE();
reply._field_88 = stream->readUint32LE();
reply._minChapter = stream->readUint32LE();
reply._maxChapter = stream->readUint32LE();
reply._noCaption = stream->readUint32LE();
reply._nextDialogIndex = stream->readSint32LE();
reply._nextScriptReference = stream->readResourceReference();
uint32 numLines = stream->readUint32LE();
for (uint k = 0; k < numLines; k++) {
reply._lines.push_back(stream->readResourceReference());
reply._lines.push_back(stream->readResourceReference());
}
topic._replies.push_back(reply);
}
_topics.push_back(topic);
}
}
void Dialog::saveLoad(ResourceSerializer *serializer) {
for (uint i = 0; i < _topics.size(); i++) {
serializer->syncAsSint32LE(_topics[i]._currentReplyIndex);
}
}
void Dialog::printData() {
Object::printData();
debug("character: %d", _character);
debug("hasAskAbout: %d", _hasAskAbout);
for (uint32 i = 0; i < _topics.size(); i++) {
Topic &topic = _topics[i];
debug("topic[%d].removeOnceDepleted: %d", i, topic._removeOnceDepleted);
for (uint j = 0; j < topic._replies.size(); j++) {
Reply reply = topic._replies[j];
debug("topic[%d].reply[%d].conditionType: %d", i, j, reply._conditionType);
debug("topic[%d].reply[%d].conditionReference: %s", i, j, reply._conditionReference.describe().c_str());
debug("topic[%d].reply[%d].conditionScriptReference: %s", i, j, reply._conditionScriptReference.describe().c_str());
debug("topic[%d].reply[%d].conditionReversed: %d", i, j, reply._conditionReversed);
debug("topic[%d].reply[%d].minChapter: %d", i, j, reply._minChapter);
debug("topic[%d].reply[%d].maxChapter: %d", i, j, reply._maxChapter);
debug("topic[%d].reply[%d].noCaption: %d", i, j, reply._noCaption);
debug("topic[%d].reply[%d].field_88: %d", i, j, reply._field_88);
debug("topic[%d].reply[%d].nextScriptReference: %s", i, j, reply._nextScriptReference.describe().c_str());
debug("topic[%d].reply[%d].nextDialogIndex: %d", i, j, reply._nextDialogIndex);
for (uint k = 0; k < reply._lines.size(); k++) {
debug("topic[%d].reply[%d].line[%d]: %s", i, j, k, reply._lines[k].describe().c_str());
}
}
}
}
Dialog::TopicArray Dialog::listAvailableTopics() {
Common::Array<Dialog::Topic *> topics;
for (uint i = 0; i < _topics.size(); i++) {
Topic *topic = &_topics[i];
if (topic->getNextReplyIndex() < 0) {
continue;
}
topics.push_back(topic);
}
return topics;
}
Dialog::Topic::Topic() :
_removeOnceDepleted(true),
_currentReplyIndex(-1) {
}
int32 Dialog::Topic::getNextReplyIndex() const {
uint32 nextIndex = _currentReplyIndex + 1;
if (nextIndex >= _replies.size()) {
// No more replies ...
if (_removeOnceDepleted || _replies.empty()) {
// Don't show this topic
return -1;
} else {
// Repeat the last reply
nextIndex = _replies.size() - 1;
}
}
uint32 currentChapter = StarkGlobal->getCurrentChapter();
// Skip replies from previous chapters
while (nextIndex < _replies.size() && _replies[nextIndex]._maxChapter < currentChapter) {
nextIndex++;
}
if (nextIndex >= _replies.size()) {
// No more replies ...
if (_removeOnceDepleted || _replies.empty()) {
// Don't show this topic
return -1;
} else {
// Repeat the last reply
nextIndex = _replies.size() - 1;
}
}
// Chapter check
const Reply &reply = _replies[nextIndex];
if (currentChapter < reply._minChapter || currentChapter >= reply._maxChapter) {
return -1;
}
return nextIndex;
}
Dialog::Reply *Dialog::Topic::startReply(uint32 index) {
_currentReplyIndex = index;
Reply *reply = &_replies[_currentReplyIndex];
reply->start();
return reply;
}
Dialog::Reply *Dialog::Topic::getReply(uint32 index) {
return &_replies[index];
}
Common::String Dialog::Topic::getCaption() const {
int32 replyIndex = getNextReplyIndex();
if (replyIndex < 0) {
error("Trying to obtain the caption of a depleted dialog topic.");
}
const Reply &reply = _replies[replyIndex];
if (reply._lines.empty()) {
error("Trying to obtain the caption of a reply with no lines.");
}
Speech *speech = reply._lines[0].resolve<Speech>();
if (speech) {
return speech->getPhrase();
} else {
return "No Caption";
}
}
Dialog::Reply::Reply() :
_conditionReversed(0),
_field_88(0),
_minChapter(0),
_maxChapter(999),
_conditionType(0),
_noCaption(0),
_nextDialogIndex(-1),
_nextSpeechIndex(-1) {
}
void Dialog::Reply::start() {
if (_noCaption) {
_nextSpeechIndex = -1;
} else {
// Skip the first line when it is a caption
_nextSpeechIndex = 0;
}
goToNextLine();
}
void Dialog::Reply::goToNextLine() {
_nextSpeechIndex++;
while ((uint32)_nextSpeechIndex < _lines.size() && _lines[_nextSpeechIndex].empty()) {
_nextSpeechIndex++;
}
if ((uint32)_nextSpeechIndex >= _lines.size()) {
_nextSpeechIndex = -2; // No more lines
}
}
Speech *Dialog::Reply::getCurrentSpeech() {
if (_nextSpeechIndex < 0) {
return nullptr;
}
return _lines[_nextSpeechIndex].resolve<Speech>();
}
bool Dialog::Reply::checkCondition() const {
bool result;
switch (_conditionType) {
case kConditionTypeAlways:
result = true;
break;
case kConditionTypeNoOtherOptions:
result = true; // Will be removed from to the options later if some other options are available
break;
case kConditionTypeHasItem: {
Item *item = _conditionReference.resolve<Item>();
result = item->isEnabled();
break;
}
case kConditionTypeCheckValue4:
case kConditionTypeCheckValue5: {
Knowledge *condition = _conditionReference.resolve<Knowledge>();
result = condition->getBooleanValue();
break;
}
case kConditionTypeRunScriptCheckValue: {
Script *conditionScript = _conditionScriptReference.resolve<Script>();
conditionScript->execute(Resources::Script::kCallModeDialogAnswer);
Knowledge *condition = _conditionReference.resolve<Knowledge>();
result = condition->getBooleanValue();
break;
}
default:
warning("Unimplemented dialog reply condition %d", _conditionType);
result = true;
break;
}
if (_conditionReversed && (_conditionType == kConditionTypeHasItem
|| _conditionType == kConditionTypeCheckValue4
|| _conditionType == kConditionTypeCheckValue5
|| _conditionType == kConditionTypeRunScriptCheckValue)) {
result = !result;
}
return result;
}
bool Dialog::Reply::isLastOnly() const {
return _conditionType == kConditionTypeNoOtherOptions;
}
Dialog *Dialog::getNextDialog(Dialog::Reply *reply) {
if (reply->_nextDialogIndex < 0) {
return nullptr;
}
return _parent->findChildWithIndex<Dialog>(reply->_nextDialogIndex);
}
Script *Dialog::getNextScript(Dialog::Reply *reply) {
if (reply->_nextScriptReference.empty()) {
return nullptr;
}
return reply->_nextScriptReference.resolve<Script>();
}
Common::String Dialog::getDiaryTitle() const {
return _parent->getName();
}
int32 Dialog::getCharacter() const {
return _character;
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,166 @@
/* 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 STARK_RESOURCES_DIALOG_H
#define STARK_RESOURCES_DIALOG_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Speech;
class Script;
/**
* A dialog between two characters.
*
* Dialogs are made of a list of topics. Each topic has a list of
* possible answers, one of which is played when the player selects the topic,
* until all the possible answers have been played.
*
* Answers are made of a list of lines. All of the lines of an answer are played,
* one after the other when an answer is played. Lines reference Speech resources.
*/
class Dialog : public Object {
public:
static const Type::ResourceType TYPE = Type::kDialog;
Dialog(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Dialog();
/**
* A topic reply
*/
class Reply {
public:
Reply();
enum ConditionType {
kConditionTypeAlways = 0,
kConditionTypeNoOtherOptions = 1,
kConditionTypeHasItem = 3,
kConditionTypeCheckValue4 = 4,
kConditionTypeCheckValue5 = 5,
kConditionTypeRunScriptCheckValue = 6
};
/** Start playing the reply. Sets the current line to the first one */
void start();
/** Select the next line to be played */
void goToNextLine();
/** Obtain the Speech resource for the current line, or null if the reply has ended */
Speech *getCurrentSpeech();
/** Evaluates the reply's condition */
bool checkCondition() const;
/** Should this reply only be made available when there are no other options left? */
bool isLastOnly() const;
// Static data
Common::Array<ResourceReference> _lines;
uint32 _conditionType;
ResourceReference _conditionReference;
ResourceReference _conditionScriptReference;
uint32 _conditionReversed;
uint32 _field_88;
uint32 _minChapter;
uint32 _maxChapter;
uint32 _noCaption;
int32 _nextDialogIndex;
ResourceReference _nextScriptReference;
// State
int32 _nextSpeechIndex;
friend class Dialog;
};
/**
* A dialog topic
*/
class Topic {
public:
Topic();
/** Compute the next possible reply index after the currently selected reply */
int32 getNextReplyIndex() const;
/** Obtain the caption for the topic */
Common::String getCaption() const;
/** Select a reply from its index */
Reply *startReply(uint32 index);
/** Get the reply with the specified index */
Reply *getReply(uint32 index);
Common::Array<Reply> _replies;
bool _removeOnceDepleted;
int32 _currentReplyIndex;
friend class Dialog;
};
typedef Common::Array<Topic *> TopicArray;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void saveLoad(ResourceSerializer *serializer) override;
/** List the currently available topics for this Dialog */
TopicArray listAvailableTopics();
/** Obtain the Dialog which should be played at the outcome of this one, if any */
Dialog *getNextDialog(Reply *reply);
/** Obtain the Script which should be executed after this dialog, if any */
Script *getNextScript(Reply *reply);
/** Get the dialog's title as shown in the diary */
Common::String getDiaryTitle() const;
/** Get the character's id - index in the KnowledgeSet */
int32 getCharacter() const;
void printData() override;
Common::Array<Topic> _topics;
uint32 _character;
uint32 _hasAskAbout;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_DIALOG_H

View File

@@ -0,0 +1,53 @@
/* 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/direction.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
Direction::~Direction() {
}
Direction::Direction(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_field_34(0),
_field_38(0),
_field_3C(0) {
_type = TYPE;
}
void Direction::readData(Formats::XRCReadStream *stream) {
_field_34 = stream->readUint32LE();
_field_38 = stream->readUint32LE();
_field_3C = stream->readUint32LE();
}
void Direction::printData() {
debug("field_34: %d", _field_34);
debug("field_38: %d", _field_38);
debug("field_3C: %d", _field_3C);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,57 @@
/* 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 STARK_RESOURCES_DIRECTION_H
#define STARK_RESOURCES_DIRECTION_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Direction : public Object {
public:
static const Type::ResourceType TYPE = Type::kDirection;
Direction(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Direction();
void readData(Formats::XRCReadStream *stream) override;
protected:
void printData() override;
uint32 _field_34;
uint32 _field_38;
uint32 _field_3C;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_DIRECTION_H

View File

@@ -0,0 +1,339 @@
/* 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/floor.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/floorface.h"
#include "engines/stark/resources/floorfield.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
Floor::Floor(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_facesCount(0) {
_type = TYPE;
}
Floor::~Floor() {
}
Math::Vector3d Floor::getVertex(uint32 index) const {
return _vertices[index];
}
int32 Floor::findFaceContainingPoint(const Math::Vector3d &point) const {
for (uint32 i = 0; i < _faces.size(); i++) {
if (_faces[i]->hasVertices() && _faces[i]->isPointInside(point)) {
return i;
}
}
return -1;
}
void Floor::computePointHeightInFace(Math::Vector3d &point, uint32 faceIndex) const {
_faces[faceIndex]->computePointHeight(point);
}
int32 Floor::findFaceHitByRay(const Math::Ray &ray, Math::Vector3d &intersection) const {
for (uint32 i = 0; i < _faces.size(); i++) {
// TODO: Check the ray's intersection with an AABB first if this ends up being slow
if (_faces[i]->intersectRay(ray, intersection)) {
if (_faces[i]->isEnabled()) {
return i;
} else {
return -1; // Disabled faces block the ray
}
}
}
return -1;
}
int32 Floor::findFaceClosestToRay(const Math::Ray &ray, Math::Vector3d &center) const {
float minDistance = FLT_MAX;
int32 minFace = -1;
// For some reason, face 0 is not being considered
for (uint32 i = 1; i < _faces.size(); i++) {
if (_faces[i]->isEnabled() && _faces[i]->hasVertices()) {
float distance = _faces[i]->distanceToRay(ray);
if (distance < minDistance) {
minFace = i;
minDistance = distance;
}
}
}
if (minFace >= 0) {
center = _faces[minFace]->getCenter();
}
return minFace;
}
float Floor::getDistanceFromCamera(uint32 faceIndex) const {
FloorFace *face = _faces[faceIndex];
return face->getDistanceFromCamera();
}
FloorFace *Floor::getFace(uint32 index) const {
return _faces[index];
}
bool Floor::isSegmentInside(const Math::Line3d &segment) const {
// The segment is inside the floor if at least one of its extremities is,
// and it does not cross any floor border / disabled floor faces
int32 beginFace = findFaceContainingPoint(segment.begin());
if (beginFace < 0) {
// The segment begin point is not on the floor
return false;
}
if (!_faces[beginFace]->isEnabled()) {
// The segment begin point is not enabled
return false;
}
for (uint i = 0; i < _edges.size(); i++) {
const FloorEdge &edge = _edges[i];
if ((edge.isFloorBorder() || !edge.isEnabled()) && edge.intersectsSegment(this, segment)) {
return false;
}
}
return true;
}
void Floor::readData(Formats::XRCReadStream *stream) {
_facesCount = stream->readUint32LE();
uint32 vertexCount = stream->readUint32LE();
for (uint i = 0; i < vertexCount; i++) {
Math::Vector3d v = stream->readVector3();
_vertices.push_back(v);
}
}
void Floor::onAllLoaded() {
Object::onAllLoaded();
_faces = listChildren<FloorFace>();
buildEdgeList();
}
void Floor::saveLoad(ResourceSerializer *serializer) {
for (uint i = 0; i < _edges.size(); i++) {
_edges[i].saveLoad(serializer);
}
}
void Floor::buildEdgeList() {
_edges.clear();
// Add the triangle edges from all our faces
for (uint i = 0; i < _faces.size(); i++) {
if (_faces[i]->hasVertices()) {
addFaceEdgeToList(i, 2, 0);
addFaceEdgeToList(i, 0, 1);
addFaceEdgeToList(i, 1, 2);
}
}
// Add the edges to their faces
for (uint i = 0; i < _edges.size(); i++) {
int32 faceIndex1 = _edges[i].getFaceIndex1();
int32 faceIndex2 = _edges[i].getFaceIndex2();
if (faceIndex1 >= 0) {
_faces[faceIndex1]->addEdge(&_edges[i]);
}
if (faceIndex2 >= 0) {
_faces[faceIndex2]->addEdge(&_edges[i]);
}
}
// Build a list of neighbours for each edge
for (uint i = 0; i < _edges.size(); i++) {
_edges[i].buildNeighbours(this);
_edges[i].computeMiddle(this);
}
}
void Floor::addFaceEdgeToList(uint32 faceIndex, uint32 index1, uint32 index2) {
uint32 vertexIndex1 = _faces[faceIndex]->getVertexIndex(index1);
uint32 vertexIndex2 = _faces[faceIndex]->getVertexIndex(index2);
uint32 startIndex = MIN(vertexIndex1, vertexIndex2);
uint32 endIndex = MAX(vertexIndex1, vertexIndex2);
// Check if we already have an edge with the same vertices
for (uint i = 0; i < _edges.size(); i++) {
if (_edges[i].hasVertices(startIndex, endIndex)) {
_edges[i].setOtherFace(faceIndex);
return;
}
}
_edges.push_back(FloorEdge(startIndex, endIndex, faceIndex));
}
void Floor::enableFloorField(FloorField *floorfield, bool enable) {
for (uint i = 0; i < _faces.size(); i++) {
if (floorfield->hasFace(i)) {
_faces[i]->enable(enable);
}
}
}
void Floor::printData() {
debug("face count: %d", _facesCount);
Common::StreamDebug debug = streamDbg();
for (uint i = 0; i < _vertices.size(); i++) {
debug << i << ": " << _vertices[i] << "\n";
}
}
FloorEdge::FloorEdge(uint16 vertexIndex1, uint16 vertexIndex2, uint32 faceIndex1) :
_vertexIndex1(vertexIndex1),
_vertexIndex2(vertexIndex2),
_faceIndex1(faceIndex1),
_faceIndex2(-1),
_enabled(true) {
}
bool FloorEdge::hasVertices(uint16 vertexIndex1, uint16 vertexIndex2) const {
return _vertexIndex1 == vertexIndex1 && _vertexIndex2 == vertexIndex2;
}
void FloorEdge::setOtherFace(uint32 faceIndex) {
_faceIndex2 = faceIndex;
}
Common::Array<FloorEdge *> FloorEdge::getNeighbours() const {
return _neighbours;
}
float FloorEdge::costTo(const FloorEdge *other) const {
return _middle.getDistanceTo(other->_middle);
}
Math::Vector3d FloorEdge::getPosition() const {
return _middle;
}
void FloorEdge::buildNeighbours(const Floor *floor) {
_neighbours.clear();
if (_faceIndex1 >= 0) {
addNeighboursFromFace(floor->getFace(_faceIndex1));
}
if (_faceIndex2 >= 0) {
addNeighboursFromFace(floor->getFace(_faceIndex2));
}
}
void FloorEdge::addNeighboursFromFace(const FloorFace *face) {
Common::Array<FloorEdge *> faceEdges = face->getEdges();
for (uint i = 0; i < faceEdges.size(); i++) {
if (faceEdges[i] != this) {
_neighbours.push_back(faceEdges[i]);
}
}
}
void FloorEdge::computeMiddle(const Floor *floor) {
Math::Vector3d vertex1 = floor->getVertex(_vertexIndex1);
Math::Vector3d vertex2 = floor->getVertex(_vertexIndex2);
_middle = (vertex1 + vertex2) / 2.0;
}
int32 FloorEdge::getFaceIndex1() const {
return _faceIndex1;
}
int32 FloorEdge::getFaceIndex2() const {
return _faceIndex2;
}
bool FloorEdge::isFloorBorder() const {
return _faceIndex2 == -1;
}
bool FloorEdge::intersectLine2d(const Math::Line3d &s1, const Math::Line3d &s2) {
const Math::Vector3d &s1begin = s1.begin();
const Math::Vector3d &s1end = s1.end();
const Math::Vector3d &s2begin = s2.begin();
const Math::Vector3d &s2end = s2.end();
float denom = ((s2end.y() - s2begin.y()) * (s1end.x() - s1begin.x())) -
((s2end.x() - s2begin.x()) * (s1end.y() - s1begin.y()));
float nume_a = ((s2end.x() - s2begin.x()) * (s1begin.y() - s2begin.y())) -
((s2end.y() - s2begin.y()) * (s1begin.x() - s2begin.x()));
float nume_b = ((s1end.x() - s1begin.x()) * (s1begin.y() - s2begin.y())) -
((s1end.y() - s1begin.y()) * (s1begin.x() - s2begin.x()));
if (denom == 0.0f) {
return false; // Segments are collinear
}
float ua = nume_a / denom;
float ub = nume_b / denom;
// Non inclusive bounds check, one of the vertices of one segment being inside
// the other segment is not considered to be an intersection.
// This is the only difference with Line3d::intersectLine2d.
return ua > 0 && ua < 1 && ub > 0 && ub < 1;
}
bool FloorEdge::intersectsSegment(const Floor *floor, const Math::Line3d &segment) const {
Math::Vector3d vertex1 = floor->getVertex(_vertexIndex1);
Math::Vector3d vertex2 = floor->getVertex(_vertexIndex2);
Math::Line3d edgeSegment = Math::Line3d(vertex1, vertex2);
return intersectLine2d(edgeSegment, segment);
}
void FloorEdge::enable(bool e) {
_enabled = e;
}
bool FloorEdge::isEnabled() const {
return _enabled;
}
void FloorEdge::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsUint32LE(_enabled);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,199 @@
/* 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 STARK_RESOURCES_FLOOR_H
#define STARK_RESOURCES_FLOOR_H
#include "common/array.h"
#include "common/str.h"
#include "math/line3d.h"
#include "math/ray.h"
#include "math/vector3d.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Floor;
class FloorFace;
class FloorField;
/**
* A floor face (triangle) edge
*
* Used for path finding
*/
class FloorEdge {
public:
FloorEdge(uint16 vertexIndex1, uint16 vertexIndex2, uint32 faceIndex1);
/** Build a list of neighbour edges in the graph */
void buildNeighbours(const Floor *floor);
/** Set the edge middle position */
void computeMiddle(const Floor *floor);
/** Set the edge's second face */
void setOtherFace(uint32 faceIndex);
/** Check if the edge has the same vertices as the parameters */
bool hasVertices(uint16 vertexIndex1, uint16 vertexIndex2) const;
/** List the edge neighbour edges in the floor */
Common::Array<FloorEdge *> getNeighbours() const;
/**
* Computes the cost for going to a neighbour edge
*
* This is used for pathfinding. The cost is equal to the distance
* between the middle of both edges
*/
float costTo(const FloorEdge *other) const;
/**
* Get the edge position
*
* This is the middle of the edge
*/
Math::Vector3d getPosition() const;
/** Is this edge on the floor border? */
bool isFloorBorder() const;
/** Does the segment intersect the edge in the 2D plane? */
bool intersectsSegment(const Floor *floor, const Math::Line3d &segment) const;
int32 getFaceIndex1() const;
int32 getFaceIndex2() const;
/** Allow or disallow characters to path using this edge */
void enable(bool enable);
/** Is pathing through this edge allowed for characters? */
bool isEnabled() const;
/** Save or restore the edge's status */
void saveLoad(ResourceSerializer *serializer);
private:
void addNeighboursFromFace(const FloorFace *face);
static bool intersectLine2d(const Math::Line3d &s1, const Math::Line3d &s2);
uint16 _vertexIndex1;
uint16 _vertexIndex2;
Math::Vector3d _middle;
int32 _faceIndex1;
int32 _faceIndex2;
bool _enabled;
Common::Array<FloorEdge *> _neighbours;
};
/**
* This resource represents the floor of a 3D layer.
* Characters can only walk on the floor.
*
* The floor is made of a list of faces building a mesh.
*/
class Floor : public Object {
public:
static const Type::ResourceType TYPE = Type::kFloor;
Floor(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Floor();
// Resource API
void onAllLoaded() override;
void saveLoad(ResourceSerializer *serializer) override;
/** Obtain the vertex for an index */
Math::Vector3d getVertex(uint32 index) const;
/**
* Obtain the index of the face containing the point when both the floorfield
* and the point are projected on a Z=0 plane.
*
* Return -1 if no face contains the point.
*/
int32 findFaceContainingPoint(const Math::Vector3d &point) const;
/** Fill the z coordinate of the point so that it is on the plane of a face */
void computePointHeightInFace(Math::Vector3d &point, uint32 faceIndex) const;
/**
* Check if a ray is intersecting the floor
*
* Faces where walking is disabled are ignored.
*
* @param ray The ray
* @param intersection The intersection between the ray and the floor. Only valid when the return value is positive.
* @return -1 if no face contains the point, the hit face index otherwise
*/
int32 findFaceHitByRay(const Math::Ray &ray, Math::Vector3d &intersection) const;
/**
* Find the floor face center closest to the ray
*
* Faces where walking is disabled are ignored.
*
* @param ray The ray
* @param center The closest face center to the ray. Only valid when the return value is positive.
* @return -1 if no face was found, the face index with its center closest to the ray otherwise
*/
int32 findFaceClosestToRay(const Math::Ray &ray, Math::Vector3d &center) const;
/** Obtain the distance to the camera for a face */
float getDistanceFromCamera(uint32 faceIndex) const;
/** Get a floor face by its index */
FloorFace *getFace(uint32 index) const;
/** Check if the segment is entirely inside the floor */
bool isSegmentInside(const Math::Line3d &segment) const;
/** Allow or disallow characters to walk on some faces of the floor */
void enableFloorField(FloorField *floorfield, bool enable);
protected:
void readData(Formats::XRCReadStream *stream) override;
void printData() override;
void buildEdgeList();
void addFaceEdgeToList(uint32 faceIndex, uint32 index1, uint32 index2);
uint32 _facesCount;
Common::Array<Math::Vector3d> _vertices;
Common::Array<FloorFace *> _faces;
Common::Array<FloorEdge> _edges;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_FLOOR_H

View File

@@ -0,0 +1,218 @@
/* 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/floorface.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/floor.h"
namespace Stark {
namespace Resources {
FloorFace::FloorFace(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_distanceFromCamera(0),
_unk2(0) {
_type = TYPE;
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
_indices[i] = 0;
}
}
FloorFace::~FloorFace() {
}
bool FloorFace::isPointInside(const Math::Vector3d &point) const {
// Compute the barycentric coordinates of the point in the triangle
float area = 1.0f / 2.0f
* (-_vertices[1].y() * _vertices[2].x()
+ _vertices[0].y() * (-_vertices[1].x() + _vertices[2].x())
+ _vertices[0].x() * (_vertices[1].y() - _vertices[2].y())
+ _vertices[1].x() * _vertices[2].y());
float s = (_vertices[0].y() * _vertices[2].x() - _vertices[0].x() * _vertices[2].y()
+ (_vertices[2].y() - _vertices[0].y()) * point.x()
+ (_vertices[0].x() - _vertices[2].x()) * point.y())
/ (2.0f * area);
float t = (_vertices[0].x() * _vertices[1].y() - _vertices[0].y() * _vertices[1].x()
+ (_vertices[0].y() - _vertices[1].y()) * point.x()
+ (_vertices[1].x() - _vertices[0].x()) * point.y())
/ (2.0f * area);
// Check the coordinates are in the triangle
return s > 0.0f && t > 0.0f && (1.0f - s - t) > 0.0f;
}
void FloorFace::computePointHeight(Math::Vector3d &point) const {
// Compute the barycentric coordinates of the point in the triangle
float area = 1.0f / 2.0f
* (-_vertices[1].y() * _vertices[2].x()
+ _vertices[0].y() * (-_vertices[1].x() + _vertices[2].x())
+ _vertices[0].x() * (_vertices[1].y() - _vertices[2].y())
+ _vertices[1].x() * _vertices[2].y());
float s = (_vertices[0].y() * _vertices[2].x() - _vertices[0].x() * _vertices[2].y()
+ (_vertices[2].y() - _vertices[0].y()) * point.x()
+ (_vertices[0].x() - _vertices[2].x()) * point.y())
/ (2.0f * area);
float t = (_vertices[0].x() * _vertices[1].y() - _vertices[0].y() * _vertices[1].x()
+ (_vertices[0].y() - _vertices[1].y()) * point.x()
+ (_vertices[1].x() - _vertices[0].x()) * point.y())
/ (2.0f * area);
// Compute the Z coordinate of the point
float pointZ = (1.0f - s - t) * _vertices[0].z() + s * _vertices[1].z() + t * _vertices[2].z();
point.setValue(2, pointZ);
}
bool FloorFace::intersectRay(const Math::Ray &ray, Math::Vector3d &intersection) const {
// Compute the triangle plane normal
Math::Vector3d n = Math::Vector3d::crossProduct(_vertices[1] - _vertices[0], _vertices[2] - _vertices[0]);
if (n == Math::Vector3d()) {
return false; // We don't handle degenerate triangles
}
// Point on triangle plane: dot(P - _vertices[0], n) = 0
// Point on ray: P = origin + r * direction
// Point on both => r = - dot(n, origin - _vertices[0]) / dot(n, direction)
float num = -Math::Vector3d::dotProduct(n, ray.getOrigin() - _vertices[0]);
float denom = Math::Vector3d::dotProduct(n, ray.getDirection());
if (fabs(denom) < 0.00001) {
// The ray is parallel to the plane
return false;
}
float r = num / denom;
if (r < 0.0) {
// The ray goes away from the triangle
return false;
}
// Compute the intersection point between the triangle plane and the ray
intersection = ray.getOrigin() + r * ray.getDirection();
// Check the intersection point is inside the triangle
return isPointInside(intersection);
}
float FloorFace::distanceToRay(const Math::Ray &ray) const {
Math::Vector3d center = getCenter();
return Math::Vector3d::crossProduct(ray.getDirection(), center - ray.getOrigin()).getMagnitude();
}
float FloorFace::getDistanceFromCamera() const {
return _distanceFromCamera;
}
int16 FloorFace::getVertexIndex(int32 index) const {
assert(index < 3);
return _indices[index];
}
void FloorFace::addEdge(FloorEdge *edge) {
_edges.push_back(edge);
}
Common::Array<FloorEdge *> FloorFace::getEdges() const {
return _edges;
}
FloorEdge *FloorFace::findNearestEdge(const Math::Vector3d &point) const {
float minDistance = -1;
FloorEdge *edge = nullptr;
for (uint i = 0; i < _edges.size(); i++) {
if (!_edges[i]->isEnabled()) {
continue;
}
float distance = (point - _edges[i]->getPosition()).getSquareMagnitude();
if (!edge || distance < minDistance) {
minDistance = distance;
edge = _edges[i];
}
}
return edge;
}
Math::Vector3d FloorFace::getCenter() const {
return (_vertices[0] + _vertices[1] + _vertices[2]) / 3.0;
}
bool FloorFace::hasVertices() const {
return _indices[0] != 0 || _indices[1] != 0 || _indices[2] != 0;
}
void FloorFace::enable(bool e) {
for (uint i = 0; i < _edges.size(); i++) {
_edges[i]->enable(e);
}
}
bool FloorFace::isEnabled() const {
for (uint i = 0; i < _edges.size(); i++) {
if (_edges[i]->isEnabled()) {
return true;
}
}
return false;
}
void FloorFace::readData(Formats::XRCReadStream *stream) {
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
_indices[i] = stream->readSint16LE();
}
_distanceFromCamera = stream->readFloatLE();
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
stream->readSint16LE(); // Skipped in the original
}
_unk2 = stream->readFloatLE();
}
void FloorFace::onAllLoaded() {
Object::onAllLoaded();
Floor *floor = Object::cast<Floor>(_parent);
for (uint i = 0; i < ARRAYSIZE(_indices); i++) {
_vertices[i] = floor->getVertex(_indices[i]);
}
}
void FloorFace::printData() {
debug("indices: %d %d %d, distanceFromCamera %f, unk2 %f", _indices[0], _indices[1], _indices[2], _distanceFromCamera, _unk2);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,122 @@
/* 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 STARK_RESOURCES_FLOOR_FACE_H
#define STARK_RESOURCES_FLOOR_FACE_H
#include "common/array.h"
#include "common/str.h"
#include "math/ray.h"
#include "math/vector3d.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class FloorEdge;
/**
* A floor face is a 3D triangle used to build the floor
*/
class FloorFace : public Object {
public:
static const Type::ResourceType TYPE = Type::kFloorFace;
FloorFace(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~FloorFace();
// Resource API
void onAllLoaded() override;
/** Return true if the point is inside the face when both are projected on a Z=0 plane*/
bool isPointInside(const Math::Vector3d &point) const;
/** Fill the z coordinate of the point so that it is on the plane */
void computePointHeight(Math::Vector3d &point) const;
/**
* Check if a ray is intersecting this face
*
* @param origin The ray's origin
* @param direction The ray's direction
* @param intersection The intersection between the ray and the face. Only valid when the return value is true.
* @return true if the ray intersects the face, false otherwise.
*/
bool intersectRay(const Math::Ray &ray, Math::Vector3d &intersection) const;
/**
* Compute the distance between the face center and the ray
*/
float distanceToRay(const Math::Ray &ray) const;
/** Obtain the distance to the camera */
float getDistanceFromCamera() const;
/** Get one of the three vertex indices from the face */
int16 getVertexIndex(int32 index) const;
/** Add an edge to the triangle edge list */
void addEdge(FloorEdge *edge);
/** Get the triangle's edge list */
Common::Array<FloorEdge *> getEdges() const;
/**
* Find the edge closest to a point
*
* Distance are compared using the middle point of each edge of the face
*/
FloorEdge *findNearestEdge(const Math::Vector3d &point) const;
/** Get the point at the center of the face's triangle */
Math::Vector3d getCenter() const;
/** Checks if the face is non degenerate */
bool hasVertices() const;
/** Allow or disallow characters to walk on this face */
void enable(bool enable);
bool isEnabled() const;
protected:
void readData(Formats::XRCReadStream *stream) override;
void printData() override;
int16 _indices[3];
Math::Vector3d _vertices[3];
Common::Array<FloorEdge *> _edges; // Owned by Floor
float _distanceFromCamera;
float _unk2;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_FLOOR_FACE_H

View File

@@ -0,0 +1,59 @@
/* 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/floorfield.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
FloorField::~FloorField() {
}
FloorField::FloorField(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name) {
_type = TYPE;
}
void FloorField::readData(Formats::XRCReadStream *stream) {
uint32 count = stream->readUint32LE();
for (uint i = 0; i < count; i++) {
_facesInFloorField.push_back(stream->readByte());
}
}
bool FloorField::hasFace(int32 floorFaceIndex) const {
if (floorFaceIndex < 0 || floorFaceIndex >= (int32) _facesInFloorField.size()) {
return false;
}
return _facesInFloorField[floorFaceIndex] != 0;
}
void FloorField::printData() {
for (uint i = 0; i < _facesInFloorField.size(); i++) {
debug("faceInFloorField[%d]: %d", i, _facesInFloorField[i]);
}
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,63 @@
/* 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 STARK_RESOURCES_FLOORFIELD_H
#define STARK_RESOURCES_FLOORFIELD_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A floor field represents a portion of the floor in a 3D layer
*
* A floor field is bounded by the faces it contains
*/
class FloorField : public Object {
public:
static const Type::ResourceType TYPE = Type::kFloorField;
FloorField(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~FloorField();
/** Is the specified face inside the floorfield? */
bool hasFace(int32 floorFaceIndex) const;
protected:
void readData(Formats::XRCReadStream *stream) override;
void printData() override;
private:
Common::Array<byte> _facesInFloorField;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_FLOORFIELD_H

View File

@@ -0,0 +1,64 @@
/* 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/fmv.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/diary.h"
#include "engines/stark/services/userinterface.h"
namespace Stark {
namespace Resources {
FMV::~FMV() {
}
FMV::FMV(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_diaryAddEntryOnPlay(true),
_gameDisc(1) {
_type = TYPE;
}
void FMV::readData(Formats::XRCReadStream *stream) {
_filename = Common::Path(stream->readString());
_diaryAddEntryOnPlay = stream->readBool();
_gameDisc = stream->readUint32LE();
}
void FMV::requestPlayback() {
if (_diaryAddEntryOnPlay) {
StarkDiary->addFMVEntry(_filename, getName(), _gameDisc);
}
StarkUserInterface->requestFMVPlayback(_filename);
}
void FMV::printData() {
debug("filename: %s", _filename.toString().c_str());
debug("diaryAddEntryOnPlay: %d", _diaryAddEntryOnPlay);
debug("gameDisc: %d", _gameDisc);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,65 @@
/* 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 STARK_RESOURCES_FMV_H
#define STARK_RESOURCES_FMV_H
#include "common/path.h"
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A full motion video
*/
class FMV : public Object {
public:
static const Type::ResourceType TYPE = Type::kFMV;
FMV(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~FMV();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
/** Request the user interface to start playing the movie */
void requestPlayback();
protected:
void printData() override;
Common::Path _filename;
bool _diaryAddEntryOnPlay;
uint32 _gameDisc;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_FMV_H

View File

@@ -0,0 +1,377 @@
/* 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/image.h"
#include "common/debug.h"
#include "image/png.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/settings.h"
#include "engines/stark/services/services.h"
#include "engines/stark/visual/effects/bubbles.h"
#include "engines/stark/visual/effects/fireflies.h"
#include "engines/stark/visual/effects/fish.h"
#include "engines/stark/visual/image.h"
#include "engines/stark/visual/text.h"
#include "math/line2d.h"
#include "math/vector2d.h"
namespace Stark {
namespace Resources {
Object *Image::construct(Object *parent, byte subType, uint16 index, const Common::String &name) {
switch (subType) {
case kImageSub2:
case kImageSub3:
return new ImageStill(parent, subType, index, name);
case kImageText:
return new ImageText(parent, subType, index, name);
default:
error("Unknown image subtype %d", subType);
}
}
Image::~Image() {
delete _visual;
}
Image::Image(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_transparent(false),
_transparentColor(0),
_field_44_ADF(0),
_field_48_ADF(30),
_visual(nullptr) {
_type = TYPE;
}
void Image::readData(Formats::XRCReadStream *stream) {
_filename = stream->readString();
_hotspot = stream->readPoint();
_transparent = stream->readBool();
_transparentColor = stream->readUint32LE();
uint32 polygonCount = stream->readUint32LE();
for (uint32 i = 0; i < polygonCount; i++) {
Polygon polygon;
uint32 pointCount = stream->readUint32LE();
for (uint32 j = 0; j < pointCount; j++) {
polygon.push_back(stream->readPoint());
}
_polygons.push_back(polygon);
}
_archiveName = stream->getArchiveName();
}
Visual *Image::getVisual() {
initVisual();
return _visual;
}
void Image::printData() {
debug("filename: %s", _filename.toString().c_str());
debug("hotspot: x %d, y %d", _hotspot.x, _hotspot.y);
debug("transparent: %d", _transparent);
debug("transparentColor: %d", _transparentColor);
debug("field_44: %d", _field_44_ADF);
debug("field_48: %d", _field_48_ADF);
for (uint32 i = 0; i < _polygons.size(); i++) {
Common::String description;
for (uint32 j = 0; j < _polygons[i].size(); j++) {
description += Common::String::format("(x %d, y %d) ", _polygons[i][j].x, _polygons[i][j].y);
}
debug("polygon %d: %s", i, description.c_str());
}
}
int Image::indexForPoint(const Common::Point &point) const {
int index = -1;
for (uint32 i = 0; i < _polygons.size(); i++) {
if (isPointInPolygon(_polygons[i], point)) {
index = i;
}
}
return index;
}
bool Image::isPointInPolygon(const Polygon &polygon, const Common::Point &point) const {
if (polygon.size() <= 1) {
return false; // Empty polygon
}
// A ray cast from the point
Math::Segment2d testLine(Math::Vector2d(point.x, point.y), Math::Vector2d(-100, -100));
// Special case the line created between the last point and the first
Math::Vector2d prevPoint = Math::Vector2d(polygon.back().x, polygon.back().y);
// Count the intersections of the ray with the polygon's edges
int intersectCount = 0;
for (uint32 j = 0; j < polygon.size(); j++) {
Math::Vector2d curPoint = Math::Vector2d(polygon[j].x, polygon[j].y);
if (Math::Segment2d(prevPoint, curPoint).intersectsSegment(testLine, nullptr)) {
intersectCount++;
}
prevPoint = curPoint;
}
// If the ray crosses the polygon an odd number of times, the point is inside the polygon
return intersectCount % 2 != 0;
}
Common::Point Image::getHotspotPosition(uint index) const {
if (index >= _polygons.size()) {
return Common::Point(-1, -1);
}
Polygon polygon = _polygons[index];
// Return the top-middle point as the hotspot
int right = polygon[0].x, top = polygon[0].y;
for (uint i = 1; i < polygon.size(); ++i) {
right += polygon[i].x;
if (polygon[i].y < top) {
top = polygon[i].y;
}
}
right /= polygon.size();
if (top < 0) {
top = 0;
}
return Common::Point(right, top);
}
ImageStill::~ImageStill() {
}
ImageStill::ImageStill(Object *parent, byte subType, uint16 index, const Common::String &name) :
Image(parent, subType, index, name),
_noName(false) {
}
void ImageStill::readData(Formats::XRCReadStream *stream) {
Image::readData(stream);
if (stream->isDataLeft()) {
_field_44_ADF = stream->readUint32LE();
_field_44_ADF /= 33;
}
if (stream->isDataLeft()) {
_field_48_ADF = stream->readUint32LE();
}
_noName = _filename == "noname" || _filename == "noname.xmg";
}
void ImageStill::onPostRead() {
initVisual();
}
void ImageStill::initVisual() {
if (_visual) {
return; // The visual is already there
}
if (_noName) {
return; // No file to load
}
Common::ReadStream *xmgStream = StarkArchiveLoader->getFile(_filename, _archiveName);
VisualImageXMG *visual = new VisualImageXMG(StarkGfx);
if (StarkSettings->isAssetsModEnabled() && StarkGfx->supportsModdedAssets() && loadPNGOverride(visual)) {
visual->readOriginalSize(xmgStream);
} else {
visual->load(xmgStream);
}
visual->setHotSpot(_hotspot);
_visual = visual;
delete xmgStream;
}
bool ImageStill::loadPNGOverride(VisualImageXMG *visual) const {
if (!_filename.baseName().hasSuffixIgnoreCase(".xmg")) {
return false;
}
Common::String pngFilename = _filename.baseName();
pngFilename = Common::String(pngFilename.c_str(), pngFilename.size() - 4) + ".png";
Common::Path pngFilePath = _filename.getParent().appendComponent(pngFilename);
pngFilePath = StarkArchiveLoader->getExternalFilePath(pngFilePath, _archiveName);
debugC(kDebugModding, "Attempting to load %s", pngFilePath.toString(Common::Path::kNativeSeparator).c_str());
Common::SeekableReadStream *pngStream = SearchMan.createReadStreamForMember(pngFilePath);
if (!pngStream) {
return false;
}
if (!visual->loadPNG(pngStream)) {
warning("Failed to load %s. It is not a valid PNG file.", pngFilePath.toString(Common::Path::kNativeSeparator).c_str());
delete pngStream;
return false;
}
debugC(kDebugModding, "Loaded %s", pngFilePath.toString(Common::Path::kNativeSeparator).c_str());
delete pngStream;
return true;
}
void ImageStill::printData() {
Image::printData();
}
ImageText::ImageText(Object *parent, byte subType, uint16 index, const Common::String &name) :
Image(parent, subType, index, name),
_color(Gfx::Color(0, 0, 0)),
_font(0) {
}
ImageText::~ImageText() {
}
void ImageText::readData(Formats::XRCReadStream *stream) {
Image::readData(stream);
_size = stream->readPoint();
_text = stream->readString();
_color.r = stream->readByte();
_color.g = stream->readByte();
_color.b = stream->readByte();
_color.a = 0xFF; stream->readByte();
_font = stream->readUint32LE();
// WORKAROUND: Give more space to text in the Archives' computer
// So there are no line breaks in the French version of the game
// when using a scaled font.
Location *location = findParent<Location>();
if (_name == "MAIN" && location && location->getName() == "Archive Database") {
_size.x = 80;
}
}
void ImageText::initVisual() {
if (_visual) {
return; // The visual is already there
}
if (_text.hasPrefix("GFX_Bubbles")) {
VisualEffectBubbles *bubbles = new VisualEffectBubbles(StarkGfx, _size);
bubbles->setParams(_text);
_visual = bubbles;
} else if (_text.hasPrefix("GFX_FireFlies")) {
VisualEffectFireFlies *fireFlies = new VisualEffectFireFlies(StarkGfx, _size);
fireFlies->setParams(_text);
_visual = fireFlies;
} else if (_text.hasPrefix("GFX_Fish")) {
VisualEffectFish *fish = new VisualEffectFish(StarkGfx, _size);
fish->setParams(_text);
_visual = fish;
} else if (_text.hasPrefix("GFX_")) {
error("Unknown effect '%s'", _text.c_str());
} else {
VisualText *text = new VisualText(StarkGfx);
text->setText(_text);
text->setColor(_color);
text->setTargetWidth(_size.x);
text->setTargetHeight(_size.y);
text->setFont(FontProvider::kCustomFont, _font);
// WORKAROUND: Move the "White Cardinal" hotspot in the Archives'
// computer so it matches the text. Fixes the hotspot being hard to
// use with scaled fonts in the Spanish version of the game.
if (_name == "The Church" && _polygons.size() == 2) {
fixWhiteCardinalHotspot(text);
}
_visual = text;
}
}
void ImageText::fixWhiteCardinalHotspot(VisualText *text) {
Common::Rect textRect = text->getRect();
Polygon &hotspotPoly = _polygons.back();
if (hotspotPoly.size() != 4) {
return;
}
Common::Point &topLeft = hotspotPoly[0];
Common::Point &topRight = hotspotPoly[1];
Common::Point &bottomRight = hotspotPoly[2];
Common::Point &bottomLeft = hotspotPoly[3];
int16 hotspotHeight = bottomLeft.y - topLeft.y;
if (hotspotHeight <= 0) {
return;
}
bottomLeft .y = textRect.bottom;
bottomRight.y = textRect.bottom;
topLeft .y = textRect.bottom - hotspotHeight;
topRight .y = textRect.bottom - hotspotHeight;
}
void ImageText::resetVisual() {
if (!_visual) {
return;
}
VisualText *text = _visual->get<VisualText>();
if (text) {
text->reset();
}
}
void ImageText::printData() {
Image::printData();
debug("size: x %d, y %d", _size.x, _size.y);
debug("text: %s", _text.c_str());
debug("color: (%d, %d, %d, %d)", _color.r, _color.g, _color.b, _color.a);
debug("font: %d", _font);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,152 @@
/* 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 STARK_RESOURCES_IMAGE_H
#define STARK_RESOURCES_IMAGE_H
#include "common/path.h"
#include "common/rect.h"
#include "common/str.h"
#include "engines/stark/resources/object.h"
#include "engines/stark/gfx/color.h"
namespace Stark {
class Visual;
class VisualImageXMG;
class VisualText;
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A still image resource
*/
class Image : public Object {
public:
static const Type::ResourceType TYPE = Type::kImage;
enum SubType {
kImageSub2 = 2,
kImageSub3 = 3,
kImageText = 4
};
/** Image factory */
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name);
typedef Common::Array<Common::Point> Polygon;
Image(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Image();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
/** Initialize the renderable for the image */
virtual Visual *getVisual();
/** Get the pat-table index for a given point */
int indexForPoint(const Common::Point &point) const;
/** Get the hotspot position for a given index of a pat-table */
Common::Point getHotspotPosition(uint index) const;
protected:
void printData() override;
bool isPointInPolygon(const Polygon &polygon, const Common::Point &point) const;
virtual void initVisual() = 0;
Common::Path _filename;
Common::Path _archiveName;
Visual *_visual;
bool _transparent;
uint32 _transparentColor;
uint32 _field_44_ADF;
uint32 _field_48_ADF;
Common::Point _hotspot;
Common::Array<Polygon> _polygons;
};
/**
* A still image resource loading its data from an XMG file
*/
class ImageStill : public Image {
public:
ImageStill(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~ImageStill();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onPostRead() override;
protected:
// Resource API
void printData() override;
// Image API
void initVisual() override;
bool loadPNGOverride(Stark::VisualImageXMG *visual) const;
bool _noName;
};
/**
* Text image rendered from a TTF font
*/
class ImageText : public Image {
public:
ImageText(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~ImageText();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
/** Reset the text visual so it is recomputed the next frame it is rendered */
void resetVisual();
protected:
// Resource API
void printData() override;
// Image API
void initVisual() override;
void fixWhiteCardinalHotspot(VisualText *text);
Common::Point _size;
Common::String _text;
Gfx::Color _color;
uint32 _font;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_IMAGE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,497 @@
/* 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 STARK_RESOURCES_ITEM_H
#define STARK_RESOURCES_ITEM_H
#include "engines/stark/resources/object.h"
#include "engines/stark/resourcereference.h"
#include "common/rect.h"
#include "common/str.h"
#include "math/vector3d.h"
namespace Stark {
class AnimHandler;
class Movement;
class Visual;
namespace Gfx {
class RenderEntry;
}
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Anim;
class AnimHierarchy;
class BonesMesh;
class Bookmark;
class ItemVisual;
class Script;
class TextureSet;
/**
* A scene element
*
* Can be a character, background, animation, ...
*/
class Item : public Object {
public:
static const Type::ResourceType TYPE = Type::kItem;
enum SubType {
kItemGlobalTemplate = 1,
kItemInventory = 2,
kItemLevelTemplate = 3,
kItemStaticProp = 5,
kItemAnimatedProp = 6,
kItemBackgroundElement = 7,
kItemBackground = 8,
kItemModel = 10
};
/** Item factory */
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name);
Item(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Item();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onGameLoop() override;
void saveLoad(ResourceSerializer *serializer) override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
/** Is the item present in the scene */
bool isEnabled() const;
/** Enable or disable the item */
virtual void setEnabled(bool enabled);
/** Get the item's character index */
int32 getCharacterIndex() const;
/** Obtain the render entry to use to display the item */
virtual Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset);
/** Obtain the concrete instance of an item template */
virtual ItemVisual *getSceneInstance() = 0;
/** Replace the current movement with an other */
void setMovement(Movement *movement);
/** Get the current movement if any */
Movement *getMovement() const;
/**
* Set the script waiting for the item's movement to complete.
*
* This script will be updated with the outcome of the movement
* (completion or abortion)
*/
void setMovementSuspendedScript(Script *script);
/** Set the currently active anim hierarchy */
virtual void setAnimHierarchy(AnimHierarchy *animHierarchy) = 0;
/** List all the exit positions */
virtual Common::Array<Common::Point> listExitPositions();
protected:
void printData() override;
bool _enabled;
int32 _characterIndex;
Movement *_movement;
Script *_movementSuspendedScript;
};
/**
* A renderable item
*
* Renderable items are found in location layers
*/
class ItemVisual : public Item {
public:
ItemVisual(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~ItemVisual();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
void saveLoad(ResourceSerializer *serializer) override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
// Item API
void setEnabled(bool enabled) override;
ItemVisual *getSceneInstance() override;
void setAnimHierarchy(AnimHierarchy *animHierarchy) override;
/**
* Change the item's 2D position.
*
* Only applies to 2D items
*/
virtual void setPosition2D(const Common::Point &position);
/** Get the hotspot index for an item relative position */
int getHotspotIndexForPoint(const Common::Point &point);
/** Obtain the title for one of the item's hotspots */
Common::String getHotspotTitle(uint32 hotspotIndex);
/** Check whether the item has runnable scripts for the specified action */
bool canPerformAction(uint32 action, uint32 hotspotIndex);
/** Perform an action on one of the item's hotspots */
bool doAction(uint32 action, uint32 hotspotIndex);
/** Define the current animation kind for the item */
void setAnimActivity(int32 activity);
/** Get the current animation kind */
int32 getAnimActivity() const;
/** Get the currently playing animation */
Anim *getAnim() const;
/** Get the currently playing action animation, if any */
Anim *getActionAnim() const;
/** Replace the current generic animation with an action specific animation */
void playActionAnim(Anim *anim);
/** Remove the current specific animation and revert to a generic one */
void resetActionAnim();
protected:
// Resource API
void printData() override;
/** Implemented version used in FloorPositionedImageItem and ImageItem */
Common::Array<Common::Point> listExitPositionsImpl();
Visual *getVisual();
Gfx::RenderEntry *_renderEntry;
Anim *_actionAnim;
AnimHierarchy *_animHierarchy;
int32 _currentAnimActivity;
bool _clickable;
};
/**
* An item template
*
* Item templates need to be instanciated into renderable items to be displayed
*/
class ItemTemplate : public Item {
public:
ItemTemplate(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~ItemTemplate();
// Resource API
void onAllLoaded() override;
void onEnterLocation() override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
// Item API
ItemVisual *getSceneInstance() override;
void setAnimHierarchy(AnimHierarchy *animHierarchy) override;
/** Obtain the bone mesh to use to render the item */
virtual BonesMesh *findBonesMesh() = 0;
/** Obtain the texture to use to render the item */
virtual TextureSet *findTextureSet(uint32 textureType) = 0;
/** Obtain the animation hierarchy to fetch animations from */
virtual AnimHierarchy *findStockAnimHierarchy() = 0;
/** Define the anim hierarchy to be persisted across locations */
void setStockAnimHierachy(AnimHierarchy *animHierarchy);
/** Change the item's mesh */
void setBonesMesh(int32 index);
/** Set the mesh main or face texture */
void setTexture(int32 index, uint32 textureType);
/** Set the scene instanciation for this template */
void setInstanciatedItem(Item *instance);
protected:
int32 _meshIndex;
int32 _textureNormalIndex;
int32 _textureFaceIndex;
int32 _animHierarchyIndex;
Item *_instanciatedItem;
ItemTemplate *_referencedItem;
};
/**
* A global item template
*
* Global item templates are found in the global level
*/
class GlobalItemTemplate : public ItemTemplate {
public:
GlobalItemTemplate(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~GlobalItemTemplate();
// ItemTemplate API
BonesMesh *findBonesMesh() override;
TextureSet *findTextureSet(uint32 textureType) override;
AnimHierarchy *findStockAnimHierarchy() override;
protected:
};
/**
* An inventory item
*/
class InventoryItem : public ItemVisual {
public:
InventoryItem(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~InventoryItem();
// Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
void setEnabled(bool enabled) override;
/** Obtain an action menu icon */
Visual *getActionVisual(bool active) const;
/** Obtain an inventory item cursor */
Visual *getCursorVisual() const;
protected:
};
/**
* A level item template
*
* Level item templates are found in levels so that they can be shared between
* locations.
*/
class LevelItemTemplate : public ItemTemplate {
public:
LevelItemTemplate(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~LevelItemTemplate();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
// ItemTemplate API
BonesMesh *findBonesMesh() override;
TextureSet *findTextureSet(uint32 textureType) override;
AnimHierarchy *findStockAnimHierarchy() override;
/** Get the item's level or global template if any */
ItemTemplate *getItemTemplate() const;
protected:
void printData() override;
ResourceReference _reference;
};
/**
* 3D positioned item
*
* Items with a 3D position, used in 3D layers. The sort key determines the order
* in which such items are drawn in.
*/
class FloorPositionedItem : public ItemVisual {
public:
FloorPositionedItem(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~FloorPositionedItem();
// Object API
void saveLoad(ResourceSerializer *serializer) override;
/** Move the item to a bookmarked position */
void placeOnBookmark(Bookmark *target);
/** Place the item on the center of the first floor face */
void placeDefaultPosition();
/** Get the item position */
Math::Vector3d getPosition3D() const;
/** Move the item */
void setPosition3D(const Math::Vector3d &position);
/** Get the floor face index the item is standing on */
int32 getFloorFaceIndex() const;
/** Change the face the item is standing on */
void setFloorFaceIndex(int32 faceIndex);
/** Get a vector pointing in the same direction as the item */
Math::Vector3d getDirectionVector() const;
/** Set the direction the item faces */
void setDirection(const Math::Angle &direction);
/** Obtain the sort value for the item, used to compute the draw order */
float getSortKey() const;
/**
* Don't rely on the floor face to compute the sort key, use the provided value instead.
*
* This can be used to handle cases where the item is not over the floor.
*/
void overrideSortKey(float sortKey);
protected:
int32 _floorFaceIndex;
Math::Vector3d _position3D;
float _direction3D;
bool _sortKeyOverride;
float _sortKeyOverridenValue;
};
/**
* 3D positioned image item
*
* Used to display still images or animated images in 3D layers
*/
class FloorPositionedImageItem : public FloorPositionedItem {
public:
FloorPositionedImageItem(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~FloorPositionedImageItem();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
// Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
Common::Array<Common::Point> listExitPositions() override;
// ItemVisual API
void setPosition2D(const Common::Point &position) override;
protected:
void printData() override;
Common::Point _position;
};
/**
* Model item
*
* Used to draw characters
*/
class ModelItem : public FloorPositionedItem {
public:
ModelItem(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~ModelItem();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
void onEnterLocation() override;
void onExitLocation() override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
// Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
/** Set the mesh main or face texture */
void setTexture(int32 index, uint32 textureType);
/** Change the item's mesh */
void setBonesMesh(int32 index);
/** Obtain the bone mesh to use to render the item */
BonesMesh *findBonesMesh();
/** Obtain the texture to use to render the item */
TextureSet *findTextureSet(uint32 textureType);
/** Get the item's level or global template if any */
ItemTemplate *getItemTemplate() const;
/** Update the item's animation after a texture / mesh change */
void updateAnim();
/** Reset animation blending */
void resetAnimationBlending();
/** Randomize an idle action animation */
Anim *getIdleActionAnim() const;
AnimHandler *getAnimHandler() const;
protected:
void printData() override;
int32 _meshIndex;
int32 _textureNormalIndex;
int32 _textureFaceIndex;
ResourceReference _reference;
ItemTemplate *_referencedItem;
AnimHandler *_animHandler;
};
/**
* 2D positioned image item
*
* Used to display background elements in 2D layers
*/
class ImageItem : public ItemVisual {
public:
ImageItem(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~ImageItem();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
// Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
Common::Array<Common::Point> listExitPositions() override;
// ItemVisual API
void setPosition2D(const Common::Point &position) override;
protected:
void printData() override;
ResourceReference _reference;
Common::Point _position;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_ITEM_H

View File

@@ -0,0 +1,102 @@
/* 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/knowledge.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
Knowledge::~Knowledge() {
}
Knowledge::Knowledge(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_booleanValue(false),
_integerValue(0) {
_type = TYPE;
}
void Knowledge::setBooleanValue(bool value) {
_booleanValue = value;
}
bool Knowledge::getBooleanValue() {
return _booleanValue;
}
void Knowledge::setIntegerValue(int32 value) {
_integerValue = value;
}
int32 Knowledge::getIntegerValue() {
return _integerValue;
}
void Knowledge::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_integerValue);
serializer->syncAsSint32LE(_booleanValue);
serializer->syncAsResourceReference(_referenceValue);
}
void Knowledge::readData(Formats::XRCReadStream *stream) {
Object::readData(stream);
switch (_subType) {
case kBoolean:
case kBooleanWithChild:
_booleanValue = stream->readBool();
break;
case kInteger:
case kInteger2:
_integerValue = stream->readSint32LE();
break;
case kReference:
_referenceValue = stream->readResourceReference();
break;
default:
error("Unknown knowledge subtype %d", _subType);
}
}
void Knowledge::printData() {
switch (_subType) {
case kBoolean:
case kBooleanWithChild:
debug("value: %d", _booleanValue);
break;
case kInteger:
case kInteger2:
debug("value: %d", _integerValue);
break;
case kReference:
debug("value: %s", _referenceValue.describe().c_str());
break;
default:
error("Unknown knowledge subtype %d", _subType);
}
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,83 @@
/* 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 STARK_RESOURCES_KNOWLEDGE_H
#define STARK_RESOURCES_KNOWLEDGE_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A game logic state value holder
*/
class Knowledge : public Object {
public:
static const Type::ResourceType TYPE = Type::kKnowledge;
enum SubType {
kBoolean = 0,
kInteger = 2,
kInteger2 = 3,
kReference = 4,
kBooleanWithChild = 5
};
Knowledge(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Knowledge();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void saveLoad(ResourceSerializer *serializer) override;
/** Define the value for boolean Knowledge elements */
void setBooleanValue(bool value);
/** Obtain the value for boolean Knowledge elements */
bool getBooleanValue();
/** Define the value for integer Knowledge elements */
void setIntegerValue(int32 value);
/** Obtain the value for integer Knowledge elements */
int32 getIntegerValue();
protected:
void printData() override;
bool _booleanValue;
int32 _integerValue;
ResourceReference _referenceValue;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_KNOWLEDGE_H

View File

@@ -0,0 +1,126 @@
/* 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/knowledgeset.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
KnowledgeSet::~KnowledgeSet() {
}
KnowledgeSet::KnowledgeSet(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name) {
_type = TYPE;
}
void KnowledgeSet::printData() {
}
Gfx::RenderEntryArray KnowledgeSet::getInventoryRenderEntries() const {
Common::Array<Resources::Item *> inventoryItems = listChildren<Resources::Item>(Resources::Item::kItemInventory);
// First add the inventory items from old saves which don't have an order
Gfx::RenderEntryArray result;
for (uint i = 0; i < inventoryItems.size(); i++) {
// The first 4 elements are UI elements (Eye, Mouth, Hand, ...)
if (i < 4 || !inventoryItems[i]->isEnabled()) continue;
bool orderFound = false;
for (uint j = 0; j < _inventoryItemOrder.size(); j++) {
if (_inventoryItemOrder[j] == inventoryItems[i]->getIndex()) {
orderFound = true;
break;
}
}
if (!orderFound) {
result.push_back(inventoryItems[i]->getRenderEntry(Common::Point(0, 0)));
}
}
// Then add the inventory items for which an order has been recorded
for (uint i = 0; i < _inventoryItemOrder.size(); i++) {
for (uint j = 0; j < inventoryItems.size(); j++) {
if (inventoryItems[j]->isEnabled() && inventoryItems[j]->getIndex() == _inventoryItemOrder[i]) {
result.push_back(inventoryItems[j]->getRenderEntry(Common::Point(0, 0)));
}
}
}
return result;
}
void KnowledgeSet::addItem(InventoryItem *item) {
_inventoryItemOrder.push_back(item->getIndex());
}
void KnowledgeSet::removeItem(InventoryItem *item) {
Common::Array<uint16>::iterator it;
for (it = _inventoryItemOrder.begin(); it != _inventoryItemOrder.end(); it++) {
if (*it == item->getIndex()) {
_inventoryItemOrder.erase(it);
break;
}
}
}
void KnowledgeSet::saveLoad(ResourceSerializer *serializer) {
if (_subType == kInventory) {
serializer->syncArraySize(_inventoryItemOrder);
for (uint i = 0; i < _inventoryItemOrder.size(); i++) {
serializer->syncAsUint16LE(_inventoryItemOrder[i]);
}
}
}
Visual *KnowledgeSet::getInventoryItemVisual(uint16 itemIndex) {
InventoryItem *item = findChildWithIndex<InventoryItem>(itemIndex, Item::kItemInventory);
assert(item);
return item->getCursorVisual();
}
int16 KnowledgeSet::getNeighborInventoryItem(int16 selectedIndex, bool forward) {
if (selectedIndex < 0 && !_inventoryItemOrder.empty()) {
return forward ? _inventoryItemOrder.front() : _inventoryItemOrder.back();
}
for (uint i = 0; i < _inventoryItemOrder.size(); ++i) {
if (_inventoryItemOrder[i] == selectedIndex) {
if ((i == 0 && !forward) || (i == _inventoryItemOrder.size() - 1 && forward)) {
return -1;
} else {
i += forward ? 1 : -1;
return _inventoryItemOrder[i];
}
}
}
return -1;
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,85 @@
/* 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 STARK_RESOURCES_KNOWLEDGE_SET_H
#define STARK_RESOURCES_KNOWLEDGE_SET_H
#include "common/str.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class InventoryItem;
/**
* A typed collection of Knowledge resources
*/
class KnowledgeSet : public Object {
public:
static const Type::ResourceType TYPE = Type::kKnowledgeSet;
enum SubType {
kInventory = 1,
kState = 2,
kPersons = 3,
kLocations = 4,
kDiary = 5
};
KnowledgeSet(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~KnowledgeSet();
// Resource API
void saveLoad(ResourceSerializer *serializer) override;
/** Add an inventory item, and keep track of its acquisition order */
void addItem(InventoryItem *item);
/** Remove an inventory item */
void removeItem(InventoryItem *item);
/** Get the render entries for the inventory items, in the order they were obtained */
Gfx::RenderEntryArray getInventoryRenderEntries() const;
/** Get a cursor style visual for an inventory item */
Visual *getInventoryItemVisual(uint16 itemIndex);
/** Get the index of a neighbor inventory item of a selected item, return -1 in corner case */
int16 getNeighborInventoryItem(int16 selectedItem, bool forward);
protected:
void printData() override;
Common::Array<uint16> _inventoryItemOrder;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_KNOWLEDGE_SET_H

View File

@@ -0,0 +1,268 @@
/* 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/layer.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/camera.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/light.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h"
#include "engines/stark/scene.h"
#include "common/debug.h"
namespace Stark {
namespace Resources {
Object *Layer::construct(Object *parent, byte subType, uint16 index, const Common::String &name) {
switch (subType) {
case kLayer2D:
return new Layer2D(parent, subType, index, name);
case kLayer3D:
return new Layer3D(parent, subType, index, name);
default:
error("Unknown layer subtype %d", subType);
}
}
Layer::~Layer() {
}
Layer::Layer(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_scrollScale(1.0),
_enabled(true) {
_type = TYPE;
}
void Layer::readData(Formats::XRCReadStream *stream) {
_scrollScale = stream->readFloatLE();
if (_scrollScale > 10.0 || _scrollScale < -1.0)
_scrollScale = 0;
}
void Layer::printData() {
debug("scrollScale: %f", _scrollScale);
debug("enabled: %d", _enabled);
}
void Layer::setScrollPosition(const Common::Point &position) {
// The location scroll position is scaled to create a parallax effect
_scroll.x = (_scrollScale + 1.0) * (float) position.x;
_scroll.y = (_scrollScale + 1.0) * (float) position.y;
}
Common::Point Layer::getScroll() const {
return _scroll;
}
void Layer::setScroll(const Common::Point &scroll) {
_scroll = scroll;
}
bool Layer::isEnabled() const {
return _enabled;
}
void Layer::enable(bool enabled) {
_enabled = enabled;
}
Gfx::LightEntryArray Layer::listLightEntries() {
Common::Array<Light *> lights = listChildren<Light>();
Gfx::LightEntryArray lightEntries;
for (uint i = 0; i < lights.size(); i++) {
lightEntries.push_back(lights[i]->getLightEntry());
}
return lightEntries;
}
void Layer::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_enabled);
}
void Layer::saveLoadCurrent(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_scroll.x);
serializer->syncAsSint32LE(_scroll.y);
}
Layer2D::~Layer2D() {
}
Layer2D::Layer2D(Object *parent, byte subType, uint16 index, const Common::String &name) :
Layer(parent, subType, index, name) {
}
void Layer2D::readData(Formats::XRCReadStream *stream) {
Layer::readData(stream);
uint32 itemsCount = stream->readUint32LE();
for (uint i = 0; i < itemsCount; i++) {
uint32 itemIndex = stream->readUint32LE();
_itemIndices.push_back(itemIndex);
}
_enabled = stream->readBool();
}
void Layer2D::onEnterLocation() {
Layer::onEnterLocation();
Common::Array<Item *> items = listChildren<Item>();
// Build the item list in the appropriate order
_items.clear();
for (uint i = 0; i < _itemIndices.size(); i++) {
for (uint j = 0; j < items.size(); j++) {
if (items[j]->getIndex() == _itemIndices[i]) {
_items.push_back(items[j]);
break;
}
}
}
}
void Layer2D::onExitLocation() {
Layer::onExitLocation();
_items.clear();
}
Gfx::RenderEntryArray Layer2D::listRenderEntries() {
Gfx::RenderEntryArray renderEntries;
for (uint i = 0; i < _items.size(); i++) {
Item *item = _items[i];
Gfx::RenderEntry *renderEntry = item->getRenderEntry(_scroll);
if (!renderEntry) {
// warning("No render entry for item '%s'", item->getName().c_str());
continue;
}
renderEntries.push_back(renderEntry);
}
return renderEntries;
}
void Layer2D::printData() {
Layer::printData();
}
Layer3D::~Layer3D() {
}
Layer3D::Layer3D(Object *parent, byte subType, uint16 index, const Common::String &name) :
Layer(parent, subType, index, name),
_shouldRenderShadows(true),
_maxShadowLength(75),
_nearClipPlane(100.0),
_farClipPlane(64000.0),
_backgroundItem(nullptr) {
}
void Layer3D::readData(Formats::XRCReadStream *stream) {
Layer::readData(stream);
_shouldRenderShadows = stream->readBool();
_nearClipPlane = stream->readFloatLE();
_farClipPlane = stream->readFloatLE();
if (stream->isDataLeft()) {
_maxShadowLength = stream->readUint32LE();
}
}
void Layer3D::onAllLoaded() {
Layer::onAllLoaded();
_items = listChildren<Item>();
_backgroundItem = findChildWithSubtype<Item>(Item::kItemBackground);
Camera *camera = findChild<Camera>();
camera->setClipPlanes(_nearClipPlane, _farClipPlane);
}
void Layer3D::onEnterLocation() {
Layer::onEnterLocation();
StarkScene->setupShadows(_shouldRenderShadows, _maxShadowLength / 1000.0f);
}
Gfx::RenderEntry *Layer3D::getBackgroundRenderEntry() {
if (!_backgroundItem) {
return nullptr;
}
return _backgroundItem->getRenderEntry(_scroll);
}
Gfx::RenderEntryArray Layer3D::listRenderEntries() {
// Sort the items by distance to the camera
Gfx::RenderEntryArray itemEntries;
for (uint i = 0; i < _items.size(); i++) {
Item *item = _items[i];
if (item->getSubType() != Item::kItemBackground) {
Gfx::RenderEntry *renderEntry = item->getRenderEntry(_scroll);
if (!renderEntry) {
// warning("No render entry for item '%s'", item->getName().c_str());
continue;
}
itemEntries.push_back(renderEntry);
}
}
Common::sort(itemEntries.begin(), itemEntries.end(), Gfx::RenderEntry::compare);
Gfx::RenderEntryArray renderEntries;
// Add the background render entry to the list first
Gfx::RenderEntry *backgroundRenderEntry = getBackgroundRenderEntry();
if (backgroundRenderEntry) {
renderEntries.push_back(backgroundRenderEntry);
}
// Add the other items
renderEntries.push_back(itemEntries);
return renderEntries;
}
void Layer3D::printData() {
Layer::printData();
debug("shouldRenderShadows: %d", _shouldRenderShadows);
debug("maxShadowLength: %d", _maxShadowLength);
debug("nearClipPlane: %f", _nearClipPlane);
debug("farClipPlane: %f", _farClipPlane);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,156 @@
/* 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 STARK_RESOURCES_LAYER_H
#define STARK_RESOURCES_LAYER_H
#include "common/array.h"
#include "common/str.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Item;
/**
* A location layer
*
* Layers own the scene items
*/
class Layer : public Object {
public:
static const Type::ResourceType TYPE = Type::kLayer;
enum SubType {
kLayer2D = 1,
kLayer3D = 2
};
/** Layer factory */
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name);
Layer(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Layer();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void saveLoad(ResourceSerializer *serializer) override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
/** Obtain the render entries for all items, including the background */
virtual Gfx::RenderEntryArray listRenderEntries() = 0;
/** Obtain a list of render entries for all the lights in the layer */
Gfx::LightEntryArray listLightEntries();
/** Scroll the layer to the specified position */
void setScrollPosition(const Common::Point &position);
/** Get the current scroll for this layer */
Common::Point getScroll() const;
/** Set the current scroll for this layer */
void setScroll(const Common::Point &scroll);
/** Enable the layer */
void enable(bool enabled);
/** Is the layer enabled? Disabled layers are not drawn. */
bool isEnabled() const;
protected:
void printData() override;
Common::Point _scroll;
float _scrollScale; // Used for the parallax effect
bool _enabled;
};
/**
* A 2D layer
*
* 2D layers contain 2D positioned items
*/
class Layer2D : public Layer {
public:
Layer2D(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Layer2D();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onEnterLocation() override;
void onExitLocation() override;
// Layer API
Gfx::RenderEntryArray listRenderEntries() override;
protected:
void printData() override;
Common::Array<uint32> _itemIndices;
Common::Array<Item *> _items;
};
/**
* A 3D layer
*
* 3D layers contain 3D positioned items, a camera and a floorfield
*/
class Layer3D : public Layer {
public:
Layer3D(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Layer3D();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
void onEnterLocation() override;
// Layer API
Gfx::RenderEntryArray listRenderEntries() override;
/** Obtain the render entry for the background item */
Gfx::RenderEntry *getBackgroundRenderEntry();
protected:
void printData() override;
bool _shouldRenderShadows;
uint32 _maxShadowLength;
float _nearClipPlane;
float _farClipPlane;
Item *_backgroundItem;
Common::Array<Item *> _items;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_LAYER_H

View File

@@ -0,0 +1,41 @@
/* 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/level.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
Level::~Level() {
}
Level::Level(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name) {
_type = TYPE;
}
void Level::printData() {
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,59 @@
/* 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 STARK_RESOURCES_LEVEL_H
#define STARK_RESOURCES_LEVEL_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Resources {
/**
* Levels are holder resources for the locations
*
* Levels are used to share resources between related locations.
* Resources in a level are kept when switching to another location of the same level.
*/
class Level : public Object {
public:
static const Type::ResourceType TYPE = Type::kLevel;
enum SubType {
kGlobal = 1,
kGame = 2,
kStatic = 3
};
Level(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Level();
protected:
void printData() override;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_LEVEL_H

View File

@@ -0,0 +1,110 @@
/* 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/light.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
Light::~Light() {
delete _lightEntry;
}
Light::Light(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_outerConeAngle(0),
_innerConeAngle(0),
_falloffNear(100.0),
_falloffFar(500.0),
_lightEntry(nullptr),
_multiplier(1.0) {
_type = TYPE;
}
void Light::readData(Formats::XRCReadStream *stream) {
_color = stream->readVector3();
_position = stream->readVector3();
_direction = stream->readVector3();
_outerConeAngle = stream->readFloatLE();
_innerConeAngle = stream->readFloatLE();
if (stream->isDataLeft()) {
_falloffNear = stream->readFloatLE();
_falloffFar = stream->readFloatLE();
}
}
void Light::onPostRead() {
Object::onPostRead();
_lightEntry = new Gfx::LightEntry();
_lightEntry->type = (Gfx::LightEntry::Type) _subType;
_lightEntry->direction = _direction;
_lightEntry->innerConeAngle = _innerConeAngle / 2.0;
_lightEntry->outerConeAngle = _outerConeAngle / 2.0;
_lightEntry->falloffNear = _falloffNear;
_lightEntry->falloffFar = _falloffFar;
// Negative lights add darkness
_multiplier = _name.hasPrefix("x_neg") ? -1.0 : 1.0;
}
void Light::saveLoad(ResourceSerializer *serializer) {
Object::saveLoad(serializer);
serializer->syncAsVector3d(_color);
serializer->syncAsVector3d(_position);
}
void Light::setColor(int32 red, int32 green, int32 blue) {
_color.x() = (float) red / 255.0f;
_color.y() = (float) green / 255.0f;
_color.z() = (float) blue / 255.0f;
}
void Light::setPosition(const Math::Vector3d &position) {
_position = position;
}
Gfx::LightEntry *Light::getLightEntry() {
_lightEntry->color = _multiplier * _color;
_lightEntry->position = _position;
return _lightEntry;
}
void Light::printData() {
Common::StreamDebug debug = streamDbg();
debug << "color: " << _color << "\n";
debug << "position: " << _position << "\n";
debug << "direction: " << _direction << "\n";
debug << "innerConeAngle: " << _innerConeAngle << "\n";
debug << "outerConeAngle: " << _outerConeAngle << "\n";
debug << "falloffNear: " << _falloffNear << "\n";
debug << "falloffFar: " << _falloffFar << "\n";
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,85 @@
/* 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 STARK_RESOURCES_LIGHT_H
#define STARK_RESOURCES_LIGHT_H
#include "common/str.h"
#include "math/vector3d.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Gfx {
struct LightEntry;
}
namespace Resources {
/**
* A light source
*/
class Light : public Object {
public:
static const Type::ResourceType TYPE = Type::kLight;
Light(Object *parent, byte subType, uint16 index, const Common::String &name);
~Light() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onPostRead() override;
void saveLoad(ResourceSerializer *serializer) override;
/** Get the rendering object used to represent this light */
Gfx::LightEntry *getLightEntry();
/** Change this light's diffuse color */
void setColor(int32 red, int32 green, int32 blue);
/** Change this light's position, in world coordinates */
void setPosition(const Math::Vector3d &position);
protected:
void printData() override;
Math::Vector3d _color;
Math::Vector3d _position;
Math::Vector3d _direction;
float _innerConeAngle;
float _outerConeAngle;
float _falloffNear;
float _falloffFar;
float _multiplier;
Gfx::LightEntry *_lightEntry;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_LIGHT_H

View File

@@ -0,0 +1,173 @@
/* 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/lipsync.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/item.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/visual/visual.h"
#include "engines/stark/visual/actor.h"
namespace Stark {
namespace Resources {
LipSync::~LipSync() {
}
LipSync::LipSync(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_item(nullptr),
_sceneItem(nullptr),
_faceTexture(nullptr),
_visual(nullptr),
_enabled(false),
_checkForNewVisual(false),
_positionMs(0) {
_type = TYPE;
}
void LipSync::readData(Formats::XRCReadStream *stream) {
uint32 shapeCount = stream->readUint32LE();
for (uint i = 0; i < shapeCount; i++) {
uint32 shape = stream->readUint32LE();
_shapes.push_back(shape);
// The original does not use that data
stream->skip(4);
}
// The original does not use that data
uint32 unkCount = stream->readUint32LE();
stream->skip(unkCount);
}
void LipSync::printData() {
Object::printData();
Common::String phrase;
for (uint i = 0; i < _shapes.size(); i++) {
phrase += _shapes[i];
}
debug("shapes: %s", phrase.c_str());
}
void LipSync::setItem(ItemVisual *item, bool playTalkAnim) {
_item = item;
_checkForNewVisual = !playTalkAnim;
if (_item->getSubType() != Item::kItemModel) {
return;
}
_sceneItem = Object::cast<ModelItem>(item);
_faceTexture = _sceneItem->findTextureSet(TextureSet::kTextureFace);
if (!_faceTexture) {
return;
}
Anim *anim = _item->getAnim();
_visual = nullptr;
if (!anim || anim->getSubType() != Anim::kAnimSkeleton) {
return;
}
AnimSkeleton *animSkeleton = Object::cast<AnimSkeleton>(anim);
_visual = animSkeleton->getVisual()->get<VisualActor>();
if (!_visual) {
return;
}
_visual->setTextureFacial(_faceTexture->getTexture());
_enabled = true;
_positionMs = 0;
}
void LipSync::reset() {
_enabled = false;
_visual = nullptr;
_positionMs = 0;
_item = nullptr;
_sceneItem = nullptr;
_checkForNewVisual = false;
_faceTexture = nullptr;
}
void LipSync::onGameLoop() {
Object::onGameLoop();
if (!_enabled) {
return;
}
if (_checkForNewVisual && _sceneItem && _faceTexture) {
Anim *anim = _sceneItem->getAnim();
if (anim && anim->getSubType() == Anim::kAnimSkeleton) {
AnimSkeleton *animSkeleton = Object::cast<AnimSkeleton>(anim);
_visual = animSkeleton->getVisual()->get<VisualActor>();
if (_visual) {
_visual->setTextureFacial(_faceTexture->getTexture());
}
}
}
if (_visual) {
uint32 shapeIndex = (_positionMs + 100) / 100;
if (shapeIndex < _shapes.size()) {
_visual->setNewFace(_shapes[shapeIndex]);
} else {
reset();
}
_positionMs += StarkGlobal->getMillisecondsPerGameloop();
}
if (_enabled && !_visual) {
reset();
}
}
void LipSync::saveLoadCurrent(ResourceSerializer *serializer) {
serializer->syncAsUint32LE(_enabled);
if (_enabled) {
serializer->syncAsResourceReference(&_item);
serializer->syncAsUint32LE(_positionMs);
if (serializer->isLoading()) {
setItem(_item, false);
}
}
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,81 @@
/* 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 STARK_RESOURCES_LIPSYNC_H
#define STARK_RESOURCES_LIPSYNC_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
class VisualActor;
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class ItemVisual;
class ModelItem;
class TextureSet;
/**
* Speech lipsync data
*/
class LipSync : public Object {
public:
static const Type::ResourceType TYPE = Type::kLipSync;
LipSync(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~LipSync();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onGameLoop() override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
/** Set the item for which the facial texture should be updated according to the lipsync data */
void setItem(ItemVisual *item, bool playTalkAnim);
/** Removes all item related data from the LipSync object */
void reset();
protected:
void printData() override;
Common::Array<char> _shapes;
ItemVisual *_item;
ModelItem *_sceneItem;
TextureSet *_faceTexture;
VisualActor *_visual;
bool _checkForNewVisual;
bool _enabled;
uint32 _positionMs;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_LIPSYNC_H

View File

@@ -0,0 +1,587 @@
/* 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/location.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/movement/movement.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/container.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/layer.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/scroll.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/scene.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 "common/random.h"
namespace Stark {
namespace Resources {
Location::~Location() {
}
Location::Location(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_canScroll(false),
_currentLayer(nullptr),
_hasActiveScroll(false),
_scrollFollowCharacter(false),
_rumbleDurationRemaining(0),
_fadeOut(false),
_fadePosition(0),
_fadeDuration(0),
_swayPeriodMs(0),
_swayAmplitude(0),
_swayOffset(0),
_swayPosition(0),
_idleActionWaitMs(5500),
_floatPeriodMs(0),
_floatAmplitude(0),
_floatPosition(0) {
_type = TYPE;
}
void Location::onAllLoaded() {
Object::onAllLoaded();
_layers = listChildren<Layer>();
Layer *threeDLayer = findChildWithSubtype<Layer>(Layer::kLayer3D);
if (threeDLayer) {
_modelItems = threeDLayer->listChildren<ModelItem>(Item::kItemModel);
}
}
void Location::onEnterLocation() {
Object::onEnterLocation();
StarkScene->setFadeLevel(1.0f);
}
void Location::onGameLoop() {
Object::onGameLoop();
ModelItem *april = StarkGlobal->getCurrent()->getInteractive();
if (april) {
_idleActionWaitMs -= StarkGlobal->getMillisecondsPerGameloop();
if (_idleActionWaitMs <= 0) {
if (!april->getActionAnim()
&& april->getAnimActivity() == Anim::kActorActivityIdle
&& StarkUserInterface->isInteractive()) {
Anim *idleAction = april->getIdleActionAnim();
if (idleAction) {
april->playActionAnim(idleAction);
}
}
_idleActionWaitMs = 11000; // 330 frames at 30 fps
}
}
if (_floatPeriodMs > 0) {
_floatPosition += StarkGlobal->getMillisecondsPerGameloop() / (float) _floatPeriodMs;
if (_floatPosition > 1.0) {
_floatPosition -= 1.0;
}
float floatOffset = sinf(_floatPosition * 2.0f * (float)M_PI) * _floatAmplitude;
StarkScene->setFloatOffset(floatOffset);
}
if (_swayPeriodMs > 0) {
_swayPosition += StarkGlobal->getMillisecondsPerGameloop() / (float) _swayPeriodMs;
if (_swayPosition > 1.0) {
_swayPosition -= 1.0;
}
float sway = sinf((_swayOffset + _swayPosition) * 2.0f * (float)M_PI) * _swayAmplitude;
StarkScene->setSwayAngle(_swayAngle * sway);
}
if (_fadeDuration > 0) {
float fadeSpeed = StarkGlobal->getMillisecondsPerGameloop() / (float) _fadeDuration;
_fadePosition += fadeSpeed * (_fadeOut ? -1.0 : 1.0);
if (_fadeOut && _fadePosition < 0.0) {
_fadePosition = 0.0;
_fadeDuration = 0;
} else if (!_fadeOut && _fadePosition > 1.0) {
_fadePosition = 1.0;
_fadeDuration = 0;
}
StarkScene->setFadeLevel(_fadePosition);
}
if (_hasActiveScroll) {
// Script triggered scrolling has precedence over following the character
_scrollFollowCharacter = false;
}
if (_scrollFollowCharacter) {
assert(april);
Movement *movement = april->getMovement();
bool scrollComplete = scrollToCharacter(april);
if (scrollComplete && (!movement || movement->hasEnded())) {
_scrollFollowCharacter = false;
}
}
if (_rumbleDurationRemaining > 0) {
_rumbleDurationRemaining -= StarkGlobal->getMillisecondsPerGameloop();
}
}
bool Location::has3DLayer() {
return findChildWithSubtype<Layer>(Layer::kLayer3D) != nullptr;
}
Gfx::RenderEntryArray Location::listRenderEntries() {
Gfx::RenderEntryArray renderEntries;
for (uint i = 0; i < _layers.size(); i++) {
Layer *layer = _layers[i];
if (layer->isEnabled()) {
Common::Point baseScroll;
if (_rumbleDurationRemaining > 0) {
baseScroll = layer->getScroll();
Common::Point offsetScroll = baseScroll;
offsetScroll.x = StarkRandomSource->getRandomBit() - 1;
offsetScroll.y = StarkRandomSource->getRandomBit() - 1;
layer->setScroll(offsetScroll);
}
renderEntries.push_back(layer->listRenderEntries());
if (_rumbleDurationRemaining > 0) {
layer->setScroll(baseScroll);
}
}
}
return renderEntries;
}
Gfx::LightEntryArray Location::listLightEntries() {
Gfx::LightEntry *ambient = nullptr;
Gfx::LightEntryArray others;
// Build a list of lights from all the layers ...
for (uint i = 0; i < _layers.size(); i++) {
Layer *layer = _layers[i];
if (layer->isEnabled()) {
Gfx::LightEntryArray layerLights = layer->listLightEntries();
for (uint j = 0; j < layerLights.size(); j++) {
Gfx::LightEntry *light = layerLights[j];
// ... but store the ambient light in a separate variable ...
if (light->type == Gfx::LightEntry::kAmbient) {
ambient = light;
} else {
Math::Matrix4 view = StarkScene->getViewMatrix();
light->worldPosition.x() = light->position.x();
light->worldPosition.y() = light->position.y();
light->worldPosition.z() = light->position.z();
light->worldPosition.w() = 1.0f;
light->eyePosition = view * light->worldPosition;
light->eyeDirection = view.getRotation() * light->direction;
light->eyeDirection.normalize();
others.push_back(light);
}
}
}
}
// ... so that it is first in the final light list
Gfx::LightEntryArray lightEntries;
lightEntries.push_back(ambient);
lightEntries.push_back(others);
return lightEntries;
}
void Location::initScroll(const Common::Point &maxScroll) {
_maxScroll = maxScroll;
_canScroll = _maxScroll.x != 0 || _maxScroll.y != 0;
}
Common::Point Location::getScrollPosition() const {
return _scroll;
}
void Location::setScrollPosition(const Common::Point &position) {
_scroll.x = CLIP<int16>(position.x, 0, _maxScroll.x);
_scroll.y = CLIP<int16>(position.y, 0, _maxScroll.y);
// Setup the layers scroll position
for (uint i = 0; i < _layers.size(); i++) {
_layers[i]->setScrollPosition(_scroll);
}
// Reconfigure the camera
Common::Rect viewport(Gfx::Driver::kGameViewportWidth, Gfx::Driver::kGameViewportHeight);
viewport.translate(_scroll.x, _scroll.y);
StarkScene->scrollCamera(viewport);
}
Common::Point Location::getCharacterScrollPosition(ModelItem *item) {
Common::Point position2D = StarkScene->convertPosition3DToGameScreenOriginal(item->getPosition3D());
Common::Point newScroll;
if (_maxScroll.x > 0) {
newScroll.x = _scroll.x + position2D.x - Gfx::Driver::kGameViewportWidth / 2;
newScroll.y = _scroll.y;
} else {
Gfx::RenderEntry *renderEntry = item->getRenderEntry(_scroll);
Common::Rect boundingRect = renderEntry->getBoundingRect();
if (!boundingRect.isEmpty()) {
position2D.y = (boundingRect.top + boundingRect.bottom) / 2;
}
newScroll.x = _scroll.x;
newScroll.y = _scroll.y + position2D.y - Gfx::Driver::kGameViewportHeight / 2;
}
return newScroll;
}
bool Location::scrollToCharacter(ModelItem *item) {
if (!_canScroll) {
return true;
}
Common::Point newScroll = getCharacterScrollPosition(item);
if (_maxScroll.x > 0) {
if (newScroll.x < _scroll.x - 15 || newScroll.x > _scroll.x + 15) {
newScroll.x = CLIP<int16>(newScroll.x, 0, _maxScroll.x);
return scrollToSmooth(newScroll, true);
}
} else {
if (newScroll.y < _scroll.y - 15 || newScroll.y > _scroll.y + 15) {
newScroll.y = CLIP<int16>(newScroll.y, 0, _maxScroll.y);
return scrollToSmooth(newScroll, true);
}
}
return false;
}
void Location::scrollToCharacterImmediate() {
if (!_canScroll) {
return;
}
ModelItem *april = StarkGlobal->getCurrent()->getInteractive();
setScrollPosition(getCharacterScrollPosition(april));
}
uint Location::getScrollStepFollow() {
ModelItem *april = StarkGlobal->getCurrent()->getInteractive();
Common::Point position2D = StarkScene->convertPosition3DToGameScreenOriginal(april->getPosition3D());
// TODO: Complete
uint scrollStep;
if (_maxScroll.x > 0) {
scrollStep = abs((Gfx::Driver::kGameViewportWidth / 2 - position2D.x) / 16);
} else {
scrollStep = abs((Gfx::Driver::kGameViewportHeight / 2 - position2D.y) / 16);
}
return CLIP<uint>(scrollStep, 1, 4);
}
uint Location::getScrollStep() {
uint scrollStep;
if (_maxScroll.x > 0) {
if (_scroll.x <= _maxScroll.x / 2) {
scrollStep = _scroll.x / 16;
} else {
scrollStep = (_maxScroll.x - _scroll.x) / 16;
}
} else {
if (_scroll.y <= _maxScroll.y / 2) {
scrollStep = _scroll.y / 16;
} else {
scrollStep = (_maxScroll.y - _scroll.y) / 16;
}
}
return CLIP<uint>(scrollStep, 1, 4);
}
bool Location::scrollToSmooth(const Common::Point &position, bool followCharacter) {
uint scrollStep;
if (followCharacter) {
scrollStep = getScrollStepFollow();
} else {
scrollStep = getScrollStep();
}
Common::Point delta;
if (position.x < _scroll.x) {
delta.x = -(int)scrollStep;
delta.x = CLIP<int16>(delta.x, position.x - _scroll.x, 0);
} else if (position.x > _scroll.x) {
delta.x = scrollStep;
delta.x = CLIP<int16>(delta.x, 0, position.x - _scroll.x);
}
if (position.y < _scroll.y) {
delta.y = -(int)scrollStep;
delta.y = CLIP<int16>(delta.y, position.y - _scroll.y, 0);
} else if (position.y > _scroll.y) {
delta.y = scrollStep;
delta.y = CLIP<int16>(delta.y, 0, position.y - _scroll.y);
}
if (delta.x == 0 && delta.y == 0) {
// We already are at the target position, scrolling has completed
return true;
}
setScrollPosition(_scroll + delta);
return false;
}
bool Location::scrollToCoordinateSmooth(uint32 coordinate) {
Common::Point newScroll = getScrollPointFromCoordinate(coordinate);
return scrollToSmooth(newScroll, false);
}
void Location::scrollToCoordinateImmediate(uint32 coordinate) {
Common::Point newScroll = getScrollPointFromCoordinate(coordinate);
return setScrollPosition(newScroll);
}
Common::Point Location::getScrollPointFromCoordinate(uint32 coordinate) const {
Common::Point newScroll = _scroll;
if (_maxScroll.x > 0) {
newScroll.x = coordinate;
} else {
newScroll.y = coordinate;
}
return newScroll;
}
void Location::stopFollowingCharacter() {
_scrollFollowCharacter = false;
}
void Location::startFollowingCharacter() {
_scrollFollowCharacter = true;
}
void Location::setHasActiveScroll() {
_hasActiveScroll = true;
}
void Location::stopAllScrolls() {
Common::Array<Scroll *> scrolls = listChildrenRecursive<Scroll>();
for (uint i = 0; i < scrolls.size(); i++) {
scrolls[i]->stop();
}
_hasActiveScroll = false;
}
void Location::goToLayer(Layer *layer) {
if (_currentLayer) {
_currentLayer->enable(false);
}
layer->enable(true);
_currentLayer = layer;
}
ItemVisual *Location::getCharacterItem(int32 character) const {
return _characterItemMap.getValOrDefault(character, nullptr);
}
void Location::registerCharacterItem(int32 character, ItemVisual *item) {
if (character >= 0) {
_characterItemMap[character] = item;
}
}
const Common::Array<ModelItem *> &Location::listModelItems() const {
return _modelItems;
}
void Location::printData() {
}
void Location::resetAnimationBlending() {
Common::Array<ModelItem *> items = listChildren<ModelItem>(Item::kItemModel);
for (uint i = 0; i < items.size(); i++) {
items[i]->resetAnimationBlending();
}
}
Sound *Location::findStockSound(uint32 stockSoundType) const {
Sound *sound = findStockSound(this, stockSoundType);
if (!sound) {
Level *currentLevel = StarkGlobal->getCurrent()->getLevel();
sound = findStockSound(currentLevel, stockSoundType);
}
if (!sound) {
Level *globalLevel = StarkGlobal->getLevel();
sound = findStockSound(globalLevel, stockSoundType);
}
return sound;
}
Sound *Location::findStockSound(const Object *parent, uint32 stockSoundType) const {
Container *stockSoundContainer = parent->findChildWithSubtype<Container>(Container::kStockSounds);
if (stockSoundContainer) {
Common::Array<Sound *> stockSounds = stockSoundContainer->listChildren<Sound>(Sound::kSoundStock);
for (uint i = 0; i < stockSounds.size(); i++) {
Sound *sound = stockSounds[i];
if (sound->getStockSoundType() == stockSoundType) {
return sound;
}
}
}
return nullptr;
}
void Location::startRumble(int32 rumbleDurationRemaining) {
_rumbleDurationRemaining = rumbleDurationRemaining;
}
void Location::fadeInInit(int32 fadeDuration) {
_fadeOut = false;
_fadePosition = 0.0;
_fadeDuration = fadeDuration;
}
void Location::fadeOutInit(int32 fadeDuration) {
_fadeOut = true;
_fadePosition = 1.0;
_fadeDuration = fadeDuration;
}
void Location::swayScene(int32 periodMs, const Math::Angle &angle, float amplitude, float offset) {
if (periodMs < 33) {
periodMs = 1000;
}
_swayPeriodMs = periodMs;
_swayAngle = angle;
_swayAmplitude = amplitude;
_swayOffset = offset;
_swayPosition = offset;
}
void Location::floatScene(int32 periodMs, float amplitude, float offset) {
if (periodMs < 33) {
periodMs = 1000;
}
_floatPeriodMs = periodMs;
_floatAmplitude = amplitude;
_floatPosition = offset;
}
void Location::saveLoadCurrent(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_scroll.x);
serializer->syncAsSint32LE(_scroll.y);
if (serializer->isLoading()) {
setScrollPosition(_scroll);
}
serializer->syncAsResourceReference(&_currentLayer);
serializer->syncAsSint32LE(_floatPeriodMs);
serializer->syncAsFloat(_floatAmplitude);
serializer->syncAsFloat(_floatPosition);
serializer->syncAsSint32LE(_swayPeriodMs);
serializer->syncAsFloat(_swayAmplitude);
serializer->syncAsFloat(_swayOffset);
serializer->syncAsFloat(_swayPosition);
float swayAngle = _swayAngle.getDegrees();
serializer->syncAsFloat(swayAngle);
if (serializer->isLoading()) {
_swayAngle = swayAngle;
}
}
Layer *Location::getLayerByName(const Common::String &name) {
for (uint i = 0; i < _layers.size(); ++i) {
if (_layers[i]->getName().equalsIgnoreCase(name)) {
return _layers[i];
}
}
return nullptr;
}
Gfx::RenderEntry *Location::getRenderEntryByName(const Common::String &name) {
Gfx::RenderEntryArray renderEntries = listRenderEntries();
for (uint i = 0; i < renderEntries.size(); ++i) {
if (renderEntries[i]->getName().equalsIgnoreCase(name)) {
return renderEntries[i];
}
}
return nullptr;
}
Common::Array<Common::Point> Location::listExitPositions() {
Common::Array<Item *> items = listChildrenRecursive<Item>();
Common::Array<Common::Point> positions;
Common::Array<Item *>::iterator element = items.begin();
while (element != items.end()) {
positions.push_back((*element)->listExitPositions());
++element;
}
return positions;
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,201 @@
/* 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 STARK_RESOURCES_LOCATION_H
#define STARK_RESOURCES_LOCATION_H
#include "common/hashmap.h"
#include "common/rect.h"
#include "common/str.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class ItemVisual;
class Layer;
class ModelItem;
class Sound;
/**
* A location is a scene of the game
*
* Locations contain layers. The game engine retrieves the list of renderable
* items from the current location.
*/
class Location : public Object {
public:
static const Type::ResourceType TYPE = Type::kLocation;
Location(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Location();
// Resource API
void onAllLoaded() override;
void onEnterLocation() override;
void onGameLoop() override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
/** Does the location have a 3D layer ? */
bool has3DLayer();
/** Obtain a list of render entries for all the items in the location */
Gfx::RenderEntryArray listRenderEntries();
/** Obtain a list of render entries for all the lights in the location */
Gfx::LightEntryArray listLightEntries();
/** Initialize scrolling from Camera data */
void initScroll(const Common::Point &maxScroll);
/** Obtain the current scroll position */
Common::Point getScrollPosition() const;
/** Scroll the location to the specified position if possible */
void setScrollPosition(const Common::Point &position);
/** Smoothly scroll to a position in 2D world coordinates */
bool scrollToCoordinateSmooth(uint32 coordinate);
/** Immediatly scroll the character location */
void scrollToCharacterImmediate();
/** Replace the currently active layer */
void goToLayer(Layer *layer);
/**
* Indicate on script driven scroll is active.
*
* This means that the location should not follow the character
*/
void setHasActiveScroll();
/**
* Stop all script driven scrolls
*/
void stopAllScrolls();
/** Tell the location to scroll to follow the character */
void startFollowingCharacter();
/** Tell the location not to scroll to follow the character */
void stopFollowingCharacter();
void scrollToCoordinateImmediate(uint32 coordinate);
/** Get an item from its character index */
ItemVisual *getCharacterItem(int32 character) const;
/** Register an item as a character to the location */
void registerCharacterItem(int32 character, ItemVisual *item);
/** Get the list of items with a 3d model present in the location */
const Common::Array<ModelItem *> &listModelItems() const;
/** Reset animation blending for all the items in the location */
void resetAnimationBlending();
/** Find a stock sound by its type in the location, the level, or the global level */
Sound *findStockSound(uint32 stockSoundType) const;
/** Set remaining frames to rumble on this lcation */
void startRumble(int32 rumbleDurationRemaining);
/** Setup fading for this location */
void fadeInInit(int32 fadeDuration);
void fadeOutInit(int32 fadeDuration);
/** Setup a swaying movement for the 3d items in this location */
void swayScene(int32 periodMs, const Math::Angle &angle, float amplitude, float offset);
/** Setup a up / down floating movement for the 3d items in this location */
void floatScene(int32 periodMs, float amplitude, float offset);
/** Get the layer with a given name, return null when not found */
Layer *getLayerByName(const Common::String &name);
/** Get a render entry with a given name, return null when not found */
Gfx::RenderEntry *getRenderEntryByName(const Common::String &name);
/** Obtain the list of all the inner layers */
Common::Array<Layer *> listLayers() { return _layers; }
/** List all the exit positions */
Common::Array<Common::Point> listExitPositions();
protected:
void printData() override;
private:
bool scrollToSmooth(const Common::Point &position, bool followCharacter);
bool scrollToCharacter(ModelItem *item);
Common::Point getCharacterScrollPosition(ModelItem *item);
uint getScrollStepFollow();
Common::Point getScrollPointFromCoordinate(uint32 coordinate) const;
Sound *findStockSound(const Object *parent, uint32 stockSoundType) const;
Common::Array<Layer *> _layers;
Layer *_currentLayer;
bool _canScroll;
bool _hasActiveScroll;
bool _scrollFollowCharacter;
Common::Point _scroll;
Common::Point _maxScroll;
uint getScrollStep();
typedef Common::HashMap<int32, ItemVisual *> CharacterMap;
CharacterMap _characterItemMap;
Common::Array<ModelItem *> _modelItems;
int32 _rumbleDurationRemaining;
bool _fadeOut;
int32 _fadeDuration;
float _fadePosition;
int32 _swayPeriodMs;
Math::Angle _swayAngle;
float _swayAmplitude;
float _swayOffset;
float _swayPosition;
int32 _idleActionWaitMs;
int32 _floatPeriodMs;
float _floatAmplitude;
float _floatPosition;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_LOCATION_H

View File

@@ -0,0 +1,311 @@
/* 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/object.h"
#include "common/debug-channels.h"
#include "common/streamdebug.h"
#include "common/util.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
Type::Type(ResourceType type) {
_type = type;
}
Type::Type() {
_type = kInvalid;
}
const char *Type::getName() const {
static const struct {
Type::ResourceType type;
const char *name;
} typeNames[] = {
{ Type::kInvalid, "Invalid" },
{ Type::kRoot, "Root" },
{ Type::kLevel, "Level" },
{ Type::kLocation, "Location" },
{ Type::kLayer, "Layer" },
{ Type::kCamera, "Camera" },
{ Type::kFloor, "Floor" },
{ Type::kFloorFace, "FloorFace" },
{ Type::kItem, "Item" },
{ Type::kScript, "Script" },
{ Type::kAnimHierarchy, "AnimHierarchy" },
{ Type::kAnim, "Anim" },
{ Type::kDirection, "Direction" },
{ Type::kImage, "Image" },
{ Type::kAnimScript, "AnimScript" },
{ Type::kAnimScriptItem, "AnimScriptItem" },
{ Type::kSoundItem, "SoundItem" },
{ Type::kPath, "Path" },
{ Type::kFloorField, "FloorField" },
{ Type::kBookmark, "Bookmark" },
{ Type::kKnowledgeSet, "KnowledgeSet" },
{ Type::kKnowledge, "Knowledge" },
{ Type::kCommand, "Command" },
{ Type::kPATTable, "PATTable" },
{ Type::kContainer, "Container" },
{ Type::kDialog, "Dialog" },
{ Type::kSpeech, "Speech" },
{ Type::kLight, "Light" },
{ Type::kCursor, "Cursor" },
{ Type::kBonesMesh, "BonesMesh" },
{ Type::kScroll, "Scroll" },
{ Type::kFMV, "FMV" },
{ Type::kLipSync, "LipSynch" },
{ Type::kAnimSoundTrigger, "AnimSoundTrigger" },
{ Type::kString, "String" },
{ Type::kTextureSet, "TextureSet" }
};
for (uint i = 0; i < ARRAYSIZE(typeNames); i++) {
if (typeNames[i].type == _type) {
return typeNames[i].name;
}
}
return nullptr;
}
Type::ResourceType Type::get() const {
return _type;
}
Object::Object(Object *parent, byte subType, uint16 index, const Common::String &name) :
_parent(parent),
_type(Type::kInvalid),
_subType(subType),
_index(index),
_name(name) {
}
Object::~Object() {
// Delete the children resources
Common::Array<Object *>::iterator i = _children.begin();
while (i != _children.end()) {
delete *i;
i++;
}
}
void Object::readData(Formats::XRCReadStream *stream) {
}
void Object::printData() {
}
void Object::saveLoad(ResourceSerializer *serializer) {
}
void Object::saveLoadCurrent(ResourceSerializer *serializer) {
}
void Object::onPostRead() {
}
void Object::onAllLoaded() {
Common::Array<Object *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onAllLoaded();
i++;
}
}
void Object::onEnterLocation() {
Common::Array<Object *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onEnterLocation();
i++;
}
}
void Object::onGameLoop() {
Common::Array<Object *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onGameLoop();
i++;
}
}
void Object::onEnginePause(bool pause) {
Common::Array<Object *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onEnginePause(pause);
i++;
}
}
void Object::onExitLocation() {
Common::Array<Object *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onExitLocation();
i++;
}
}
void Object::onPreDestroy() {
Common::Array<Object *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onPreDestroy();
i++;
}
}
void Object::print(uint depth) {
printDescription(depth);
printData();
// Recursively print the children resources
for (uint i = 0; i < _children.size(); i++) {
_children[i]->print(depth + 1);
}
}
void Object::printDescription(uint depth) const {
// Build the resource type string
Common::String type(_type.getName());
if (type.empty()) {
type = Common::String::format("%d", _type.get());
}
// Build the resource description
Common::String description = Common::String::format("%s - %s - (sub=%d, index=%d)", type.c_str(), _name.c_str(), _subType, _index);
printWithDepth(depth, description);
}
void Object::printWithDepth(uint depth, const Common::String &string) const {
Common::String prefix;
for (uint i = 0; i < depth; i++) {
prefix += "-";
}
debug("%s %s", prefix.c_str(), string.c_str());
}
Object *Object::findChildWithIndex(Type type, uint16 index, int subType) const {
for (uint i = 0; i < _children.size(); i++) {
if (_children[i]->getType() == type
&& (_children[i]->getSubType() == subType || subType == -1)
&& _children[i]->getIndex() == index) {
// Found a matching child
return _children[i];
}
}
return nullptr;
}
Object *Object::findChildWithOrder(Type type, uint16 order, int subType) const {
uint16 count = 0;
for (uint i = 0; i < _children.size(); i++) {
if (_children[i]->getType() == type
&& (_children[i]->getSubType() == subType || subType == -1)) {
if (count == order) {
// Found a matching child
return _children[i];
}
count++;
}
}
return nullptr;
}
Object *Object::findChildWithName(Type type, const Common::String &name, int subType) const {
for (uint i = 0; i < _children.size(); ++i) {
if (_children[i]->getType() == type
&& (_children[i]->getSubType() == subType || subType == -1)
&& _children[i]->getName() == name) {
// Found a matching child
return _children[i];
}
}
return nullptr;
}
template<>
Object *Object::cast<Object>(Object *resource) {
// No type check when asking for the abstract resource
return resource;
}
template<>
Common::Array<Object *> Object::listChildren<Object>(int subType) const {
assert(subType == -1);
Common::Array<Object *> list;
for (uint i = 0; i < _children.size(); i++) {
list.push_back(_children[i]);
}
return list;
}
template<>
Object *Object::findParent() {
return _parent;
}
void Object::addChild(Object *child) {
_children.push_back(child);
}
UnimplementedResource::UnimplementedResource(Object *parent, Type type, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_dataLength(0),
_data(nullptr) {
_type = type;
}
UnimplementedResource::~UnimplementedResource() {
// Delete this resource's data
delete[] _data;
}
void UnimplementedResource::readData(Formats::XRCReadStream *stream) {
// Read the data
_dataLength = stream->size();
_data = new byte[_dataLength];
uint32 bytesRead = stream->read(_data, _dataLength);
// Verify the whole array could be read
if (bytesRead != _dataLength) {
error("Stark::UnimplementedResource: data length mismatch (%d != %d)", bytesRead, _dataLength);
}
}
void UnimplementedResource::printData() {
// Print the resource data
if (_data) {
Common::hexdump(_data, _dataLength);
}
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,407 @@
/* 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 STARK_RESOURCES_RESOURCE_H
#define STARK_RESOURCES_RESOURCE_H
#include "common/array.h"
#include "common/str.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
class ResourceSerializer;
namespace Resources {
class Type {
public:
enum ResourceType {
kInvalid = 0,
kRoot = 1,
kLevel = 2,
kLocation = 3,
kLayer = 4,
kCamera = 5,
kFloor = 6,
kFloorFace = 7,
kItem = 8,
kScript = 9,
kAnimHierarchy = 10,
kAnim = 11,
kDirection = 12,
kImage = 13,
kAnimScript = 14,
kAnimScriptItem = 15,
kSoundItem = 16,
kPath = 17,
kFloorField = 18,
kBookmark = 19,
kKnowledgeSet = 20,
kKnowledge = 21,
kCommand = 22,
kPATTable = 23,
kContainer = 26,
kDialog = 27,
kSpeech = 29,
kLight = 30,
kCursor = 31, // Not sure about this one
kBonesMesh = 32,
kScroll = 33,
kFMV = 34,
kLipSync = 35,
kAnimSoundTrigger = 36,
kString = 37,
kTextureSet = 38
};
Type();
Type(ResourceType type);
ResourceType get() const;
const char *getName() const;
bool operator==(const Type &other) const {
return other._type == _type;
}
bool operator!=(const Type &other) const {
return other._type != _type;
}
bool operator==(const Type::ResourceType other) const {
return other == _type;
}
bool operator!=(const Type::ResourceType other) const {
return other != _type;
}
private:
ResourceType _type;
};
/**
* Game resource base object
*
* The in-game objects are represented using subclasses of this class.
*
* The game world is made of a tree of resources, with each level further down
* the tree adding further details. An instance of this class is a node in that
* tree.
*
* The first few tree levels are as follow:
* - Root
* - Level
* - Location
* - Layer
*
* The actual world tree is cut off in several sub-trees. There is one sub-tree
* per xarc archive. For resource management reasons the sub-trees are not merged
* in memory, the sub-trees are loaded and unloaded as needed, according to the
* current level / location.
*
* The xarc archives contain each an xrc file, which is a serialized version
* of the initial state of a resource sub-tree. The readData method is called for
* each resource by the archive loader when a resource tree is loaded to set up
* its initial state.
*
* As the game plays, modifications are made to the resources to reflect
* the game's state. When the resource sub-trees are loaded or unloaded their
* state is restored or persisted by the state provider. The saveLoad method
* is called to perform the serialization / deserialization of a resource.
* The saveLoadCurrent method is additionally called when loading or saving
* a sub-tree corresponding to the current level / location. This allows to
* persist additional data needed when restoring an active location.
*
* The OnEnterLocation and OnExitLocation methods are called by the resource
* provider when entering or leaving a level / location.
*
* The OnGameLoop method is called during the game loop.
*
*/
class Object {
public:
virtual ~Object();
/** Get the resource type */
Type getType() const { return _type; }
/** Get the resource sub type */
byte getSubType() const { return _subType; }
/** Get the resource index */
uint16 getIndex() const { return _index; }
/** Get the resource index as a string */
Common::String getIndexAsString() const { return Common::String::format("%02x", _index); }
/** Get the name of the resource */
Common::String getName() const { return _name; }
/**
* Deserialize the resource static data and initial state.
*/
virtual void readData(Formats::XRCReadStream *stream);
/**
* Persist / restore the resource state
*/
virtual void saveLoad(ResourceSerializer *serializer);
/**
* Persist / restore the resource state
*
* Called only for active locations
*/
virtual void saveLoadCurrent(ResourceSerializer *serializer);
/**
* Called when the node's initialization is complete.
*
* Allows to load additional data from file.
*/
virtual void onPostRead();
/**
* Called when the resource sub-tree is entirely loaded.
*
* Allows to load data from other nodes.
*/
virtual void onAllLoaded();
/**
* Called when entering a location
*/
virtual void onEnterLocation();
/**
* Called once per game loop
*/
virtual void onGameLoop();
/**
* Called when ScummVM pauses or resumes the engine
*/
virtual void onEnginePause(bool pause);
/**
* Called when exiting a location
*/
virtual void onExitLocation();
/**
* Called before a resource sub-tree is unloaded.
*/
virtual void onPreDestroy();
/**
* Cast a resource, performing a type check
*/
template<class T>
static T *cast(Object *resource);
/** Find the first parent resource with the specified type */
template<class T>
T *findParent();
/** Find a child resource matching the specified type, index and subtype */
Object *findChildWithIndex(Type type, uint16 index, int subType = -1) const;
/** Find a child resource matching the specified type, order in the children list and subtype */
Object *findChildWithOrder(Type type, uint16 order, int subType = -1) const;
/** Find a child resource matching the specified type, name and subtype */
Object *findChildWithName(Type type, const Common::String &name, int subType = -1) const;
/** Find a child matching the template parameter type */
template<class T>
T *findChild(bool mustBeUnique = true) const;
/** Find a child matching the template parameter type and the specified subtype */
template<class T>
T *findChildWithSubtype(int subType, bool mustBeUnique = true) const;
/** Find a child matching the template parameter type and the specified index */
template<class T>
T *findChildWithIndex(uint16 index, int subType = -1) const;
/** Find a child matching the template parameter type, order in the children list and subtype */
template<class T>
T *findChildWithOrder(uint16 order, int subType = -1) const;
/** Find a child matching the template parameter type, name and subtype */
template<class T>
T *findChildWithName(const Common::String &name, int subType = -1) const;
/** List children matching the template parameter type and the specified subtype */
template<class T>
Common::Array<T *> listChildren(int subType = -1) const;
/** List children recursively matching the template parameter type and the specified subtype */
template<class T>
Common::Array<T *> listChildrenRecursive(int subType = -1);
/** Add a resource to the children list */
void addChild(Object *child);
/** Print debug information for the resource */
virtual void print(uint depth = 0);
protected:
Object(Object *parent, byte subType, uint16 index, const Common::String &name);
void printWithDepth(uint depth, const Common::String &string) const;
void printDescription(uint depth) const;
virtual void printData();
Type _type;
byte _subType;
uint16 _index;
Common::String _name;
Object *_parent;
Common::Array<Object *> _children;
};
/**
* An unimplemented resource type.
*
* Used to display the raw resource data when dumping a resource tree.
* To be removed once all the resource types are implemented.
*/
class UnimplementedResource : public Object {
public:
UnimplementedResource(Object *parent, Type type, byte subType, uint16 index, const Common::String &name);
virtual ~UnimplementedResource();
protected:
void readData(Formats::XRCReadStream *stream) override;
void printData() override;
uint32 _dataLength;
byte *_data;
};
template <class T>
T* Object::cast(Object *resource) {
if (resource && resource->_type != T::TYPE) {
error("Unexpected resource type when casting resource %s instead of %s",
resource->_type.getName(), Type(T::TYPE).getName());
}
return (T *) resource;
}
template<>
Object *Object::cast<Object>(Object *resource);
template<class T>
T *Object::findParent() {
if (getType() == T::TYPE) {
return cast<T>(this);
} else if (!_parent) {
return nullptr;
} else {
return _parent->findParent<T>();
}
}
template<>
Object *Object::findParent();
template <class T>
Common::Array<T *> Object::listChildren(int subType) const {
Common::Array<T *> list;
for (uint i = 0; i < _children.size(); i++) {
if (_children[i]->getType() == T::TYPE
&& (_children[i]->getSubType() == subType || subType == -1)) {
// Found a matching child
list.push_back(Object::cast<T>(_children[i]));
}
}
return list;
}
template<class T>
Common::Array<T *> Object::listChildrenRecursive(int subType) {
Common::Array<T *> list;
for (uint i = 0; i < _children.size(); i++) {
if (_children[i]->getType() == T::TYPE
&& (_children[i]->getSubType() == subType || subType == -1)) {
// Found a matching child
list.push_back(Object::cast<T>(_children[i]));
}
// Look for matching resources in the child's children
list.push_back(_children[i]->listChildrenRecursive<T>(subType));
}
return list;
}
template<>
Common::Array<Object *> Object::listChildren<Object>(int subType) const;
template<class T>
T *Object::findChild(bool mustBeUnique) const {
return findChildWithSubtype<T>(-1, mustBeUnique);
}
template <class T>
T *Object::findChildWithSubtype(int subType, bool mustBeUnique) const {
Common::Array<T *> list = listChildren<T>(subType);
if (list.empty()) {
return nullptr;
}
if (list.size() > 1 && mustBeUnique) {
error("Several children resources matching criteria type = %s, subtype = %d", Type(T::TYPE).getName(), subType);
}
return list.front();
}
template <class T>
T *Object::findChildWithIndex(uint16 index, int subType) const {
return Object::cast<T>(findChildWithIndex(T::TYPE, index, subType));
}
template <class T>
T *Object::findChildWithOrder(uint16 order, int subType) const {
return Object::cast<T>(findChildWithOrder(T::TYPE, order, subType));
}
template<class T>
T *Object::findChildWithName(const Common::String &name, int subType) const {
return Object::cast<T>(findChildWithName(T::TYPE, name, subType));
}
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_RESOURCE_H

View File

@@ -0,0 +1,221 @@
/* 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/path.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
namespace Stark {
namespace Resources {
Object *Path::construct(Object *parent, byte subType, uint16 index, const Common::String &name) {
switch (subType) {
case kPath2D:
return new Path2D(parent, subType, index, name);
case kPath3D:
return new Path3D(parent, subType, index, name);
default:
error("Unknown path subtype %d", subType);
}
}
Path::~Path() {
}
Path::Path(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_field_30(0) {
_type = TYPE;
}
void Path::readData(Formats::XRCReadStream *stream) {
_field_30 = stream->readUint32LE();
}
void Path::printData() {
debug("field_30: %d", _field_30);
}
float Path::getEdgeLength(uint edgeIndex) const {
Math::Vector3d edgeStart = getVertexPosition(edgeIndex);
Math::Vector3d edgeEnd = getVertexPosition(edgeIndex + 1);
return edgeStart.getDistanceTo(edgeEnd);
}
float Path::getWeightedEdgeLength(uint edgeIndex) const {
float length = getEdgeLength(edgeIndex);
float startWeight = getVertexWeight(edgeIndex);
float endWeight = getVertexWeight(edgeIndex + 1);
return 2000.0 * length / (startWeight + endWeight);
}
Math::Vector3d Path::getWeightedPositionInEdge(uint edgeIndex, float positionInEdge) {
float edgeLength = getEdgeLength(edgeIndex);
float weightedEdgeLength = getWeightedEdgeLength(edgeIndex);
float startWeight = getVertexWeight(edgeIndex);
float endWeight = getVertexWeight(edgeIndex + 1);
float weightedEdgePosition = ((endWeight - startWeight) / (2 * weightedEdgeLength) * positionInEdge + startWeight) * 0.001
* positionInEdge / edgeLength;
Math::Vector3d edgeStart = getVertexPosition(edgeIndex);
Math::Vector3d edgeEnd = getVertexPosition(edgeIndex + 1);
return edgeEnd * weightedEdgePosition + edgeStart * (1.0 - weightedEdgePosition);
}
float Path::getSortKey() const {
return 0;
}
Math::Vector3d Path::getEdgeDirection(uint edgeIndex) const {
return Math::Vector3d();
}
Path2D::Path2D(Object *parent, byte subType, uint16 index, const Common::String &name) :
Path(parent, subType, index, name) {
}
void Path2D::readData(Formats::XRCReadStream *stream) {
Path::readData(stream);
uint32 vertexCount = stream->readUint32LE();
for (uint i = 0; i < vertexCount; i++) {
Vertex vertex;
vertex.weight = stream->readFloatLE();
vertex.position = stream->readPoint();
_vertices.push_back(vertex);
}
stream->readUint32LE(); // Unused in the original
}
void Path2D::printData() {
Path::printData();
for (uint i = 0; i < _vertices.size(); i++) {
debug("vertex[%d]: (x %d, y %d), weight: %f", i,
_vertices[i].position.x, _vertices[i].position.y, _vertices[i].weight);
}
}
Path2D::~Path2D() {
}
uint Path2D::getEdgeCount() const {
return _vertices.size() - 1;
}
Math::Vector3d Path2D::getVertexPosition(uint vertexIndex) const {
Common::Point point = _vertices[vertexIndex].position;
return Math::Vector3d(point.x, point.y, 0.0);
}
float Path2D::getVertexWeight(uint vertexIndex) const {
return _vertices[vertexIndex].weight;
}
Path3D::Path3D(Object *parent, byte subType, uint16 index, const Common::String &name) :
Path(parent, subType, index, name),
_sortKey(0) {
}
void Path3D::readData(Formats::XRCReadStream *stream) {
Path::readData(stream);
uint32 vertexCount = stream->readUint32LE();
for (uint i = 0; i < vertexCount; i++) {
Vertex vertex;
vertex.weight = stream->readFloatLE();
vertex.position = stream->readVector3();
_vertices.push_back(vertex);
}
_sortKey = stream->readFloatLE();
}
void Path3D::printData() {
Path::printData();
for (uint i = 0; i < _vertices.size(); i++) {
debug("vertex[%d]: (x %f, y %f, z %f), weight: %f", i,
_vertices[i].position.x(), _vertices[i].position.y(), _vertices[i].position.z(), _vertices[i].weight);
}
debug("sortKey: %f", _sortKey);
}
Path3D::~Path3D() {
}
uint Path3D::getEdgeCount() const {
return _vertices.size() - 1;
}
Math::Vector3d Path3D::getVertexPosition(uint vertexIndex) const {
return _vertices[vertexIndex].position;
}
float Path3D::getVertexWeight(uint vertexIndex) const {
return _vertices[vertexIndex].weight;
}
float Path3D::getSortKey() const {
return _sortKey;
}
Math::Vector3d Path3D::getEdgeDirection(uint edgeIndex) const {
Math::Vector3d direction = getVertexPosition(edgeIndex) - getVertexPosition(edgeIndex + 1);
direction.normalize();
return direction;
}
Math::Vector3d Path3D::getVertexPosition3D(uint vertexIndex, int32 *faceIndex) {
Math::Vector3d vertex = getVertexPosition(vertexIndex);
Floor *floor = StarkGlobal->getCurrent()->getFloor();
if (floor) {
int32 face = floor->findFaceContainingPoint(vertex);
if (face >= 0) {
floor->computePointHeightInFace(vertex, face);
}
if (faceIndex) {
*faceIndex = face;
}
}
return vertex;
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,161 @@
/* 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 STARK_RESOURCES_PATH_H
#define STARK_RESOURCES_PATH_H
#include "common/rect.h"
#include "math/vector3d.h"
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A path can be followed by an item in a location
*
* Path are made of a list of vertices. Two consecutive vertices delimit an edge.
* Each vertex has a weight. A higher weight means a higher movement speed.
*/
class Path : public Object {
public:
static const Type::ResourceType TYPE = Type::kPath;
enum SubType {
kPath2D = 1,
kPath3D = 2
};
/** Path factory */
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name);
Path(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Path();
// Resource API
virtual void readData(Formats::XRCReadStream *stream) override;
/** Get the edge count in the path */
virtual uint getEdgeCount() const = 0;
/**
* Get a unit vector pointing in the direction of an edge
*
* Only valid for 3D paths
*/
virtual Math::Vector3d getEdgeDirection(uint edgeIndex) const;
/** Get the sort key to be used by the item following the path */
virtual float getSortKey() const;
/** Get an edge's length */
float getWeightedEdgeLength(uint edgeIndex) const;
/** Get the scene position from a position in an edge */
Math::Vector3d getWeightedPositionInEdge(uint edgeIndex, float positionInEdge);
protected:
void printData() override;
float getEdgeLength(uint edgeIndex) const;
virtual float getVertexWeight(uint vertexIndex) const = 0;
virtual Math::Vector3d getVertexPosition(uint vertexIndex) const = 0;
uint32 _field_30;
};
/**
* A 2D path for 2D items
*/
class Path2D : public Path {
public:
Path2D(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Path2D();
struct Vertex {
float weight;
Common::Point position;
};
// Resource API
void readData(Formats::XRCReadStream *stream) override;
// Path API
uint getEdgeCount() const override;
protected:
float getVertexWeight(uint vertexIndex) const override;
Math::Vector3d getVertexPosition(uint vertexIndex) const override;
private:
// Resource API
void printData() override;
Common::Array<Vertex> _vertices;
};
/**
* A 3D path for 3D items
*/
class Path3D : public Path {
public:
Path3D(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Path3D();
struct Vertex {
float weight;
Math::Vector3d position;
};
// Resource API
void readData(Formats::XRCReadStream *stream) override;
// Path API
uint getEdgeCount() const override;
float getSortKey() const override;
Math::Vector3d getEdgeDirection(uint edgeIndex) const override;
/** Get the full position in world coordinates of one of the vertices of the path */
Math::Vector3d getVertexPosition3D(uint vertexIndex, int32 *faceIndex);
protected:
float getVertexWeight(uint vertexIndex) const override;
Math::Vector3d getVertexPosition(uint vertexIndex) const override;
private:
// Resource API
void printData() override;
Common::Array<Vertex> _vertices;
float _sortKey;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_PATH_H

View File

@@ -0,0 +1,187 @@
/* 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/pattable.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/string.h"
#include "engines/stark/services/stateprovider.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
PATTable::~PATTable() {
}
PATTable::PATTable(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_defaultAction(-1),
_tooltipOverrideIndex(-1) {
_type = TYPE;
}
void PATTable::readData(Formats::XRCReadStream *stream) {
uint32 entryCount = stream->readUint32LE();
for (uint i = 0; i < entryCount; i++) {
Entry entry;
entry._actionType = stream->readSint32LE();
entry._scriptIndex = stream->readSint32LE();
entry._script = nullptr;
_ownEntries.push_back(entry);
}
_defaultAction = stream->readSint32LE();
}
void PATTable::printData() {
for (uint i = 0; i < _ownEntries.size(); i++) {
debug("entry[%d].actionType: %d", i, _ownEntries[i]._actionType);
debug("entry[%d].scriptIndex: %d", i, _ownEntries[i]._scriptIndex);
}
debug("defaultAction: %d", _defaultAction);
}
void PATTable::onAllLoaded() {
Object::onAllLoaded();
_itemEntries.clear();
addOwnEntriesToItemEntries();
}
void PATTable::onEnterLocation() {
Object::onEnterLocation();
_itemEntries.clear();
// Add our own entries to the list of available actions
addOwnEntriesToItemEntries();
// If the PAT's owning item has a template, find it
ItemTemplate *itemTemplate = findItemTemplate();
// Add the item template actions to the list of available actions
if (itemTemplate) {
PATTable *templatePAT = itemTemplate->findChild<PATTable>();
Common::Array<Entry> templateEntries = templatePAT->listItemEntries();
for (uint i = 0; i < templateEntries.size(); i++) {
if (!_itemEntries.contains(templateEntries[i]._actionType)) {
_itemEntries[templateEntries[i]._actionType] = templateEntries[i];
}
}
}
}
void PATTable::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_tooltipOverrideIndex);
if (serializer->isLoading() && _tooltipOverrideIndex >= 0) {
String *string = findChildWithIndex<String>(_tooltipOverrideIndex);
setTooltip(string);
}
}
ItemTemplate *PATTable::findItemTemplate() {
Item *parent = findParent<Item>();
ItemTemplate *itemTemplate = nullptr;
if (parent->getSubType() == Item::kItemModel) {
ModelItem *item = Object::cast<ModelItem>(parent);
itemTemplate = item->getItemTemplate();
} else if (parent->getSubType() == Item::kItemLevelTemplate) {
LevelItemTemplate *item = Object::cast<LevelItemTemplate>(parent);
itemTemplate = item->getItemTemplate();
}
return itemTemplate;
}
void PATTable::addOwnEntriesToItemEntries() {
for (uint i = 0; i < _ownEntries.size(); i++) {
if (_ownEntries[i]._scriptIndex != -1) {
Entry entry = _ownEntries[i];
entry._script = findChildWithIndex<Script>(_ownEntries[i]._scriptIndex);
_itemEntries[entry._actionType] = entry;
}
}
}
Common::Array<PATTable::Entry> PATTable::listItemEntries() const {
Common::Array<PATTable::Entry> entries;
for (EntryMap::const_iterator it = _itemEntries.begin(); it != _itemEntries.end(); it++) {
entries.push_back(it->_value);
}
return entries;
}
ActionArray PATTable::listPossibleActions() const {
ActionArray actions;
for (EntryMap::const_iterator it = _itemEntries.begin(); it != _itemEntries.end(); it++) {
// Check the script can be launched
if (it->_value._script->shouldExecute(Script::kCallModePlayerAction)) {
actions.push_back(it->_key);
}
}
return actions;
}
bool PATTable::canPerformAction(uint32 action) const {
if (_itemEntries.contains(action)) {
return _itemEntries[action]._script->shouldExecute(Script::kCallModePlayerAction);
}
return false;
}
int32 PATTable::getDefaultAction() const {
if (_defaultAction != -1 && canPerformAction(_defaultAction)) {
return _defaultAction;
} else {
return -1;
}
}
bool PATTable::runScriptForAction(uint32 action) {
if (_itemEntries.contains(action)) {
_itemEntries[action]._script->execute(Script::kCallModePlayerAction);
return true;
}
return false;
}
void PATTable::setTooltip(String *string) {
_name = string->getName();
_tooltipOverrideIndex = string->getIndex();
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,101 @@
/* 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 STARK_RESOURCES_PAT_TABLE_H
#define STARK_RESOURCES_PAT_TABLE_H
#include "common/str.h"
#include "common/hashmap.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Script;
class String;
class ItemTemplate;
typedef Common::Array<uint32> ActionArray;
class PATTable : public Object {
public:
static const Type::ResourceType TYPE = Type::kPATTable;
enum ActionType {
kActionUse = 1,
kActionLook = 2,
kActionTalk = 3,
kActionExit = 7
};
PATTable(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~PATTable();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
void onEnterLocation() override;
void saveLoad(ResourceSerializer *serializer) override;
ActionArray listPossibleActions() const;
bool runScriptForAction(uint32 action);
bool canPerformAction(uint32 action) const;
/** If a default action is available, only it can be executed */
int32 getDefaultAction() const;
/** Replace the PAT tooltip with the name of a string resource */
void setTooltip(String *string);
protected:
struct Entry {
uint32 _actionType;
int32 _scriptIndex;
Script *_script;
};
typedef Common::HashMap<uint32, Entry> EntryMap;
void addOwnEntriesToItemEntries();
Common::Array<Entry> listItemEntries() const;
ItemTemplate *findItemTemplate();
void printData() override;
Common::Array<Entry> _ownEntries;
EntryMap _itemEntries;
int32 _defaultAction;
int32 _tooltipOverrideIndex;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_PAT_TABLE_H

View File

@@ -0,0 +1,41 @@
/* 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/root.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
Root::~Root() {
}
Root::Root(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name) {
_type = TYPE;
}
void Root::printData() {
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,56 @@
/* 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 STARK_RESOURCES_ROOT_H
#define STARK_RESOURCES_ROOT_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* The top level element of the game resource tree.
*
* Contains all the levels.
*/
class Root : public Object {
public:
static const Type::ResourceType TYPE = Type::kRoot;
Root(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Root();
protected:
void printData() override;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_ROOT_H

View File

@@ -0,0 +1,445 @@
/* 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/script.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/command.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/scroll.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/resources/speech.h"
#include "engines/stark/services/dialogplayer.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/tools/decompiler.h"
namespace Stark {
namespace Resources {
Script::~Script() {
}
Script::Script(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_scriptType(0),
_runEvent(0),
_minChapter(0),
_maxChapter(999),
_shouldResetGameSpeed(false),
_enabled(false),
_nextCommand(nullptr),
_pauseTimeLeft(-1),
_suspendingResource(nullptr),
_resumeStatus(kResumeSuspend) {
_type = TYPE;
}
void Script::readData(Formats::XRCReadStream *stream) {
uint32 type = stream->readUint32LE();
_runEvent = stream->readUint32LE();
_minChapter = stream->readUint32LE();
_maxChapter = stream->readUint32LE();
_shouldResetGameSpeed = stream->readBool();
_enabled = type == 0;
switch (_subType) {
case kSubTypeGameEvent:
_scriptType = type == 2 ? kScriptTypePassiveDialog : kScriptTypeOnGameEvent;
break;
case kSubTypePlayerAction:
_scriptType = kScriptTypeOnPlayerAction;
break;
case kSubTypeDialog:
_scriptType = kScriptType4;
break;
default:
error("Unknown script subtype %d for script %s", _subType, getName().c_str());
}
}
void Script::onAllLoaded() {
Object::onAllLoaded();
reset();
}
void Script::onGameLoop() {
Object::onGameLoop();
execute(kCallModeGameLoop);
}
void Script::reset() {
if (_suspendingResource && _suspendingResource->getType() == Type::kItem) {
Item *item = _suspendingResource->cast<Item>(_suspendingResource);
item->setMovement(nullptr);
}
_suspendingResource = nullptr;
_resumeStatus = kResumeSuspend;
_pauseTimeLeft = -1;
_nextCommand = getBeginCommand();
}
bool Script::isOnBegin() {
return _nextCommand && _nextCommand->getSubType() == Command::kCommandBegin;
}
bool Script::isOnEnd() {
return _nextCommand && _nextCommand->getSubType() == Command::kCommandEnd;
}
Command *Script::getBeginCommand() {
return findChildWithSubtype<Command>(Command::kCommandBegin, false);
}
bool Script::isEnabled() {
switch (_scriptType) {
case kScriptTypeOnGameEvent:
case kScriptTypeOnPlayerAction:
return _enabled;
case kScriptType3:
return false;
case kScriptTypePassiveDialog:
case kScriptType4:
return true;
default:
error("Unknown script type %d for script %s", _scriptType, getName().c_str());
}
}
void Script::enable(bool value) {
if (_scriptType == kScriptTypeOnGameEvent || _scriptType == kScriptTypeOnPlayerAction) {
_enabled = value;
}
}
bool Script::shouldExecute(uint32 callMode) {
if ((!isEnabled() && isOnBegin()) || !_nextCommand) {
return false; // Don't execute disabled scripts
}
if (callMode == kCallModeGameLoop && !isOnBegin()) {
return true; // Continue previously running script
}
if (_scriptType == kScriptTypeOnGameEvent) {
if (_runEvent == kGameEventOnGameLoop && callMode != kCallModeGameLoop) {
return false; // Wrong call mode for this script
}
if (_runEvent == kGameEventOnEnterLocation && callMode != kCallModeEnterLocation) {
return false; // Wrong call mode for this script
}
if (_runEvent == kGameEventOnExitLocation && callMode != kCallModeExitLocation) {
return false; // Wrong call mode for this script
}
Item *parentItem = findParent<Item>();
if (parentItem && !parentItem->isEnabled()) {
return false; // Disabled parent
}
} else if (_scriptType == kScriptTypePassiveDialog) {
if (callMode != kCallModeDialogCreateSelections && callMode != kCallModeDialogAnswer) {
return false; // Wrong call mode for this script
}
} else if (_scriptType == kScriptTypeOnPlayerAction) {
if (callMode != kCallModePlayerAction) {
return false; // Wrong call mode for this script
}
} else {
return false; // Wrong script type
}
uint32 currentChapter = StarkGlobal->getCurrentChapter();
if (currentChapter < _minChapter || currentChapter >= _maxChapter) {
return false; // Wrong chapter
}
return true;
}
bool Script::isSuspended() {
return _pauseTimeLeft >= 0 || _suspendingResource;
}
Object *Script::getSuspendingResource() const {
return _suspendingResource;
}
void Script::updateSuspended() {
if (_pauseTimeLeft >= 0) {
// Decrease the remaining pause time
_pauseTimeLeft -= StarkGlobal->getMillisecondsPerGameloop();
} else {
_pauseTimeLeft = -1;
}
if (_nextCommand->getSubType() == Command::kScriptPauseSkippable
&& (StarkUserInterface->wasInteractionDenied() || _pauseTimeLeft < 0)) {
StarkUserInterface->setInteractive(true);
_pauseTimeLeft = -1;
}
bool commandChanged = false;
if (_suspendingResource) {
// Check if the suspending resource is still active
switch (_suspendingResource->getType().get()) {
case Type::kDialog: {
if (!StarkDialogPlayer->isRunning()) {
// Resume the script execution if the dialog is complete
_suspendingResource = nullptr;
}
break;
}
case Type::kFMV: {
// Scripts are not running during an FMV, if we are here, then it has stopped playing
_suspendingResource = nullptr;
break;
}
case Type::kSoundItem: {
Sound *soundItem = Object::cast<Sound>(_suspendingResource);
if (!soundItem->isPlaying()) {
// Resume the script execution once the sound has stopped playing
_suspendingResource = nullptr;
}
break;
}
case Type::kSpeech: {
Speech *speech = Object::cast<Speech>(_suspendingResource);
if (!StarkDialogPlayer->isSpeechReady(speech) && !speech->isPlaying()) {
// Resume the script execution once the speech has stopped playing
_suspendingResource = nullptr;
}
break;
}
case Type::kScroll: {
Scroll *scroll = Object::cast<Scroll>(_suspendingResource);
if (!scroll->isActive()) {
// Resume the script execution once the scroll target position is reached
_suspendingResource = nullptr;
}
break;
}
case Type::kItem: {
if (_nextCommand->getSubType() == Command::kWalkTo) {
if (_resumeStatus == kResumeComplete) {
// Resume the script execution once the item has stopped its movement
_suspendingResource = nullptr;
_nextCommand = _nextCommand->nextCommandIf(false);
commandChanged = true;
} else if (_resumeStatus == kResumeAbort) {
// Resume the script execution once the item has stopped its movement
_suspendingResource = nullptr;
_nextCommand = _nextCommand->nextCommandIf(true);
commandChanged = true;
}
} else {
if (_resumeStatus != kResumeSuspend) {
// Resume the script execution once the item has stopped its movement
_suspendingResource = nullptr;
}
}
break;
}
case Type::kAnim: {
Anim *anim = Object::cast<Anim>(_suspendingResource);
if (anim->isDone()) {
anim->resetItem();
// Resume the script execution once the animation is complete
_suspendingResource = nullptr;
}
break;
}
default:
error("Unhandled suspending resource type %s", _suspendingResource->getType().getName());
}
}
if (_nextCommand->getSubType() == Command::kItemSetActivity && !_suspendingResource) {
_nextCommand->resumeItemSetActivity();
}
if (!isSuspended() && _shouldResetGameSpeed) {
StarkGlobal->setNormalSpeed();
}
if (!isSuspended() && !commandChanged) {
// Resume to the next command
goToNextCommand();
}
}
void Script::stop() {
reset();
_enabled = false;
_returnObjects.clear();
}
void Script::pause(int32 msecs) {
_pauseTimeLeft = msecs;
}
void Script::suspend(Object *cause) {
_suspendingResource = cause;
_resumeStatus = kResumeSuspend;
}
void Script::setResumeStatus(ResumeStatus status) {
_resumeStatus = status;
}
void Script::goToNextCommand() {
_nextCommand = _nextCommand->nextCommand();
}
void Script::execute(uint32 callMode) {
if (!shouldExecute(callMode)) {
return;
}
if (isSuspended()) {
// If the script is suspended, check if it can be resumed
updateSuspended();
}
uint32 executedCommands = 0;
while (1) {
if (isSuspended()) {
break;
}
if (!_nextCommand) {
break; // No next command, stop here
}
if (isOnEnd()) {
break; // Reached the end of the script
}
_nextCommand = _nextCommand->execute(callMode, this);
executedCommands++;
if (executedCommands > 50) {
break; // Too many consecutive commands
}
}
if (isOnEnd() || !_nextCommand) {
// Reset ended scripts so they can be started again
reset();
// Check if we should return to some caller script
if (!_returnObjects.empty()) {
Object *callerObject = _returnObjects.back();
_returnObjects.pop_back();
// Resume execution of the caller object
resumeCallerExecution(callerObject);
}
}
}
void Script::resumeCallerExecution(Object *callerObject) {
switch (callerObject->getType().get()) {
case Type::kCommand: {
Command *callerCommand = Object::cast<Command>(callerObject);
_nextCommand = callerCommand->nextCommand();
break;
}
case Type::kDialog: {
Dialog *callerDialog = Object::cast<Dialog>(callerObject);
StarkDialogPlayer->resume(callerDialog);
break;
}
default:
error("Unhandled caller object type %s", callerObject->getType().getName());
}
}
void Script::addReturnObject(Object *object) {
_returnObjects.push_back(object);
}
void Script::print(uint depth) {
printDescription(depth);
printData();
// Print anything that is not a command
for (uint i = 0; i < _children.size(); i++) {
if (_children[i]->getType() != Type::kCommand) {
_children[i]->print(depth + 1);
}
}
Tools::Decompiler *decompiler = new Tools::Decompiler(this);
// Print the decompiled output
printWithDepth(depth + 1, "Decompiled output");
if (decompiler->getError() == "") {
decompiler->printDecompiled();
} else {
debug("Decompilation failure: %s", decompiler->getError().c_str());
}
delete decompiler;
}
void Script::printData() {
debug("scriptType: %d", _scriptType);
debug("runEvent: %d", _runEvent);
debug("minChapter: %d", _minChapter);
debug("maxChapter: %d", _maxChapter);
debug("shouldResetGameSpeed: %d", _shouldResetGameSpeed);
}
void Script::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_enabled);
}
void Script::saveLoadCurrent(ResourceSerializer *serializer) {
bool isStarted = !isOnBegin();
serializer->syncAsUint32LE(isStarted);
if (isStarted) {
serializer->syncAsResourceReference(&_nextCommand);
serializer->syncArraySize(_returnObjects);
for (uint i = 0; i < _returnObjects.size(); i++) {
serializer->syncAsResourceReference(&_returnObjects[i]);
}
serializer->syncAsSint32LE(_pauseTimeLeft);
serializer->syncAsResourceReference(&_suspendingResource);
serializer->syncAsSint32LE(_resumeStatus);
}
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,180 @@
/* 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 STARK_RESOURCES_SCRIPT_H
#define STARK_RESOURCES_SCRIPT_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class Command;
/**
* A script resource
*
* Scripts are made of a collection of Command resources. Commands
* return the next command to be executed, allowing for branches.
*/
class Script : public Object {
public:
static const Type::ResourceType TYPE = Type::kScript;
enum SubType {
kSubTypeGameEvent = 4,
kSubTypePlayerAction = 5,
kSubTypeDialog = 6
};
enum ScriptType {
kScriptTypeOnGameEvent = 0,
kScriptTypePassiveDialog = 1,
kScriptTypeOnPlayerAction = 2,
kScriptType3 = 3,
kScriptType4 = 4
};
enum GameEvent {
kGameEventOnGameLoop = 0,
kGameEventOnEnterLocation = 1,
kGameEventOnExitLocation = 2
};
/**
* Script call modes.
*
* Most script types are only meaningful for a call mode.
* The shouldExecute method checks the consistency between
* the script type and the call mode.
*/
enum CallMode {
kCallModeGameLoop = 1,
kCallModeExitLocation = 2,
kCallModeEnterLocation = 3,
kCallModePlayerAction = 4,
kCallModeDialogCreateSelections = 5,
kCallModeDialogAnswer = 6
};
enum ResumeStatus {
kResumeComplete,
kResumeAbort,
kResumeSuspend
};
Script(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Script();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void saveLoad(ResourceSerializer *serializer) override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
void onAllLoaded() override;
void onGameLoop() override;
/** Reset the script so that it can be executed again from the beginning */
void reset();
/** Is the script enabled? */
bool isEnabled();
/** Enable the script */
void enable(bool value);
/** Disable and reset the script */
void stop();
/** Is the script on its Begin command? */
bool isOnBegin();
/** Has the script ended? */
bool isOnEnd();
/** Get the script's startup command */
Command *getBeginCommand();
/** Attempt to run the script using the specified call mode */
void execute(uint32 callMode);
/** Pause the script for the specified time */
void pause(int32 msecs);
/** Suspend the script while the specified resource is running */
void suspend(Object *cause);
/** Is the script paused, or waiting for a resource to complete? */
bool isSuspended();
/** Get the resource the script is waiting to complete, if any */
Object *getSuspendingResource() const;
/** Returns true if the script is enabled and valid for this call mode */
bool shouldExecute(uint32 callMode);
/** Step the script to the next command, overriding all checks */
void goToNextCommand();
/**
* Add an object to the return list.
*
* The script will resume execution of this object once it reaches an End opcode
*/
void addReturnObject(Object *object);
/** Set the outcome of a suspension (completion or abortion) */
void setResumeStatus(ResumeStatus status);
protected:
void print(uint depth) override;
void printData() override;
void updateSuspended();
void resumeCallerExecution(Object *callerObject);
uint32 _scriptType;
uint32 _runEvent;
uint32 _minChapter;
uint32 _maxChapter;
bool _shouldResetGameSpeed;
bool _enabled;
Command *_nextCommand;
int32 _pauseTimeLeft;
Object *_suspendingResource;
ResumeStatus _resumeStatus;
Common::Array<Object *> _returnObjects;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_SCRIPT_H

View File

@@ -0,0 +1,100 @@
/* 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/scroll.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
Scroll::~Scroll() {
}
Scroll::Scroll(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_coordinate(0),
_field_30(0),
_field_34(0),
_bookmarkIndex(0),
_active(false) {
_type = TYPE;
}
void Scroll::applyToLocationImmediate() {
Location *location = findParent<Location>();
location->scrollToCoordinateImmediate(_coordinate);
}
void Scroll::start() {
_active = true;
Location *location = findParent<Location>();
location->setHasActiveScroll();
}
void Scroll::stop() {
_active = false;
}
bool Scroll::isActive() {
return _active;
}
void Scroll::onGameLoop() {
Object::onGameLoop();
if (_active) {
Location *location = findParent<Location>();
bool complete = location->scrollToCoordinateSmooth(_coordinate);
if (complete) {
_active = false;
location->stopAllScrolls();
}
}
}
void Scroll::readData(Formats::XRCReadStream *stream) {
_coordinate = stream->readUint32LE();
_field_30 = stream->readUint32LE();
_field_34 = stream->readUint32LE();
_bookmarkIndex = stream->readUint32LE();
}
void Scroll::saveLoadCurrent(ResourceSerializer *serializer) {
serializer->syncAsUint32LE(_active);
if (serializer->isLoading() && _active) {
start();
}
}
void Scroll::printData() {
debug("coordinate: %d", _coordinate);
debug("field_30: %d", _field_30);
debug("field_34: %d", _field_34);
debug("bookmarkIndex: %d", _bookmarkIndex);
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,78 @@
/* 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 STARK_RESOURCES_SCROLL_H
#define STARK_RESOURCES_SCROLL_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* Scroll position for a location
*/
class Scroll : public Object {
public:
static const Type::ResourceType TYPE = Type::kScroll;
Scroll(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Scroll();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onGameLoop() override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
/** Start scrolling the location to this position */
void start();
/** Stop scrolling the location from this object */
void stop();
/** Is this scroll currently running? */
bool isActive();
/** Scroll the location to this position, with immediate effect */
void applyToLocationImmediate();
protected:
void printData() override;
uint32 _coordinate;
uint32 _field_30;
uint32 _field_34;
uint32 _bookmarkIndex;
bool _active;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_SCROLL_H

View File

@@ -0,0 +1,280 @@
/* 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/sound.h"
#include "audio/decoders/vorbis.h"
#include "common/system.h"
#include "engines/stark/formats/iss.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h"
namespace Stark {
namespace Resources {
Sound::~Sound() {
}
Sound::Sound(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_enabled(0),
_looping(false),
_field_64(0),
_loopIndefinitely(false),
_loadFromFile(true),
_maxDuration(0),
_stockSoundType(0),
_field_6C(0),
_soundType(0),
_pan(0),
_volume(0),
_fadeDurationRemaining(0),
_fadeTargetVolume(0.0),
_fadeTargetPan(0.0),
_shouldStopOnDestroy(true) {
_type = TYPE;
}
Audio::RewindableAudioStream *Sound::makeAudioStream() {
Common::SeekableReadStream *stream = nullptr;
Audio::RewindableAudioStream *audioStream = nullptr;
// First try the .iss / isn files
if (_loadFromFile) {
stream = StarkArchiveLoader->getExternalFile(_filename, _archiveName);
} else {
stream = StarkArchiveLoader->getFile(_filename, _archiveName);
}
if (stream) {
audioStream = Formats::makeISSStream(stream, DisposeAfterUse::YES);
}
if (!audioStream) {
// The 2 CD version uses Ogg Vorbis
Common::Path filename = _filename;
Common::String baseName(filename.baseName());
if (baseName.hasSuffix(".iss") || baseName.hasSuffix(".isn") || baseName.hasSuffix(".ssn")) {
baseName = Common::String(baseName.c_str(), baseName.size() - 4) + ".ovs";
filename = _filename.getParent().appendComponent(baseName);
}
stream = StarkArchiveLoader->getExternalFile(filename, _archiveName);
if (stream) {
#ifdef USE_VORBIS
audioStream = Audio::makeVorbisStream(stream, DisposeAfterUse::YES);
#else
warning("Cannot decode sound '%s', Vorbis support is not compiled in", filename.toString().c_str());
delete stream;
#endif
}
}
if (!audioStream) {
warning("Unable to load sound '%s'", _filename.toString().c_str());
}
return audioStream;
}
Audio::Mixer::SoundType Sound::getMixerSoundType() {
switch (_soundType) {
case kSoundTypeVoice:
return Audio::Mixer::kSpeechSoundType;
case kSoundTypeEffect:
return Audio::Mixer::kSFXSoundType;
case kSoundTypeMusic:
return Audio::Mixer::kMusicSoundType;
default:
error("Unknown sound type '%d'", _soundType);
}
}
void Sound::play() {
if (isPlaying()) {
return;
}
Audio::RewindableAudioStream *rewindableStream = makeAudioStream();
if (!rewindableStream) {
return;
}
Audio::AudioStream *playStream;
if (_looping) {
playStream = Audio::makeLoopingAudioStream(rewindableStream, 0);
} else {
playStream = rewindableStream;
}
g_system->getMixer()->playStream(getMixerSoundType(), &_handle, playStream, -1,
_volume * Audio::Mixer::kMaxChannelVolume, _pan * 127);
}
bool Sound::isPlaying() {
return g_system->getMixer()->isSoundHandleActive(_handle);
}
void Sound::stop() {
g_system->getMixer()->stopHandle(_handle);
_handle = Audio::SoundHandle();
}
void Sound::onPreDestroy() {
Object::onPreDestroy();
if (_shouldStopOnDestroy) {
stop();
}
}
void Sound::readData(Formats::XRCReadStream *stream) {
_filename = stream->readString();
_enabled = stream->readUint32LE();
_looping = stream->readBool();
_field_64 = stream->readUint32LE();
_loopIndefinitely = stream->readBool();
_maxDuration = stream->readUint32LE();
_loadFromFile = stream->readBool(); // Used only in the 4CD version
_stockSoundType = stream->readUint32LE();
_soundName = stream->readString();
_field_6C = stream->readUint32LE();
_soundType = stream->readUint32LE();
_pan = stream->readFloatLE();
_volume = stream->readFloatLE();
_archiveName = stream->getArchiveName();
}
void Sound::printData() {
debug("filename: %s", _filename.toString().c_str());
debug("enabled: %d", _enabled);
debug("looping: %d", _looping);
debug("field_64: %d", _field_64);
debug("loopIndefinitely: %d", _loopIndefinitely);
debug("maxDuration: %d", _maxDuration);
debug("loadFromFile: %d", _loadFromFile);
debug("stockSoundType: %d", _stockSoundType);
debug("soundName: %s", _soundName.c_str());
debug("field_6C: %d", _field_6C);
debug("soundType: %d", _soundType);
debug("pan: %f", _pan);
debug("volume: %f", _volume);
}
void Sound::onGameLoop() {
Object::onGameLoop();
if (_subType == kSoundBackground && !isPlaying()) {
Location *location = StarkGlobal->getCurrent()->getLocation();
if (location->getName() != "Amongst Stalls" || StarkGlobal->getCurrentChapter() < 100) {
play();
}
}
if (_looping && !_loopIndefinitely) {
// Automatically stop after the maximum run time has been reached
uint32 elapsedTime = g_system->getMixer()->getSoundElapsedTime(_handle);
if (elapsedTime > _maxDuration) {
stop();
}
}
if (_fadeDurationRemaining > 0 && isPlaying()) {
_volume += (_fadeTargetVolume - _volume) * StarkGlobal->getMillisecondsPerGameloop() / (float) _fadeDurationRemaining;
_pan += (_fadeTargetPan - _pan) * StarkGlobal->getMillisecondsPerGameloop() / (float) _fadeDurationRemaining;
_fadeDurationRemaining -= StarkGlobal->getMillisecondsPerGameloop();
if (_fadeDurationRemaining <= 0) {
_fadeDurationRemaining = 0;
_volume = _fadeTargetVolume;
_pan = _fadeTargetPan;
}
g_system->getMixer()->setChannelVolume(_handle, _volume * Audio::Mixer::kMaxChannelVolume);
g_system->getMixer()->setChannelBalance(_handle, _pan * 127);
}
}
uint32 Sound::getStockSoundType() const {
return _stockSoundType;
}
void Sound::changeVolumePan(int32 volume, int32 pan, int32 duration) {
if (isPlaying()) {
_fadeDurationRemaining = duration;
if (_fadeDurationRemaining > 0) {
_fadeTargetVolume = volume / 100.0f;
_fadeTargetPan = pan / 100.0f;
} else {
_volume = volume / 100.0f;
_pan = pan / 100.0f;
g_system->getMixer()->setChannelVolume(_handle, _volume * Audio::Mixer::kMaxChannelVolume);
g_system->getMixer()->setChannelBalance(_handle, _pan * 127);
}
} else {
if (_fadeDurationRemaining == 0) {
_volume = volume / 100.0f;
_pan = pan / 100.0f;
}
}
}
void Sound::saveLoadCurrent(ResourceSerializer *serializer) {
bool playing = isPlaying();
serializer->syncAsUint32LE(playing);
if (_subType != kSoundBackground && playing) {
uint32 elapsed = g_system->getMixer()->getSoundElapsedTime(_handle);
serializer->syncAsUint32LE(elapsed);
serializer->syncAsFloat(_volume);
serializer->syncAsFloat(_pan);
serializer->syncAsUint32LE(_fadeDurationRemaining);
serializer->syncAsFloat(_fadeTargetVolume);
serializer->syncAsFloat(_fadeTargetPan);
if (serializer->isLoading()) {
play();
// TODO: Seek to the "elapsed" position
}
}
}
void Sound::onEnginePause(bool pause) {
g_system->getMixer()->pauseHandle(_handle, pause);
}
void Sound::setStopOnDestroy(bool stopOnDestroy) {
_shouldStopOnDestroy = stopOnDestroy;
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,128 @@
/* 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 STARK_RESOURCES_SOUND_H
#define STARK_RESOURCES_SOUND_H
#include "audio/mixer.h"
#include "common/path.h"
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Audio {
class RewindableAudioStream;
}
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A sound resource
*/
class Sound : public Object {
public:
static const Type::ResourceType TYPE = Type::kSoundItem;
enum SubType {
kSoundBackground = 3,
kSoundStock = 5
};
enum SoundType {
kSoundTypeVoice = 0,
kSoundTypeEffect = 1,
kSoundTypeMusic = 2
};
Sound(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Sound();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onPreDestroy() override;
void onGameLoop() override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
void onEnginePause(bool pause) override;
/** Start playing the sound */
void play();
/** Is the sound playing */
bool isPlaying();
/** Stop the sound */
void stop();
/** Get the type for stock sounds */
uint32 getStockSoundType() const;
/** Fade the sound's current volume and pan to the specified target over duration milliseconds */
void changeVolumePan(int32 volume, int32 pan, int32 duration);
/** Set whether to loop or not */
void setLooping(bool looping) { _looping = looping; }
/**
* In the menus, we don't want sounds to be cut when changing screens.
* The actual sounds need to outlive the entity. This flag allows to do so.
*/
void setStopOnDestroy(bool stopOnDestroy);
protected:
void printData() override;
Audio::RewindableAudioStream *makeAudioStream();
Audio::Mixer::SoundType getMixerSoundType();
Common::Path _filename;
Common::Path _archiveName;
uint32 _enabled;
bool _looping;
uint32 _field_64;
bool _loopIndefinitely;
uint32 _maxDuration;
bool _loadFromFile;
uint32 _stockSoundType;
Common::String _soundName;
uint32 _field_6C;
uint32 _soundType;
float _pan;
float _volume;
bool _shouldStopOnDestroy;
int32 _fadeDurationRemaining;
float _fadeTargetVolume;
float _fadeTargetPan;
Audio::SoundHandle _handle;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_SOUND_H

View File

@@ -0,0 +1,247 @@
/* 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/speech.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/settings.h"
#include "engines/stark/services/dialogplayer.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/stateprovider.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/lipsync.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/sound.h"
namespace Stark {
namespace Resources {
Speech::~Speech() {
}
Speech::Speech(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_character(0),
_soundResource(nullptr),
_playTalkAnim(true),
_removeTalkAnimWhenComplete(true),
_lipSync(nullptr),
_waitTimeRemaining(-1) {
_type = TYPE;
}
Common::String Speech::getPhrase() const {
return _phrase;
}
void Speech::playSound() {
StarkGlobal->setNormalSpeed();
if (_playTalkAnim) {
setCharacterTalkAnim();
}
stopOtherSpeechesFromSameCharacter();
_soundResource = findChild<Sound>();
_soundResource->play();
}
void Speech::setCharacterTalkAnim() {
ItemVisual *characterItem = getCharacterItem();
if (characterItem) {
characterItem->setAnimActivity(Anim::kActorActivityTalk);
_lipSync = findChild<LipSync>();
if (_lipSync) {
_lipSync->setItem(characterItem, _playTalkAnim);
}
}
}
void Speech::removeCharacterTalkAnim() const {
ItemVisual *characterItem = getCharacterItem();
if (characterItem && characterItem->getAnimActivity() == Anim::kActorActivityTalk) {
characterItem->setAnimActivity(Anim::kActorActivityIdle);
}
}
ItemVisual *Speech::getCharacterItem() const {
Current *current = StarkGlobal->getCurrent();
if (!current) {
return nullptr;
}
Location *location = current->getLocation();
if (!location) {
return nullptr;
}
return location->getCharacterItem(_character);
}
int32 Speech::getCharacterId() {
return _character;
}
bool Speech::isPlaying() {
return _soundResource || _waitTimeRemaining > 0;
}
void Speech::stop() {
if (_soundResource) {
_soundResource->stop();
_soundResource = nullptr;
}
_waitTimeRemaining = -1;
if (_lipSync) {
_lipSync->reset();
}
if (_removeTalkAnimWhenComplete) {
removeCharacterTalkAnim();
}
_removeTalkAnimWhenComplete = true;
_playTalkAnim = true;
}
bool Speech::characterIsApril() const {
int32 aprilCharacterIndex = StarkGlobal->getApril()->getCharacterIndex();
return _character == aprilCharacterIndex;
}
int32 Speech::getPauseAfterSpeechDuration() const {
if (_phrase.hasSuffix("...")) {
return 1400;
} else if (_phrase.hasSuffix("--")) {
return 0;
} else {
return 1000;
}
}
void Speech::readData(Formats::XRCReadStream *stream) {
Object::readData(stream);
_phrase = stream->readString();
_character = stream->readSint32LE();
// bug fix for #12967 (STARK: Cortez says "no", but subtitles say "si")
if (StarkSettings->getLanguage() == Common::EN_ANY
&& _character == 1 // Cortez
&& getIndex() == 1
&& getSubType() == 0
&& getName().equals("Cortez_Laying low #1")) {
_phrase = "Nyo! So it was a good thing I didn't stick my head out the door to look for you, then, no?";
}
// For debug purposes
//printData();
}
void Speech::onGameLoop() {
Object::onGameLoop();
if (_soundResource && !_soundResource->isPlaying()) {
_soundResource->stop();
_soundResource = nullptr;
_waitTimeRemaining = getPauseAfterSpeechDuration();
}
if (_waitTimeRemaining >= 0) {
_waitTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
if (StarkGlobal->isFastForward()) {
_waitTimeRemaining = -1;
}
if (_waitTimeRemaining <= 0) {
stop();
}
}
}
void Speech::onExitLocation() {
stop();
}
void Speech::onPreDestroy() {
stop();
}
void Speech::printData() {
Object::printData();
debug("phrase: %s", _phrase.c_str());
debug("character: %d", _character);
}
void Speech::setPlayTalkAnim(bool playTalkAnim) {
_playTalkAnim = playTalkAnim;
}
void Speech::stopOtherSpeechesFromSameCharacter() {
Level *globalLevel = StarkGlobal->getLevel();
Level *currentLevel = StarkGlobal->getCurrent()->getLevel();
Location *currentLocation = StarkGlobal->getCurrent()->getLocation();
Common::Array<Speech *> globalLevelSpeeches = globalLevel->listChildrenRecursive<Speech>();
Common::Array<Speech *> currentLevelSpeeches = currentLevel->listChildrenRecursive<Speech>();
Common::Array<Speech *> currentLocationSpeeches = currentLocation->listChildrenRecursive<Speech>();
Common::Array<Speech *> speeches;
speeches.push_back(globalLevelSpeeches);
speeches.push_back(currentLevelSpeeches);
speeches.push_back(currentLocationSpeeches);
for (uint i = 0; i < speeches.size(); i++) {
Speech *speech = speeches[i];
if (speech->_character == _character && speech->isPlaying()) {
speech->stop();
}
}
}
void Speech::saveLoadCurrent(ResourceSerializer *serializer) {
bool playing = isPlaying();
serializer->syncAsUint32LE(playing);
if (playing) {
serializer->syncAsUint32LE(_removeTalkAnimWhenComplete);
serializer->syncAsResourceReference(&_soundResource);
serializer->syncAsResourceReference(&_lipSync);
if (serializer->isLoading()) {
StarkDialogPlayer->playSingle(this);
}
}
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,106 @@
/* 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 STARK_RESOURCES_SPEECH_H
#define STARK_RESOURCES_SPEECH_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
class ItemVisual;
class LipSync;
class Sound;
/**
* Speech resource
*
* Speech resources are used to define dialog lines.
* A Speech resource contains text for a character and references
* a Sound resource for the dubbing.
*/
class Speech : public Object {
public:
static const Type::ResourceType TYPE = Type::kSpeech;
Speech(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~Speech();
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onGameLoop() override;
void onExitLocation() override;
void onPreDestroy() override;
void saveLoadCurrent(ResourceSerializer *serializer) override;
/** Obtain the text associated to the speech line */
Common::String getPhrase() const;
/** Play the voice over */
void playSound();
/** Return true if the speech is playing */
bool isPlaying();
/** Stop the speech if it is playing */
void stop();
/** Is the character saying the line April ? */
bool characterIsApril() const;
/** Should the character change to the talk anim while this is playing? */
void setPlayTalkAnim(bool playTalkAnim);
/** Get the character's id - index in the KnowledgeSet */
int32 getCharacterId();
protected:
void printData() override;
void setCharacterTalkAnim();
void removeCharacterTalkAnim() const;
ItemVisual *getCharacterItem() const;
void stopOtherSpeechesFromSameCharacter();
int32 getPauseAfterSpeechDuration() const;
Common::String _phrase;
int32 _character;
bool _playTalkAnim;
bool _removeTalkAnimWhenComplete;
Sound *_soundResource;
LipSync *_lipSync;
int32 _waitTimeRemaining;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_SPEECH_H

View File

@@ -0,0 +1,41 @@
/* 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/string.h"
#include "engines/stark/formats/xrc.h"
namespace Stark {
namespace Resources {
String::~String() {
}
String::String(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name) {
_type = TYPE;
}
void String::printData() {
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,57 @@
/* 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 STARK_RESOURCES_STRING_H
#define STARK_RESOURCES_STRING_H
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A character string resource.
*
* String resources are used by scripts to change the title
* of items.
*/
class String : public Object {
public:
static const Type::ResourceType TYPE = Type::kString;
String(Object *parent, byte subType, uint16 index, const Common::String &name);
~String() override;
protected:
void printData() override;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_STRING_H

View File

@@ -0,0 +1,202 @@
/* 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/textureset.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/dds.h"
#include "engines/stark/formats/tm.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/settings.h"
#include "common/file.h"
#include "common/compression/unzip.h"
#include "image/png.h"
namespace Stark {
namespace Resources {
TextureSet::~TextureSet() {
delete _textureSet;
}
TextureSet::TextureSet(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_textureSet(nullptr) {
_type = TYPE;
}
Gfx::TextureSet *TextureSet::getTexture() {
return _textureSet;
}
void TextureSet::readData(Formats::XRCReadStream *stream) {
_filename = Common::Path(stream->readString());
_archiveName = stream->getArchiveName();
}
void TextureSet::onPostRead() {
if (StarkSettings->isAssetsModEnabled() && StarkGfx->supportsModdedAssets()) {
_textureSet = readOverrideDdsArchive();
}
if (!_textureSet) {
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_filename, _archiveName);
_textureSet = Formats::TextureSetReader::read(stream);
delete stream;
}
}
static Common::String stripExtension(const Common::String &filename) {
if (filename.hasSuffixIgnoreCase(".bmp")) {
return Common::String(filename.c_str(), filename.size() - 4);
}
return filename;
}
void TextureSet::extractArchive() {
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_filename, _archiveName);
Formats::BiffArchive *archive = Formats::TextureSetReader::readArchive(stream);
Common::Array<Formats::Texture *> textures = archive->listObjectsRecursive<Formats::Texture>();
for (uint i = 0; i < textures.size(); i++) {
Common::Path filename(Common::String::format(
"dump/%s/%s.png",
_filename.baseName().c_str(),
stripExtension(textures[i]->getName()).c_str()));
if (Common::File::exists(filename)) {
continue;
}
Common::DumpFile out;
if (!out.open(filename, true)) {
warning("Unable to open file '%s' for writing", filename.toString().c_str());
return;
}
Graphics::Surface *surface = textures[i]->getSurface();
Image::writePNG(out, *surface);
out.close();
surface->free();
delete surface;
}
delete archive;
delete stream;
}
Gfx::TextureSet *TextureSet::readOverrideDdsArchive() {
Common::Path archiveName = _filename.append(".zip");
debugC(kDebugModding, "Attempting to load %s", archiveName.toString().c_str());
Common::Archive *archive = Common::makeZipArchive(archiveName);
if (!archive) {
return nullptr;
}
Common::ArchiveMemberList files;
archive->listMatchingMembers(files, "*.dds");
if (files.empty()) {
warning("No DDS files found in archive %s", archiveName.toString().c_str());
delete archive;
return nullptr;
}
uint loadedCount = 0;
Gfx::TextureSet *textureSet = new Gfx::TextureSet();
for (Common::ArchiveMemberList::const_iterator it = files.begin(); it != files.end(); it++) {
const Common::String &name = (*it)->getName();
debugC(kDebugModding, "Attempting to load texture %s", name.c_str());
Common::SeekableReadStream *ddsStream = (*it)->createReadStream();
if (!ddsStream) {
warning("Unable to open %s for reading in %s", (*it)->getName().c_str(), archiveName.toString().c_str());
continue;
}
Formats::DDS dds;
if (!dds.load(*ddsStream, name + " in " + archiveName.toString('/'))) {
delete ddsStream;
continue;
}
const Formats::DDS::MipMaps &mipmaps = dds.getMipMaps();
if (mipmaps.empty()) {
warning("No mipmaps in %s", name.c_str());
delete ddsStream;
continue;
}
Gfx::Texture *texture = StarkGfx->createTexture();
texture->setLevelCount(mipmaps.size());
for (uint i = 0; i < mipmaps.size(); i++) {
texture->addLevel(i, &mipmaps[i]);
}
// Remove the .dds extension, add .bmp to match the names
// used by the models.
Common::String textureName = Common::String(name.c_str(), name.size() - 4);
// Fix for loading one of Emma's textures in mods caused by inconsistent zip filename encoding
// The original game uses a 1 byte character (\xe6 which is ae in CP1252)
// \x91 is ae in CP437 and CP850 which is used by some compression utilities
// \xc3\xa6 is the UTF-8 2-byte representation
// Both get converted to the game's original encoding here
if (textureName == "pupp\x91r" || textureName == "pupp\xc3\xa6r") {
textureName = "pupp\xe6r";
}
textureSet->addTexture(textureName + ".bmp", texture);
delete ddsStream;
loadedCount++;
}
debugC(kDebugModding, "Loaded %d textures from %s", loadedCount, archiveName.toString().c_str());
delete archive;
return textureSet;
}
void TextureSet::printData() {
debug("filename: %s", _filename.toString().c_str());
}
} // End of namespace Resources
} // End of namespace Stark

View File

@@ -0,0 +1,82 @@
/* 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 STARK_RESOURCES_TEXTURE_SET_H
#define STARK_RESOURCES_TEXTURE_SET_H
#include "common/path.h"
#include "common/str.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Gfx {
class TextureSet;
}
namespace Formats {
class XRCReadStream;
}
namespace Resources {
/**
* A texture resource
*
* Used by items to provide textures to meshes
*/
class TextureSet : public Object {
public:
static const Type::ResourceType TYPE = Type::kTextureSet;
enum SubType {
kTextureNormal = 1,
kTextureFace = 2
};
TextureSet(Object *parent, byte subType, uint16 index, const Common::String &name);
~TextureSet() override;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
void onPostRead() override;
/** Obtain the texture to be rendered */
Gfx::TextureSet *getTexture();
/** Extract the texture set archive to the dump directory relative to the current directory */
void extractArchive();
protected:
void printData() override;
Gfx::TextureSet *readOverrideDdsArchive();
Common::Path _filename;
Common::Path _archiveName;
Gfx::TextureSet *_textureSet;
};
} // End of namespace Resources
} // End of namespace Stark
#endif // STARK_RESOURCES_TEXTURE_SET_H