DarkflameServer/dScripts/NjMonastryBossInstance.cpp
EmosewaMC 968114199b The shooting Gallery now properly ends when a player leaves the instance
The shooting Gallery now properly ends when a player leaves the instance

Frakjaw player update

Update the Frakjaw battle instance script to remove players when they leave the instance

Simplify comparison

Simplify comparison for entity pointer to be implicit
2022-06-19 01:40:16 -07:00

525 lines
24 KiB
C++

#include <algorithm>
#include "RebuildComponent.h"
#include "NjMonastryBossInstance.h"
#include "DestroyableComponent.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "dZoneManager.h"
#include "GameMessages.h"
#include "BaseCombatAIComponent.h"
#include "BuffComponent.h"
#include "SkillComponent.h"
#include "TeamManager.h"
// // // // // // //
// Event handling //
// // // // // // //
void NjMonastryBossInstance::OnStartup(Entity *self) {
auto spawnerNames = std::vector<std::string> { LedgeFrakjawSpawner, LowerFrakjawSpawner, BaseEnemiesSpawner + std::to_string(1),
BaseEnemiesSpawner + std::to_string(2), BaseEnemiesSpawner + std::to_string(3),
BaseEnemiesSpawner + std::to_string(4), CounterweightSpawner };
// Add a notification request for all the spawned entities, corresponds to notifySpawnedObjectLoaded
for (const auto& spawnerName : spawnerNames) {
for (auto* spawner : dZoneManager::Instance()->GetSpawnersByName(spawnerName)) {
spawner->AddEntitySpawnedCallback([self, this](Entity* entity) {
const auto lot = entity->GetLOT();
switch (lot) {
case LedgedFrakjawLOT:
NjMonastryBossInstance::HandleLedgedFrakjawSpawned(self, entity);
return;
case CounterWeightLOT:
NjMonastryBossInstance::HandleCounterWeightSpawned(self, entity);
return;
case LowerFrakjawLOT:
NjMonastryBossInstance::HandleLowerFrakjawSpawned(self, entity);
return;
default:
NjMonastryBossInstance::HandleWaveEnemySpawned(self, entity);
return;
}
});
}
}
}
void NjMonastryBossInstance::OnPlayerLoaded(Entity *self, Entity *player) {
ActivityTimerStop(self, WaitingForPlayersTimer);
// Join the player in the activity
UpdatePlayer(self, player->GetObjectID());
// Buff the player
auto* destroyableComponent = player->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) {
destroyableComponent->SetHealth((int32_t) destroyableComponent->GetMaxHealth());
destroyableComponent->SetArmor((int32_t) destroyableComponent->GetMaxArmor());
destroyableComponent->SetImagination((int32_t) destroyableComponent->GetMaxImagination());
}
// Add player ID to instance
auto totalPlayersLoaded = self->GetVar<std::vector<LWOOBJID>>(TotalPlayersLoadedVariable);
totalPlayersLoaded.push_back(player->GetObjectID());
// Properly position the player
self->SetVar<std::vector<LWOOBJID>>(TotalPlayersLoadedVariable, totalPlayersLoaded);
// This was always spawning all players at position one before and other values cause players to be invisible.
TeleportPlayer(player, 1);
// Large teams face a tougher challenge
if (totalPlayersLoaded.size() >= 3)
self->SetVar<bool>(LargeTeamVariable, true);
// Start the game if all players in the team have loaded
auto* team = TeamManager::Instance()->GetTeam(player->GetObjectID());
if (team == nullptr || totalPlayersLoaded.size() == team->members.size()) {
StartFight(self);
return;
}
self->AddCallbackTimer(0.0f, [self, player]() {
if (player != nullptr) {
// If we don't have enough players yet, wait for the others to load and notify the client to play a cool cinematic
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayerLoaded", 0, 0,
player->GetObjectID(), "", player->GetSystemAddress());
}
});
ActivityTimerStart(self, WaitingForPlayersTimer, 45.0f, 45.0f);
}
void NjMonastryBossInstance::OnPlayerExit(Entity *self, Entity *player) {
UpdatePlayer(self, player->GetObjectID(), true);
// Fetch the total players loaded from the vars
auto totalPlayersLoaded = self->GetVar<std::vector<LWOOBJID> >(TotalPlayersLoadedVariable);
// Find the player to remove
auto playerToRemove = std::find(totalPlayersLoaded.begin(), totalPlayersLoaded.end(), player->GetObjectID());
// If we found the player remove them from out list of players
if (playerToRemove != totalPlayersLoaded.end()) {
totalPlayersLoaded.erase(playerToRemove);
} else {
Game::logger->Log("NjMonastryBossInstance", "Failed to remove player at exit.\n");
}
// Set the players loaded var back
self->SetVar<std::vector<LWOOBJID>>(TotalPlayersLoadedVariable, totalPlayersLoaded);
// Since this is an exit method, check if enough players have left. If enough have left
// resize the instance to account for such.
if (totalPlayersLoaded.size() <= 2) self->SetVar<bool>(LargeTeamVariable, false);
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayerLeft", 0, 0, player->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS);
}
void NjMonastryBossInstance::OnActivityTimerDone(Entity *self, const std::string &name) {
auto split = GeneralUtils::SplitString(name, TimerSplitChar);
auto timerName = split[0];
auto objectID = split.size() > 1 ? (LWOOBJID) std::stoull(split[1]) : LWOOBJID_EMPTY;
if (timerName == WaitingForPlayersTimer) {
StartFight(self);
} else if (timerName == SpawnNextWaveTimer) {
auto* frakjaw = EntityManager::Instance()->GetEntity(self->GetVar<LWOOBJID>(LedgeFrakjawVariable));
if (frakjaw != nullptr) {
SummonWave(self, frakjaw);
}
} else if (timerName == SpawnWaveTimer) {
auto wave = self->GetVar<uint32_t>(WaveNumberVariable);
self->SetVar<uint32_t>(WaveNumberVariable, wave + 1);
self->SetVar<uint32_t>(TotalAliveInWaveVariable, 0);
if (wave < m_Waves.size()) {
auto waves = m_Waves.at(wave);
auto counter = 0;
for (const auto& waveEnemy : waves) {
const auto numberToSpawn = self->GetVar<bool>(LargeTeamVariable)
? waveEnemy.largeNumber : waveEnemy.smallNumber;
auto spawnIndex = counter % 4 + 1;
SpawnOnNetwork(self, waveEnemy.lot, numberToSpawn, BaseEnemiesSpawner + std::to_string(spawnIndex));
counter++;
}
}
} else if (timerName + TimerSplitChar == UnstunTimer) {
auto* entity = EntityManager::Instance()->GetEntity(objectID);
if (entity != nullptr) {
auto* combatAI = entity->GetComponent<BaseCombatAIComponent>();
if (combatAI != nullptr) {
combatAI->SetDisabled(false);
}
}
} else if (timerName == SpawnCounterWeightTimer) {
auto spawners = dZoneManager::Instance()->GetSpawnersByName(CounterweightSpawner);
if (!spawners.empty()) {
// Spawn the counter weight at a specific waypoint, there's one for each round
auto* spawner = spawners.front();
spawner->Spawn({
spawner->m_Info.nodes.at((self->GetVar<uint32_t>(WaveNumberVariable) - 1) % 3)
}, true);
}
} else if (timerName == LowerFrakjawCamTimer) {
// Destroy the frakjaw on the ledge
auto* ledgeFrakjaw = EntityManager::Instance()->GetEntity(self->GetVar<LWOOBJID>(LedgeFrakjawVariable));
if (ledgeFrakjaw != nullptr) {
ledgeFrakjaw->Kill();
}
ActivityTimerStart(self, SpawnLowerFrakjawTimer, 1.0f, 1.0f);
GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0,
LWOOBJID_EMPTY, BottomFrakSpawn, UNASSIGNED_SYSTEM_ADDRESS);
} else if (timerName == SpawnLowerFrakjawTimer) {
auto spawners = dZoneManager::Instance()->GetSpawnersByName(LowerFrakjawSpawner);
if (!spawners.empty()) {
auto* spawner = spawners.front();
spawner->Activate();
}
} else if (timerName == SpawnRailTimer) {
GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0,
LWOOBJID_EMPTY, FireRailSpawn, UNASSIGNED_SYSTEM_ADDRESS);
auto spawners = dZoneManager::Instance()->GetSpawnersByName(FireRailSpawner);
if (!spawners.empty()) {
auto* spawner = spawners.front();
spawner->Activate();
}
} else if (timerName + TimerSplitChar == FrakjawSpawnInTimer) {
auto* lowerFrakjaw = EntityManager::Instance()->GetEntity(objectID);
if (lowerFrakjaw != nullptr) {
LowerFrakjawSummon(self, lowerFrakjaw);
}
} else if (timerName == WaveOverTimer) {
WaveOver(self);
} else if (timerName == FightOverTimer) {
FightOver(self);
}
}
// // // // // // // //
// Custom functions //
// // // // // // // //
void NjMonastryBossInstance::StartFight(Entity *self) {
if (self->GetVar<bool>(FightStartedVariable))
return;
self->SetVar<bool>(FightStartedVariable, true);
// Activate the frakjaw spawner
for (auto* spawner : dZoneManager::Instance()->GetSpawnersByName(LedgeFrakjawSpawner)) {
spawner->Activate();
}
}
void NjMonastryBossInstance::HandleLedgedFrakjawSpawned(Entity *self, Entity *ledgedFrakjaw) {
self->SetVar<LWOOBJID>(LedgeFrakjawVariable, ledgedFrakjaw->GetObjectID());
SummonWave(self, ledgedFrakjaw);
}
void NjMonastryBossInstance::HandleCounterWeightSpawned(Entity *self, Entity *counterWeight) {
auto* rebuildComponent = counterWeight->GetComponent<RebuildComponent>();
if (rebuildComponent != nullptr) {
rebuildComponent->AddRebuildStateCallback([this, self, counterWeight](eRebuildState state) {
switch (state) {
case REBUILD_BUILDING:
GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification,
0, 0, counterWeight->GetObjectID(),
BaseCounterweightQB + std::to_string(self->GetVar<uint32_t>(WaveNumberVariable)),
UNASSIGNED_SYSTEM_ADDRESS);
return;
case REBUILD_INCOMPLETE:
GameMessages::SendNotifyClientObject(self->GetObjectID(), EndCinematicNotification,
0, 0, LWOOBJID_EMPTY,"",
UNASSIGNED_SYSTEM_ADDRESS);
return;
case REBUILD_RESETTING:
ActivityTimerStart(self, SpawnCounterWeightTimer, 0.0f, 0.0f);
return;
case REBUILD_COMPLETED: {
// TODO: Move the platform?
// The counterweight is actually a moving platform and we should listen to the last waypoint event here
// 0.5f is a rough estimate of that path, though, and results in less needed logic
self->AddCallbackTimer(0.5f, [this, self, counterWeight]() {
if (counterWeight != nullptr) {
counterWeight->Kill();
}
auto* frakjaw = EntityManager::Instance()->GetEntity(self->GetVar<LWOOBJID>(LedgeFrakjawVariable));
if (frakjaw == nullptr) {
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"LedgeFrakjawDead", 0,
0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS);
return;
}
auto* skillComponent = frakjaw->GetComponent<SkillComponent>();
if (skillComponent != nullptr) {
skillComponent->CalculateBehavior(1635, 39097, frakjaw->GetObjectID(), true, false);
}
GameMessages::SendPlayAnimation(frakjaw, StunnedAnimation);
GameMessages::SendPlayNDAudioEmitter(frakjaw, UNASSIGNED_SYSTEM_ADDRESS, CounterSmashAudio);
// Before wave 4 we should lower frakjaw from the ledge
if (self->GetVar<uint32_t>(WaveNumberVariable) == 3) {
LowerFrakjaw(self, frakjaw);
return;
}
ActivityTimerStart(self, SpawnNextWaveTimer, 2.0f, 2.0f);
});
}
default:
return;
}
});
}
}
void NjMonastryBossInstance::HandleLowerFrakjawSpawned(Entity *self, Entity *lowerFrakjaw) {
GameMessages::SendPlayAnimation(lowerFrakjaw, TeleportInAnimation);
self->SetVar<LWOOBJID>(LowerFrakjawVariable, lowerFrakjaw->GetObjectID());
auto* combatAI = lowerFrakjaw->GetComponent<BaseCombatAIComponent>();
if (combatAI != nullptr) {
combatAI->SetDisabled(true);
}
auto* destroyableComponent = lowerFrakjaw->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) {
destroyableComponent->AddOnHitCallback([this, self, lowerFrakjaw](Entity* attacker) {
NjMonastryBossInstance::HandleLowerFrakjawHit(self, lowerFrakjaw, attacker);
});
}
lowerFrakjaw->AddDieCallback([this, self, lowerFrakjaw]() {
NjMonastryBossInstance::HandleLowerFrakjawDied(self, lowerFrakjaw);
});
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"LedgeFrakjawDead", 0, 0,
LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS);
if (self->GetVar<bool>(LargeTeamVariable)) {
// Double frakjaws health for large teams
if (destroyableComponent != nullptr) {
const auto doubleHealth = destroyableComponent->GetHealth() * 2;
destroyableComponent->SetHealth(doubleHealth);
destroyableComponent->SetMaxHealth((float_t) doubleHealth);
}
ActivityTimerStart(self, FrakjawSpawnInTimer + std::to_string(lowerFrakjaw->GetObjectID()),
2.0f, 2.0f);
ActivityTimerStart(self, UnstunTimer + std::to_string(lowerFrakjaw->GetObjectID()),
7.0f, 7.0f);
} else {
ActivityTimerStart(self, UnstunTimer + std::to_string(lowerFrakjaw->GetObjectID()),
5.0f, 5.0f);
}
}
void NjMonastryBossInstance::HandleLowerFrakjawHit(Entity *self, Entity *lowerFrakjaw, Entity *attacker) {
auto* destroyableComponent = lowerFrakjaw->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr)
return;
// Progress the fight to the last wave if frakjaw has less than 50% of his health left
if (destroyableComponent->GetHealth() <= (uint32_t) destroyableComponent->GetMaxHealth() / 2 && !self->GetVar<bool>(OnLastWaveVarbiale)) {
self->SetVar<bool>(OnLastWaveVarbiale, true);
// Stun frakjaw during the cinematic
auto* combatAI = lowerFrakjaw->GetComponent<BaseCombatAIComponent>();
if (combatAI != nullptr) {
combatAI->SetDisabled(true);
}
ActivityTimerStart(self, UnstunTimer + std::to_string(lowerFrakjaw->GetObjectID()), 5.0f, 5.0f);
const auto trashMobsAlive = self->GetVar<std::vector<LWOOBJID>>(TrashMobsAliveVariable);
std::vector<LWOOBJID> newTrashMobs = {};
for (const auto& trashMobID : trashMobsAlive) {
auto* trashMob = EntityManager::Instance()->GetEntity(trashMobID);
if (trashMob != nullptr) {
newTrashMobs.push_back(trashMobID);
// Stun all the enemies until the cinematic is over
auto* trashMobCombatAI = trashMob->GetComponent<BaseCombatAIComponent>();
if (trashMobCombatAI != nullptr) {
trashMobCombatAI->SetDisabled(true);
}
ActivityTimerStart(self, UnstunTimer + std::to_string(trashMobID), 5.0f, 5.0f);
}
}
self->SetVar<std::vector<LWOOBJID>>(TrashMobsAliveVariable, newTrashMobs);
LowerFrakjawSummon(self, lowerFrakjaw);
RemovePoison(self);
}
}
void NjMonastryBossInstance::HandleLowerFrakjawDied(Entity *self, Entity *lowerFrakjaw) {
ActivityTimerStart(self, FightOverTimer, 2.0f, 2.0f);
}
void NjMonastryBossInstance::HandleWaveEnemySpawned(Entity *self, Entity *waveEnemy) {
waveEnemy->AddDieCallback([this, self, waveEnemy]() {
NjMonastryBossInstance::HandleWaveEnemyDied(self, waveEnemy);
});
auto waveEnemies = self->GetVar<std::vector<LWOOBJID>>(TrashMobsAliveVariable);
waveEnemies.push_back(waveEnemy->GetObjectID());
self->SetVar<std::vector<LWOOBJID>>(TrashMobsAliveVariable, waveEnemies);
auto* combatAI = waveEnemy->GetComponent<BaseCombatAIComponent>();
if (combatAI != nullptr) {
combatAI->SetDisabled(true);
ActivityTimerStart(self, UnstunTimer + std::to_string(waveEnemy->GetObjectID()), 3.0f, 3.0f);
}
}
void NjMonastryBossInstance::HandleWaveEnemyDied(Entity *self, Entity* waveEnemy) {
auto waveEnemies = self->GetVar<std::vector<LWOOBJID>>(TrashMobsAliveVariable);
waveEnemies.erase(std::remove(waveEnemies.begin(), waveEnemies.end(), waveEnemy->GetObjectID()), waveEnemies.end());
self->SetVar<std::vector<LWOOBJID>>(TrashMobsAliveVariable, waveEnemies);
if (waveEnemies.empty()) {
ActivityTimerStart(self, WaveOverTimer, 2.0f, 2.0f);
}
}
void NjMonastryBossInstance::TeleportPlayer(Entity *player, uint32_t position) {
for (const auto* spawnPoint : EntityManager::Instance()->GetEntitiesInGroup("SpawnPoint" + std::to_string(position))) {
GameMessages::SendTeleport(player->GetObjectID(), spawnPoint->GetPosition(), spawnPoint->GetRotation(),
player->GetSystemAddress(), true);
}
}
void NjMonastryBossInstance::SummonWave(Entity* self, Entity* frakjaw) {
GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY,
LedgeFrakSummon, UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendPlayAnimation(frakjaw, SummonAnimation);
// Stop the music for the first, fourth and fifth wave
const auto wave = self->GetVar<uint32_t>(WaveNumberVariable);
if (wave >= 1 || wave < (m_Waves.size() - 1)) {
GameMessages::SendNotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0,
LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave - 1),
UNASSIGNED_SYSTEM_ADDRESS);
}
// After frakjaw moves down the music stays the same
if (wave < (m_Waves.size() - 1)) {
GameMessages::SendNotifyClientObject(self->GetObjectID(), StartMusicNotification, 0, 0,
LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave),
UNASSIGNED_SYSTEM_ADDRESS);
}
ActivityTimerStart(self, SpawnWaveTimer, 4.0f, 4.0f);
}
void NjMonastryBossInstance::LowerFrakjawSummon(Entity *self, Entity *frakjaw) {
GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0,
LWOOBJID_EMPTY, BottomFrakSummon, UNASSIGNED_SYSTEM_ADDRESS);
ActivityTimerStart(self, SpawnWaveTimer, 2.0f, 2.0f);
GameMessages::SendPlayAnimation(frakjaw, SummonAnimation);
}
void NjMonastryBossInstance::RemovePoison(Entity *self) {
const auto& totalPlayer = self->GetVar<std::vector<LWOOBJID>>(TotalPlayersLoadedVariable);
for (const auto& playerID : totalPlayer) {
auto* player = EntityManager::Instance()->GetEntity(playerID);
if (player != nullptr) {
auto* buffComponent = player->GetComponent<BuffComponent>();
if (buffComponent != nullptr) {
buffComponent->RemoveBuff(PoisonBuff);
}
}
}
}
void NjMonastryBossInstance::LowerFrakjaw(Entity *self, Entity* frakjaw) {
GameMessages::SendPlayAnimation(frakjaw, TeleportOutAnimation);
ActivityTimerStart(self, LowerFrakjawCamTimer, 2.0f, 2.0f);
GameMessages::SendNotifyClientObject(frakjaw->GetObjectID(), StopMusicNotification, 0, 0,
LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 3), UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendNotifyClientObject(frakjaw->GetObjectID(), StartMusicNotification, 0, 0,
LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 2), UNASSIGNED_SYSTEM_ADDRESS);
}
void NjMonastryBossInstance::SpawnOnNetwork(Entity* self, const LOT& toSpawn, const uint32_t& numberToSpawn, const std::string& spawnerName) {
auto spawners = dZoneManager::Instance()->GetSpawnersByName(spawnerName);
if (spawners.empty() || numberToSpawn <= 0)
return;
auto* spawner = spawners.front();
// Spawn the lot N times
spawner->SetSpawnLot(toSpawn);
for (auto i = 0; i < numberToSpawn; i++)
spawner->Spawn({ spawner->m_Info.nodes.at(i % spawner->m_Info.nodes.size()) }, true);
}
void NjMonastryBossInstance::WaveOver(Entity *self) {
auto wave = self->GetVar<uint32_t>(WaveNumberVariable);
if (wave >= m_Waves.size() - 1)
return;
GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0,
LWOOBJID_EMPTY, BaseCounterweightSpawn + std::to_string(wave),
UNASSIGNED_SYSTEM_ADDRESS);
ActivityTimerStart(self, SpawnCounterWeightTimer, 1.5f, 1.5f);
RemovePoison(self);
}
void NjMonastryBossInstance::FightOver(Entity *self) {
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"GroundFrakjawDead", 0, 0,
LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS);
// Remove all the enemies from the battlefield
for (auto i = 1; i < 5; i++) {
auto spawners = dZoneManager::Instance()->GetSpawnersByName(BaseEnemiesSpawner + std::to_string(i));
if (!spawners.empty()) {
auto* spawner = spawners.front();
spawner->Deactivate();
spawner->Reset();
}
}
RemovePoison(self);
ActivityTimerStart(self, SpawnRailTimer, 1.5f, 1.5f);
// Set the music to play the victory music
GameMessages::SendNotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0,
LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 2),
UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendNotifyClientObject(self->GetObjectID(), FlashMusicNotification, 0, 0,
LWOOBJID_EMPTY, "Monastery_Frakjaw_Battle_Win", UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0,
LWOOBJID_EMPTY, TreasureChestSpawning, UNASSIGNED_SYSTEM_ADDRESS);
auto treasureChests = EntityManager::Instance()->GetEntitiesInGroup(ChestSpawnpointGroup);
for (auto* treasureChest : treasureChests) {
auto info = EntityInfo {};
info.lot = ChestLOT;
info.pos = treasureChest->GetPosition();
info.rot = treasureChest->GetRotation();
info.spawnerID = self->GetObjectID();
info.settings = {
new LDFData<LWOOBJID>(u"parent_tag", self->GetObjectID())
};
// Finally spawn a treasure chest at the correct spawn point
auto* chestObject = EntityManager::Instance()->CreateEntity(info);
EntityManager::Instance()->ConstructEntity(chestObject);
}
}