fix shooting gallery bugs (#1702)

This commit is contained in:
David Markowitz 2024-12-29 16:21:22 -08:00 committed by GitHub
parent 1b9f7e44c7
commit 0b261e934f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 368 additions and 163 deletions

View File

@ -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,

View File

@ -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{};

View File

@ -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);
}

View File

@ -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);

View File

@ -95,7 +95,7 @@ void QueryToLdf(Leaderboard& leaderboard, const std::vector<ILeaderboard::Entry>
// Score:1
entry.push_back(new LDFData<int32_t>(u"Streak", leaderboardEntry.secondaryScore));
// Streak:1
entry.push_back(new LDFData<float>(u"HitPercentage", (leaderboardEntry.tertiaryScore / 100.0f)));
entry.push_back(new LDFData<float>(u"HitPercentage", leaderboardEntry.tertiaryScore));
// HitPercentage:3 between 0 and 1
break;
case Racing:
@ -199,9 +199,9 @@ std::vector<ILeaderboard::Entry> FilterFriends(const std::vector<ILeaderboard::E
std::vector<ILeaderboard::Entry> 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);
}
}

View File

@ -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();

View File

@ -265,6 +265,7 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
void MovementAIComponent::SetPath(std::vector<PathWaypoint> 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);
});

View File

@ -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) {

View File

@ -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<LDFData<int32_t>>(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);
}

View File

@ -40,6 +40,8 @@ struct ProjectileSyncEntry {
BehaviorBranchContext branchContext{ 0, 0 };
int32_t skillId{ 0 };
explicit ProjectileSyncEntry();
};

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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<std::unique_ptr<LDFBaseData>> 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<std::unique_ptr<LDFBaseData>> 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

View File

@ -12,7 +12,7 @@ void VeBricksampleServer::OnUse(Entity* self, Entity* user) {
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
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");

View File

@ -10,7 +10,7 @@ void VeMissionConsole::OnUse(Entity* self, Entity* user) {
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
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<number>

View File

@ -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) {

View File

@ -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);

View File

@ -16,6 +16,8 @@
#include "eReplicaComponentType.h"
#include "RenderComponent.h"
#include "eGameActivity.h"
#include "Item.h"
#include <ranges>
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<LWOOBJID>(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<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetIsRacing(true);
characterComponent->SetCurrentActivity(eGameActivity::SHOOTING_GALLERY);
auto possessor = player->GetComponent<PossessorComponent>();
if (possessor) {
@ -114,20 +111,12 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int
Game::entityManager->SerializeEntity(player);
}
self->SetNetworkVar<bool>(HideScoreBoardVariable, true);
self->SetNetworkVar<bool>(ReSetSuperChargeVariable, true);
self->SetNetworkVar<bool>(ShowLoadingUI, true);
self->AddCallbackTimer(1.0f, [self, this]() {
self->SetNetworkVar<bool>(HideScoreBoardVariable, true);
self->SetNetworkVar<bool>(ReSetSuperChargeVariable, true);
self->SetNetworkVar<bool>(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<bool>(GameStartedVariable)) {
LOG_DEBUG("time name %s %s", name.c_str(), name.substr(7).c_str());
const auto spawnNumber = static_cast<uint32_t>(std::stoi(name.substr(7)));
const auto& activeSpawns = self->GetVar<std::vector<SGEnemy>>(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<float_t>(0, toSpawn.spawnPaths.size() - 1);
LOG_DEBUG("index %f", pathIndex);
LOG_DEBUG("%s", toSpawn.spawnPaths.at(pathIndex).c_str());
const auto pathIndex = GeneralUtils::GenerateRandomNumber<size_t>(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<SGEnemy>(u"SpawnData", toSpawn),
new LDFData<std::string>(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"),
new LDFData<std::string>(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"), // this script is never loaded
new LDFData<std::string>(u"custom_script_client", "scripts/client/ai/SG_TARGET_CLIENT.lua"),
new LDFData<std::string>(u"attached_path", path->pathName),
new LDFData<uint32_t>(u"attached_path_start", 0),
new LDFData<std::u16string>(u"groupID", u"SGEnemy")
new LDFData<std::u16string>(u"groupID", u"SGEnemy"),
new LDFData<uint32_t>(u"wave", self->GetVar<uint32_t>(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<MovementAIComponent>(MovementAIInfo{});
auto* simplePhysicsComponent = enemy->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent) {
simplePhysicsComponent->SetPhysicsMotionState(4);
}
Game::entityManager->ConstructEntity(enemy);
movementAI->SetMaxSpeed(toSpawn.initialSpeed);
movementAI->SetCurrentSpeed(toSpawn.initialSpeed);
movementAI->SetHaltDistance(0.0f);
std::vector<PathWaypoint> pathWaypoints = path->pathWaypoints;
if (GeneralUtils::GenerateRandomNumber<float_t>(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<LWOOBJID>(PlayerIDVariable));
@ -400,6 +375,10 @@ void SGCannon::DoGameStartup(Entity* self) {
constants.firstWaveStartTime);
}
void SGCannon::OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) {
self.SetVar<uint32_t>(ShotsFiredVariable, self.GetVar<uint32_t>(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<std::vector<LOT>>(RewardsVariable);
rewards.push_back(currentReward);
self->SetVar<std::vector<LOT>>(RewardsVariable, rewards);
self->SetNetworkVar<int32_t>(RewardAddedVariable, currentReward);
}
@ -438,9 +418,13 @@ void SGCannon::SpawnNewModel(Entity* self) {
std::unordered_map<LOT, int32_t> toDrop = {};
toDrop = Loot::RollLootMatrix(player, lootMatrix);
for (auto drop : toDrop) {
rewardModel->OnFireEventServerSide(self, ModelToBuildEvent, drop.first);
self->SetVar<LOT>(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<LOT>(CurrentRewardVariable, lot);
}
}
}
@ -514,11 +498,11 @@ void SGCannon::RecordPlayerScore(Entity* self) {
const auto currentWave = self->GetVar<uint32_t>(ThisWaveVariable);
if (currentWave > 0) {
auto totalWaveScore = 0;
auto totalWaveScore = totalScore;
auto playerScores = self->GetVar<std::vector<int32_t>>(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<std::vector<int32_t>>(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<uint32_t>(std::ceil(ActivityTimerGetCurrentTime(self, SuperChargeTimer))), static_cast<uint32_t>(1));
const auto time = std::max(static_cast<uint32_t>(std::ceil(ActivityTimerGetRemainingTime(self, SuperChargeTimer))), static_cast<uint32_t>(1));
self->SetVar<bool>(SuperChargePausedVariable, true);
self->SetVar<uint32_t>(CurrentSuperChargedTimeVariable, time);
self->SetNetworkVar<uint32_t>(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<uint32_t>(MissesVariable);
auto fired = self->GetVar<uint32_t>(ShotsFiredVariable);
float percentage = 0.0f;
float misses = self->GetVar<uint32_t>(MissesVariable);
float fired = self->GetVar<uint32_t>(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<MissionComponent>();
if (missionComponent != nullptr) {
@ -596,13 +583,27 @@ void SGCannon::StopGame(Entity* self, bool cancel) {
auto* inventory = player->GetComponent<InventoryComponent>();
if (inventory != nullptr) {
for (const auto rewardLot : self->GetVar<std::vector<LOT>>(RewardsVariable)) {
inventory->AddItem(rewardLot, 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS);
inventory->AddItem(rewardLot, 1, eLootSourceType::NONE, eInventoryType::MODELS);
}
}
self->SetNetworkVar<std::u16string>(u"UI_Rewards",
GeneralUtils::to_u16string(self->GetVar<int32_t>(TotalScoreVariable)) + u"_0_0_0_0_0_0"
);
const auto& waveScores = self->GetVar<std::vector<int32_t>>(PlayerScoresVariable);
std::stringstream stream;
stream << self->GetVar<int32_t>(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<uint32_t>(MaxStreakVariable);
self->SetNetworkVar<std::u16string>(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<bool>(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<LDFData<int32_t>*>(param.get())->GetValue() == GetConstants().cannonSuperChargeSkill;
const auto& hitTargets = self->GetVar<std::vector<LWOOBJID>>(u"CannonBallKills");
if (hitTargets.empty() && !superChargeShotDone) {
self->SetVar<uint32_t>(u"m_curStreak", 0);
self->SetVar<uint32_t>(MissesVariable, self->GetVar<uint32_t>(MissesVariable) + 1);
self->SetNetworkVar<bool>(u"HideStreak", true);
self->SetNetworkVar<bool>(u"UnMarkAll", true);
UpdateStreak(self);
} else if (hitTargets.size() > 1) {
self->SetNetworkVar<bool>(u"mHit", true);
}
self->SetVar<std::vector<LWOOBJID>>(u"CannonBallKills", {});
}
void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& timerName) {
if (!self->GetVar<bool>(GameStartedVariable)) return;
auto cannonBallKills = self->GetVar<std::vector<LWOOBJID>>(u"CannonBallKills");
cannonBallKills.push_back(target->GetObjectID());
self->SetVar<std::vector<LWOOBJID>>(u"CannonBallKills", cannonBallKills);
const auto& spawnInfo = target->GetVar<SGEnemy>(u"SpawnData");
if (spawnInfo.respawns) {
if (spawnInfo.respawns && target->GetVar<uint32_t>(u"wave") == self->GetVar<uint32_t>(ThisWaveVariable)) {
const auto respawnTime = GeneralUtils::GenerateRandomNumber<float_t>(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<bool>(SuperChargeActiveVariable)) {
self->SetVar<uint32_t>(u"m_curStreak", 0);
self->SetVar<uint32_t>(MissesVariable, self->GetVar<uint32_t>(MissesVariable) + 1);
}
self->SetNetworkVar<bool>(u"hitFriend", true);
@ -646,10 +680,6 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time
auto scScore = self->GetVar<int32_t>(TotalScoreVariable) - lastSuperTotal;
LOG("LastSuperTotal: %i, scScore: %i, constants.chargedPoints: %i",
lastSuperTotal, scScore, constants.chargedPoints
);
if (!self->GetVar<bool>(SuperChargeActiveVariable) && scScore >= constants.chargedPoints && score >= 0) {
StartChargedCannon(self);
self->SetNetworkVar<float>(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<uint32_t>(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<uint32_t>(MatrixVariable, matrix + 1);
}
}
}
}
void SGCannon::UpdateStreak(Entity* self) {
@ -745,41 +809,34 @@ void SGCannon::ToggleSuperCharge(Entity* self, bool enable) {
auto* selfInventoryComponent = self->GetComponent<InventoryComponent>();
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<float>(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<uint32_t>(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<std::vector<SGEnemy>> SGCannon::GetWaves() {
1.0, false, true
},
// Sub 1
// Sub 1 but for dlu
{
std::vector<std::string> { "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<std::string> { "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<std::string> { "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<std::string> { "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<std::string> { "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<std::string> { "Wave_3_FShip_1", "Wave_3_FShip_2" },
@ -897,10 +970,18 @@ std::vector<std::vector<SGEnemy>> SGCannon::GetWaves() {
},
// Sub 2
//{
// std::vector<std::string> { "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<std::string> { "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<std::vector<SGEnemy>> SGCannon::GetWaves() {
1.0, true, true
},
// Sub 2
//{
// std::vector<std::string> { "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<std::string> { "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<std::vector<SGEnemy>> 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<std::string> { "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<std::string> { "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<std::string> { "Wave_3_FShip_1", "Wave_3_FShip_2" },
@ -1033,11 +1130,8 @@ void SGCannon::ResetVars(Entity* self) {
self->SetVar<int32_t>(TotalScoreVariable, 0);
self->SetVar<uint32_t>(u"m_curStreak", 0);
self->SetNetworkVar<float>(u"SuperChargeBar", 0);
self->SetVar<uint32_t>(u"LastSuperTotal", 0);
self->SetNetworkVar<float>(u"SuperChargeBar", 0.0f);
self->SetNetworkVar<bool>(u"ShowStreak", 0);
self->SetNetworkVar<bool>(u"UnMarkAll", true);
self->SetVar<std::vector<LWOOBJID>>(u"LastHitTarget", {});
const_cast<std::vector<SGEnemy>&>(self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable)).clear();
self->SetVar<std::vector<SGEnemy>>(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
};
}

View File

@ -44,15 +44,11 @@ struct SGConstants {
uint32_t chargedPoints;
std::string rewardModelGroup;
uint32_t activityID;
uint32_t scoreReward1;
std::map<std::string, uint32_t> 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";