DarkflameServer/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp
David Markowitz ae349d6b15
feat: Add isolated and simplified path to add components (#1204)
* Components: Make ComponentType inline

Prevents the next commits ODR violation

* Components: Add new components

* Entity: Add headers

inline script component ComponentType

* Components: Flip constructor argument order

Entity comes first always

* Entity: Add generic AddComponent

Allows for much easier adding of components and is error proof by not allowing the user to add more than 1 of a specific component type to an Entity.

* Entity: Migrate all component constructors

Move all to the new variadic templates AddComponent function to reduce clutter and ways the component map is modified.
The new function makes no assumptions.  Component is assumed to not exist and is checked for with operator[].  This will construct a null component which will then be newed if the component didnt exist, or it will just get the current component if it does already exist.  No new component will be allocated or constructed if the component already exists and the already existing pointer is returned instead.

* Entity: Add placement new

For the case where the component may already exist, use a placement new to construct the component again, it would be constructed again, but would not need to go through the allocator.

* Entity: Add comments on likely new code

* Tests: Fix tests

* Update Entity.cpp

* Update SGCannon.cpp

* Entity: call destructor when re-constructing

* Update Entity.cpp

Update Entity.cpp

---------

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2023-10-22 20:08:49 -05:00

1097 lines
33 KiB
C++

#include "SGCannon.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "dZoneManager.h"
#include "Player.h"
#include "Character.h"
#include "ShootingGalleryComponent.h"
#include "PossessorComponent.h"
#include "CharacterComponent.h"
#include "SimplePhysicsComponent.h"
#include "MovementAIComponent.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "MissionComponent.h"
#include "Loot.h"
#include "InventoryComponent.h"
#include "eMissionTaskType.h"
#include "eReplicaComponentType.h"
#include "RenderComponent.h"
#include "eGameActivity.h"
void SGCannon::OnStartup(Entity* self) {
LOG("OnStartup");
m_Waves = GetWaves();
constants = GetConstants();
ResetVars(self);
self->SetVar<bool>(GameStartedVariable, false);
self->SetVar<Vector3>(InitialVelocityVariable, {});
self->SetVar<uint32_t>(ImpactSkillVariale, constants.impactSkillID);
auto* shootingGalleryComponent = self->GetComponent<ShootingGalleryComponent>();
if (shootingGalleryComponent != nullptr) {
shootingGalleryComponent->SetStaticParams({
Vector3 { -327.8609924316406, 256.8999938964844, 1.6482199430465698 },
Vector3 { -181.4320068359375, 212.39999389648438, 2.5182199478149414 }
});
shootingGalleryComponent->SetDynamicParams({
Vector3 { 0.0, 4.3, 9.0 },
Vector3 { },
129.0,
800.0,
30.0,
0.0,
-1.0,
58.6
});
}
self->SetVar<uint32_t>(TimeLimitVariable, 30);
self->SetVar<std::vector<LOT>>(ValidActorsVariable, { 3109, 3110, 3111, 3112, 3125, 3126 });
self->SetVar<std::vector<LOT>>(ValidEffectsVariable, { 3122 });
self->SetVar<std::vector<uint32_t>>(StreakBonusVariable, { 1, 2, 5, 10 });
self->SetVar<bool>(SuperChargeActiveVariable, false);
self->SetVar<uint32_t>(MatrixVariable, 1);
self->SetVar<bool>(InitVariable, true);
auto* simplePhysicsComponent = self->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetPhysicsMotionState(5);
}
}
void SGCannon::OnPlayerLoaded(Entity* self, Entity* player) {
LOG("Player loaded");
self->SetVar<LWOOBJID>(PlayerIDVariable, player->GetObjectID());
}
void SGCannon::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2,
int32_t param3) {
Script::OnFireEventServerSide(self, sender, args, param1, param2, param3);
}
void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int32_t value1, int32_t value2,
const std::u16string& stringValue) {
LOG("Got activity state change request: %s", GeneralUtils::UTF16ToWTF8(stringValue).c_str());
if (stringValue == u"clientready") {
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());
auto* shootingGalleryComponent = self->GetComponent<ShootingGalleryComponent>();
if (shootingGalleryComponent != nullptr) {
shootingGalleryComponent->SetCurrentPlayerID(player->GetObjectID());
LOG("Setting player ID");
Game::entityManager->SerializeEntity(self);
} else {
LOG("Shooting gallery component is null");
}
auto* characterComponent = player->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetIsRacing(true);
characterComponent->SetCurrentActivity(eGameActivity::SHOOTING_GALLERY);
auto possessor = player->GetComponent<PossessorComponent>();
if (possessor) {
possessor->SetPossessable(self->GetObjectID());
possessor->SetPossessableType(ePossessionType::NO_POSSESSION);
}
Game::entityManager->SerializeEntity(player);
}
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");
}
} else if (value1 == 1200) {
StartGame(self);
}
}
void SGCannon::OnMessageBoxResponse(Entity* self, Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) {
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (!player) return;
if (identifier == u"Scoreboardinfo") {
GameMessages::SendDisplayMessageBox(player->GetObjectID(), true,
Game::zoneManager->GetZoneControlObject()->GetObjectID(),
u"Shooting_Gallery_Retry", 2, u"Retry?",
u"", player->GetSystemAddress());
} else {
if ((button == 1 && (identifier == u"Shooting_Gallery_Retry" || identifier == u"RePlay")) || identifier == u"SG1" || button == 0) {
if (IsPlayerInActivity(self, player->GetObjectID())) return;
self->SetNetworkVar<bool>(ClearVariable, true);
StartGame(self);
} else if (button == 0 && ((identifier == u"Shooting_Gallery_Retry" || identifier == u"RePlay"))) {
RemovePlayer(player->GetObjectID());
UpdatePlayer(self, player->GetObjectID(), true);
} else if (button == 1 && identifier == u"Shooting_Gallery_Exit") {
UpdatePlayer(self, player->GetObjectID(), true);
RemovePlayer(player->GetObjectID());
}
}
}
void SGCannon::SuperChargeTimerFunc(Entity* self) {
if (self->GetVar<bool>(WaveStatusVariable) || self->GetVar<uint32_t>(CurrentSuperChargedTimeVariable) < 1) {
self->SetNetworkVar<uint32_t>(ChargeCountingVariable, 99);
self->SetNetworkVar<uint32_t>(SuperChargeBarVariable, 0);
ToggleSuperCharge(self, false);
}
}
void SGCannon::SpawnWaveTimerFunc(Entity* self) {
if (self->GetVar<bool>(GameStartedVariable)) {
self->SetVar<bool>(WaveStatusVariable, true);
const auto wave = (int32_t)self->GetVar<uint32_t>(ThisWaveVariable);
if (wave != 0 && self->GetVar<bool>(SuperChargePausedVariable)) {
StartChargedCannon(self, self->GetVar<uint32_t>(CurrentSuperChargedTimeVariable));
self->SetVar<uint32_t>(CurrentSuperChargedTimeVariable, 0);
}
TimerToggle(self, true);
for (const auto& enemyToSpawn : m_Waves.at(self->GetVar<uint32_t>(ThisWaveVariable))) {
SpawnObject(self, enemyToSpawn, true);
}
LOG("Current wave spawn: %i/%i", wave, m_Waves.size());
// All waves completed
const auto timeLimit = (float_t)self->GetVar<uint32_t>(TimeLimitVariable);
if (wave >= m_Waves.size()) {
ActivityTimerStart(self, GameOverTimer, timeLimit, timeLimit);
} else {
ActivityTimerStart(self, EndWaveTimer, timeLimit, timeLimit);
}
const auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player != nullptr) {
GameMessages::SendPlayFXEffect(player->GetObjectID(), -1, u"SG-start", "");
GameMessages::SendStartActivityTime(self->GetObjectID(), timeLimit, player->GetSystemAddress());
LOG("Sending ActivityPause false");
GameMessages::SendActivityPause(self->GetObjectID(), false, player->GetSystemAddress());
}
}
}
void SGCannon::EndWaveTimerFunc(Entity* self) {
self->SetVar<bool>(WaveStatusVariable, false);
TimerToggle(self);
RecordPlayerScore(self);
if (self->GetVar<uint32_t>(ThisWaveVariable) >= 2) {
GameMessages::SendActivityPause(self->GetObjectID(), true);
ActivityTimerStart(self, GameOverTimer, 0.1, 0.1);
return;
}
self->SetVar<uint32_t>(ThisWaveVariable, self->GetVar<uint32_t>(ThisWaveVariable) + 1);
PlaySceneAnimation(self, u"wave" + GeneralUtils::to_u16string(self->GetVar<uint32_t>(ThisWaveVariable)), true, true, 1.7f);
self->SetNetworkVar<uint32_t>(WaveNumVariable, self->GetVar<uint32_t>(ThisWaveVariable) + 1);
self->SetNetworkVar<uint32_t>(WaveStrVariable, self->GetVar<uint32_t>(TimeLimitVariable));
LOG("Current wave: %i/%i", self->GetVar<uint32_t>(ThisWaveVariable), m_Waves.size());
if (self->GetVar<uint32_t>(ThisWaveVariable) >= m_Waves.size()) {
ActivityTimerStart(self, GameOverTimer, 0.1, 0.1);
} else {
ActivityTimerStart(self, SpawnWaveTimer, constants.inBetweenWavePause, constants.inBetweenWavePause);
}
LOG("Sending ActivityPause true");
GameMessages::SendActivityPause(self->GetObjectID(), true);
if (self->GetVar<bool>(SuperChargeActiveVariable) && !self->GetVar<bool>(SuperChargePausedVariable)) {
PauseChargeCannon(self);
}
}
void SGCannon::GameOverTimerFunc(Entity* self) {
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player != nullptr) {
LOG_DEBUG("Sending ActivityPause true");
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);
}
}
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 = (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* 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);
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.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_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")
};
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{});
movementAI->SetMaxSpeed(toSpawn.initialSpeed);
movementAI->SetCurrentSpeed(toSpawn.initialSpeed);
movementAI->SetHaltDistance(0.0f);
std::vector<NiPoint3> pathWaypoints;
for (const auto& waypoint : path->pathWaypoints) {
pathWaypoints.push_back(waypoint.position);
}
if (GeneralUtils::GenerateRandomNumber<float_t>(0, 1) < 0.5f) {
std::reverse(pathWaypoints.begin(), pathWaypoints.end());
}
movementAI->SetPath(pathWaypoints);
enemy->AddDieCallback([this, self, enemy, name]() {
RegisterHit(self, enemy, name);
});
// Save the enemy and tell it to start pathing
if (enemy != nullptr) {
const_cast<std::vector<LWOOBJID>&>(self->GetVar<std::vector<LWOOBJID>>(SpawnedObjects)).push_back(enemy->GetObjectID());
GameMessages::SendPlatformResync(enemy, UNASSIGNED_SYSTEM_ADDRESS);
}
}
}
void SGCannon::EndGameBufferTimerFunc(Entity* self) {
RecordPlayerScore(self);
StopGame(self, false);
}
void SGCannon::OnActivityTimerDone(Entity* self, const std::string& name) {
if (name == SuperChargeTimer && !self->GetVar<bool>(SuperChargePausedVariable)) {
SuperChargeTimerFunc(self);
} else if (name == SpawnWaveTimer) {
SpawnWaveTimerFunc(self);
} else if (name == EndWaveTimer) {
EndWaveTimerFunc(self);
} else if (name == GameOverTimer) {
GameOverTimerFunc(self);
} else if (name.rfind(DoSpawnTimer, 0) == 0) {
DoSpawnTimerFunc(self, name);
} else if (name == EndGameBufferTimer) {
EndGameBufferTimerFunc(self);
}
}
void
SGCannon::OnActivityTimerUpdate(Entity* self, const std::string& name, float_t timeRemaining, float_t elapsedTime) {
ActivityManager::OnActivityTimerUpdate(self, name, timeRemaining, elapsedTime);
}
void SGCannon::StartGame(Entity* self) {
if (self->GetVar<bool>(GameStartedVariable)) return;
self->SetNetworkVar<uint32_t>(TimeLimitVariable, self->GetVar<uint32_t>(TimeLimitVariable));
self->SetNetworkVar<bool>(AudioStartIntroVariable, true);
self->SetVar<LOT>(CurrentRewardVariable, LOT_NULL);
auto rewardObjects = Game::entityManager->GetEntitiesInGroup(constants.rewardModelGroup);
for (auto* reward : rewardObjects) {
reward->OnFireEventServerSide(self, ModelToBuildEvent);
}
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player != nullptr) {
GetLeaderboardData(self, player->GetObjectID(), GetActivityID(self), 1);
LOG("Sending ActivityStart");
GameMessages::SendActivityStart(self->GetObjectID(), player->GetSystemAddress());
GameMessages::SendPlayFXEffect(self->GetObjectID(), -1, u"start", "");
self->SetNetworkVar<bool>(ClearVariable, true);
DoGameStartup(self);
if (!self->GetVar<bool>(FirstTimeDoneVariable)) {
TakeActivityCost(self, player->GetObjectID());
}
self->SetVar<bool>(FirstTimeDoneVariable, true);
}
SpawnNewModel(self);
}
void SGCannon::DoGameStartup(Entity* self) {
ResetVars(self);
self->SetVar<bool>(GameStartedVariable, true);
self->SetNetworkVar<bool>(ClearVariable, true);
self->SetVar<uint32_t>(ThisWaveVariable, 0);
if (constants.firstWaveStartTime < 1) {
constants.firstWaveStartTime = 1;
}
ActivityTimerStart(self, SpawnWaveTimer, constants.firstWaveStartTime,
constants.firstWaveStartTime);
}
void SGCannon::SpawnNewModel(Entity* self) {
// Add a new reward to the existing rewards
const auto currentReward = self->GetVar<LOT>(CurrentRewardVariable);
if (currentReward != -1) {
auto rewards = self->GetVar<std::vector<LOT>>(RewardsVariable);
rewards.push_back(currentReward);
self->SetNetworkVar<int32_t>(RewardAddedVariable, currentReward);
}
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player != nullptr) {
for (auto* rewardModel : Game::entityManager->GetEntitiesInGroup(constants.rewardModelGroup)) {
uint32_t lootMatrix;
switch (self->GetVar<uint32_t>(MatrixVariable)) {
case 1:
lootMatrix = constants.scoreLootMatrix1;
break;
case 2:
lootMatrix = constants.scoreLootMatrix2;
break;
case 3:
lootMatrix = constants.scoreLootMatrix3;
break;
case 4:
lootMatrix = constants.scoreLootMatrix4;
break;
case 5:
lootMatrix = constants.scoreLootMatrix5;
break;
default:
lootMatrix = 0;
}
if (lootMatrix != 0) {
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);
}
}
}
}
}
void SGCannon::RemovePlayer(LWOOBJID playerID) {
auto* player = Game::entityManager->GetEntity(playerID);
if (player == nullptr)
return;
auto* playerObject = dynamic_cast<Player*>(player);
if (playerObject == nullptr)
return;
auto* character = playerObject->GetCharacter();
if (character != nullptr) {
playerObject->SendToZone(character->GetLastNonInstanceZoneID());
}
}
void SGCannon::OnRequestActivityExit(Entity* self, LWOOBJID player, bool canceled) {
if (canceled) {
StopGame(self, canceled);
RemovePlayer(player);
}
}
void SGCannon::StartChargedCannon(Entity* self, uint32_t optionalTime) {
optionalTime = optionalTime == 0 ? constants.chargedTime : optionalTime;
self->SetVar<bool>(SuperChargePausedVariable, false);
ToggleSuperCharge(self, true);
ActivityTimerStart(self, SuperChargeTimer, 1, optionalTime);
if (!self->GetVar<bool>(WaveStatusVariable)) {
PauseChargeCannon(self);
}
}
void SGCannon::TimerToggle(Entity* self, bool start) {
if (start) {
self->SetNetworkVar<uint32_t>(CountVariable, self->GetVar<uint32_t>(TimeLimitVariable));
self->SetVar<bool>(GameStartedVariable, true);
} else {
self->SetNetworkVar<bool>(StopVariable, true);
}
}
void SGCannon::SpawnObject(Entity* self, const SGEnemy& toSpawn, bool spawnNow) {
auto activeSpawns = self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable);
activeSpawns.push_back(toSpawn);
self->SetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable, activeSpawns);
self->SetVar(SpawnNumberVariable, activeSpawns.size() - 1);
const auto timerName = DoSpawnTimer + std::to_string(activeSpawns.size() - 1);
if (spawnNow) {
if (toSpawn.minSpawnTime > 0 && toSpawn.maxSpawnTime > 0) {
const auto spawnTime = GeneralUtils::GenerateRandomNumber<float_t>(toSpawn.minSpawnTime, toSpawn.maxSpawnTime);
ActivityTimerStart(self, timerName, spawnTime, spawnTime);
} else {
ActivityTimerStart(self, timerName, 1, 1);
}
} else if (toSpawn.respawns) {
const auto spawnTime = GeneralUtils::GenerateRandomNumber<float_t>(toSpawn.minRespawnTime, toSpawn.maxRespawnTime);
ActivityTimerStart(self, timerName, spawnTime, spawnTime);
}
}
void SGCannon::RecordPlayerScore(Entity* self) {
const auto totalScore = self->GetVar<uint32_t>(TotalScoreVariable);
const auto currentWave = self->GetVar<uint32_t>(ThisWaveVariable);
if (currentWave > 0) {
auto totalWaveScore = 0;
auto playerScores = self->GetVar<std::vector<int32_t>>(PlayerScoresVariable);
for (const auto& waveScore : playerScores) {
totalWaveScore += waveScore;
}
if (currentWave >= playerScores.size()) {
playerScores.push_back(totalWaveScore);
} else {
playerScores[currentWave] = totalWaveScore;
}
}
}
void SGCannon::PlaySceneAnimation(Entity* self, const std::u16string& animationName, bool onCannon, bool onPlayer, float_t priority) {
for (auto* cannon : Game::entityManager->GetEntitiesInGroup("cannongroup")) {
RenderComponent::PlayAnimation(cannon, animationName, priority);
}
if (onCannon) {
RenderComponent::PlayAnimation(self, animationName, priority);
}
if (onPlayer) {
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player != nullptr) {
RenderComponent::PlayAnimation(player, animationName, priority);
}
}
}
void SGCannon::PauseChargeCannon(Entity* self) {
const auto time = std::max((uint32_t)std::ceil(ActivityTimerGetCurrentTime(self, SuperChargeTimer)), (uint32_t)1);
self->SetVar<bool>(SuperChargePausedVariable, true);
self->SetVar<uint32_t>(CurrentSuperChargedTimeVariable, time);
self->SetNetworkVar<uint32_t>(ChargeCountingVariable, time);
ActivityTimerStop(self, SuperChargeTimer);
}
void SGCannon::StopGame(Entity* self, bool cancel) {
self->SetNetworkVar<bool>(ReSetSuperChargeVariable, true);
self->SetNetworkVar<bool>(HideSuperChargeVariable, true);
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player == nullptr)
return;
ToggleSuperCharge(self, false);
// 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);
if (fired > 0) {
percentage = misses / fired;
}
auto* missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent != nullptr) {
missionComponent->Progress(eMissionTaskType::PERFORM_ACTIVITY, self->GetVar<uint32_t>(TotalScoreVariable), self->GetObjectID(), "performact_score");
missionComponent->Progress(eMissionTaskType::PERFORM_ACTIVITY, self->GetVar<uint32_t>(MaxStreakVariable), self->GetObjectID(), "performact_streak");
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_CannonLot, 0, "", self->GetVar<uint32_t>(TotalScoreVariable));
}
Loot::GiveActivityLoot(player, self, GetGameID(self), self->GetVar<uint32_t>(TotalScoreVariable));
SaveScore(self, player->GetObjectID(),
static_cast<float>(self->GetVar<uint32_t>(TotalScoreVariable)), static_cast<float>(self->GetVar<uint32_t>(MaxStreakVariable)), percentage);
StopActivity(self, player->GetObjectID(), self->GetVar<uint32_t>(TotalScoreVariable), self->GetVar<uint32_t>(MaxStreakVariable), percentage);
self->SetNetworkVar<bool>(AudioFinalWaveDoneVariable, true);
// Give the player the model rewards they earned
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);
}
}
self->SetNetworkVar<std::u16string>(u"UI_Rewards",
GeneralUtils::to_u16string(self->GetVar<uint32_t>(TotalScoreVariable)) + u"_0_0_0_0_0_0"
);
}
GameMessages::SendActivityStop(self->GetObjectID(), false, cancel, player->GetSystemAddress());
self->SetVar<bool>(GameStartedVariable, false);
ActivityTimerStopAllTimers(self);
// Destroy all spawners
for (auto* entity : Game::entityManager->GetEntitiesInGroup("SGEnemy")) {
entity->Kill();
}
ResetVars(self);
}
void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& timerName) {
const auto& spawnInfo = target->GetVar<SGEnemy>(u"SpawnData");
if (spawnInfo.respawns) {
const auto respawnTime = GeneralUtils::GenerateRandomNumber<float_t>(spawnInfo.minRespawnTime, spawnInfo.maxRespawnTime);
ActivityTimerStart(self, timerName, respawnTime, respawnTime);
}
int score = spawnInfo.score;
if (score > 0) {
score += score * GetCurrentBonus(self);
if (!self->GetVar<bool>(SuperChargeActiveVariable)) {
self->SetVar<uint32_t>(u"m_curStreak", self->GetVar<uint32_t>(u"m_curStreak") + 1);
}
} else {
if (!self->GetVar<bool>(SuperChargeActiveVariable)) {
self->SetVar<uint32_t>(u"m_curStreak", 0);
}
self->SetNetworkVar<bool>(u"hitFriend", true);
}
auto lastSuperTotal = self->GetVar<uint32_t>(u"LastSuperTotal");
auto scScore = self->GetVar<uint32_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);
self->SetVar<uint32_t>(u"LastSuperTotal", self->GetVar<uint32_t>(TotalScoreVariable));
}
UpdateStreak(self);
GameMessages::SendNotifyClientShootingGalleryScore(self->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS,
0.0f,
score,
target->GetObjectID(),
target->GetPosition()
);
auto newScore = (int)self->GetVar<uint32_t>(TotalScoreVariable) + score;
if (newScore < 0) {
newScore = 0;
}
self->SetVar<uint32_t>(TotalScoreVariable, newScore);
self->SetNetworkVar<uint32_t>(u"updateScore", newScore);
self->SetNetworkVar<std::u16string>(u"beatHighScore", GeneralUtils::to_u16string(newScore));
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player == nullptr) return;
auto missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent == nullptr) return;
missionComponent->Progress(eMissionTaskType::SMASH, spawnInfo.lot, self->GetObjectID());
}
void SGCannon::UpdateStreak(Entity* self) {
const auto streakBonus = GetCurrentBonus(self);
const auto curStreak = self->GetVar<uint32_t>(u"m_curStreak");
const auto marks = curStreak % 3;
self->SetNetworkVar<uint32_t>(u"cStreak", curStreak);
if (curStreak >= 0 && curStreak < 13) {
if (marks == 1) {
self->SetNetworkVar<bool>(u"Mark1", true);
} else if (marks == 2) {
self->SetNetworkVar<bool>(u"Mark2", true);
} else if (marks == 0 && curStreak > 0) {
self->SetVar<float_t>(u"StreakBonus", streakBonus);
self->SetNetworkVar<bool>(u"ShowStreak", streakBonus + 1);
self->SetNetworkVar<bool>(u"Mark3", true);
} else {
self->SetVar<float_t>(u"StreakBonus", streakBonus);
self->SetNetworkVar<bool>(u"UnMarkAll", true);
}
}
auto maxStreak = self->GetVar<uint32_t>(MaxStreakVariable);
if (maxStreak < curStreak) self->SetVar<uint32_t>(MaxStreakVariable, curStreak);
}
float_t SGCannon::GetCurrentBonus(Entity* self) {
auto streak = self->GetVar<uint32_t>(u"m_curStreak");
if (streak > 12) {
streak = 12;
}
return streak / 3;
}
void SGCannon::ToggleSuperCharge(Entity* self, bool enable) {
if (enable && self->GetVar<bool>(SuperChargeActiveVariable))
return;
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player == nullptr) {
LOG("Player not found in toggle super charge");
return;
}
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
auto equippedItems = inventoryComponent->GetEquippedItems();
LOG("Player has %d equipped items", equippedItems.size());
auto skillID = constants.cannonSkill;
auto cooldown = constants.cannonRefireRate;
auto* selfInventoryComponent = self->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) {
LOG("Inventory component not found");
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 });
// 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 });
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);
}
const auto& constants = GetConstants();
auto* shootingGalleryComponent = self->GetComponent<ShootingGalleryComponent>();
if (shootingGalleryComponent == nullptr) {
return;
}
DynamicShootingGalleryParams properties = shootingGalleryComponent->GetDynamicParams();
properties.cannonFOV = 58.6f;
properties.cannonVelocity = 129.0;
properties.cannonRefireRate = cooldown;
properties.cannonMinDistance = 30;
properties.cannonTimeout = -1;
shootingGalleryComponent->SetDynamicParams(properties);
Game::entityManager->SerializeEntity(self);
Game::entityManager->SerializeEntity(player);
self->SetNetworkVar<uint64_t>(CannonBallSkillIDVariable, skillID);
self->SetVar<bool>(SuperChargeActiveVariable, enable);
}
std::vector<std::vector<SGEnemy>> SGCannon::GetWaves() {
return {
// Wave 1
{
// Ship 1
{
std::vector<std::string> { "Wave_1_Ship_1", "Wave_1_Ship_3" },
6015, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1500, false, 0.0, 1.0,
1.0, false, true
},
// Ship 2
{
std::vector<std::string> { "Wave_1_Ship_2", "Wave_1_Ship_4" },
6300, 0.0, 2.0, true, 0.0, 2.0,
2.0, 500, false, 0.0, 1.0,
1.0, false, 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
{
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" },
2168,0.0,5.0,true, 2.0, 5.0,
1.0, -1000, false, 0.0, 1.0,
1.0, false,true
}
},
// Wave 2
{
// Ship 1
{
std::vector<std::string> { "Wave_1_Ship_1", "Wave_1_Ship_3" },
6015, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1500, false, 0.0, 1.0,
1.0, false, true
},
// Ship 2
{
std::vector<std::string> { "Wave_1_Ship_2", "Wave_1_Ship_4" },
6300, 0.0, 2.0, true, 0.0, 2.0,
2.0, 500, false, 0.0, 1.0,
1.0, false, true
},
// Ship 3
{
std::vector<std::string> { "Wave_2_Ship_1" },
6300, 0.0, 2.0, true, 0.0, 2.0,
2.0, 500, false, 0.0, 1.0,
1.0, false, true
},
// Ship 4
{
std::vector<std::string> { "Wave_2_Ship_2" },
6015, 0.0, 2.0, true, 0.0, 2.0,
2.0, 500, false, 0.0, 1.0,
1.0, false, 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,
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,
1.0, true, true
},
// Duck
{
std::vector<std::string> { "Wave_1_Duck_1", "Wave_1_Duck_2" },
5946, 5.0, 10.0, true, 5.0, 10.0,
4.0, 5000, false, 0.0, 1.0,
1.0, false, true
},
// Friendly
{
std::vector<std::string> { "Wave_3_FShip_1", "Wave_3_FShip_2" },
2168,0.0,5.0,true, 2.0, 5.0,
1.0, -1000, false, 0.0, 1.0,
1.0, false,true
}
},
// Wave 3
{
// Ship 1
{
std::vector<std::string> { "Wave_1_Ship_1", "Wave_1_Ship_3" },
6015, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1500, false, 0.0, 1.0,
1.0, false, true
},
// Ship 2
{
std::vector<std::string> { "Wave_1_Ship_2", "Wave_1_Ship_4" },
6300, 0.0, 2.0, true, 0.0, 2.0,
2.0, 500, false, 0.0, 1.0,
1.0, false, true
},
// Ship 3
{
std::vector<std::string> { "Wave_2_Ship_1", "Wave_2_Ship_2" },
6015, 0.0, 2.0, true, 0.0, 2.0,
2.0, 500, false, 0.0, 1.0,
1.0, false, true
},
// Ship 4
{
std::vector<std::string> { "Wave_3_Ship_1", "Wave_3_Ship_2" },
6300, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1500, false, 0.0, 1.0,
1.0, false, 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,
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,
1.0, true, true
},
// Sub 3
{
std::vector<std::string> { "Wave_3_Sub_1", "Wave_3_Sub_2" },
6016, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1000, false, 0.0, 1.0,
1.0, true, true
},
// Duck
{
std::vector<std::string> { "Wave_1_Duck_1", "Wave_1_Duck_2" },
5946, 5.0, 10.0, true, 5.0, 10.0,
4.0, 5000, false, 0.0, 1.0,
1.0, false, 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" },
2168,0.0,5.0,true, 2.0, 5.0,
1.0, -1000, false, 0.0, 1.0,
1.0, false,true
},
// Friendly 2
{
std::vector<std::string> { "Wave_3_FShip_1", "Wave_3_FShip_2" },
2168,0.0,5.0,true, 2.0, 5.0,
1.0, -1000, false, 0.0, 1.0,
1.0, false,true
}
}
};
}
void SGCannon::ResetVars(Entity* self) {
self->SetVar<uint32_t>(SpawnNumberVariable, 0);
self->SetVar<uint32_t>(CurrentSpawnNumberVariable, 0);
self->SetVar<uint32_t>(ThisWaveVariable, 0);
self->SetVar<uint32_t>(GameScoreVariable, 0);
self->SetVar<uint32_t>(GameTimeVariable, 0);
self->SetVar<bool>(GameStartedVariable, false);
self->SetVar<uint32_t>(ShotsFiredVariable, 0);
self->SetVar<uint32_t>(MaxStreakVariable, 0);
self->SetVar<uint32_t>(MissesVariable, 0);
self->SetVar<uint32_t>(CurrentStreakVariable, 0);
self->SetVar<uint32_t>(CurrentSuperChargedTimeVariable, 0);
self->SetVar<std::vector<uint32_t>>(StreakBonusVariable, {});
self->SetVar<uint32_t>(LastSuperTotalVariable, 0);
self->SetVar<LOT>(CurrentRewardVariable, LOT_NULL);
self->SetVar<std::vector<LOT>>(RewardsVariable, {});
self->SetVar<uint32_t>(TotalScoreVariable, 0);
const_cast<std::vector<SGEnemy>&>(self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable)).clear();
self->SetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable, {});
const_cast<std::vector<LWOOBJID>&>(self->GetVar<std::vector<LWOOBJID>>(SpawnedObjects)).clear();
self->SetVar<std::vector<LWOOBJID>>(SpawnedObjects, {});
if (self->GetVar<bool>(InitVariable)) {
ToggleSuperCharge(self, false);
}
self->SetVar<uint32_t>(ImpactSkillVariale, constants.impactSkillID);
self->SetVar<std::vector<int32_t>>(PlayerScoresVariable, {});
ActivityTimerStopAllTimers(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
};
}