#include "BaseSurvivalServer.h" #include "GameMessages.h" #include "DestroyableComponent.h" #include "EntityManager.h" #include "dZoneManager.h" #include "Player.h" #include "eMissionTaskType.h" #include "eMissionState.h" #include "MissionComponent.h" #include "Character.h" void BaseSurvivalServer::SetGameVariables(Entity* self) { this->constants = std::move(GetConstants()); this->mobSets = std::move(GetMobSets()); this->spawnerNetworks = std::move(GetSpawnerNetworks()); this->missionsToUpdate = std::move(GetMissionsToUpdate()); } void BaseSurvivalServer::BasePlayerLoaded(Entity* self, Entity* player) { const auto& waitingIter = std::find(state.waitingPlayers.begin(), state.waitingPlayers.end(), player->GetObjectID()); const auto& playersIter = std::find(state.players.begin(), state.players.end(), player->GetObjectID()); if (waitingIter != state.waitingPlayers.end() || playersIter != state.players.end()) { static_cast(player)->SendToZone(player->GetCharacter()->GetLastNonInstanceZoneID()); return; } state.waitingPlayers.push_back(player->GetObjectID()); state.players.push_back(player->GetObjectID()); self->SetNetworkVar(NumberOfPlayersVariable, self->GetNetworkVar(NumberOfPlayersVariable) + 1); self->SetNetworkVar(DefinePlayerToUIVariable, std::to_string(player->GetObjectID()), player->GetSystemAddress()); // Notify the players of all other players if (!self->GetNetworkVar(WavesStartedVariable)) { auto counter = 1; for (const auto& playerID : state.players) { self->SetNetworkVar(UpdateScoreboardPlayersVariable + GeneralUtils::to_u16string(counter), std::to_string(playerID)); counter++; } self->SetNetworkVar(ShowScoreboardVariable, true); } SetPlayerSpawnPoints(); if (!self->GetNetworkVar(WavesStartedVariable)) { PlayerConfirmed(self); } else { UpdatePlayer(self, player->GetObjectID()); GetLeaderboardData(self, player->GetObjectID(), GetActivityID(self), 50); ResetStats(player->GetObjectID()); } player->AddCallbackTimer(5.0f, [this, self, player]() { self->SetNetworkVar(NumberOfPlayersVariable, self->GetNetworkVar(NumberOfPlayersVariable)); self->SetNetworkVar(DefinePlayerToUIVariable, std::to_string(player->GetObjectID()), player->GetSystemAddress()); if (!self->GetNetworkVar(WavesStartedVariable)) { auto counter = 1; for (const auto& playerID : state.players) { self->SetNetworkVar(UpdateScoreboardPlayersVariable + GeneralUtils::to_u16string(counter), std::to_string(playerID)); counter++; } self->SetNetworkVar(ShowScoreboardVariable, true); } }); } void BaseSurvivalServer::BaseStartup(Entity* self) { self->SetVar(PlayersAcceptedVariable, 0); self->SetVar(PlayersReadyVariable, false); } void BaseSurvivalServer::BasePlayerExit(Entity* self, Entity* player) { const auto& waitingIter = std::find(state.waitingPlayers.begin(), state.waitingPlayers.end(), player->GetObjectID()); const auto& playersIter = std::find(state.players.begin(), state.players.end(), player->GetObjectID()); if (waitingIter != state.waitingPlayers.end()) { state.waitingPlayers.erase(waitingIter); } if (playersIter != state.players.end()) { state.players.erase(playersIter); } if (waitingIter == state.waitingPlayers.end() && playersIter == state.players.end()) { return; } if (!self->GetNetworkVar(WavesStartedVariable)) { PlayerConfirmed(self); if (state.players.empty()) return; if (state.waitingPlayers.empty()) { ActivityTimerStopAllTimers(self); ActivityTimerStart(self, AllAcceptedDelayTimer, 1.0f, constants.startDelay); } else if (state.players.size() > state.waitingPlayers.size()) { if (!self->GetVar(AcceptedDelayStartedVariable)) { self->SetVar(AcceptedDelayStartedVariable, true); ActivityTimerStart(self, AcceptedDelayTimer, 1.0f, constants.acceptedDelay); } } } else { UpdatePlayer(self, player->GetObjectID(), true); if (CheckAllPlayersDead()) { GameOver(self); } } SetActivityValue(self, player->GetObjectID(), 1, 0); self->SetNetworkVar(NumberOfPlayersVariable, std::min((uint32_t)0, self->GetNetworkVar(NumberOfPlayersVariable) - 1)); } void BaseSurvivalServer::BaseFireEvent(Entity* self, Entity* sender, const std::string& args, int32_t param1, int32_t param2, int32_t param3) { if (args == "start") { StartWaves(self); } else if (args == "DeactivateRewards") { SpawnerReset(spawnerNetworks.rewardNetworks); } else if (sender != nullptr && IsPlayerInActivity(self, sender->GetObjectID())) { auto currentScore = GetActivityValue(self, sender->GetObjectID(), 0); SetActivityValue(self, sender->GetObjectID(), 0, currentScore + param1); } } void BaseSurvivalServer::BasePlayerDied(Entity* self, Entity* player) { if (self->GetNetworkVar(WavesStartedVariable)) { const auto finalTime = ActivityTimerGetCurrentTime(self, ClockTickTimer); SetActivityValue(self, player->GetObjectID(), 1, finalTime); auto paramString = CheckAllPlayersDead() ? "true" : "false"; GameMessages::SendNotifyClientZoneObject(self->GetObjectID(), u"Player_Died", finalTime, 0, player->GetObjectID(), paramString, player->GetSystemAddress()); GameOver(self); } else { player->Resurrect(); SetPlayerSpawnPoints(); } } void BaseSurvivalServer::BasePlayerResurrected(Entity* self, Entity* player) { self->SetNetworkVar(ShowScoreboardVariable, true); } void BaseSurvivalServer::BaseMessageBoxResponse(Entity* self, Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) { if (identifier == u"RePlay") { PlayerAccepted(self, sender->GetObjectID()); PlayerConfirmed(self); } else if (identifier == u"Exit_Question" && button == 1) { ResetStats(sender->GetObjectID()); self->SetNetworkVar(ExitWavesVariable, std::to_string(sender->GetObjectID())); if (sender->IsPlayer()) { auto* character = sender->GetCharacter(); if (character != nullptr) { auto* player = dynamic_cast(sender); player->SendToZone(character->GetLastNonInstanceZoneID()); } } } } void BaseSurvivalServer::OnActivityTimerUpdate(Entity* self, const std::string& name, float_t remainingTime, float_t elapsedTime) { if (name == AcceptedDelayTimer) { self->SetNetworkVar(UpdateDefaultStartTimerVariable, remainingTime); } else if (name == ClockTickTimer) { self->SetNetworkVar(UpdateTimerVariable, elapsedTime); } else if (name == SpawnTickTimer && !self->GetVar(IsCooldownVariable)) { SpawnMobs(self); } } void BaseSurvivalServer::OnActivityTimerDone(Entity* self, const std::string& name) { auto cooldownTime = constants.rewardInterval * constants.waveTime; if (name == AcceptedDelayTimer) { self->SetNetworkVar(UpdateDefaultStartTimerVariable, 0); ActivityTimerStart(self, AllAcceptedDelayTimer, 1, 1); } else if (name == AllAcceptedDelayTimer) { self->SetNetworkVar(ClearScoreboardVariable, true); ActivityTimerStart(self, StartDelayTimer, 3, 3); StartWaves(self); } else if (name == StartDelayTimer) { ActivityTimerStart(self, ClockTickTimer, 1); ActivityTimerStart(self, SpawnTickTimer, constants.waveTime); SpawnMobs(self); ActivityTimerStart(self, PlaySpawnSoundTimer, 3, 3); ActivityTimerStart(self, CoolDownStartTimer, cooldownTime, cooldownTime); } else if (name == CoolDownStartTimer) { self->SetVar(IsCooldownVariable, true); ActivityTimerStop(self, SpawnTickTimer); ActivityTimerStart(self, CoolDownStopTimer, 1, constants.coolDownTime); ActivateSpawnerNetwork(spawnerNetworks.rewardNetworks); SpawnerReset(spawnerNetworks.baseNetworks, false); SpawnerReset(spawnerNetworks.randomNetworks, false); } else if (name == CoolDownStopTimer) { self->SetVar(IsCooldownVariable, false); ActivityTimerStart(self, SpawnTickTimer, constants.waveTime); ActivityTimerStart(self, CoolDownStartTimer, cooldownTime, cooldownTime); SpawnMobs(self); ActivityTimerStart(self, PlaySpawnSoundTimer, 3, 3); } else if (name == PlaySpawnSoundTimer) { for (const auto& playerID : state.players) { auto* player = EntityManager::Instance()->GetEntity(playerID); if (player != nullptr) { GameMessages::SendPlayNDAudioEmitter(player, player->GetSystemAddress(), spawnSoundGUID); } } } } void BaseSurvivalServer::ResetStats(LWOOBJID playerID) { auto* player = EntityManager::Instance()->GetEntity(playerID); if (player != nullptr) { // Boost all the player stats when loading in auto* destroyableComponent = player->GetComponent(); if (destroyableComponent != nullptr) { destroyableComponent->SetHealth(destroyableComponent->GetMaxHealth()); destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor()); destroyableComponent->SetImagination(destroyableComponent->GetMaxImagination()); } } } void BaseSurvivalServer::PlayerConfirmed(Entity* self) { std::vector confirmedPlayers{}; for (const auto& playerID : state.players) { auto pass = false; for (const auto& waitingPlayerID : state.waitingPlayers) { if (waitingPlayerID == playerID) pass = true; } if (!pass) confirmedPlayers.push_back(playerID); } auto playerIndex = 1; for (const auto& playerID : confirmedPlayers) { self->SetNetworkVar(PlayerConfirmVariable + GeneralUtils::to_u16string(playerIndex), std::to_string(playerID)); playerIndex++; } } void BaseSurvivalServer::PlayerAccepted(Entity* self, LWOOBJID playerID) { const auto& iter = std::find(state.waitingPlayers.begin(), state.waitingPlayers.end(), playerID); if (iter == state.waitingPlayers.end()) { return; } state.waitingPlayers.erase(iter); if (state.waitingPlayers.empty() && state.players.size() >= self->GetNetworkVar(NumberOfPlayersVariable)) { ActivityTimerStopAllTimers(self); ActivityTimerStart(self, AllAcceptedDelayTimer, 1, constants.startDelay); } else if (!self->GetVar(AcceptedDelayStartedVariable)) { self->SetVar(AcceptedDelayStartedVariable, true); ActivityTimerStart(self, AcceptedDelayTimer, 1, constants.acceptedDelay); } } void BaseSurvivalServer::StartWaves(Entity* self) { GameMessages::SendActivityStart(self->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); self->SetVar(PlayersReadyVariable, true); self->SetVar(BaseMobSetIndexVariable, 0); self->SetVar(RandMobSetIndexVariable, 0); self->SetVar(AcceptedDelayStartedVariable, false); state.waitingPlayers.clear(); for (const auto& playerID : state.players) { const auto player = EntityManager::Instance()->GetEntity(playerID); if (player != nullptr) { state.waitingPlayers.push_back(playerID); UpdatePlayer(self, playerID); GetLeaderboardData(self, playerID, GetActivityID(self), 50); ResetStats(playerID); if (!self->GetVar(FirstTimeDoneVariable)) { TakeActivityCost(self, playerID); } GameMessages::SendPlayerSetCameraCyclingMode(playerID, player->GetSystemAddress()); } } self->SetVar(FirstTimeDoneVariable, true); self->SetVar(MissionTypeVariable, state.players.size() == 1 ? "survival_time_solo" : "survival_time_team"); ActivateSpawnerNetwork(spawnerNetworks.smashNetworks); self->SetNetworkVar(WavesStartedVariable, true); self->SetNetworkVar(StartWaveMessageVariable, "Start!"); } bool BaseSurvivalServer::CheckAllPlayersDead() { auto deadPlayers = 0; for (const auto& playerID : state.players) { auto* player = EntityManager::Instance()->GetEntity(playerID); if (player == nullptr || player->IsDead()) { deadPlayers++; } } return deadPlayers >= state.players.size(); } void BaseSurvivalServer::SetPlayerSpawnPoints() { auto spawnerIndex = 1; for (const auto& playerID : state.players) { auto* player = EntityManager::Instance()->GetEntity(playerID); if (player != nullptr) { auto possibleSpawners = EntityManager::Instance()->GetEntitiesInGroup("P" + std::to_string(spawnerIndex) + "_Spawn"); if (!possibleSpawners.empty()) { auto* spawner = possibleSpawners.at(0); GameMessages::SendTeleport(playerID, spawner->GetPosition(), spawner->GetRotation(), player->GetSystemAddress(), true); } } spawnerIndex++; } } void BaseSurvivalServer::GameOver(Entity* self) { if (!CheckAllPlayersDead()) return; ActivityTimerStopAllTimers(self); // Reset all the spawners SpawnerReset(spawnerNetworks.baseNetworks); SpawnerReset(spawnerNetworks.randomNetworks); SpawnerReset(spawnerNetworks.rewardNetworks); for (const auto& playerID : state.players) { auto* player = EntityManager::Instance()->GetEntity(playerID); if (player == nullptr) continue; const auto score = GetActivityValue(self, playerID, 0); const auto time = GetActivityValue(self, playerID, 1); GameMessages::SendNotifyClientZoneObject(self->GetObjectID(), u"Update_ScoreBoard", time, 0, playerID, std::to_string(score), UNASSIGNED_SYSTEM_ADDRESS); player->Resurrect(); // Update all mission progression auto* missionComponent = player->GetComponent(); if (missionComponent != nullptr) { missionComponent->Progress(eMissionTaskType::PERFORM_ACTIVITY, time, self->GetObjectID(), self->GetVar(MissionTypeVariable)); for (const auto& survivalMission : missionsToUpdate) { auto* mission = missionComponent->GetMission(survivalMission.first); if (mission != nullptr && (uint32_t)time >= survivalMission.second && (mission->GetMissionState() == eMissionState::ACTIVE || mission->GetMissionState() == eMissionState::COMPLETE_ACTIVE)) { mission->Progress(eMissionTaskType::SCRIPT, self->GetLOT()); } } } StopActivity(self, playerID, score, time); } state.waveNumber = 1; state.rewardTick = 1; state.totalSpawned = 0; self->SetNetworkVar(WavesStartedVariable, false); if (constants.useMobLots) { constants.lotPhase = 0; UpdateMobLots(spawnerNetworks.baseNetworks); UpdateMobLots(spawnerNetworks.randomNetworks); } SetPlayerSpawnPoints(); } void BaseSurvivalServer::SpawnerReset(SpawnerNetworkCollection& spawnerNetworkCollection, bool hardReset) { auto totalSpawned = 0; for (auto& spawner : spawnerNetworkCollection.networks) { for (auto& spawnerName : spawner.names) { auto spawners = dZoneManager::Instance()->GetSpawnersByName(spawnerName + spawner.number); if (!spawners.empty()) { auto* spawnerObject = spawners.at(0); auto amountSpawned = spawnerObject->GetAmountSpawned(); totalSpawned += amountSpawned; spawner.isActive = false; if (hardReset) { spawnerObject->Reset(); } else { spawnerObject->SoftReset(); } spawnerObject->Deactivate(); } } } state.totalSpawned = std::max((uint32_t)totalSpawned, state.totalSpawned); } void BaseSurvivalServer::SpawnerUpdate(Entity* self, SpawnerNetworkCollection& spawnerNetworkCollection, uint32_t amount) { if (spawnerNetworkCollection.networks.empty()) return; // If we want to spawn something specific now if (amount != 0) { auto spawnerNetwork = spawnerNetworkCollection.networks.at(0); auto possibleSpawners = dZoneManager::Instance()->GetSpawnersByName(spawnerNetwork.names.at(0) + spawnerNetwork.number); if (!possibleSpawners.empty()) { SpawnNow(possibleSpawners.at(0), amount); return; } } auto setNumber = self->GetVar(GeneralUtils::ASCIIToUTF16(spawnerNetworkCollection.mobSetName + "Num")); auto newSet = GetRandomMobSet(spawnerNetworkCollection, setNumber); if (!newSet.empty()) { auto spawnerNetwork = GetRandomSpawner(spawnerNetworkCollection); for (auto i = 0; i < spawnerNetwork.names.size(); i++) { const auto& name = spawnerNetwork.names.at(i); const auto& toSpawn = newSet.at(i); auto possibleSpawners = dZoneManager::Instance()->GetSpawnersByName(name + spawnerNetwork.number); if (!possibleSpawners.empty()) { SpawnNow(possibleSpawners.front(), toSpawn); } } } } void BaseSurvivalServer::SpawnNow(Spawner* spawner, uint32_t amount) { if (spawner != nullptr) { if (!spawner->m_Active) { spawner->m_Info.amountMaintained = amount; spawner->Activate(); } else { spawner->m_Info.amountMaintained = amount; } } } std::vector BaseSurvivalServer::GetRandomMobSet(SpawnerNetworkCollection& spawnerNetworkCollection, uint32_t setNumber) { if (mobSets.sets.find(spawnerNetworkCollection.mobSetName) != mobSets.sets.end()) { auto mobSet = mobSets.sets.at(spawnerNetworkCollection.mobSetName); if (setNumber < mobSet.size()) { return mobSet.at(setNumber).at(rand() % mobSet.at(setNumber).size()); } } return {}; } SpawnerNetwork BaseSurvivalServer::GetRandomSpawner(SpawnerNetworkCollection& spawnerNetworkCollection) { std::vector validSpawners{}; for (const auto& spawner : spawnerNetworkCollection.networks) { if (!spawner.isLocked) validSpawners.push_back(spawner); } if (!validSpawners.empty()) { auto spawner = validSpawners.at(rand() % validSpawners.size()); spawner.isActive = true; return spawner; } return {}; } void BaseSurvivalServer::ActivateSpawnerNetwork(SpawnerNetworkCollection& spawnerNetworkCollection) { for (auto& spawner : spawnerNetworkCollection.networks) { for (const auto& spawnerName : spawner.names) { auto possibleSpawners = dZoneManager::Instance()->GetSpawnersByName(spawnerName + spawner.number); if (!possibleSpawners.empty()) { auto* spawnerObject = possibleSpawners.at(0); spawnerObject->Activate(); spawnerObject->Reset(); } } } } void BaseSurvivalServer::UpdateMobLots(SpawnerNetworkCollection& spawnerNetworkCollection) { for (auto& spawner : spawnerNetworkCollection.networks) { for (auto& spawnerName : spawner.names) { if (!spawnerName.empty()) { auto spawnerObjects = dZoneManager::Instance()->GetSpawnersByName(spawnerName + spawner.number); if (!spawnerObjects.empty()) { auto splitName = GeneralUtils::SplitString(spawnerName, '_'); auto cleanName = splitName.size() > 1 ? splitName.at(1) : splitName.at(0); if (!cleanName.empty()) { auto spawnerObject = spawnerObjects.at(0); spawnerObject->SetSpawnLot(mobSets.mobLots.at(cleanName).at(constants.lotPhase)); } } } } } } void BaseSurvivalServer::SpawnMobs(Entity* self) { if (!self->GetNetworkVar(WavesStartedVariable)) return; state.waveNumber++; auto spawnNumber = state.waveNumber > constants.rewardInterval ? state.waveNumber - state.rewardTick - 1 : state.waveNumber; for (const auto& tier : constants.baseMobsStartTier) { if (tier == spawnNumber) self->SetVar(BaseMobSetIndexVariable, (self->GetVar(BaseMobSetIndexVariable) + 1) % (constants.baseMobsStartTier.size() - 1)); } for (const auto& tier : constants.randMobsStartTier) { if (tier == spawnNumber) self->SetVar(RandMobSetIndexVariable, (self->GetVar(RandMobSetIndexVariable) + 1) % (constants.randMobsStartTier.size() - 1)); } if (state.waveNumber == constants.unlockNetwork3) spawnerNetworks.randomNetworks.networks.at(2).isLocked = false; SpawnerReset(spawnerNetworks.baseNetworks, false); SpawnerReset(spawnerNetworks.randomNetworks, false); SpawnerUpdate(self, spawnerNetworks.baseNetworks); if (spawnNumber >= constants.mobSet2Wave) { if (spawnNumber == constants.mobSet2Wave) self->SetNetworkVar(SpawnMobVariable, "2"); SpawnerUpdate(self, spawnerNetworks.randomNetworks); } if (spawnNumber >= constants.mobSet3Wave) { if (spawnNumber == constants.mobSet3Wave) self->SetNetworkVar(SpawnMobVariable, "3"); SpawnerUpdate(self, spawnerNetworks.randomNetworks); } // If we reached the end of the spawn phase we increase the lost to make it more difficult if (constants.useMobLots && constants.lotPhase < (mobSets.mobLots.begin()->second.size() - 1) && spawnNumber >= constants.baseMobsStartTier.back()) { state.waveNumber = 1; constants.lotPhase++; UpdateMobLots(spawnerNetworks.baseNetworks); UpdateMobLots(spawnerNetworks.randomNetworks); } }