#include "AnimatedEffectObject.h"
#include "Utils/Attachments.h"
#include "Utils/ParticleUtils.h"
#include "Utils/ObjectUtils.h"
#include "Utils/RangeFor.h"
#include "EventSystem/EventService.h"
#include "EventSystem/SystemEventTypes.h"
#include <Game.h>
#include <Events/RKEvent.h>
#include <Graphics/Model.h>
using namespace bne;
using namespace CasualCore;
AnimatedEffectObject::CreateCallback *AnimatedEffectObject::m_pCreateCallback = nullptr;
namespace
{
static const uint32_t c_millisecondsInSecond = 1000;
}
namespace details
{
struct YMatrix
{
YMatrix() { matrix.MakeRotationYMatrix(-RKPI/2); }
operator RKMatrix() const { return matrix; }
RKMatrix matrix;
};
static YMatrix s_pfxMatrix;
} // namespace details
SERIALIZE_BEGIN(RKAnimEvent)
SERIALIZERKLIST(m_desc.m_animationPFXs, AnimationPfxDesc, "Animation event PFX", "The PFX to spawn for each animation event", "Event", RKSERIAL_FLAG_RKLIST);
SERIALIZERKLIST(m_desc.m_animationObjects, AnimationObjectDesc, "Animation event objects", "The objects to spawn for each animation event", "Event", RKSERIAL_FLAG_RKLIST);
SERIALIZERKLIST(m_desc.m_animationSFXs, AnimationSfxDesc, "Animation event SFX", "The SFX to play for each animation event", "Event", RKSERIAL_FLAG_RKLIST);
SERIALIZE_END()
AnimatedEffectObject::AnimatedEffectObject()
{
EventService::GetInstance()->AddReceiver(this, EventType_ObjectSelfDestroy);
}
AnimatedEffectObject::~AnimatedEffectObject()
{
EventService::GetInstance()->RemoveReceiver(this);
#if defined(BNE_OBJECT_CROSSOVER)
ClearData(false);
#else // (!)defined(BNE_OBJECT_CROSSOVER)
Clear();
#endif // defined(BNE_OBJECT_CROSSOVER)
}
void AnimatedEffectObject::ReceiveEvent(int a_eventID, void* userData)
{
void *pObject = userData;
if(a_eventID == EventType_ObjectSelfDestroy)
{
// Check for object in child list
for(size_t c = 0; c < m_animationEventObjects.Size(); c++)
{
const ChildObject<> &childObject = m_animationEventObjects.GetAt(c);
if(childObject.Get() == pObject)
{
// If we found it in child list then remove child object from list (without destroying actual CasualCore::Object contained inside, ie. Don't call Clear(), as it is about to be destroyed by the sender of this event)
m_animationEventObjects.EraseFastAt(c);
break;
}
}
}
}
#if defined(BNE_OBJECT_CROSSOVER)
void AnimatedEffectObject::ClearData(bool bDestroyChildObject) {
// Destory any animation objects
if(bDestroyChildObject)
{
for (ChildObject<>& cObject : m_animationEventObjects) {
cObject.Clear();
}
}
m_animationEventObjects.Clear();
#else // (!)defined(BNE_OBJECT_CROSSOVER)
void AnimatedEffectObject::Clear() {
// Destory any animation objects
for (ChildObject<>& cObject : m_animationEventObjects) {
cObject.Clear();
}
m_animationEventObjects.Clear();
#endif // defined(BNE_OBJECT_CROSSOVER)
// Stop any SFX
for (SoundInfo& soundInfo : m_sfxInfo)
{
SOUND->Stop(soundInfo.m_handle);
}
m_sfxInfo.Clear();
}
void AnimatedEffectObject::SetParent(Object* a_pParent, PFXCallback *pCallback) {
m_pParent = a_pParent;
m_pParticleEffectCallback = pCallback;
RKASSERT(pCallback, "NULL PFX Callback specified for AnimatedEffectObject");
}
void AnimatedEffectObject::SetEffects(const AnimationEventsDesc* a_pDesc) {
#if defined(BNE_OBJECT_CROSSOVER)
ClearData(true);
#else // (!)defined(BNE_OBJECT_CROSSOVER)
Clear();
#endif // defined(BNE_OBJECT_CROSSOVER)
m_pDesc = a_pDesc;
}
void AnimatedEffectObject::Update(float a_elapsedTime) {
if (m_pDesc == nullptr || m_pParent == nullptr) return;
for (ChildObject<>& cObject : m_animationEventObjects) {
Object* pObject = cObject.Get();
if (pObject != nullptr && pObject->IsRenderable()) {
for (const AnimationObjectDesc& objectEvent : m_pDesc->m_animationObjects) {
if (pObject->GetName().StartsWith(objectEvent.m_objectTemplateName)) {
RKMatrix m = GetAttachmentPointLocalTransform(m_pParent, objectEvent.AttachmentBoneName().GetString());
pObject->SetTransform(m);
}
}
}
}
// update SFX 3D location.
const RKVector parentWorldPosition = m_pParent->GetWorldPosition();
for (SoundInfo& soundInfo : m_sfxInfo) {
SOUND->SetEmitterPosition(soundInfo.m_handle, parentWorldPosition.x, parentWorldPosition.y, parentWorldPosition.z);
}
}
void AnimatedEffectObject::OnAnimEvent(const RKString* a_pHandle) {
if (m_pDesc == nullptr) return;
// loop over the SFX events
for (const AnimationSfxDesc& sfxEvent : m_pDesc->m_animationSFXs)
{
RKEventObject* pEventObject = static_cast<RKEventObject*>(sfxEvent.GetEventObject());
if (pEventObject != nullptr && pEventObject->m_EventString.ICompare(*a_pHandle) == 0)
{
// Retrieve sound handle for event
int32_t infoIndex = -1;
for (uint32_t i = 0; i < m_sfxInfo.Size(); ++i)
{
if (m_sfxInfo[i].m_sfxName.ICompare(sfxEvent.m_sfxName) == 0)
{
infoIndex = i;
break;
}
}
float fFadeTime = sfxEvent.m_fadeTime / static_cast<float>(c_millisecondsInSecond);
if (sfxEvent.m_bPlay)
{
const bool bHasSoundHandle = (infoIndex != -1) && !SOUND->IsPlaying(m_sfxInfo[infoIndex].m_handle);
// if applicable, set initial SFX 3D location.
if (m_pParent)
{
const RKVector parentWorldPosition = m_pParent->GetWorldPosition();
if (bHasSoundHandle)
{
SOUND->Play(m_sfxInfo[infoIndex].m_handle, parentWorldPosition.x, parentWorldPosition.y, parentWorldPosition.z, fFadeTime);
}
else
{
SoundHandle sfxHandle = SOUND->Play(sfxEvent.m_sfxName.GetString(), parentWorldPosition.x, parentWorldPosition.y, parentWorldPosition.z, fFadeTime);
m_sfxInfo.Append(SoundInfo{ sfxEvent.m_sfxName, sfxHandle });
}
}
else
{
if (bHasSoundHandle)
{
SOUND->Play(m_sfxInfo[infoIndex].m_handle, fFadeTime);
}
else
{
SoundHandle sfxHandle = SOUND->Play(sfxEvent.m_sfxName.GetString(), fFadeTime);
m_sfxInfo.Append(SoundInfo{ sfxEvent.m_sfxName, sfxHandle });
}
}
}
else
{
if (infoIndex != -1)
{
SOUND->Stop(m_sfxInfo[infoIndex].m_handle, fFadeTime);
}
}
}
}
// loop over the PFX events
for (const AnimationPfxDesc& pfxEvent : m_pDesc->m_animationPFXs) {
RKEventObject* pEventObject = static_cast<RKEventObject*>(pfxEvent.GetEventObject());
if (pEventObject != nullptr && pEventObject->m_EventString.ICompare(*a_pHandle) == 0) {
CreateEffect(pfxEvent.GetPFXName().GetString(), pfxEvent.AttachmentBoneName().GetString());
}
}
// loop over the mesh events
for (const AnimationObjectDesc& objectEvent : m_pDesc->m_animationObjects) {
RKEventObject* pEventObject = static_cast<RKEventObject*>(objectEvent.GetEventObject());
if (pEventObject != nullptr && pEventObject->m_EventString.ICompare(*a_pHandle) == 0) {
if (objectEvent.IsShown()) {
// show (mesh) object
Object* pAffectedObject = nullptr;
for (ChildObject<>& cObject : m_animationEventObjects) {
Object* pObject = cObject.Get();
if (pObject != nullptr && pObject->GetName().StartsWith(objectEvent.m_objectTemplateName)) {
pAffectedObject = pObject;
break;
}
}
// if object is not found - recreate
if (pAffectedObject == nullptr) {
ChildObject<> cObject = CreateObject(objectEvent.ObjectTemplateName().GetString());
pAffectedObject = cObject.Get();
if (pAffectedObject != nullptr) {
m_animationEventObjects.Append(cObject);
}
}
if (pAffectedObject != nullptr) {
pAffectedObject->SetRenderable(true, false);
if (!objectEvent.AttachmentBoneName().IsEmpty())
{
RKMatrix m = GetAttachmentPointLocalTransform(m_pParent, objectEvent.AttachmentBoneName().GetString());
pAffectedObject->SetTransform(m);
}
if (!objectEvent.m_objectAnimationName.IsEmpty() && pAffectedObject->GetRenderable() != nullptr) {
Model* model = pAffectedObject->GetRenderable()->GetModel();
if (model != nullptr) {
model->PlayAnimationEx(objectEvent.m_objectAnimationName.GetString(), 1.0f);
}
}
}
} else {
// hide (mesh) object
for (ChildObject<>& cObject : m_animationEventObjects) {
Object* pObject = cObject.Get();
if (pObject != nullptr && pObject->GetName().StartsWith(objectEvent.m_objectTemplateName)) {
pObject->SetRenderable(false, false);
break;
}
}
}
}
}
}
void AnimatedEffectObject::HideAnimationObjects() {
for (ChildObject<>& cObject : m_animationEventObjects) {
Object* pObject = cObject.Get();
if (pObject != nullptr) {
pObject->SetRenderable(false, false);
}
}
}
void AnimatedEffectObject::CreateEffect(const char* a_templateName, const char* a_attachmentBoneName) {
if (m_pParent == nullptr) return;
Object* effect = nullptr;
if(m_pParticleEffectCallback)
{
effect = m_pParticleEffectCallback(a_templateName);
}
if (effect != nullptr) {
effect->SetRenderable(true, false);
#if defined(BNE_OBJECT_CROSSOVER)
bne::object::AttachToParent(effect, m_pParent, GetAttachmentPointIndex(m_pParent, a_attachmentBoneName), true);
#else // (!)defined(BNE_OBJECT_CROSSOVER)
effect->SetParent(m_pParent, GetAttachmentPointIndex(m_pParent, a_attachmentBoneName), true);
#endif // defined(BNE_OBJECT_CROSSOVER)
effect->SetTransform(details::s_pfxMatrix);
}
else {
RKLOGt_ERROR("animEvent", "Failed to create PFX: '%s'", a_templateName);
}
// Run user callback on newly created object
if(m_pCreateCallback && effect)
{
m_pCreateCallback(effect);
}
}
ChildObject<> AnimatedEffectObject::CreateObject(const char* a_templateName) {
ChildObject<> childObject{};
if (m_pParent != nullptr) {
if (!childObject.Create(m_pParent, a_templateName)) {
RKLOGt_ERROR("animEvent", "Unable to create animation event object '%s'.", a_templateName);
}
}
// Run user callback on newly created object
if(m_pCreateCallback)
{
CasualCore::Object *pObject = childObject.Get();
if(pObject)
{
m_pCreateCallback(pObject);
}
}
return childObject;
}