#include "Runtime/World/CScriptPickupGenerator.hpp" #include "Runtime/CStateManager.hpp" #include "Runtime/World/CScriptPickup.hpp" #include "Runtime/World/CWallCrawlerSwarm.hpp" #include "TCastTo.hpp" // Generated file, do not modify include path namespace metaforce { CScriptPickupGenerator::CScriptPickupGenerator(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CVector3f& pos, float frequency, bool active) : CEntity(uid, info, active, name), x34_position(pos), x40_frequency(frequency) { ResetDelayTimer(); } void CScriptPickupGenerator::Accept(IVisitor& visitor) { visitor.Visit(this); } void CScriptPickupGenerator::ResetDelayTimer() { if (x40_frequency > 0.f) { x44_delayTimer += 100.f / x40_frequency; } else { x44_delayTimer = FLT_MAX; } } void CScriptPickupGenerator::GetGeneratorIds(CStateManager& mgr, TUniqueId sender, std::vector& idsOut) const { idsOut.reserve(std::max(size_t(1), GetConnectionList().size())); for (const auto& conn : GetConnectionList()) { if (conn.x0_state == EScriptObjectState::Zero && conn.x4_msg == EScriptObjectMessage::Follow) { const TUniqueId id = mgr.GetIdForScript(conn.x8_objId); if (id != kInvalidUniqueId) { if (const CEntity* ent = mgr.GetObjectById(id)) { if (ent->GetActive()) { idsOut.push_back(id); } } } } } if (idsOut.empty()) { idsOut.push_back(sender); } } float CScriptPickupGenerator::GetPickupTemplates(CStateManager& mgr, std::vector>& idsOut) const { float totalPossibility = 0.f; CPlayerState& pState = *mgr.GetPlayerState(); idsOut.reserve(GetConnectionList().size()); for (const auto& conn : GetConnectionList()) { if (conn.x0_state == EScriptObjectState::Zero && conn.x4_msg == EScriptObjectMessage::Activate) { const TUniqueId id = mgr.GetIdForScript(conn.x8_objId); if (id != kInvalidUniqueId) { if (const TCastToConstPtr pickup = mgr.GetObjectById(id)) { const CPlayerState::EItemType item = pickup->GetItem(); float possibility = pickup->GetPossibility(); float multiplier = 1.f; bool doAlways = false; bool doThirtyPerc = false; switch (item) { case CPlayerState::EItemType::Missiles: if (pState.HasPowerUp(CPlayerState::EItemType::Missiles)) { if (pState.GetItemAmount(CPlayerState::EItemType::Missiles) < pState.GetItemCapacity(CPlayerState::EItemType::Missiles)) { doAlways = true; } else { doThirtyPerc = true; } } break; case CPlayerState::EItemType::PowerBombs: if (pState.HasPowerUp(CPlayerState::EItemType::PowerBombs)) { if (pState.GetItemAmount(CPlayerState::EItemType::PowerBombs) < pState.GetItemCapacity(CPlayerState::EItemType::PowerBombs)) { doAlways = true; if (pState.GetItemAmount(CPlayerState::EItemType::PowerBombs) < 2 && possibility >= 10.f && possibility < 25.f) { multiplier = 2.f; } } else { doThirtyPerc = true; } } break; case CPlayerState::EItemType::HealthRefill: if (pState.GetHealthInfo().GetHP() < pState.CalculateHealth()) { doAlways = true; } else { doThirtyPerc = true; } break; default: doAlways = true; break; } const bool thirtyPercTest = mgr.GetActiveRandom()->Float() < 0.3f; if ((doAlways || (doThirtyPerc && thirtyPercTest)) && possibility > 0.f) { totalPossibility += possibility * multiplier; idsOut.emplace_back(possibility, conn.x8_objId); } } } } } return totalPossibility; } void CScriptPickupGenerator::GeneratePickup(CStateManager& mgr, TEditorId templateId, TUniqueId generatorId) const { CEntity* pickupTempl = mgr.ObjectById(mgr.GetIdForScript(templateId)); CEntity* generator = mgr.ObjectById(generatorId); if (pickupTempl == nullptr || generator == nullptr) { return; } const bool oldGeneratingObject = mgr.GetIsGeneratingObject(); mgr.SetIsGeneratingObject(true); const auto p = mgr.GenerateObject(templateId); mgr.SetIsGeneratingObject(oldGeneratingObject); if (p.second == kInvalidUniqueId) { return; } CEntity* newObj = mgr.ObjectById(p.second); CActor* newAct = TCastToPtr(newObj).GetPtr(); CScriptPickup* newPickup = TCastToPtr(newObj).GetPtr(); const CActor* generatorAct = TCastToConstPtr(generator).GetPtr(); const CWallCrawlerSwarm* swarmAct = TCastToConstPtr(generator).GetPtr(); if (newAct && swarmAct) { newAct->SetTranslation(swarmAct->GetLastKilledOffset() + x34_position); } else if (newAct && generatorAct) { newAct->SetTranslation(generatorAct->GetTranslation() + x34_position); } if (newPickup) { newPickup->SetGenerated(); } mgr.SendScriptMsg(newObj, GetUniqueId(), EScriptObjectMessage::Activate); } void CScriptPickupGenerator::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& stateMgr) { if (msg == EScriptObjectMessage::SetToZero && x30_24_active && x40_frequency != 100.f) { x44_delayTimer -= 1.f; if (x44_delayTimer < 0.000009f) { ResetDelayTimer(); } else { std::vector generatorIds; GetGeneratorIds(stateMgr, sender, generatorIds); std::vector> pickupTemplates; const float totalProb = GetPickupTemplates(stateMgr, pickupTemplates); if (!pickupTemplates.empty()) { const float r = stateMgr.GetActiveRandom()->Range(0.f, totalProb); float f2 = 0.f; size_t count = 0; for (const auto& id : pickupTemplates) { if (r >= f2 && r <= f2 + id.first) { break; } f2 += id.first; ++count; } if (count != pickupTemplates.size()) { const TEditorId templateId = pickupTemplates[count].second; GeneratePickup(stateMgr, templateId, generatorIds[stateMgr.GetActiveRandom()->Float() * generatorIds.size() * 0.99f]); } } } } CEntity::AcceptScriptMsg(msg, sender, stateMgr); } } // namespace metaforce