Skip to content

Commit

Permalink
Begin MvM Behavior + Fix Upgrades
Browse files Browse the repository at this point in the history
  • Loading branch information
caxanga334 committed Apr 4, 2024
1 parent 6462f0e commit ab2a35e
Show file tree
Hide file tree
Showing 20 changed files with 427 additions and 49 deletions.
1 change: 1 addition & 0 deletions configs/tf/mvm_upgrades.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ TFMvMUpgrades
* "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
* "maxlevel" "0" -- limit that max upgrade level, 0 (default) to use the upgrade max cap
* }
* }
*
Expand Down
2 changes: 2 additions & 0 deletions extension/AMBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ sourceFiles = [
'bot/tf2/tasks/medic/tf2bot_medic_main_task.cpp',
'bot/tf2/tasks/medic/tf2bot_medic_retreat_task.cpp',
'bot/tf2/tasks/scenario/tf2bot_map_ctf.cpp',
'bot/tf2/tasks/scenario/mvm/tf2bot_mvm_idle.cpp',
'bot/tf2/tasks/scenario/mvm/tf2bot_mvm_upgrade.cpp',
'mods/tf2/teamfortress2mod.cpp',
'mods/tf2/tf2lib.cpp',
'mods/tf2/tf2mod_gameevents.cpp',
Expand Down
50 changes: 50 additions & 0 deletions extension/bot/tf2/tasks/scenario/mvm/tf2bot_mvm_idle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <extension.h>
#include <util/entprops.h>
#include <mods/tf2/teamfortress2mod.h>
#include <mods/tf2/nav/tfnavmesh.h>
#include <mods/tf2/nav/tfnavarea.h>
#include <bot/tf2/tf2bot.h>
#include "tf2bot_mvm_upgrade.h"
#include "tf2bot_mvm_idle.h"

TaskResult<CTF2Bot> CTF2BotMvMIdleTask::OnTaskStart(CTF2Bot* bot, AITask<CTF2Bot>* pastTask)
{
return Continue();
}

TaskResult<CTF2Bot> CTF2BotMvMIdleTask::OnTaskUpdate(CTF2Bot* bot)
{
if (entprops->GameRules_GetRoundState() == RoundState_RoundRunning)
{
// Wave has started!
// TO-DO: Switch to combat task
return Continue();
}

// Wave has not started yet
if (entprops->GameRules_GetRoundState() == RoundState_BetweenRounds)
{
// Should the bot buy upgrades?
if (bot->GetUpgradeManager().ShouldGoToAnUpgradeStation())
{
// Buy upgrades
return PauseFor(new CTF2BotMvMUpgradeTask, "Going to use an upgrade station!");
}
}

// TO-DO: Move to defensive positions near the robot spawn
// TO-DO: Engineer
// TO-DO: Add ready up logic

return Continue();
}

TaskEventResponseResult<CTF2Bot> CTF2BotMvMIdleTask::OnMoveToFailure(CTF2Bot* bot, CPath* path, IEventListener::MovementFailureType reason)
{
return TryContinue();
}

TaskEventResponseResult<CTF2Bot> CTF2BotMvMIdleTask::OnMoveToSuccess(CTF2Bot* bot, CPath* path)
{
return TryContinue();
}
25 changes: 25 additions & 0 deletions extension/bot/tf2/tasks/scenario/mvm/tf2bot_mvm_idle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef NAVBOT_TF2BOT_TASK_MVM_IDLE_H_
#define NAVBOT_TF2BOT_TASK_MVM_IDLE_H_
#pragma once

#include <bot/interfaces/path/meshnavigator.h>

class CTF2Bot;

class CTF2BotMvMIdleTask : public AITask<CTF2Bot>
{
public:
TaskResult<CTF2Bot> OnTaskStart(CTF2Bot* bot, AITask<CTF2Bot>* pastTask) override;
TaskResult<CTF2Bot> OnTaskUpdate(CTF2Bot* bot) override;

TaskEventResponseResult<CTF2Bot> OnMoveToFailure(CTF2Bot* bot, CPath* path, IEventListener::MovementFailureType reason) override;
TaskEventResponseResult<CTF2Bot> OnMoveToSuccess(CTF2Bot* bot, CPath* path) override;

const char* GetName() const override { return "MvMIdle"; }
private:
CMeshNavigator m_nav;
Vector m_goal;
CountdownTimer m_repathtimer;
};

#endif // !NAVBOT_TF2BOT_TASK_MVM_IDLE_H_
157 changes: 157 additions & 0 deletions extension/bot/tf2/tasks/scenario/mvm/tf2bot_mvm_upgrade.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include <limits>

#include <extension.h>
#include <bot/tf2/tf2bot.h>
#include <util/helpers.h>
#include <entities/tf2/tf_entities.h>
#include "tf2bot_mvm_upgrade.h"

#undef min // undef valve mathlib stuff that causes issues with C++ STD lib
#undef max
#undef clamp

TaskResult<CTF2Bot> CTF2BotMvMUpgradeTask::OnTaskStart(CTF2Bot* bot, AITask<CTF2Bot>* pastTask)
{
if (!SelectNearestUpgradeStation(bot))
{
bot->GetUpgradeManager().OnDoneUpgrading(); // Mark as done
return Done("No upgrade stations available!");
}

SetGoalPosition();

CTF2BotPathCost cost(bot);
if (!m_nav.ComputePathToPosition(bot, m_goal, cost))
{
return Done("Failed to find a path to the upgrade station");
}

m_repathtimer.Start(2.0f);
return Continue();
}

TaskResult<CTF2Bot> CTF2BotMvMUpgradeTask::OnTaskUpdate(CTF2Bot* bot)
{
auto& manager = bot->GetUpgradeManager();

if (manager.IsDoneForCurrentWave())
{
return Done("Done upgrading!");
}

if (!bot->IsInUpgradeZone())
{
if (m_repathtimer.IsElapsed())
{
m_repathtimer.Start(2.0f);

CTF2BotPathCost cost(bot);
if (!m_nav.ComputePathToPosition(bot, m_goal, cost))
{
return Done("Failed to find a path to the upgrade station");
}
}

// Path to the upgrade station
m_nav.Update(bot);
}
else
{
manager.Update(); // bot is inside an upgrade zone, buy upgrades
}

return Continue();
}

TaskEventResponseResult<CTF2Bot> CTF2BotMvMUpgradeTask::OnMoveToFailure(CTF2Bot* bot, CPath* path, IEventListener::MovementFailureType reason)
{
m_repathtimer.Start(2.0f);

CTF2BotPathCost cost(bot);
if (!m_nav.ComputePathToPosition(bot, m_goal, cost))
{
return TryDone(PRIORITY_HIGH, "Failed to find a path to the upgrade station");
}

return TryContinue(PRIORITY_LOW);
}

TaskEventResponseResult<CTF2Bot> CTF2BotMvMUpgradeTask::OnMoveToSuccess(CTF2Bot* bot, CPath* path)
{
if (!bot->IsInUpgradeZone())
{
return TryDone(PRIORITY_HIGH, "Reached goal but outside upgrade zone.");
}

return TryContinue(PRIORITY_LOW);
}

bool CTF2BotMvMUpgradeTask::SelectNearestUpgradeStation(CTF2Bot* me)
{
std::vector<edict_t*> upgradestations;
upgradestations.reserve(8);

UtilHelpers::ForEachEntityOfClassname("func_upgradestation", [&upgradestations](int index, edict_t* edict, CBaseEntity* entity) {
if (edict == nullptr)
{
return true; // return early, but keep looping. Upgrade stations should never have a NULL edict
}

tfentities::HTFBaseEntity basentity(edict);

if (basentity.IsDisabled())
{
return true; // return early, keep loop
}

// upgrade station is not disabled, add to the list
upgradestations.push_back(edict);

return true; // continue looping
});

if (upgradestations.empty())
{
return false;
}

Vector origin = me->GetAbsOrigin();
float best = std::numeric_limits<float>::max();
edict_t* target = nullptr;

for (auto upgradeentity : upgradestations)
{
Vector dest = UtilHelpers::getWorldSpaceCenter(upgradeentity);
float distance = (dest - origin).Length();

if (distance < best)
{
target = upgradeentity;
best = distance;
}

}

if (target == nullptr)
{
return false;
}

UtilHelpers::SetHandleEntity(m_upgradestation, target);
return true;
}

void CTF2BotMvMUpgradeTask::SetGoalPosition()
{
Vector center = UtilHelpers::getWorldSpaceCenter(UtilHelpers::GetEdictFromCBaseHandle(m_upgradestation));
auto area = TheNavMesh->GetNearestNavArea(center, 1024.0f, false, false);

if (area == nullptr)
{
m_goal = center;
return;
}

area->GetClosestPointOnArea(center, &m_goal);
m_goal.z = area->GetCenter().z;
}
30 changes: 30 additions & 0 deletions extension/bot/tf2/tasks/scenario/mvm/tf2bot_mvm_upgrade.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef NAVBOT_TF2BOT_TASK_MVM_UPGRADE_H_
#define NAVBOT_TF2BOT_TASK_MVM_UPGRADE_H_
#pragma once

#include <bot/interfaces/path/meshnavigator.h>
#include <basehandle.h>

class CTF2Bot;

class CTF2BotMvMUpgradeTask : public AITask<CTF2Bot>
{
public:
TaskResult<CTF2Bot> OnTaskStart(CTF2Bot* bot, AITask<CTF2Bot>* pastTask) override;
TaskResult<CTF2Bot> OnTaskUpdate(CTF2Bot* bot) override;

TaskEventResponseResult<CTF2Bot> OnMoveToFailure(CTF2Bot* bot, CPath* path, IEventListener::MovementFailureType reason) override;
TaskEventResponseResult<CTF2Bot> OnMoveToSuccess(CTF2Bot* bot, CPath* path) override;

const char* GetName() const override { return "MvMUpgrade"; }
private:
CMeshNavigator m_nav;
Vector m_goal;
CountdownTimer m_repathtimer;
CBaseHandle m_upgradestation;

bool SelectNearestUpgradeStation(CTF2Bot* me);
void SetGoalPosition();
};

#endif // !NAVBOT_TF2BOT_TASK_MVM_UPGRADE_H_
2 changes: 1 addition & 1 deletion extension/bot/tf2/tasks/tf2bot_roam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,4 @@ void CTF2BotRoamTask::FindRandomGoalPosition(CTF2Bot* me)
m_goal = randomArea->GetCenter();
me->DebugPrintToConsole(BOTDEBUG_TASKS, 0, 130, 0, "%s Random Nav Area Goal #%i <%3.2f, %3.2f, %3.2f>", me->GetDebugIdentifier(), randomArea->GetID(),
m_goal.x, m_goal.y, m_goal.z);
}
}
5 changes: 5 additions & 0 deletions extension/bot/tf2/tasks/tf2bot_scenario.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "bot/tf2/tf2bot.h"
#include <bot/tf2/tasks/scenario/tf2bot_map_ctf.h>
#include <bot/tf2/tasks/medic/tf2bot_medic_main_task.h>
#include "scenario/mvm/tf2bot_mvm_idle.h"
#include "tf2bot_scenario.h"

TaskResult<CTF2Bot> CTF2BotScenarioTask::OnTaskUpdate(CTF2Bot* bot)
Expand All @@ -24,6 +25,10 @@ TaskResult<CTF2Bot> CTF2BotScenarioTask::OnTaskUpdate(CTF2Bot* bot)
auto newtask = new CTF2BotCTFMonitorTask;
return SwitchTo(newtask, "Starting CTF Behavior");
}
case TeamFortress2::GameModeType::GM_MVM:
{
return SwitchTo(new CTF2BotMvMIdleTask, "Starting MvM Behavior");
}
default:
break;
}
Expand Down
10 changes: 10 additions & 0 deletions extension/bot/tf2/tf2bot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,16 @@ bool CTF2Bot::IsInUpgradeZone() const
return val;
}

void CTF2Bot::ToggleTournamentReadyStatus(bool isready) const
{
char command[64];
ke::SafeSprintf(command, sizeof(command), "tournament_player_readystate %i", isready ? 1 : 0);

// Use 'FakeClientCommand'.
// Alternative method is manually setting the array on gamerules
serverpluginhelpers->ClientCommand(GetEdict(), command);
}

const CTF2Bot::KnownSpy* CTF2Bot::GetKnownSpy(edict_t* spy) const
{
int index = gamehelpers->IndexOfEdict(spy);
Expand Down
5 changes: 5 additions & 0 deletions extension/bot/tf2/tf2bot.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ class CTF2Bot : public CBaseBot
void FindMyBuildings();
int GetCurrency() const;
bool IsInUpgradeZone() const;
/**
* @brief Toggles the ready status in Tournament modes. Also used in Mann vs Machine.
* @param isready Is the bot ready?
*/
void ToggleTournamentReadyStatus(bool isready = true) const;

/**
* @brief Gets a known spy information about a spy or NULL if none
Expand Down
Loading

0 comments on commit ab2a35e

Please sign in to comment.