From 0b261e934f840c933851cbc49d92b3c194f9629b Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 29 Dec 2024 16:21:22 -0800 Subject: [PATCH] fix shooting gallery bugs (#1702) --- dCommon/dEnums/MessageType/Game.h | 1 + dDatabase/GameDatabase/ITables/ILeaderboard.h | 2 +- dGame/Entity.cpp | 8 + dGame/Entity.h | 7 + dGame/LeaderboardManager.cpp | 6 +- dGame/dComponents/InventoryComponent.cpp | 7 +- dGame/dComponents/MovementAIComponent.cpp | 1 + dGame/dComponents/PetComponent.cpp | 2 +- dGame/dComponents/SkillComponent.cpp | 6 + dGame/dComponents/SkillComponent.h | 2 + dGame/dGameMessages/GameMessageHandler.cpp | 6 + dGame/dGameMessages/GameMessages.cpp | 31 ++ dGame/dGameMessages/GameMessages.h | 31 ++ .../02_server/Map/VE/VeBricksampleServer.cpp | 2 +- .../02_server/Map/VE/VeMissionConsole.cpp | 2 +- dScripts/ActivityManager.cpp | 2 +- dScripts/CppScripts.h | 16 + .../ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp | 390 +++++++++++------- dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.h | 9 +- 19 files changed, 368 insertions(+), 163 deletions(-) diff --git a/dCommon/dEnums/MessageType/Game.h b/dCommon/dEnums/MessageType/Game.h index ee8b8515..b4c44915 100644 --- a/dCommon/dEnums/MessageType/Game.h +++ b/dCommon/dEnums/MessageType/Game.h @@ -957,6 +957,7 @@ namespace MessageType { MODIFY_PLAYER_ZONE_STATISTIC = 1046, APPLY_EXTERNAL_FORCE = 1049, GET_APPLIED_EXTERNAL_FORCE = 1050, + ACTIVITY_NOTIFY = 1051, ITEM_EQUIPPED = 1052, ACTIVITY_STATE_CHANGE_REQUEST = 1053, OVERRIDE_FRICTION = 1054, diff --git a/dDatabase/GameDatabase/ITables/ILeaderboard.h b/dDatabase/GameDatabase/ITables/ILeaderboard.h index f88497b0..0a7922e4 100644 --- a/dDatabase/GameDatabase/ITables/ILeaderboard.h +++ b/dDatabase/GameDatabase/ITables/ILeaderboard.h @@ -14,7 +14,7 @@ public: uint32_t lastPlayedTimestamp{}; float primaryScore{}; float secondaryScore{}; - uint32_t tertiaryScore{}; + float tertiaryScore{}; uint32_t numWins{}; uint32_t numTimesPlayed{}; uint32_t ranking{}; diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index f5887996..bd1a2793 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -1493,6 +1493,14 @@ void Entity::OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16s GetScript()->OnChoiceBoxResponse(this, sender, button, buttonIdentifier, identifier); } +void Entity::OnActivityNotify(GameMessages::ActivityNotify& notify) { + GetScript()->OnActivityNotify(this, notify); +} + +void Entity::OnShootingGalleryFire(GameMessages::ShootingGalleryFire& fire) { + GetScript()->OnShootingGalleryFire(*this, fire); +} + void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) { GetScript()->OnRequestActivityExit(sender, player, canceled); } diff --git a/dGame/Entity.h b/dGame/Entity.h index 2ed7aa53..1e07aff0 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -13,6 +13,11 @@ #include "eKillType.h" #include "Observable.h" +namespace GameMessages { + struct ActivityNotify; + struct ShootingGalleryFire; +}; + namespace Loot { class Info; }; @@ -210,6 +215,8 @@ public: void OnZonePropertyModelRemoved(Entity* player); void OnZonePropertyModelRemovedWhileEquipped(Entity* player); void OnZonePropertyModelRotated(Entity* player); + void OnActivityNotify(GameMessages::ActivityNotify& notify); + void OnShootingGalleryFire(GameMessages::ShootingGalleryFire& notify); void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData); void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier); diff --git a/dGame/LeaderboardManager.cpp b/dGame/LeaderboardManager.cpp index da27e88b..5109e663 100644 --- a/dGame/LeaderboardManager.cpp +++ b/dGame/LeaderboardManager.cpp @@ -95,7 +95,7 @@ void QueryToLdf(Leaderboard& leaderboard, const std::vector // Score:1 entry.push_back(new LDFData(u"Streak", leaderboardEntry.secondaryScore)); // Streak:1 - entry.push_back(new LDFData(u"HitPercentage", (leaderboardEntry.tertiaryScore / 100.0f))); + entry.push_back(new LDFData(u"HitPercentage", leaderboardEntry.tertiaryScore)); // HitPercentage:3 between 0 and 1 break; case Racing: @@ -199,9 +199,9 @@ std::vector FilterFriends(const std::vector friendsLeaderboard; for (const auto& entry : leaderboard) { const auto res = std::ranges::find_if(friendOfPlayer, [&entry, relatedPlayer](const FriendData& data) { - return entry.charId == data.friendID || entry.charId == relatedPlayer; + return entry.charId == data.friendID; }); - if (res != friendOfPlayer.cend()) { + if (res != friendOfPlayer.cend() || entry.charId == relatedPlayer) { friendsLeaderboard.push_back(entry); } } diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 667956f6..aa7614aa 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -69,9 +69,10 @@ InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) { auto slot = 0u; for (const auto& item : items) { - if (!item.equip || !Inventory::IsValidItem(item.itemid)) { - continue; - } + if (!Inventory::IsValidItem(item.itemid)) continue; + AddItem(item.itemid, item.count); + + if (!item.equip) continue; const LWOOBJID id = ObjectIDManager::GenerateObjectID(); diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index b6a16803..efe711b3 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -265,6 +265,7 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) { void MovementAIComponent::SetPath(std::vector path) { if (path.empty()) return; + while (!m_CurrentPath.empty()) m_CurrentPath.pop(); std::for_each(path.rbegin(), path.rend() - 1, [this](const PathWaypoint& point) { this->m_CurrentPath.push(point); }); diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index c2783a15..7121dd44 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -524,7 +524,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress()); - inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); + inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::INVENTORY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); if (item == nullptr) { diff --git a/dGame/dComponents/SkillComponent.cpp b/dGame/dComponents/SkillComponent.cpp index 999bf9db..e16e3a2e 100644 --- a/dGame/dComponents/SkillComponent.cpp +++ b/dGame/dComponents/SkillComponent.cpp @@ -123,6 +123,11 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B behavior->Handle(sync_entry.context, bitStream, branch); this->m_managedProjectiles.erase(this->m_managedProjectiles.begin() + index); + + GameMessages::ActivityNotify notify; + notify.notification.push_back( std::make_unique>(u"shot_done", sync_entry.skillId)); + + m_Parent->OnActivityNotify(notify); } void SkillComponent::RegisterPlayerProjectile(const LWOOBJID projectileId, BehaviorContext* context, const BehaviorBranchContext& branch, const LOT lot) { @@ -132,6 +137,7 @@ void SkillComponent::RegisterPlayerProjectile(const LWOOBJID projectileId, Behav entry.branchContext = branch; entry.lot = lot; entry.id = projectileId; + entry.skillId = context->skillID; this->m_managedProjectiles.push_back(entry); } diff --git a/dGame/dComponents/SkillComponent.h b/dGame/dComponents/SkillComponent.h index f74b4411..b7cd7786 100644 --- a/dGame/dComponents/SkillComponent.h +++ b/dGame/dComponents/SkillComponent.h @@ -40,6 +40,8 @@ struct ProjectileSyncEntry { BehaviorBranchContext branchContext{ 0, 0 }; + int32_t skillId{ 0 }; + explicit ProjectileSyncEntry(); }; diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index baa3a84e..5ecc5e68 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -703,6 +703,12 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System case MessageType::Game::UPDATE_INVENTORY_GROUP_CONTENTS: GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr); break; + case MessageType::Game::SHOOTING_GALLERY_FIRE: { + GameMessages::ShootingGalleryFire fire{}; + fire.Deserialize(inStream); + fire.Handle(*entity, sysAddr); + break; + } default: LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data()); diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index cc74436b..10259890 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -6395,4 +6395,35 @@ namespace GameMessages { bitStream.Write(targetPosition.y); bitStream.Write(targetPosition.z); } + + void SetModelToBuild::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(modelLot != -1); + if (modelLot != -1) bitStream.Write(modelLot); + } + + void SpawnModelBricks::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(amount != 0.0f); + if (amount != 0.0f) bitStream.Write(amount); + bitStream.Write(position != NiPoint3Constant::ZERO); + if (position != NiPoint3Constant::ZERO) { + bitStream.Write(position.x); + bitStream.Write(position.y); + bitStream.Write(position.z); + } + } + + bool ShootingGalleryFire::Deserialize(RakNet::BitStream& bitStream) { + if (!bitStream.Read(target.x)) return false; + if (!bitStream.Read(target.y)) return false; + if (!bitStream.Read(target.z)) return false; + if (!bitStream.Read(rotation.w)) return false; + if (!bitStream.Read(rotation.x)) return false; + if (!bitStream.Read(rotation.y)) return false; + if (!bitStream.Read(rotation.z)) return false; + return true; + } + + void ShootingGalleryFire::Handle(Entity& entity, const SystemAddress& sysAddr) { + entity.OnShootingGalleryFire(*this); + } } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index a77e3c12..0b1bcf52 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -54,6 +54,8 @@ namespace GameMessages { virtual ~GameMsg() = default; void Send(const SystemAddress& sysAddr) const; virtual void Serialize(RakNet::BitStream& bitStream) const {} + virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; } + virtual void Handle(Entity& entity, const SystemAddress& sysAddr) {}; MessageType::Game msgId; LWOOBJID target{ LWOOBJID_EMPTY }; }; @@ -727,6 +729,35 @@ namespace GameMessages { ConfigureRacingControl() : GameMsg(MessageType::Game::CONFIGURE_RACING_CONTROL) {} std::vector> racingSettings{}; }; + + struct SetModelToBuild : public GameMsg { + SetModelToBuild() : GameMsg(MessageType::Game::SET_MODEL_TO_BUILD) {} + void Serialize(RakNet::BitStream& bitStream) const override; + LOT modelLot{ -1 }; + }; + + struct SpawnModelBricks : public GameMsg { + SpawnModelBricks() : GameMsg(MessageType::Game::SPAWN_MODEL_BRICKS) {} + void Serialize(RakNet::BitStream& bitStream) const override; + + float amount{ 0.0f }; + NiPoint3 position{ NiPoint3Constant::ZERO }; + }; + + struct ActivityNotify : public GameMsg { + ActivityNotify() : GameMsg(MessageType::Game::ACTIVITY_NOTIFY) {} + + std::vector> notification{}; + }; + + struct ShootingGalleryFire : public GameMsg { + ShootingGalleryFire() : GameMsg(MessageType::Game::SHOOTING_GALLERY_FIRE) {} + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + NiPoint3 target{}; + NiQuaternion rotation{}; + }; }; #endif // GAMEMESSAGES_H diff --git a/dScripts/02_server/Map/VE/VeBricksampleServer.cpp b/dScripts/02_server/Map/VE/VeBricksampleServer.cpp index 7535d261..382ce751 100644 --- a/dScripts/02_server/Map/VE/VeBricksampleServer.cpp +++ b/dScripts/02_server/Map/VE/VeBricksampleServer.cpp @@ -12,7 +12,7 @@ void VeBricksampleServer::OnUse(Entity* self, Entity* user) { auto* inventoryComponent = user->GetComponent(); if (loot && inventoryComponent != nullptr && inventoryComponent->GetLotCount(loot) == 0) { - inventoryComponent->AddItem(loot, 1, eLootSourceType::ACTIVITY); + inventoryComponent->AddItem(loot, 1, eLootSourceType::NONE); for (auto* brickEntity : Game::entityManager->GetEntitiesInGroup("Bricks")) { GameMessages::SendNotifyClientObject(brickEntity->GetObjectID(), u"Pickedup"); diff --git a/dScripts/02_server/Map/VE/VeMissionConsole.cpp b/dScripts/02_server/Map/VE/VeMissionConsole.cpp index 7a8a1439..aa9abb93 100644 --- a/dScripts/02_server/Map/VE/VeMissionConsole.cpp +++ b/dScripts/02_server/Map/VE/VeMissionConsole.cpp @@ -10,7 +10,7 @@ void VeMissionConsole::OnUse(Entity* self, Entity* user) { auto* inventoryComponent = user->GetComponent(); if (inventoryComponent != nullptr) { - inventoryComponent->AddItem(12547, 1, eLootSourceType::ACTIVITY); // Add the panel required for pickup + inventoryComponent->AddItem(12547, 1, eLootSourceType::NONE); // Add the panel required for pickup } // The flag to set is 101 diff --git a/dScripts/ActivityManager.cpp b/dScripts/ActivityManager.cpp index 8ba4834e..14cb9ed0 100644 --- a/dScripts/ActivityManager.cpp +++ b/dScripts/ActivityManager.cpp @@ -163,7 +163,7 @@ int32_t ActivityManager::GetGameID(Entity* self) const { float_t ActivityManager::ActivityTimerGetRemainingTime(Entity* self, const std::string& timerName) const { auto* timer = GetTimer(timerName); - return timer != nullptr ? std::min(timer->stopTime - timer->runTime, 0.0f) : 0.0f; + return timer != nullptr ? std::max(timer->stopTime - timer->runTime, 0.0f) : 0.0f; } void ActivityManager::ActivityTimerReset(Entity* self, const std::string& timerName) { diff --git a/dScripts/CppScripts.h b/dScripts/CppScripts.h index b7e5ad8b..573dab89 100644 --- a/dScripts/CppScripts.h +++ b/dScripts/CppScripts.h @@ -357,6 +357,22 @@ namespace CppScripts { virtual void OnRequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) {}; virtual void OnZoneLoadedInfo(Entity* self, const GameMessages::ZoneLoadedInfo& info) {}; + + /** + * @brief Handles notifying when activity data is done + * + * @param self + * @param notify The parameters of the notification + */ + virtual void OnActivityNotify(Entity* self, GameMessages::ActivityNotify& notify) {}; + + /** + * @brief handles shooting gallery fire + * + * @param self + * @param fire The firing data + */ + virtual void OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) {}; }; Script* const GetScript(Entity* parent, const std::string& scriptName); diff --git a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp index 5b178e06..abe9c350 100644 --- a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp +++ b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp @@ -16,6 +16,8 @@ #include "eReplicaComponentType.h" #include "RenderComponent.h" #include "eGameActivity.h" +#include "Item.h" +#include void SGCannon::OnStartup(Entity* self) { LOG("OnStartup"); @@ -81,10 +83,6 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int auto* player = Game::entityManager->GetEntity(self->GetVar(PlayerIDVariable)); if (player != nullptr) { LOG("Player is ready"); - /*GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true);*/ - - LOG("Sending ActivityEnter"); GameMessages::SendActivityEnter(self->GetObjectID(), player->GetSystemAddress()); @@ -103,7 +101,6 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int auto* characterComponent = player->GetComponent(); if (characterComponent != nullptr) { - characterComponent->SetIsRacing(true); characterComponent->SetCurrentActivity(eGameActivity::SHOOTING_GALLERY); auto possessor = player->GetComponent(); if (possessor) { @@ -114,20 +111,12 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int Game::entityManager->SerializeEntity(player); } - self->SetNetworkVar(HideScoreBoardVariable, true); - self->SetNetworkVar(ReSetSuperChargeVariable, true); - self->SetNetworkVar(ShowLoadingUI, true); + self->AddCallbackTimer(1.0f, [self, this]() { + self->SetNetworkVar(HideScoreBoardVariable, true); + self->SetNetworkVar(ReSetSuperChargeVariable, true); + self->SetNetworkVar(ShowLoadingUI, true); + }); - /* - GameMessages::SendTeleport( - player->GetObjectID(), - {-292.6415710449219, 230.20237731933594, -3.9090466499328613}, - {0.7067984342575073, -6.527870573336259e-05, 0.707414984703064, 0.00021762956748716533}, - player->GetSystemAddress(), true - ); - */ - - //GameMessages::SendRequestActivityEnter(self->GetObjectID(), player->GetSystemAddress(), false, player->GetObjectID()); } else { LOG("Player not found"); } @@ -245,14 +234,6 @@ void SGCannon::GameOverTimerFunc(Entity* self) { GameMessages::SendActivityPause(self->GetObjectID(), true, player->GetSystemAddress()); - /*const auto leftoverCannonballs = Game::entityManager->GetEntitiesInGroup("cannonball"); - if (leftoverCannonballs.empty()) { - RecordPlayerScore(self); - - } else { - ActivityTimerStart(self, EndGameBufferTimer, 1, leftoverCannonballs.size()); - }*/ - ActivityTimerStart(self, EndGameBufferTimer, 1, 1); TimerToggle(self); @@ -261,60 +242,51 @@ void SGCannon::GameOverTimerFunc(Entity* self) { void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) { if (self->GetVar(GameStartedVariable)) { - LOG_DEBUG("time name %s %s", name.c_str(), name.substr(7).c_str()); const auto spawnNumber = static_cast(std::stoi(name.substr(7))); const auto& activeSpawns = self->GetVar>(ActiveSpawnsVariable); - LOG_DEBUG("size %i, %i", activeSpawns.size(), spawnNumber); if (activeSpawns.size() <= spawnNumber) { LOG_DEBUG("Trying to spawn %i when spawns size is only %i", spawnNumber, activeSpawns.size()); return; } const auto& toSpawn = activeSpawns.at(spawnNumber); - LOG_DEBUG("toSpawn %i", toSpawn.spawnPaths.size()); - const auto pathIndex = GeneralUtils::GenerateRandomNumber(0, toSpawn.spawnPaths.size() - 1); - LOG_DEBUG("index %f", pathIndex); - LOG_DEBUG("%s", toSpawn.spawnPaths.at(pathIndex).c_str()); + const auto pathIndex = GeneralUtils::GenerateRandomNumber(0, toSpawn.spawnPaths.size() - 1); const auto* path = Game::zoneManager->GetZone()->GetPath(toSpawn.spawnPaths.at(pathIndex)); - if (!path) { - LOG_DEBUG("Path %s at index %i is null", toSpawn.spawnPaths.at(pathIndex).c_str(), pathIndex); + if (!path || path->pathWaypoints.empty()) { + LOG_DEBUG("Path %s at index %i or has 0 waypoints", toSpawn.spawnPaths.at(pathIndex).c_str(), pathIndex); return; } - LOG_DEBUG("%s", path->pathName.c_str()); - auto info = EntityInfo{}; info.lot = toSpawn.lot; info.spawnerID = self->GetObjectID(); - info.pos = path->pathWaypoints.at(0).position; + info.pos = path->pathWaypoints[0].position; info.settings = { new LDFData(u"SpawnData", toSpawn), - new LDFData(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"), + new LDFData(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"), // this script is never loaded new LDFData(u"custom_script_client", "scripts/client/ai/SG_TARGET_CLIENT.lua"), new LDFData(u"attached_path", path->pathName), new LDFData(u"attached_path_start", 0), - new LDFData(u"groupID", u"SGEnemy") + new LDFData(u"groupID", u"SGEnemy"), + new LDFData(u"wave", self->GetVar(ThisWaveVariable)), }; - LOG_DEBUG("Spawning enemy %i on path %s", toSpawn.lot, path->pathName.c_str()); - auto* enemy = Game::entityManager->CreateEntity(info, nullptr, self); - Game::entityManager->ConstructEntity(enemy); auto* movementAI = enemy->AddComponent(MovementAIInfo{}); + auto* simplePhysicsComponent = enemy->GetComponent(); + if (simplePhysicsComponent) { + simplePhysicsComponent->SetPhysicsMotionState(4); + } + + Game::entityManager->ConstructEntity(enemy); movementAI->SetMaxSpeed(toSpawn.initialSpeed); movementAI->SetCurrentSpeed(toSpawn.initialSpeed); movementAI->SetHaltDistance(0.0f); - std::vector pathWaypoints = path->pathWaypoints; - - if (GeneralUtils::GenerateRandomNumber(0, 1) < 0.5f) { - std::reverse(pathWaypoints.begin(), pathWaypoints.end()); - } - - movementAI->SetPath(pathWaypoints); + movementAI->SetPath(path->pathWaypoints); enemy->AddDieCallback([this, self, enemy, name]() { RegisterHit(self, enemy, name); @@ -362,7 +334,10 @@ void SGCannon::StartGame(Entity* self) { auto rewardObjects = Game::entityManager->GetEntitiesInGroup(constants.rewardModelGroup); for (auto* reward : rewardObjects) { - reward->OnFireEventServerSide(self, ModelToBuildEvent); + GameMessages::SetModelToBuild modelToBuild{}; + modelToBuild.modelLot = LOT_NULL; + modelToBuild.target = reward->GetObjectID(); + modelToBuild.Send(UNASSIGNED_SYSTEM_ADDRESS); } auto* player = Game::entityManager->GetEntity(self->GetVar(PlayerIDVariable)); @@ -400,6 +375,10 @@ void SGCannon::DoGameStartup(Entity* self) { constants.firstWaveStartTime); } +void SGCannon::OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) { + self.SetVar(ShotsFiredVariable, self.GetVar(ShotsFiredVariable) + 1); +} + void SGCannon::SpawnNewModel(Entity* self) { // Add a new reward to the existing rewards @@ -407,6 +386,7 @@ void SGCannon::SpawnNewModel(Entity* self) { if (currentReward != -1) { auto rewards = self->GetVar>(RewardsVariable); rewards.push_back(currentReward); + self->SetVar>(RewardsVariable, rewards); self->SetNetworkVar(RewardAddedVariable, currentReward); } @@ -438,9 +418,13 @@ void SGCannon::SpawnNewModel(Entity* self) { std::unordered_map toDrop = {}; toDrop = Loot::RollLootMatrix(player, lootMatrix); - for (auto drop : toDrop) { - rewardModel->OnFireEventServerSide(self, ModelToBuildEvent, drop.first); - self->SetVar(CurrentRewardVariable, drop.first); + for (const auto [lot, count] : toDrop) { + GameMessages::SetModelToBuild modelToBuild{}; + modelToBuild.modelLot = lot; + modelToBuild.target = rewardModel->GetObjectID(); + modelToBuild.Send(player->GetSystemAddress()); + + self->SetVar(CurrentRewardVariable, lot); } } } @@ -514,11 +498,11 @@ void SGCannon::RecordPlayerScore(Entity* self) { const auto currentWave = self->GetVar(ThisWaveVariable); if (currentWave > 0) { - auto totalWaveScore = 0; + auto totalWaveScore = totalScore; auto playerScores = self->GetVar>(PlayerScoresVariable); for (const auto& waveScore : playerScores) { - totalWaveScore += waveScore; + totalWaveScore -= waveScore; } if (currentWave >= playerScores.size()) { @@ -526,6 +510,7 @@ void SGCannon::RecordPlayerScore(Entity* self) { } else { playerScores[currentWave] = totalWaveScore; } + self->SetVar>(PlayerScoresVariable, playerScores); } } @@ -547,12 +532,11 @@ void SGCannon::PlaySceneAnimation(Entity* self, const std::u16string& animationN } void SGCannon::PauseChargeCannon(Entity* self) { - const auto time = std::max(static_cast(std::ceil(ActivityTimerGetCurrentTime(self, SuperChargeTimer))), static_cast(1)); + const auto time = std::max(static_cast(std::ceil(ActivityTimerGetRemainingTime(self, SuperChargeTimer))), static_cast(1)); self->SetVar(SuperChargePausedVariable, true); self->SetVar(CurrentSuperChargedTimeVariable, time); self->SetNetworkVar(ChargeCountingVariable, time); - ActivityTimerStop(self, SuperChargeTimer); } @@ -568,14 +552,17 @@ void SGCannon::StopGame(Entity* self, bool cancel) { // The player won, store all the score and send rewards if (!cancel) { - int32_t percentage = 0.0f; - auto misses = self->GetVar(MissesVariable); - auto fired = self->GetVar(ShotsFiredVariable); + float percentage = 0.0f; + float misses = self->GetVar(MissesVariable); + float fired = self->GetVar(ShotsFiredVariable); - if (fired > 0) { + if (fired > 0.0f) { percentage = misses / fired; } + percentage = 1.0f - percentage; + percentage = std::max(percentage, 0.0f); + auto* missionComponent = player->GetComponent(); if (missionComponent != nullptr) { @@ -596,13 +583,27 @@ void SGCannon::StopGame(Entity* self, bool cancel) { auto* inventory = player->GetComponent(); if (inventory != nullptr) { for (const auto rewardLot : self->GetVar>(RewardsVariable)) { - inventory->AddItem(rewardLot, 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS); + inventory->AddItem(rewardLot, 1, eLootSourceType::NONE, eInventoryType::MODELS); } } - self->SetNetworkVar(u"UI_Rewards", - GeneralUtils::to_u16string(self->GetVar(TotalScoreVariable)) + u"_0_0_0_0_0_0" - ); + const auto& waveScores = self->GetVar>(PlayerScoresVariable); + std::stringstream stream; + + stream << self->GetVar(TotalScoreVariable) << "_"; + + // technically unused in shooting gallery but serialize it regardless. + for (const auto& score : waveScores) { + stream << score << "_"; + } + auto totalmissed = fired - misses; + if (totalmissed < 0) { + totalmissed = 0; + } + + stream << fired << "_" << totalmissed << "_" << self->GetVar(MaxStreakVariable); + + self->SetNetworkVar(u"UI_Rewards", GeneralUtils::ASCIIToUTF16(stream.str())); } GameMessages::SendActivityStop(self->GetObjectID(), false, cancel, player->GetSystemAddress()); @@ -617,10 +618,42 @@ void SGCannon::StopGame(Entity* self, bool cancel) { ResetVars(self); } + +void SGCannon::OnActivityNotify(Entity* self, GameMessages::ActivityNotify& notify) { + if (!self->GetVar(GameStartedVariable)) return; + + const auto& params = notify.notification; + if (params.empty()) return; + + const auto& param = params[0]; + if (param->GetValueType() != LDF_TYPE_S32 || param->GetKey() != u"shot_done") return; + + const auto superChargeShotDone = static_cast*>(param.get())->GetValue() == GetConstants().cannonSuperChargeSkill; + + const auto& hitTargets = self->GetVar>(u"CannonBallKills"); + + if (hitTargets.empty() && !superChargeShotDone) { + self->SetVar(u"m_curStreak", 0); + self->SetVar(MissesVariable, self->GetVar(MissesVariable) + 1); + self->SetNetworkVar(u"HideStreak", true); + self->SetNetworkVar(u"UnMarkAll", true); + UpdateStreak(self); + } else if (hitTargets.size() > 1) { + self->SetNetworkVar(u"mHit", true); + } + + self->SetVar>(u"CannonBallKills", {}); +} + void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& timerName) { + if (!self->GetVar(GameStartedVariable)) return; + + auto cannonBallKills = self->GetVar>(u"CannonBallKills"); + cannonBallKills.push_back(target->GetObjectID()); + self->SetVar>(u"CannonBallKills", cannonBallKills); const auto& spawnInfo = target->GetVar(u"SpawnData"); - if (spawnInfo.respawns) { + if (spawnInfo.respawns && target->GetVar(u"wave") == self->GetVar(ThisWaveVariable)) { const auto respawnTime = GeneralUtils::GenerateRandomNumber(spawnInfo.minRespawnTime, spawnInfo.maxRespawnTime); ActivityTimerStart(self, timerName, respawnTime, respawnTime); @@ -637,6 +670,7 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time } else { if (!self->GetVar(SuperChargeActiveVariable)) { self->SetVar(u"m_curStreak", 0); + self->SetVar(MissesVariable, self->GetVar(MissesVariable) + 1); } self->SetNetworkVar(u"hitFriend", true); @@ -646,10 +680,6 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time auto scScore = self->GetVar(TotalScoreVariable) - lastSuperTotal; - LOG("LastSuperTotal: %i, scScore: %i, constants.chargedPoints: %i", - lastSuperTotal, scScore, constants.chargedPoints - ); - if (!self->GetVar(SuperChargeActiveVariable) && scScore >= constants.chargedPoints && score >= 0) { StartChargedCannon(self); self->SetNetworkVar(u"SuperChargeBar", 100.0f); @@ -684,6 +714,40 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time if (missionComponent == nullptr) return; missionComponent->Progress(eMissionTaskType::SMASH, spawnInfo.lot, self->GetObjectID()); + + auto matrix = self->GetVar(MatrixVariable); + + float rewardS = 0.0f; + float rewardF = 0.0f; + if (matrix <= 5) { + const auto scoreRewardNum = "Score_Reward_" + std::to_string(matrix); + const auto rewardAmountItr = constants.scoreRewards.find(scoreRewardNum); + if (rewardAmountItr != constants.scoreRewards.end()) { + const float rewardAmount = rewardAmountItr->second / 100 * 3; + rewardS = newScore / rewardAmount; + rewardF = std::round(rewardS * 3); + + if (rewardF > 100.0f) rewardF = 100.0f; + + self->SetNetworkVar(ModelPercentVariable, rewardF); + } + } + + if (rewardF > 0.0f && rewardF < 200.0f && matrix <= 5) { + const auto rewardModelGroup = Game::entityManager->GetEntitiesInGroup(constants.rewardModelGroup); + if (!rewardModelGroup.empty()) { + auto* rewardModel = rewardModelGroup[0]; + GameMessages::SpawnModelBricks spawnBricks{}; + spawnBricks.target = rewardModel->GetObjectID(); + spawnBricks.amount = rewardF / 100.0f; + spawnBricks.position = target->GetPosition(); + spawnBricks.Send(player->GetSystemAddress()); + if (rewardF >= 100.0f) { + SpawnNewModel(self); + self->SetVar(MatrixVariable, matrix + 1); + } + } + } } void SGCannon::UpdateStreak(Entity* self) { @@ -745,41 +809,34 @@ void SGCannon::ToggleSuperCharge(Entity* self, bool enable) { auto* selfInventoryComponent = self->GetComponent(); - if (inventoryComponent == nullptr) { - LOG("Inventory component not found"); + // This is a gm in the original script + Item* meItem1{}; + Item* meItem2{}; + for (const auto item : selfInventoryComponent->GetInventory(eInventoryType::ITEMS)->GetItems() | std::views::values) { + if (item->GetSlot() == 0) meItem1 = item; + else if (item->GetSlot() == 1) meItem2 = item; + } + + if (!meItem1 || !meItem2) { + LOG("Cannon does not have the required items equipped"); return; } if (enable) { LOG("Player is activating super charge"); - selfInventoryComponent->UpdateSlot("greeble_r", { ObjectIDManager::GenerateRandomObjectID(), 6505, 1, 0 }); - selfInventoryComponent->UpdateSlot("greeble_l", { ObjectIDManager::GenerateRandomObjectID(), 6506, 1, 0 }); + selfInventoryComponent->EquipItem(meItem1); + selfInventoryComponent->EquipItem(meItem2); - // TODO: Equip items skillID = constants.cannonSuperChargeSkill; cooldown = 400; } else { - selfInventoryComponent->UpdateSlot("greeble_r", { ObjectIDManager::GenerateRandomObjectID(), 0, 0, 0 }); - selfInventoryComponent->UpdateSlot("greeble_l", { ObjectIDManager::GenerateRandomObjectID(), 0, 0, 0 }); + selfInventoryComponent->UnEquipItem(meItem1); + selfInventoryComponent->UnEquipItem(meItem2); self->SetNetworkVar(u"SuperChargeBar", 0); LOG("Player disables super charge"); - // TODO: Unequip items - for (const auto& equipped : equippedItems) { - if (equipped.first == "special_r" || equipped.first == "special_l") { - LOG("Trying to unequip a weapon, %i", equipped.second.lot); - - auto* item = inventoryComponent->FindItemById(equipped.second.id); - - if (item != nullptr) { - inventoryComponent->UnEquipItem(item); - } else { - LOG("Item not found, %i", equipped.second.lot); - } - } - } cooldown = 800; self->SetVar(NumberOfChargesVariable, 0); } @@ -794,10 +851,10 @@ void SGCannon::ToggleSuperCharge(Entity* self, bool enable) { DynamicShootingGalleryParams properties = shootingGalleryComponent->GetDynamicParams(); - properties.cannonFOV = 58.6f; - properties.cannonVelocity = 129.0; + properties.cannonFOV = constants.cannonFOV; + properties.cannonVelocity = constants.cannonVelocity; properties.cannonRefireRate = cooldown; - properties.cannonMinDistance = 30; + properties.cannonMinDistance = constants.cannonMinDistance; properties.cannonTimeout = -1; shootingGalleryComponent->SetDynamicParams(properties); @@ -829,22 +886,38 @@ std::vector> SGCannon::GetWaves() { 1.0, false, true }, - // Sub 1 + // Sub 1 but for dlu { std::vector { "Wave_1_Sub_1", "Wave_1_Sub_2" }, 6016, 0.0, 2.0, true, 0.0, 2.0, - 10.0, 1000, false, 0.0, 1.0, - 1.0, true, true - }, - - // Sub 2 - { - std::vector { "Wave_2_Sub_1", "Wave_2_Sub_2" }, - 6016, 0.0, 2.0, true, 0.0, 2.0, 2.0, 1000, false, 0.0, 1.0, 1.0, true, true }, + //// Sub 1 + //{ + // std::vector { "Wave_1_Sub_1", "Wave_1_Sub_2" }, + // 6016, 0.0, 2.0, true, 0.0, 2.0, + // 10.0, 1000, false, 0.0, 1.0, + // 1.0, true, true + //}, + + // Sub 2 but for dlu + { + std::vector { "Wave_2_Sub_1", "Wave_2_Sub_2" }, + 6016, 0.0, 2.0, true, 0.0, 2.0, + 3.0, 1000, false, 0.0, 1.0, + 1.0, true, true + }, + + // Sub 2 + //{ + // std::vector { "Wave_2_Sub_1", "Wave_2_Sub_2" }, + // 6016, 0.0, 2.0, true, 0.0, 2.0, + // 2.0, 1000, false, 0.0, 1.0, + // 1.0, true, true + //}, + // Friendly { std::vector { "Wave_3_FShip_1", "Wave_3_FShip_2" }, @@ -897,10 +970,18 @@ std::vector> SGCannon::GetWaves() { }, // Sub 2 + //{ + // std::vector { "Wave_2_Sub_1", "Wave_2_Sub_2" }, + // 6016, 0.0, 2.0, true, 0.0, 2.0, + // 2.0, 1000, false, 0.0, 1.0, + // 1.0, true, true + //}, + + // Sub 2 but for dlu { std::vector { "Wave_2_Sub_1", "Wave_2_Sub_2" }, 6016, 0.0, 2.0, true, 0.0, 2.0, - 2.0, 1000, false, 0.0, 1.0, + 3.0, 1000, false, 0.0, 1.0, 1.0, true, true }, @@ -963,11 +1044,19 @@ std::vector> SGCannon::GetWaves() { 1.0, true, true }, + // Sub 2 + //{ + // std::vector { "Wave_2_Sub_1", "Wave_2_Sub_2" }, + // 6016, 0.0, 2.0, true, 0.0, 2.0, + // 2.0, 1000, false, 0.0, 1.0, + // 1.0, true, true + //}, + // Sub 2 { std::vector { "Wave_2_Sub_1", "Wave_2_Sub_2" }, 6016, 0.0, 2.0, true, 0.0, 2.0, - 2.0, 1000, false, 0.0, 1.0, + 3.0, 1000, false, 0.0, 1.0, 1.0, true, true }, @@ -987,14 +1076,22 @@ std::vector> SGCannon::GetWaves() { 1.0, false, true }, - // Ness + // Ness temp fix for dlu where speeds are set to 7 to match a speed closer to live while we work on movingplatform components. { std::vector { "Wave_1_Ness_1", "Wave_1_Ness_2", "Wave_2_Ness_1" }, 2565, 10.0, 15.0, true, 10.0, 15.0, - 2.0, 10000, false, 0.0, 1.0, - 1.0, true, true + 7.0, 10000, false, 0.0, 7.0, + 7.0, true, true }, + // // Ness + // { + // std::vector { "Wave_1_Ness_1", "Wave_1_Ness_2", "Wave_2_Ness_1" }, + // 2565, 10.0, 15.0, true, 10.0, 15.0, + // 2.0, 10000, false, 0.0, 1.0, + // 1.0, true, true + // }, + // Friendly 1 { std::vector { "Wave_3_FShip_1", "Wave_3_FShip_2" }, @@ -1033,11 +1130,8 @@ void SGCannon::ResetVars(Entity* self) { self->SetVar(TotalScoreVariable, 0); self->SetVar(u"m_curStreak", 0); - self->SetNetworkVar(u"SuperChargeBar", 0); self->SetVar(u"LastSuperTotal", 0); - self->SetNetworkVar(u"SuperChargeBar", 0.0f); - self->SetNetworkVar(u"ShowStreak", 0); - self->SetNetworkVar(u"UnMarkAll", true); + self->SetVar>(u"LastHitTarget", {}); const_cast&>(self->GetVar>(ActiveSpawnsVariable)).clear(); self->SetVar>(ActiveSpawnsVariable, {}); @@ -1056,40 +1150,42 @@ void SGCannon::ResetVars(Entity* self) { SGConstants SGCannon::GetConstants() { return { - Vector3 { -908.542480, 229.773178, -908.542480 }, - Quaternion { 0.91913521289825, 0, 0.39394217729568, 0 }, - 1864, - 34, - 1822, - Vector3 { 6.652, -2, 1.5 }, - 157, - 129.0, - 30.0, - 800.0, - Vector3 { 0, 4.3, 9 }, - 6297, - 1822, - 249, - 228, - -1, - 58.6, - true, - 2, - 10, - 25000, - "QBRewardGroup", - 1864, - 50000, - 157, - 100000, - 187, - 200000, - 188, - 400000, - 189, - 800000, - 190, - 4.0, - 7.0 + .playerStartPosition = Vector3 { -908.542480, 229.773178, -908.542480 }, + .playerStartRotation = Quaternion { 0.91913521289825, 0, 0.39394217729568, 0 }, + .cannonLot = 1864, + .impactSkillID = 34, + .projectileLot = 1822, + .playerOffset = Vector3 { 6.652, -2, 1.5 }, + .rewardModelMatrix = 157, + .cannonVelocity = 129.0, + .cannonMinDistance = 30.0, + .cannonRefireRate = 800.0, + .cannonBarrelOffset = Vector3 { 0, 4.3, 9 }, + .cannonSuperchargedProjectileLot = 6297, + .cannonProjectileLot = 1822, + .cannonSuperChargeSkill = 249, + .cannonSkill = 228, + .cannonTimeout = -1, + .cannonFOV = 58.6, + .useLeaderboards = true, + .streakModifier = 2, + .chargedTime = 10, + .chargedPoints = 25000, + .rewardModelGroup = "QBRewardGroup", + .activityID = 1864, + .scoreRewards = { + {"Score_Reward_1", 50000}, + {"Score_Reward_2", 100000}, + {"Score_Reward_3", 200000}, + {"Score_Reward_4", 400000}, + {"Score_Reward_5", 800000}, + }, + .scoreLootMatrix1 = 157, + .scoreLootMatrix2 = 187, + .scoreLootMatrix3 = 188, + .scoreLootMatrix4 = 189, + .scoreLootMatrix5 = 190, + .firstWaveStartTime = 4.0, + .inBetweenWavePause = 7.0 }; } diff --git a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.h b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.h index 712983c9..39af6e5b 100644 --- a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.h +++ b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.h @@ -44,15 +44,11 @@ struct SGConstants { uint32_t chargedPoints; std::string rewardModelGroup; uint32_t activityID; - uint32_t scoreReward1; + std::map scoreRewards; uint32_t scoreLootMatrix1; - uint32_t scoreReward2; uint32_t scoreLootMatrix2; - uint32_t scoreReward3; uint32_t scoreLootMatrix3; - uint32_t scoreReward4; uint32_t scoreLootMatrix4; - uint32_t scoreReward5; uint32_t scoreLootMatrix5; float_t firstWaveStartTime; float_t inBetweenWavePause; @@ -68,6 +64,8 @@ public: void OnActivityTimerDone(Entity* self, const std::string& name) override; void OnActivityTimerUpdate(Entity* self, const std::string& name, float_t timeRemaining, float_t elapsedTime) override; void OnRequestActivityExit(Entity* self, LWOOBJID player, bool canceled) override; + void OnActivityNotify(Entity* self, GameMessages::ActivityNotify& notify) override; + void OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) override; void SuperChargeTimerFunc(Entity* self); void SpawnWaveTimerFunc(Entity* self); void EndWaveTimerFunc(Entity* self); @@ -142,6 +140,7 @@ private: std::u16string CannonBallSkillIDVariable = u"cbskill"; std::u16string HideSuperChargeVariable = u"HideSuper"; std::u16string AudioFinalWaveDoneVariable = u"Audio_Final_Wave_Done"; + std::u16string ModelPercentVariable = u"modelPercent"; std::string SpawnWaveTimer = "SpawnWave"; std::string EndWaveTimer = "EndWave";