Skip to content

Commit

Permalink
[TF2] Add Bot Upgrade Manager
Browse files Browse the repository at this point in the history
  • Loading branch information
caxanga334 committed Apr 3, 2024
1 parent 95fcb65 commit b6e5165
Show file tree
Hide file tree
Showing 9 changed files with 459 additions and 10 deletions.
1 change: 1 addition & 0 deletions configs/tf/mvm_upgrades.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ TFMvMUpgrades
*
* priority, bots will buy upgrades with the lowest priority first.
* in case two or more upgrades have the same priority, they will be selected randomly
* priority must start at 1, bot thinks they finished their upgrades when no upgrades are found for a given priority
* "priority" "1"
* "slot" "0" -- weapon slot used by this upgrade, -1 for player upgrades, 9 for canteen power up bottle
* "weapons" "5,7,333" -- comma-delimited list of item definition index this upgrade is restricted to
Expand Down
1 change: 1 addition & 0 deletions extension/AMBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ sourceFiles = [
'bot/tf2/tf2bot_controller.cpp',
'bot/tf2/tf2bot_movement.cpp',
'bot/tf2/tf2bot_sensor.cpp',
'bot/tf2/tf2bot_upgrades.cpp',
'bot/tf2/tasks/tf2bot_maintask.cpp',
'bot/tf2/tasks/tf2bot_tactical.cpp',
'bot/tf2/tasks/tf2bot_scenario.cpp',
Expand Down
16 changes: 16 additions & 0 deletions extension/bot/tf2/tf2bot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ CTF2Bot::CTF2Bot(edict_t* edict) : CBaseBot(edict)
m_tf2behavior = std::make_unique<CTF2BotBehavior>(this);
m_desiredclass = TeamFortress2::TFClassType::TFClass_Unknown;
m_knownspythink = TIME_TO_TICKS(knownspy_think_interval());
m_upgrademan.SetMe(this);
}

CTF2Bot::~CTF2Bot()
Expand Down Expand Up @@ -64,6 +65,7 @@ void CTF2Bot::Spawn()
void CTF2Bot::FirstSpawn()
{
CBaseBot::FirstSpawn();
m_upgrademan.InitUpgrades();
}

int CTF2Bot::GetMaxHealth() const
Expand Down Expand Up @@ -472,6 +474,20 @@ void CTF2Bot::FindMyBuildings()
}
}

int CTF2Bot::GetCurrency() const
{
int currency = 0;
entprops->GetEntProp(GetIndex(), Prop_Send, "m_nCurrency", currency);
return currency;
}

bool CTF2Bot::IsInUpgradeZone() const
{
bool val = false;
entprops->GetEntPropBool(GetIndex(), Prop_Send, "m_bInUpgradeZone", val);
return val;
}

const CTF2Bot::KnownSpy* CTF2Bot::GetKnownSpy(edict_t* spy) const
{
int index = gamehelpers->IndexOfEdict(spy);
Expand Down
6 changes: 6 additions & 0 deletions extension/bot/tf2/tf2bot.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "tf2bot_controller.h"
#include "tf2bot_movement.h"
#include "tf2bot_sensor.h"
#include "tf2bot_upgrades.h"

struct edict_t;

Expand Down Expand Up @@ -102,6 +103,8 @@ class CTF2Bot : public CBaseBot
void SetMyTeleporterEntrance(edict_t* entity);
void SetMyTeleporterExit(edict_t* entity);
void FindMyBuildings();
int GetCurrency() const;
bool IsInUpgradeZone() const;

/**
* @brief Gets a known spy information about a spy or NULL if none
Expand All @@ -111,6 +114,8 @@ class CTF2Bot : public CBaseBot
const KnownSpy* GetKnownSpy(edict_t* spy) const;
const KnownSpy* UpdateOrCreateKnownSpy(edict_t* spy, KnownSpyInfo info, const bool updateinfo = false, const bool updateclass = false);

CTF2BotUpgradeManager& GetUpgradeManager() { return m_upgrademan; }

private:
std::unique_ptr<CTF2BotMovement> m_tf2movement;
std::unique_ptr<CTF2BotPlayerController> m_tf2controller;
Expand All @@ -124,6 +129,7 @@ class CTF2Bot : public CBaseBot
CBaseHandle m_myTeleporterExit;
std::vector<KnownSpy> m_knownspies;
int m_knownspythink;
CTF2BotUpgradeManager m_upgrademan;

inline void UpdateKnownSpies()
{
Expand Down
223 changes: 223 additions & 0 deletions extension/bot/tf2/tf2bot_upgrades.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#include <extension.h>
#include <tier1/KeyValues.h>
#include <mods/tf2/teamfortress2_shareddefs.h>
#include <mods/tf2/teamfortress2mod.h>
#include <mods/tf2/mvm_upgrade_manager.h>
#include <util/librandom.h>
#include "tf2bot.h"
#include "tf2bot_upgrades.h"

CTF2BotUpgradeManager::CTF2BotUpgradeManager()
{
m_me = nullptr;
m_nextpriority = 1;
m_numupgrades = 0;
m_tobuylist.reserve(16);
m_boughtlist.reserve(32);
m_state = STATE_GOBUY;
}

CTF2BotUpgradeManager::~CTF2BotUpgradeManager()
{
}

void CTF2BotUpgradeManager::Update()
{
if (!CanAffordAnyUpgrade() && m_state != STATE_DOREFUND)
{
OnDoneUpgrading();
return;
}

switch (m_state)
{
case CTF2BotUpgradeManager::STATE_GOBUY:
m_state = STATE_BUYINGSTUFF;
ExecuteBuy();
RemoveFinishedUpgrades(); // clean up
return;
case CTF2BotUpgradeManager::STATE_BUYINGSTUFF:
ExecuteBuy();
RemoveFinishedUpgrades(); // clean up
return;
case CTF2BotUpgradeManager::STATE_WAITFORNEXTWAVE:
return;
case CTF2BotUpgradeManager::STATE_DOREFUND:
ExecuteRefund();
OnUpgradesRefunded();
return;
default:
return;
}
}

void CTF2BotUpgradeManager::ExecuteBuy()
{
if (!m_me->IsInUpgradeZone())
{
m_me->DebugPrintToConsole(BOTDEBUG_ERRORS, 255, 0, 0, "Error: CTF2BotUpgradeManager::Update called outside an upgrade zone!\n");
return;
}

if (m_tobuylist.empty())
{
m_me->DebugPrintToConsole(BOTDEBUG_TASKS, 0, 150, 0, "%s CTF2BotUpgradeManager::Update buy list is empty, advancing priority!\n", m_me->GetDebugIdentifier());
AdvancePriority();
FetchUpgrades();
return;
}

auto upgradeinfo = m_tobuylist[randomgen->GetRandomInt<size_t>(0U, m_tobuylist.size() - 1U)];
int currency = m_me->GetCurrency();
auto data = GetOrCreateUpgradeData(upgradeinfo->upgrade);

if (data->IsMaxed())
{
RemoveFinishedUpgrades(); // clean up
return;
}

if (!upgradeinfo->upgrade->CanAfford(m_me->GetCurrency()))
{
return;
}

if (m_me->IsDebugging(BOTDEBUG_TASKS))
{
m_me->DebugPrintToConsole(BOTDEBUG_TASKS, 255, 87, 51, "%s bought upgrade ID %i <%s, %i>", m_me->GetDebugIdentifier(), upgradeinfo->GetUpgradeIndex(),
upgradeinfo->attribute.c_str(), upgradeinfo->quality);
}

BeginBuyingUpgrades();
BuySingleUpgrade(upgradeinfo->GetUpgradeIndex(), upgradeinfo->itemslot, 1);
EndBuyingUpgrades();
data->times_bought++; // for now we must assume that buying an upgrade will never fail
}

void CTF2BotUpgradeManager::ExecuteRefund()
{
BeginBuyingUpgrades();
RefundUpgrades();
EndBuyingUpgrades();
}

bool CTF2BotUpgradeManager::CanAffordAnyUpgrade() const
{
int currency = m_me->GetCurrency();

for (auto data : m_tobuylist)
{
if (data->upgrade->CanAfford(currency))
{
return true;
}
}

return false;
}

void CTF2BotUpgradeManager::BeginBuyingUpgrades()
{
KeyValues* kvcmd = new KeyValues("MvM_UpgradesBegin");
engine->ClientCommandKeyValues(m_me->GetEdict(), kvcmd);
// kvcmd->deleteThis();
kvcmd = nullptr;
}

void CTF2BotUpgradeManager::BuySingleUpgrade(const int upgradeID, const int itemslot, const int quantity)
{
KeyValues* kvcmd = new KeyValues("MvM_UpgradesBegin");
KeyValues* kUpg = kvcmd->FindKey("Upgrade", true);
kUpg->SetInt("itemslot", itemslot);
kUpg->SetInt("Upgrade", upgradeID);
kUpg->SetInt("count", quantity);
engine->ClientCommandKeyValues(m_me->GetEdict(), kvcmd);
// kvcmd->deleteThis();
kvcmd = nullptr;
m_numupgrades += quantity;
}

void CTF2BotUpgradeManager::RefundUpgrades()
{
KeyValues* kvcmd = new KeyValues("MVM_Respec");
engine->ClientCommandKeyValues(m_me->GetEdict(), kvcmd);
// kvcmd->deleteThis();
kvcmd = nullptr;
m_numupgrades = 0;
}

void CTF2BotUpgradeManager::EndBuyingUpgrades()
{
KeyValues* kvcmd = new KeyValues("MvM_UpgradesDone");
kvcmd->SetInt("num_upgrades", m_numupgrades);
engine->ClientCommandKeyValues(m_me->GetEdict(), kvcmd);
// kvcmd->deleteThis();
kvcmd = nullptr;
m_numupgrades = 0;
}

void CTF2BotUpgradeManager::FetchUpgrades()
{
CTeamFortress2Mod::GetTF2Mod()->GetMvMUpgradeManager().CollectUpgradesToBuy(m_tobuylist, m_nextpriority, m_me->GetMyClassType());
FilterUpgrades();
}

void CTF2BotUpgradeManager::FilterUpgrades()
{
// Collect weapon item definition indexes
std::vector<int> myweaponindexes;
m_me->ForEveryWeapon([&myweaponindexes](const CBotWeapon& weapon) {
myweaponindexes.push_back(weapon.GetWeaponEconIndex());
});

auto start = std::remove_if(m_tobuylist.begin(), m_tobuylist.end(), [&myweaponindexes](const TF2BotUpgradeInfo_t* upgradeinfo) {
if (upgradeinfo->AnyWeapon())
{
return false; // no restrictions on weapons
}

for (auto& index : myweaponindexes)
{
if (upgradeinfo->allowedweapons.find(index) != upgradeinfo->allowedweapons.end())
{
return false; // bot has a weapon that can use this upgrade
}
}

return true; // upgrade has weapon restriction and the bot doesn't have the needed weapon, remove this upgrade
});

m_tobuylist.erase(start, m_tobuylist.end()); // remove upgrades
}

void CTF2BotUpgradeManager::RemoveFinishedUpgrades()
{
auto start = std::remove_if(m_tobuylist.begin(), m_tobuylist.end(), [this](const TF2BotUpgradeInfo_t* upgradeinfo) {
for (auto& bought : m_boughtlist)
{
if (bought.IsMaxed() && bought.upgrade->index == upgradeinfo->upgrade->index)
{
return true; // upgrade maxed, remove it
}
}

return false;
});

m_tobuylist.erase(start, m_tobuylist.end()); // remove upgrades
}

TF2BotUpgrade_t* CTF2BotUpgradeManager::GetOrCreateUpgradeData(const MvMUpgrade_t* upgrade)
{
for (auto& data : m_boughtlist)
{
if (data.upgrade->index == upgrade->index)
{
return &data;
}
}

auto& newdata = m_boughtlist.emplace_back(upgrade);
return &newdata;
}

Loading

0 comments on commit b6e5165

Please sign in to comment.