From: Samual Lenks Date: Mon, 10 Sep 2012 19:15:33 +0000 (-0400) Subject: Clean up havocbot roles a bit for mutators (link it into mutator system) X-Git-Tag: xonotic-v0.7.0~240^2~6 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=b9b3149d9df403703498fcd8e88423b72d8c25b7;p=xonotic%2Fxonotic-data.pk3dir.git Clean up havocbot roles a bit for mutators (link it into mutator system) --- diff --git a/qcsrc/server/attic/bot/havocbot/role_ctf.qc b/qcsrc/server/attic/bot/havocbot/role_ctf.qc new file mode 100644 index 000000000..0946f4342 --- /dev/null +++ b/qcsrc/server/attic/bot/havocbot/role_ctf.qc @@ -0,0 +1,684 @@ +#define HAVOCBOT_CTF_ROLE_NONE 0 +#define HAVOCBOT_CTF_ROLE_DEFENSE 2 +#define HAVOCBOT_CTF_ROLE_MIDDLE 4 +#define HAVOCBOT_CTF_ROLE_OFFENSE 8 +#define HAVOCBOT_CTF_ROLE_CARRIER 16 +#define HAVOCBOT_CTF_ROLE_RETRIEVER 32 +#define HAVOCBOT_CTF_ROLE_ESCORT 64 + +.void() havocbot_role; +.void() havocbot_previous_role; + +void() havocbot_role_ctf_middle; +void() havocbot_role_ctf_defense; +void() havocbot_role_ctf_offense; +void() havocbot_role_ctf_carrier; +void() havocbot_role_ctf_retriever; +void() havocbot_role_ctf_escort; + +void(entity bot) havocbot_ctf_reset_role; +void(float ratingscale, vector org, float sradius) havocbot_goalrating_items; +void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; + +.float havocbot_cantfindflag; +.float havocbot_role_timeout; +.entity ctf_worldflagnext; +.entity bot_basewaypoint; + +entity ctf_worldflaglist; +vector havocbot_ctf_middlepoint; +float havocbot_ctf_middlepoint_radius; + +entity havocbot_ctf_find_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team == f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +entity havocbot_ctf_find_enemy_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team != f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +float havocbot_ctf_teamcount(entity bot, vector org, float radius) +{ + if not(teamplay) + return 0; + + float c = 0; + entity head; + + FOR_EACH_PLAYER(head) + { + if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot) + continue; + + if(vlen(head.origin - org) < radius) + ++c; + } + + return c; +} + +void havocbot_goalrating_ctf_ourflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourbase(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemyflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team != head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemybase(float ratingscale) +{ + if not(bot_waypoints_for_items) + { + havocbot_goalrating_ctf_enemyflag(ratingscale); + return; + } + + entity head; + + head = havocbot_ctf_find_enemy_flag(self); + + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourstolenflag(float ratingscale) +{ + entity mf; + + mf = havocbot_ctf_find_flag(self); + + if(mf.ctf_status == FLAG_BASE) + return; + + if(mf.tag_entity) + navigation_routerating(mf.tag_entity, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + // flag is out in the field + if(head.ctf_status != FLAG_BASE) + if(head.tag_entity==world) // dropped + { + if(radius) + { + if(vlen(org-head.origin) 0) + navigation_routerating(head, t * ratingscale, 500); + } + head = head.chain; + } +} + +void havocbot_role_ctf_setrole(entity bot, float role) +{ + dprint(strcat(bot.netname," switched to ")); + switch(role) + { + case HAVOCBOT_CTF_ROLE_CARRIER: + dprint("carrier"); + bot.havocbot_role = havocbot_role_ctf_carrier; + bot.havocbot_role_timeout = 0; + bot.havocbot_cantfindflag = time + 10; + bot.bot_strategytime = 0; + break; + case HAVOCBOT_CTF_ROLE_DEFENSE: + dprint("defense"); + bot.havocbot_role = havocbot_role_ctf_defense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_MIDDLE: + dprint("middle"); + bot.havocbot_role = havocbot_role_ctf_middle; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_OFFENSE: + dprint("offense"); + bot.havocbot_role = havocbot_role_ctf_offense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_RETRIEVER: + dprint("retriever"); + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_retriever; + bot.havocbot_role_timeout = time + 10; + bot.bot_strategytime = 0; + break; + case HAVOCBOT_CTF_ROLE_ESCORT: + dprint("escort"); + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_escort; + bot.havocbot_role_timeout = time + 30; + bot.bot_strategytime = 0; + break; + } + dprint("\n"); +} + +void havocbot_role_ctf_carrier() +{ + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried == world) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourbase(50000); + + if(self.health<100) + havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000); + + navigation_goalrating_end(); + + if (self.navigation_hasgoals) + self.havocbot_cantfindflag = time + 10; + else if (time > self.havocbot_cantfindflag) + { + // Can't navigate to my own base, suicide! + // TODO: drop it and wander around + Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0'); + return; + } + } +} + +void havocbot_role_ctf_escort() +{ + entity mf, ef; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If enemy flag is back on the base switch to previous role + ef = havocbot_ctf_find_enemy_flag(self); + if(ef.ctf_status==FLAG_BASE) + { + self.havocbot_role = self.havocbot_previous_role; + self.havocbot_role_timeout = 0; + return; + } + + // If the flag carrier reached the base switch to defense + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status!=FLAG_BASE) + if(vlen(ef.origin - mf.dropped_origin) < 300) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE); + return; + } + + // Set the role timeout if necessary + if (!self.havocbot_role_timeout) + { + self.havocbot_role_timeout = time + random() * 30 + 60; + } + + // If nothing happened just switch to previous role + if (time > self.havocbot_role_timeout) + { + self.havocbot_role = self.havocbot_previous_role; + self.havocbot_role_timeout = 0; + return; + } + + // Chase the flag carrier + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_enemyflag(30000); + havocbot_goalrating_ctf_ourstolenflag(40000); + havocbot_goalrating_items(10000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_offense() +{ + entity mf, ef; + vector pos; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // Check flags + mf = havocbot_ctf_find_flag(self); + ef = havocbot_ctf_find_enemy_flag(self); + + // Own flag stolen + if(mf.ctf_status!=FLAG_BASE) + { + if(mf.tag_entity) + pos = mf.tag_entity.origin; + else + pos = mf.origin; + + // Try to get it if closer than the enemy base + if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos)) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + } + + // Escort flag carrier + if(ef.ctf_status!=FLAG_BASE) + { + if(ef.tag_entity) + pos = ef.tag_entity.origin; + else + pos = ef.origin; + + if(vlen(pos-mf.dropped_origin)>700) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT); + return; + } + } + + // About to fail, switch to middlefield + if(self.health<50) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE); + return; + } + + // Set the role timeout if necessary + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 120; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourstolenflag(50000); + havocbot_goalrating_ctf_enemybase(20000); + havocbot_goalrating_items(5000, self.origin, 1000); + havocbot_goalrating_items(1000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +// Retriever (temporary role): +void havocbot_role_ctf_retriever() +{ + entity mf; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If flag is back on the base switch to previous role + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status==FLAG_BASE) + { + havocbot_ctf_reset_role(self); + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 20; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + float radius; + radius = 10000; + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourstolenflag(50000); + havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius); + havocbot_goalrating_ctf_enemybase(30000); + havocbot_goalrating_items(500, self.origin, radius); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_middle() +{ + entity mf; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 10; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + vector org; + + org = havocbot_ctf_middlepoint; + org_z = self.origin_z; + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourstolenflag(50000); + havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000); + havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5); + havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5); + havocbot_goalrating_items(2500, self.origin, 10000); + havocbot_goalrating_ctf_enemybase(2500); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_defense() +{ + entity mf; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If own flag was captured + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 30; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + if (self.bot_strategytime < time) + { + float radius; + vector org; + + org = mf.dropped_origin; + radius = havocbot_ctf_middlepoint_radius; + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + + // if enemies are closer to our base, go there + entity head, closestplayer = world; + float distance, bestdistance = 10000; + FOR_EACH_PLAYER(head) + { + if(head.deadflag!=DEAD_NO) + continue; + + distance = vlen(org - head.origin); + if(distance1000) + if(checkpvs(self.origin,closestplayer)||random()<0.5) + havocbot_goalrating_ctf_ourbase(30000); + + havocbot_goalrating_ctf_ourstolenflag(20000); + havocbot_goalrating_ctf_droppedflags(20000, org, radius); + havocbot_goalrating_enemyplayers(15000, org, radius); + havocbot_goalrating_items(10000, org, radius); + havocbot_goalrating_items(5000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +void havocbot_calculate_middlepoint() +{ + entity f; + vector s = '0 0 0'; + vector fo = '0 0 0'; + float n = 0; + + f = ctf_worldflaglist; + while (f) + { + fo = f.origin; + s = s + fo; + f = f.ctf_worldflagnext; + } + if(!n) + return; + havocbot_ctf_middlepoint = s * (1.0 / n); + havocbot_ctf_middlepoint_radius = vlen(fo - havocbot_ctf_middlepoint); +} + +void havocbot_ctf_reset_role(entity bot) +{ + float cdefense, cmiddle, coffense; + entity mf, ef, head; + float c; + + if(bot.deadflag != DEAD_NO) + return; + + if(vlen(havocbot_ctf_middlepoint)==0) + havocbot_calculate_middlepoint(); + + // Check ctf flags + if (bot.flagcarried) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + mf = havocbot_ctf_find_flag(bot); + ef = havocbot_ctf_find_enemy_flag(bot); + + // Retrieve stolen flag + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + // If enemy flag is taken go to the middle to intercept pursuers + if(ef.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); + return; + } + + // if there is only me on the team switch to offense + c = 0; + FOR_EACH_PLAYER(head) + if(head.team==bot.team) + ++c; + + if(c==1) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); + return; + } + + // Evaluate best position to take + // Count mates on middle position + cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5); + + // Count mates on defense position + cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5); + + // Count mates on offense position + coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius); + + if(cdefense<=coffense) + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE); + else if(coffense<=cmiddle) + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); + else + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); +} + +void havocbot_chooserole_ctf() +{ + havocbot_ctf_reset_role(self); +} diff --git a/qcsrc/server/attic/bot/havocbot/role_keepaway.qc b/qcsrc/server/attic/bot/havocbot/role_keepaway.qc new file mode 100644 index 000000000..ede6208a1 --- /dev/null +++ b/qcsrc/server/attic/bot/havocbot/role_keepaway.qc @@ -0,0 +1,82 @@ +void() havocbot_role_ka_carrier; +void() havocbot_role_ka_collector; +void() havocbot_chooserole_ka; + +entity ka_ball; + +// Keepaway +// If you don't have the ball, get it; if you do, kill people. + +void havocbot_goalrating_ball(float ratingscale, vector org) +{ + float t; + entity ball_owner; + ball_owner = ka_ball.owner; + + if (ball_owner == self) + return; + + // If ball is carried by player then hunt them down. + if (ball_owner) + { + t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue); + navigation_routerating(ball_owner, t * ratingscale, 2000); + } + + // Ball has been dropped so collect. + navigation_routerating(ka_ball, ratingscale, 2000); +} + +void havocbot_role_ka_carrier() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } + + if (!self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_collector; + self.bot_strategytime = 0; + } +} + +void havocbot_role_ka_collector() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(1000, self.origin, 10000); + havocbot_goalrating_ball(20000, self.origin); + navigation_goalrating_end(); + } + + if (self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_carrier; + self.bot_strategytime = 0; + } +} + +void havocbot_chooserole_ka() +{ + if (self.ballcarried) + self.havocbot_role = havocbot_role_ka_carrier; + else + self.havocbot_role = havocbot_role_ka_collector; +} diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc index e03cbac66..a8366e5bb 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@ -1,9 +1,7 @@ #include "havocbot.qh" -#include "role_ctf.qc" #include "role_onslaught.qc" #include "role_keyhunt.qc" #include "role_freezetag.qc" -#include "role_keepaway.qc" #include "role_assault.qc" #include "roles.qc" diff --git a/qcsrc/server/bot/havocbot/havocbot.qh b/qcsrc/server/bot/havocbot/havocbot.qh index be2896291..de9469182 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qh +++ b/qcsrc/server/bot/havocbot/havocbot.qh @@ -19,6 +19,7 @@ .float havocbot_personal_waypoint_failcounter; .float havocbot_chooseenemy_finished; .float havocbot_stickenemy; +.float havocbot_role_timeout; .entity ignoregoal; .entity bot_lastseengoal; @@ -47,6 +48,10 @@ float havocbot_moveto_refresh_route(); vector havocbot_dodge(); .void() havocbot_role; +.void() havocbot_previous_role; + +void(float ratingscale, vector org, float sradius) havocbot_goalrating_items; +void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; /* * Imports diff --git a/qcsrc/server/bot/havocbot/role_ctf.qc b/qcsrc/server/bot/havocbot/role_ctf.qc deleted file mode 100644 index 0946f4342..000000000 --- a/qcsrc/server/bot/havocbot/role_ctf.qc +++ /dev/null @@ -1,684 +0,0 @@ -#define HAVOCBOT_CTF_ROLE_NONE 0 -#define HAVOCBOT_CTF_ROLE_DEFENSE 2 -#define HAVOCBOT_CTF_ROLE_MIDDLE 4 -#define HAVOCBOT_CTF_ROLE_OFFENSE 8 -#define HAVOCBOT_CTF_ROLE_CARRIER 16 -#define HAVOCBOT_CTF_ROLE_RETRIEVER 32 -#define HAVOCBOT_CTF_ROLE_ESCORT 64 - -.void() havocbot_role; -.void() havocbot_previous_role; - -void() havocbot_role_ctf_middle; -void() havocbot_role_ctf_defense; -void() havocbot_role_ctf_offense; -void() havocbot_role_ctf_carrier; -void() havocbot_role_ctf_retriever; -void() havocbot_role_ctf_escort; - -void(entity bot) havocbot_ctf_reset_role; -void(float ratingscale, vector org, float sradius) havocbot_goalrating_items; -void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; - -.float havocbot_cantfindflag; -.float havocbot_role_timeout; -.entity ctf_worldflagnext; -.entity bot_basewaypoint; - -entity ctf_worldflaglist; -vector havocbot_ctf_middlepoint; -float havocbot_ctf_middlepoint_radius; - -entity havocbot_ctf_find_flag(entity bot) -{ - entity f; - f = ctf_worldflaglist; - while (f) - { - if (bot.team == f.team) - return f; - f = f.ctf_worldflagnext; - } - return world; -} - -entity havocbot_ctf_find_enemy_flag(entity bot) -{ - entity f; - f = ctf_worldflaglist; - while (f) - { - if (bot.team != f.team) - return f; - f = f.ctf_worldflagnext; - } - return world; -} - -float havocbot_ctf_teamcount(entity bot, vector org, float radius) -{ - if not(teamplay) - return 0; - - float c = 0; - entity head; - - FOR_EACH_PLAYER(head) - { - if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot) - continue; - - if(vlen(head.origin - org) < radius) - ++c; - } - - return c; -} - -void havocbot_goalrating_ctf_ourflag(float ratingscale) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - if (self.team == head.team) - break; - head = head.ctf_worldflagnext; - } - if (head) - navigation_routerating(head, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_ourbase(float ratingscale) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - if (self.team == head.team) - break; - head = head.ctf_worldflagnext; - } - if not(head) - return; - - navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_enemyflag(float ratingscale) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - if (self.team != head.team) - break; - head = head.ctf_worldflagnext; - } - if (head) - navigation_routerating(head, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_enemybase(float ratingscale) -{ - if not(bot_waypoints_for_items) - { - havocbot_goalrating_ctf_enemyflag(ratingscale); - return; - } - - entity head; - - head = havocbot_ctf_find_enemy_flag(self); - - if not(head) - return; - - navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_ourstolenflag(float ratingscale) -{ - entity mf; - - mf = havocbot_ctf_find_flag(self); - - if(mf.ctf_status == FLAG_BASE) - return; - - if(mf.tag_entity) - navigation_routerating(mf.tag_entity, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - // flag is out in the field - if(head.ctf_status != FLAG_BASE) - if(head.tag_entity==world) // dropped - { - if(radius) - { - if(vlen(org-head.origin) 0) - navigation_routerating(head, t * ratingscale, 500); - } - head = head.chain; - } -} - -void havocbot_role_ctf_setrole(entity bot, float role) -{ - dprint(strcat(bot.netname," switched to ")); - switch(role) - { - case HAVOCBOT_CTF_ROLE_CARRIER: - dprint("carrier"); - bot.havocbot_role = havocbot_role_ctf_carrier; - bot.havocbot_role_timeout = 0; - bot.havocbot_cantfindflag = time + 10; - bot.bot_strategytime = 0; - break; - case HAVOCBOT_CTF_ROLE_DEFENSE: - dprint("defense"); - bot.havocbot_role = havocbot_role_ctf_defense; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_CTF_ROLE_MIDDLE: - dprint("middle"); - bot.havocbot_role = havocbot_role_ctf_middle; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_CTF_ROLE_OFFENSE: - dprint("offense"); - bot.havocbot_role = havocbot_role_ctf_offense; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_CTF_ROLE_RETRIEVER: - dprint("retriever"); - bot.havocbot_previous_role = bot.havocbot_role; - bot.havocbot_role = havocbot_role_ctf_retriever; - bot.havocbot_role_timeout = time + 10; - bot.bot_strategytime = 0; - break; - case HAVOCBOT_CTF_ROLE_ESCORT: - dprint("escort"); - bot.havocbot_previous_role = bot.havocbot_role; - bot.havocbot_role = havocbot_role_ctf_escort; - bot.havocbot_role_timeout = time + 30; - bot.bot_strategytime = 0; - break; - } - dprint("\n"); -} - -void havocbot_role_ctf_carrier() -{ - if(self.deadflag != DEAD_NO) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.flagcarried == world) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.bot_strategytime < time) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - - navigation_goalrating_start(); - havocbot_goalrating_ctf_ourbase(50000); - - if(self.health<100) - havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000); - - navigation_goalrating_end(); - - if (self.navigation_hasgoals) - self.havocbot_cantfindflag = time + 10; - else if (time > self.havocbot_cantfindflag) - { - // Can't navigate to my own base, suicide! - // TODO: drop it and wander around - Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0'); - return; - } - } -} - -void havocbot_role_ctf_escort() -{ - entity mf, ef; - - if(self.deadflag != DEAD_NO) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.flagcarried) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // If enemy flag is back on the base switch to previous role - ef = havocbot_ctf_find_enemy_flag(self); - if(ef.ctf_status==FLAG_BASE) - { - self.havocbot_role = self.havocbot_previous_role; - self.havocbot_role_timeout = 0; - return; - } - - // If the flag carrier reached the base switch to defense - mf = havocbot_ctf_find_flag(self); - if(mf.ctf_status!=FLAG_BASE) - if(vlen(ef.origin - mf.dropped_origin) < 300) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE); - return; - } - - // Set the role timeout if necessary - if (!self.havocbot_role_timeout) - { - self.havocbot_role_timeout = time + random() * 30 + 60; - } - - // If nothing happened just switch to previous role - if (time > self.havocbot_role_timeout) - { - self.havocbot_role = self.havocbot_previous_role; - self.havocbot_role_timeout = 0; - return; - } - - // Chase the flag carrier - if (self.bot_strategytime < time) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - navigation_goalrating_start(); - havocbot_goalrating_ctf_enemyflag(30000); - havocbot_goalrating_ctf_ourstolenflag(40000); - havocbot_goalrating_items(10000, self.origin, 10000); - navigation_goalrating_end(); - } -} - -void havocbot_role_ctf_offense() -{ - entity mf, ef; - vector pos; - - if(self.deadflag != DEAD_NO) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.flagcarried) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // Check flags - mf = havocbot_ctf_find_flag(self); - ef = havocbot_ctf_find_enemy_flag(self); - - // Own flag stolen - if(mf.ctf_status!=FLAG_BASE) - { - if(mf.tag_entity) - pos = mf.tag_entity.origin; - else - pos = mf.origin; - - // Try to get it if closer than the enemy base - if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos)) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - } - - // Escort flag carrier - if(ef.ctf_status!=FLAG_BASE) - { - if(ef.tag_entity) - pos = ef.tag_entity.origin; - else - pos = ef.origin; - - if(vlen(pos-mf.dropped_origin)>700) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT); - return; - } - } - - // About to fail, switch to middlefield - if(self.health<50) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE); - return; - } - - // Set the role timeout if necessary - if (!self.havocbot_role_timeout) - self.havocbot_role_timeout = time + 120; - - if (time > self.havocbot_role_timeout) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.bot_strategytime < time) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - navigation_goalrating_start(); - havocbot_goalrating_ctf_ourstolenflag(50000); - havocbot_goalrating_ctf_enemybase(20000); - havocbot_goalrating_items(5000, self.origin, 1000); - havocbot_goalrating_items(1000, self.origin, 10000); - navigation_goalrating_end(); - } -} - -// Retriever (temporary role): -void havocbot_role_ctf_retriever() -{ - entity mf; - - if(self.deadflag != DEAD_NO) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.flagcarried) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // If flag is back on the base switch to previous role - mf = havocbot_ctf_find_flag(self); - if(mf.ctf_status==FLAG_BASE) - { - havocbot_ctf_reset_role(self); - return; - } - - if (!self.havocbot_role_timeout) - self.havocbot_role_timeout = time + 20; - - if (time > self.havocbot_role_timeout) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.bot_strategytime < time) - { - float radius; - radius = 10000; - - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - navigation_goalrating_start(); - havocbot_goalrating_ctf_ourstolenflag(50000); - havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius); - havocbot_goalrating_ctf_enemybase(30000); - havocbot_goalrating_items(500, self.origin, radius); - navigation_goalrating_end(); - } -} - -void havocbot_role_ctf_middle() -{ - entity mf; - - if(self.deadflag != DEAD_NO) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.flagcarried) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - mf = havocbot_ctf_find_flag(self); - if(mf.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - - if (!self.havocbot_role_timeout) - self.havocbot_role_timeout = time + 10; - - if (time > self.havocbot_role_timeout) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.bot_strategytime < time) - { - vector org; - - org = havocbot_ctf_middlepoint; - org_z = self.origin_z; - - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - navigation_goalrating_start(); - havocbot_goalrating_ctf_ourstolenflag(50000); - havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000); - havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5); - havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5); - havocbot_goalrating_items(2500, self.origin, 10000); - havocbot_goalrating_ctf_enemybase(2500); - navigation_goalrating_end(); - } -} - -void havocbot_role_ctf_defense() -{ - entity mf; - - if(self.deadflag != DEAD_NO) - { - havocbot_ctf_reset_role(self); - return; - } - - if (self.flagcarried) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // If own flag was captured - mf = havocbot_ctf_find_flag(self); - if(mf.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - - if (!self.havocbot_role_timeout) - self.havocbot_role_timeout = time + 30; - - if (time > self.havocbot_role_timeout) - { - havocbot_ctf_reset_role(self); - return; - } - if (self.bot_strategytime < time) - { - float radius; - vector org; - - org = mf.dropped_origin; - radius = havocbot_ctf_middlepoint_radius; - - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - navigation_goalrating_start(); - - // if enemies are closer to our base, go there - entity head, closestplayer = world; - float distance, bestdistance = 10000; - FOR_EACH_PLAYER(head) - { - if(head.deadflag!=DEAD_NO) - continue; - - distance = vlen(org - head.origin); - if(distance1000) - if(checkpvs(self.origin,closestplayer)||random()<0.5) - havocbot_goalrating_ctf_ourbase(30000); - - havocbot_goalrating_ctf_ourstolenflag(20000); - havocbot_goalrating_ctf_droppedflags(20000, org, radius); - havocbot_goalrating_enemyplayers(15000, org, radius); - havocbot_goalrating_items(10000, org, radius); - havocbot_goalrating_items(5000, self.origin, 10000); - navigation_goalrating_end(); - } -} - -void havocbot_calculate_middlepoint() -{ - entity f; - vector s = '0 0 0'; - vector fo = '0 0 0'; - float n = 0; - - f = ctf_worldflaglist; - while (f) - { - fo = f.origin; - s = s + fo; - f = f.ctf_worldflagnext; - } - if(!n) - return; - havocbot_ctf_middlepoint = s * (1.0 / n); - havocbot_ctf_middlepoint_radius = vlen(fo - havocbot_ctf_middlepoint); -} - -void havocbot_ctf_reset_role(entity bot) -{ - float cdefense, cmiddle, coffense; - entity mf, ef, head; - float c; - - if(bot.deadflag != DEAD_NO) - return; - - if(vlen(havocbot_ctf_middlepoint)==0) - havocbot_calculate_middlepoint(); - - // Check ctf flags - if (bot.flagcarried) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - mf = havocbot_ctf_find_flag(bot); - ef = havocbot_ctf_find_enemy_flag(bot); - - // Retrieve stolen flag - if(mf.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - - // If enemy flag is taken go to the middle to intercept pursuers - if(ef.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); - return; - } - - // if there is only me on the team switch to offense - c = 0; - FOR_EACH_PLAYER(head) - if(head.team==bot.team) - ++c; - - if(c==1) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); - return; - } - - // Evaluate best position to take - // Count mates on middle position - cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5); - - // Count mates on defense position - cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5); - - // Count mates on offense position - coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius); - - if(cdefense<=coffense) - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE); - else if(coffense<=cmiddle) - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); - else - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); -} - -void havocbot_chooserole_ctf() -{ - havocbot_ctf_reset_role(self); -} diff --git a/qcsrc/server/bot/havocbot/role_keepaway.qc b/qcsrc/server/bot/havocbot/role_keepaway.qc deleted file mode 100644 index ede6208a1..000000000 --- a/qcsrc/server/bot/havocbot/role_keepaway.qc +++ /dev/null @@ -1,82 +0,0 @@ -void() havocbot_role_ka_carrier; -void() havocbot_role_ka_collector; -void() havocbot_chooserole_ka; - -entity ka_ball; - -// Keepaway -// If you don't have the ball, get it; if you do, kill people. - -void havocbot_goalrating_ball(float ratingscale, vector org) -{ - float t; - entity ball_owner; - ball_owner = ka_ball.owner; - - if (ball_owner == self) - return; - - // If ball is carried by player then hunt them down. - if (ball_owner) - { - t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue); - navigation_routerating(ball_owner, t * ratingscale, 2000); - } - - // Ball has been dropped so collect. - navigation_routerating(ka_ball, ratingscale, 2000); -} - -void havocbot_role_ka_carrier() -{ - if (self.deadflag != DEAD_NO) - return; - - if (time > self.bot_strategytime) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - - navigation_goalrating_start(); - havocbot_goalrating_items(10000, self.origin, 10000); - havocbot_goalrating_enemyplayers(20000, self.origin, 10000); - //havocbot_goalrating_waypoints(1, self.origin, 1000); - navigation_goalrating_end(); - } - - if (!self.ballcarried) - { - self.havocbot_role = havocbot_role_ka_collector; - self.bot_strategytime = 0; - } -} - -void havocbot_role_ka_collector() -{ - if (self.deadflag != DEAD_NO) - return; - - if (time > self.bot_strategytime) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - - navigation_goalrating_start(); - havocbot_goalrating_items(10000, self.origin, 10000); - havocbot_goalrating_enemyplayers(1000, self.origin, 10000); - havocbot_goalrating_ball(20000, self.origin); - navigation_goalrating_end(); - } - - if (self.ballcarried) - { - self.havocbot_role = havocbot_role_ka_carrier; - self.bot_strategytime = 0; - } -} - -void havocbot_chooserole_ka() -{ - if (self.ballcarried) - self.havocbot_role = havocbot_role_ka_carrier; - else - self.havocbot_role = havocbot_role_ka_collector; -} diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc index ac8ddd161..a20c23f7c 100644 --- a/qcsrc/server/bot/havocbot/roles.qc +++ b/qcsrc/server/bot/havocbot/roles.qc @@ -293,8 +293,8 @@ void havocbot_chooserole() { dprint("choosing a role...\n"); self.bot_strategytime = 0; - if (g_ctf) - havocbot_chooserole_ctf(); + if (MUTATOR_CALLHOOK(HavocBot_ChooseRule)) + return; else if (g_domination) havocbot_chooserole_dom(); else if (g_keyhunt) @@ -303,8 +303,6 @@ void havocbot_chooserole() havocbot_chooserole_race(); else if (g_onslaught) havocbot_chooserole_ons(); - else if (g_keepaway) - havocbot_chooserole_ka(); else if (g_freezetag) havocbot_chooserole_ft(); else if (g_assault) diff --git a/qcsrc/server/mutators/base.qh b/qcsrc/server/mutators/base.qh index f9a23e5f8..29a8e4e0b 100644 --- a/qcsrc/server/mutators/base.qh +++ b/qcsrc/server/mutators/base.qh @@ -254,3 +254,6 @@ MUTATOR_HOOKABLE(ItemTouch); MUTATOR_HOOKABLE(ClientConnect); // called at when a player connect entity self; // player + +MUTATOR_HOOKABLE(HavocBot_ChooseRule); + entity self; diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc index 26834605e..7556c6e9f 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@ -984,6 +984,663 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag } +// ================ +// Bot player logic +// ================ + +// NOTE: LEGACY CODE, needs to be re-written! + +void havocbot_calculate_middlepoint() +{ + entity f; + vector s = '0 0 0'; + vector fo = '0 0 0'; + float n = 0; + + f = ctf_worldflaglist; + while (f) + { + fo = f.origin; + s = s + fo; + f = f.ctf_worldflagnext; + } + if(!n) + return; + havocbot_ctf_middlepoint = s * (1.0 / n); + havocbot_ctf_middlepoint_radius = vlen(fo - havocbot_ctf_middlepoint); +} + + +entity havocbot_ctf_find_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team == f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +entity havocbot_ctf_find_enemy_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team != f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +float havocbot_ctf_teamcount(entity bot, vector org, float tc_radius) +{ + if not(teamplay) + return 0; + + float c = 0; + entity head; + + FOR_EACH_PLAYER(head) + { + if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot) + continue; + + if(vlen(head.origin - org) < tc_radius) + ++c; + } + + return c; +} + +void havocbot_goalrating_ctf_ourflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourbase(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemyflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team != head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemybase(float ratingscale) +{ + if not(bot_waypoints_for_items) + { + havocbot_goalrating_ctf_enemyflag(ratingscale); + return; + } + + entity head; + + head = havocbot_ctf_find_enemy_flag(self); + + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourstolenflag(float ratingscale) +{ + entity mf; + + mf = havocbot_ctf_find_flag(self); + + if(mf.ctf_status == FLAG_BASE) + return; + + if(mf.tag_entity) + navigation_routerating(mf.tag_entity, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float df_radius) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + // flag is out in the field + if(head.ctf_status != FLAG_BASE) + if(head.tag_entity==world) // dropped + { + if(df_radius) + { + if(vlen(org-head.origin) 0) + navigation_routerating(head, t * ratingscale, 500); + } + head = head.chain; + } +} + +void havocbot_ctf_reset_role(entity bot) +{ + float cdefense, cmiddle, coffense; + entity mf, ef, head; + float c; + + if(bot.deadflag != DEAD_NO) + return; + + if(vlen(havocbot_ctf_middlepoint)==0) + havocbot_calculate_middlepoint(); + + // Check ctf flags + if (bot.flagcarried) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + mf = havocbot_ctf_find_flag(bot); + ef = havocbot_ctf_find_enemy_flag(bot); + + // Retrieve stolen flag + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + // If enemy flag is taken go to the middle to intercept pursuers + if(ef.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); + return; + } + + // if there is only me on the team switch to offense + c = 0; + FOR_EACH_PLAYER(head) + if(head.team==bot.team) + ++c; + + if(c==1) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); + return; + } + + // Evaluate best position to take + // Count mates on middle position + cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5); + + // Count mates on defense position + cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5); + + // Count mates on offense position + coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius); + + if(cdefense<=coffense) + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE); + else if(coffense<=cmiddle) + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); + else + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); +} + +void havocbot_role_ctf_carrier() +{ + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried == world) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourbase(50000); + + if(self.health<100) + havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000); + + navigation_goalrating_end(); + + if (self.navigation_hasgoals) + self.havocbot_cantfindflag = time + 10; + else if (time > self.havocbot_cantfindflag) + { + // Can't navigate to my own base, suicide! + // TODO: drop it and wander around + Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0'); + return; + } + } +} + +void havocbot_role_ctf_escort() +{ + entity mf, ef; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If enemy flag is back on the base switch to previous role + ef = havocbot_ctf_find_enemy_flag(self); + if(ef.ctf_status==FLAG_BASE) + { + self.havocbot_role = self.havocbot_previous_role; + self.havocbot_role_timeout = 0; + return; + } + + // If the flag carrier reached the base switch to defense + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status!=FLAG_BASE) + if(vlen(ef.origin - mf.dropped_origin) < 300) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE); + return; + } + + // Set the role timeout if necessary + if (!self.havocbot_role_timeout) + { + self.havocbot_role_timeout = time + random() * 30 + 60; + } + + // If nothing happened just switch to previous role + if (time > self.havocbot_role_timeout) + { + self.havocbot_role = self.havocbot_previous_role; + self.havocbot_role_timeout = 0; + return; + } + + // Chase the flag carrier + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_enemyflag(30000); + havocbot_goalrating_ctf_ourstolenflag(40000); + havocbot_goalrating_items(10000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_offense() +{ + entity mf, ef; + vector pos; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // Check flags + mf = havocbot_ctf_find_flag(self); + ef = havocbot_ctf_find_enemy_flag(self); + + // Own flag stolen + if(mf.ctf_status!=FLAG_BASE) + { + if(mf.tag_entity) + pos = mf.tag_entity.origin; + else + pos = mf.origin; + + // Try to get it if closer than the enemy base + if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos)) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + } + + // Escort flag carrier + if(ef.ctf_status!=FLAG_BASE) + { + if(ef.tag_entity) + pos = ef.tag_entity.origin; + else + pos = ef.origin; + + if(vlen(pos-mf.dropped_origin)>700) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT); + return; + } + } + + // About to fail, switch to middlefield + if(self.health<50) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE); + return; + } + + // Set the role timeout if necessary + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 120; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourstolenflag(50000); + havocbot_goalrating_ctf_enemybase(20000); + havocbot_goalrating_items(5000, self.origin, 1000); + havocbot_goalrating_items(1000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +// Retriever (temporary role): +void havocbot_role_ctf_retriever() +{ + entity mf; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If flag is back on the base switch to previous role + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status==FLAG_BASE) + { + havocbot_ctf_reset_role(self); + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 20; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + float rt_radius; + rt_radius = 10000; + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourstolenflag(50000); + havocbot_goalrating_ctf_droppedflags(40000, self.origin, rt_radius); + havocbot_goalrating_ctf_enemybase(30000); + havocbot_goalrating_items(500, self.origin, rt_radius); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_middle() +{ + entity mf; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 10; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.bot_strategytime < time) + { + vector org; + + org = havocbot_ctf_middlepoint; + org_z = self.origin_z; + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + havocbot_goalrating_ctf_ourstolenflag(50000); + havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000); + havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5); + havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5); + havocbot_goalrating_items(2500, self.origin, 10000); + havocbot_goalrating_ctf_enemybase(2500); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_defense() +{ + entity mf; + + if(self.deadflag != DEAD_NO) + { + havocbot_ctf_reset_role(self); + return; + } + + if (self.flagcarried) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If own flag was captured + mf = havocbot_ctf_find_flag(self); + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + 30; + + if (time > self.havocbot_role_timeout) + { + havocbot_ctf_reset_role(self); + return; + } + if (self.bot_strategytime < time) + { + float mp_radius; + vector org; + + org = mf.dropped_origin; + mp_radius = havocbot_ctf_middlepoint_radius; + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + + // if enemies are closer to our base, go there + entity head, closestplayer = world; + float distance, bestdistance = 10000; + FOR_EACH_PLAYER(head) + { + if(head.deadflag!=DEAD_NO) + continue; + + distance = vlen(org - head.origin); + if(distance1000) + if(checkpvs(self.origin,closestplayer)||random()<0.5) + havocbot_goalrating_ctf_ourbase(30000); + + havocbot_goalrating_ctf_ourstolenflag(20000); + havocbot_goalrating_ctf_droppedflags(20000, org, mp_radius); + havocbot_goalrating_enemyplayers(15000, org, mp_radius); + havocbot_goalrating_items(10000, org, mp_radius); + havocbot_goalrating_items(5000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_setrole(entity bot, float role) +{ + dprint(strcat(bot.netname," switched to ")); + switch(role) + { + case HAVOCBOT_CTF_ROLE_CARRIER: + dprint("carrier"); + bot.havocbot_role = havocbot_role_ctf_carrier; + bot.havocbot_role_timeout = 0; + bot.havocbot_cantfindflag = time + 10; + bot.bot_strategytime = 0; + break; + case HAVOCBOT_CTF_ROLE_DEFENSE: + dprint("defense"); + bot.havocbot_role = havocbot_role_ctf_defense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_MIDDLE: + dprint("middle"); + bot.havocbot_role = havocbot_role_ctf_middle; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_OFFENSE: + dprint("offense"); + bot.havocbot_role = havocbot_role_ctf_offense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_RETRIEVER: + dprint("retriever"); + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_retriever; + bot.havocbot_role_timeout = time + 10; + bot.bot_strategytime = 0; + break; + case HAVOCBOT_CTF_ROLE_ESCORT: + dprint("escort"); + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_escort; + bot.havocbot_role_timeout = time + 30; + bot.bot_strategytime = 0; + break; + } + dprint("\n"); +} + + // ============== // Hook Functions // ============== @@ -1244,6 +1901,12 @@ MUTATOR_HOOKFUNCTION(ctf_MatchEnd) return FALSE; } +MUTATOR_HOOKFUNCTION(ctf_BotRoles) +{ + havocbot_ctf_reset_role(self); + return TRUE; +} + // ========== // Spawnfuncs @@ -1427,6 +2090,7 @@ MUTATOR_DEFINITION(gamemode_ctf) MUTATOR_HOOK(VehicleEnter, ctf_VehicleEnter, CBC_ORDER_ANY); MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY); MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY); MUTATOR_ONADD { diff --git a/qcsrc/server/mutators/gamemode_ctf.qh b/qcsrc/server/mutators/gamemode_ctf.qh index 8fa2b5f94..baf55ebd9 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qh +++ b/qcsrc/server/mutators/gamemode_ctf.qh @@ -109,3 +109,19 @@ float ctf_captimerecord; // record time for capturing the flag float ctf_captureshield_min_negscore; // punish at -20 points float ctf_captureshield_max_ratio; // punish at most 30% of each team float ctf_captureshield_force; // push force of the shield + +// bot player logic +#define HAVOCBOT_CTF_ROLE_NONE 0 +#define HAVOCBOT_CTF_ROLE_DEFENSE 2 +#define HAVOCBOT_CTF_ROLE_MIDDLE 4 +#define HAVOCBOT_CTF_ROLE_OFFENSE 8 +#define HAVOCBOT_CTF_ROLE_CARRIER 16 +#define HAVOCBOT_CTF_ROLE_RETRIEVER 32 +#define HAVOCBOT_CTF_ROLE_ESCORT 64 + +.float havocbot_cantfindflag; + +vector havocbot_ctf_middlepoint; +float havocbot_ctf_middlepoint_radius; + +void havocbot_role_ctf_setrole(entity bot, float role); diff --git a/qcsrc/server/mutators/gamemode_keepaway.qc b/qcsrc/server/mutators/gamemode_keepaway.qc index 25a41f13d..07c96671c 100644 --- a/qcsrc/server/mutators/gamemode_keepaway.qc +++ b/qcsrc/server/mutators/gamemode_keepaway.qc @@ -168,6 +168,77 @@ void ka_Reset() // used to clear the ballcarrier whenever the match switches fro } +// ================ +// Bot player logic +// ================ + +void havocbot_goalrating_ball(float ratingscale, vector org) +{ + float t; + entity ball_owner; + ball_owner = ka_ball.owner; + + if (ball_owner == self) + return; + + // If ball is carried by player then hunt them down. + if (ball_owner) + { + t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue); + navigation_routerating(ball_owner, t * ratingscale, 2000); + } + + // Ball has been dropped so collect. + navigation_routerating(ka_ball, ratingscale, 2000); +} + +void havocbot_role_ka_carrier() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } + + if (!self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_collector; + self.bot_strategytime = 0; + } +} + +void havocbot_role_ka_collector() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(1000, self.origin, 10000); + havocbot_goalrating_ball(20000, self.origin); + navigation_goalrating_end(); + } + + if (self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_carrier; + self.bot_strategytime = 0; + } +} + + // ============== // Hook Functions // ============== @@ -272,6 +343,15 @@ MUTATOR_HOOKFUNCTION(ka_PlayerPowerups) return 0; } +MUTATOR_HOOKFUNCTION(ka_BotRoles) +{ + if (self.ballcarried) + self.havocbot_role = havocbot_role_ka_carrier; + else + self.havocbot_role = havocbot_role_ka_collector; + return TRUE; +} + // ============== // Initialization @@ -337,6 +417,7 @@ MUTATOR_DEFINITION(gamemode_keepaway) MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerPowerups, ka_PlayerPowerups, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerUseKey, ka_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRule, ka_BotRoles, CBC_ORDER_ANY); MUTATOR_ONADD { diff --git a/qcsrc/server/mutators/gamemode_keepaway.qh b/qcsrc/server/mutators/gamemode_keepaway.qh index f99679e3b..062fc9e0c 100644 --- a/qcsrc/server/mutators/gamemode_keepaway.qh +++ b/qcsrc/server/mutators/gamemode_keepaway.qh @@ -5,3 +5,6 @@ entity ka_ball; #define SP_KEEPAWAY_PICKUPS 4 #define SP_KEEPAWAY_CARRIERKILLS 5 #define SP_KEEPAWAY_BCTIME 6 + +void() havocbot_role_ka_carrier; +void() havocbot_role_ka_collector;