STARK: Implement animation triggered sounds

Mainly used for footstep sounds
This commit is contained in:
Bastien Bouclet 2016-11-10 07:57:03 +01:00
parent bd262446fa
commit 368d71cfd5
13 changed files with 257 additions and 9 deletions

View File

@ -26,6 +26,7 @@
#include "engines/stark/resources/anim.h" #include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animhierarchy.h" #include "engines/stark/resources/animhierarchy.h"
#include "engines/stark/resources/animscript.h" #include "engines/stark/resources/animscript.h"
#include "engines/stark/resources/animsoundtrigger.h"
#include "engines/stark/resources/bonesmesh.h" #include "engines/stark/resources/bonesmesh.h"
#include "engines/stark/resources/bookmark.h" #include "engines/stark/resources/bookmark.h"
#include "engines/stark/resources/camera.h" #include "engines/stark/resources/camera.h"
@ -275,6 +276,9 @@ Resources::Object *XRCReader::createResource(XRCReadStream *stream, Resources::O
case Resources::Type::kLipSync: case Resources::Type::kLipSync:
resource = new Resources::LipSync(parent, subType, index, name); resource = new Resources::LipSync(parent, subType, index, name);
break; break;
case Resources::Type::kAnimSoundTrigger:
resource = new Resources::AnimSoundTrigger(parent, subType, index, name);
break;
case Resources::Type::kString: case Resources::Type::kString:
resource = new Resources::String(parent, subType, index, name); resource = new Resources::String(parent, subType, index, name);
break; break;

View File

@ -31,6 +31,7 @@ MODULE_OBJS := \
resources/anim.o \ resources/anim.o \
resources/animhierarchy.o \ resources/animhierarchy.o \
resources/animscript.o \ resources/animscript.o \
resources/animsoundtrigger.o \
resources/bonesmesh.o \ resources/bonesmesh.o \
resources/bookmark.o \ resources/bookmark.o \
resources/camera.o \ resources/camera.o \

View File

@ -514,6 +514,15 @@ uint32 AnimSkeleton::getDuration() const {
return _totalTime; return _totalTime;
} }
uint32 AnimSkeleton::getCurrentTime() const {
return _currentTime;
}
uint32 AnimSkeleton::getRemainingTime() const {
int32 remainingTime = _totalTime - _currentTime;
return CLIP<int32>(remainingTime, 0, _totalTime);
}
void AnimSkeleton::playAsAction(ItemVisual *item) { void AnimSkeleton::playAsAction(ItemVisual *item) {
_actionItem = item; _actionItem = item;

View File

@ -260,6 +260,12 @@ public:
bool isAtTime(uint32 time) const override; bool isAtTime(uint32 time) const override;
uint32 getMovementSpeed() const override; uint32 getMovementSpeed() const override;
/** Get the position in the animation loop in milliseconds */
uint32 getCurrentTime() const;
/** Get the duration in milliseconds before the animation loops ends */
uint32 getRemainingTime() const;
protected: protected:
void printData() override; void printData() override;

View File

@ -0,0 +1,101 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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) {
// TODO: If the location has a 3D layer set the source position of the sound to the item position
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,74 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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

@ -39,7 +39,8 @@ public:
static const Type::ResourceType TYPE = Type::kContainer; static const Type::ResourceType TYPE = Type::kContainer;
enum SubType { enum SubType {
kSounds = 5 kSounds = 5,
kStockSounds = 8
}; };
Container(Object *parent, byte subType, uint16 index, const Common::String &name); Container(Object *parent, byte subType, uint16 index, const Common::String &name);

View File

@ -27,9 +27,12 @@
#include "engines/stark/movement/movement.h" #include "engines/stark/movement/movement.h"
#include "engines/stark/resources/container.h"
#include "engines/stark/resources/item.h" #include "engines/stark/resources/item.h"
#include "engines/stark/resources/layer.h" #include "engines/stark/resources/layer.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/scroll.h" #include "engines/stark/resources/scroll.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/scene.h" #include "engines/stark/scene.h"
#include "engines/stark/services/services.h" #include "engines/stark/services/services.h"
@ -335,5 +338,37 @@ void Location::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;
}
} // End of namespace Resources } // End of namespace Resources
} // End of namespace Stark } // End of namespace Stark

View File

@ -41,6 +41,7 @@ namespace Resources {
class ItemVisual; class ItemVisual;
class Layer; class Layer;
class ModelItem; class ModelItem;
class Sound;
/** /**
* A location is a scene of the game * A location is a scene of the game
@ -115,6 +116,9 @@ public:
/** Reset animation blending for all the items in the location */ /** Reset animation blending for all the items in the location */
void resetAnimationBlending(); void resetAnimationBlending();
/** Find a stock sound by its type in the location, the level, or the global level */
Sound *findStockSound(uint32 stockSoundType) const;
protected: protected:
void printData() override; void printData() override;
@ -125,6 +129,8 @@ private:
uint getScrollStepFollow(); uint getScrollStepFollow();
Common::Point getScrollPointFromCoordinate(uint32 coordinate) const; Common::Point getScrollPointFromCoordinate(uint32 coordinate) const;
Sound *findStockSound(const Object *parent, uint32 stockSoundType) const;
Common::Array<Layer *> _layers; Common::Array<Layer *> _layers;
Layer *_currentLayer; Layer *_currentLayer;

View File

@ -78,7 +78,7 @@ const char *Type::getName() const {
{ Type::kScroll, "Scroll" }, { Type::kScroll, "Scroll" },
{ Type::kFMV, "FMV" }, { Type::kFMV, "FMV" },
{ Type::kLipSync, "LipSynch" }, { Type::kLipSync, "LipSynch" },
{ Type::kAnimScriptBonesTrigger, "AnimScriptBonesTrigger" }, { Type::kAnimSoundTrigger, "AnimSoundTrigger" },
{ Type::kString, "String" }, { Type::kString, "String" },
{ Type::kTextureSet, "TextureSet" } { Type::kTextureSet, "TextureSet" }
}; };
@ -235,7 +235,7 @@ Object *Object::cast<Object>(Object *resource) {
} }
template<> template<>
Common::Array<Object *> Object::listChildren<Object>(int subType) { Common::Array<Object *> Object::listChildren<Object>(int subType) const {
assert(subType == -1); assert(subType == -1);
Common::Array<Object *> list; Common::Array<Object *> list;

View File

@ -71,7 +71,7 @@ public:
kScroll = 33, kScroll = 33,
kFMV = 34, kFMV = 34,
kLipSync = 35, kLipSync = 35,
kAnimScriptBonesTrigger = 36, kAnimSoundTrigger = 36,
kString = 37, kString = 37,
kTextureSet = 38 kTextureSet = 38
}; };
@ -233,7 +233,7 @@ public:
/** Find a child matching the template parameter type and the specified subtype */ /** Find a child matching the template parameter type and the specified subtype */
template<class T> template<class T>
T *findChildWithSubtype(int subType, bool mustBeUnique = true); T *findChildWithSubtype(int subType, bool mustBeUnique = true) const;
/** Find a child matching the template parameter type and the specified index */ /** Find a child matching the template parameter type and the specified index */
template<class T> template<class T>
@ -245,7 +245,7 @@ public:
/** List children matching the template parameter type and the specified subtype */ /** List children matching the template parameter type and the specified subtype */
template<class T> template<class T>
Common::Array<T *> listChildren(int subType = -1); Common::Array<T *> listChildren(int subType = -1) const;
/** List children recursively matching the template parameter type and the specified subtype */ /** List children recursively matching the template parameter type and the specified subtype */
template<class T> template<class T>
@ -320,7 +320,7 @@ template<>
Object *Object::findParent(); Object *Object::findParent();
template <class T> template <class T>
Common::Array<T *> Object::listChildren(int subType) { Common::Array<T *> Object::listChildren(int subType) const {
Common::Array<T *> list; Common::Array<T *> list;
for (uint i = 0; i < _children.size(); i++) { for (uint i = 0; i < _children.size(); i++) {
@ -353,7 +353,7 @@ Common::Array<T *> Object::listChildrenRecursive(int subType) {
} }
template<> template<>
Common::Array<Object *> Object::listChildren<Object>(int subType); Common::Array<Object *> Object::listChildren<Object>(int subType) const;
template<class T> template<class T>
T *Object::findChild(bool mustBeUnique) { T *Object::findChild(bool mustBeUnique) {
@ -361,7 +361,7 @@ T *Object::findChild(bool mustBeUnique) {
} }
template <class T> template <class T>
T *Object::findChildWithSubtype(int subType, bool mustBeUnique) { T *Object::findChildWithSubtype(int subType, bool mustBeUnique) const {
Common::Array<T *> list = listChildren<T>(subType); Common::Array<T *> list = listChildren<T>(subType);
if (list.empty()) { if (list.empty()) {

View File

@ -181,5 +181,9 @@ void Sound::onGameLoop() {
} }
} }
} }
uint32 Sound::getStockSoundType() const {
return _stockSoundType;
}
} // End of namespace Resources } // End of namespace Resources
} // End of namespace Stark } // End of namespace Stark

View File

@ -48,6 +48,10 @@ class Sound : public Object {
public: public:
static const Type::ResourceType TYPE = Type::kSoundItem; static const Type::ResourceType TYPE = Type::kSoundItem;
enum SubType {
kSoundStock = 5
};
enum SoundType { enum SoundType {
kSoundTypeVoice = 0, kSoundTypeVoice = 0,
kSoundTypeEffect = 1, kSoundTypeEffect = 1,
@ -71,6 +75,9 @@ public:
/** Stop the sound */ /** Stop the sound */
void stop(); void stop();
/** Get the type for stock sounds */
uint32 getStockSoundType() const;
protected: protected:
void printData() override; void printData() override;