From ea2bb87a4ee83fe56aac503843b0dfc9af2173db Mon Sep 17 00:00:00 2001 From: urre Date: Wed, 21 Dec 2005 23:04:00 +0000 Subject: [PATCH] Forgot a couple of files git-svn-id: svn://svn.icculus.org/nexuiz/trunk@712 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/gamec/bots.c | 148 +++ data/qcsrc/gamec/urrebot_ai_goals.qc | 316 +++++++ data/qcsrc/gamec/urrebot_ai_main.qc | 724 +++++++++++++++ data/qcsrc/gamec/urrebot_load.qc | 1205 +++++++++++++++++++++++++ data/qcsrc/gamec/urrebot_nn_search.qc | 100 ++ data/qcsrc/gamec/urrebot_nn_use.qc | 295 ++++++ 6 files changed, 2788 insertions(+) create mode 100644 data/qcsrc/gamec/bots.c create mode 100644 data/qcsrc/gamec/urrebot_ai_goals.qc create mode 100644 data/qcsrc/gamec/urrebot_ai_main.qc create mode 100644 data/qcsrc/gamec/urrebot_load.qc create mode 100644 data/qcsrc/gamec/urrebot_nn_search.qc create mode 100644 data/qcsrc/gamec/urrebot_nn_use.qc diff --git a/data/qcsrc/gamec/bots.c b/data/qcsrc/gamec/bots.c new file mode 100644 index 000000000..651eceeef --- /dev/null +++ b/data/qcsrc/gamec/bots.c @@ -0,0 +1,148 @@ +float BOTTYPE_MAUVEBOT = 0; +float BOTTYPE_URREBOT = 1; +.float bottype; + +/* +MauveBot v1.0 for Nexuiz +*/ + +float intermission_running; + +.float skill_level; + +.float ai_time; +.float threat; +.entity dodgeent; + +float THREAT_UNFLAGGED = 0; +float THREAT_IGNORE = -1; + +float DODGE_DIST = 500; +float SEARCH_DIST = 1000; + +.float search_time; + +float bot_number; + +void() add_MauveBot; +void() remove_MauveBot; + +/* +UrreBot 1.5 for Nexuiz +*/ + +entity navnode_chain, bot_chain; +float navnodes, loadstep; +.entity list, link0, link1, link2, link3, link4, link5, link6, link7, link8, link9; +.entity link10, link11, link12, link13, link14, link15, link16, link17, link18, link19; +.float sflags; +.float lflags0, lflags1, lflags2, lflags3, lflags4, lflags5, lflags6, lflags7, lflags8, lflags9; +.float lflags10, lflags11, lflags12, lflags13, lflags14, lflags15, lflags16, lflags17, lflags18, lflags19; +.vector pointl; +.float costl; +.float lmark; +float search_distance; +float minisearch_distance; +float stratsearch_distance; +.entity goalcurrent; +.float strat_me; +.float() evalfunc; + +vector nullvector; +.vector movepoint; +.float camptime; +.vector campcheck; + +.vector aimpoint; +.float aimtime; +.float evaltime; +.float lead; + +float skill; + +float S_TELEPORT = 1; +float S_DOOR = 2; +float S_TOUCH = 4; + +float LF_NOLINK = 1; +float LF_NOWALK = 2; +float LF_BAD = 4; +float LF_BIGDROP = 8; +float LF_REMOTE = 16; + +.entity plane_chain; + +float urrebots, actualurrebots, urrebots_strategytime, urrebots_combattime; + +entity strategytoken; +float strategytime; +.float combattime; + +entity(vector org, vector minss, vector maxss) FindCurrentNavNode; +void(float distance) UrreBotPath; +void() UrreBotSetup; +void() UrreBotThink; +void() LoadNavNodes; +void() LinkNavNodes; +void() ItemEvals; +void() UrreBotAdd; +void() UrreBotRemove; + +float(vector m1, vector m2, vector m3, vector m4) boxesoverlap = {return m2_x >= m3_x && m1_x <= m4_x && m2_y >= m3_y && m1_y <= m4_y && m2_z >= m3_z && m1_z <= m4_z;}; +float(vector smins, vector smaxs, vector bmins, vector bmaxs) boxenclosed = {return smins_x >= bmins_x && smaxs_x <= bmaxs_x && smins_y >= bmins_y && smaxs_y <= bmaxs_y && smins_z >= bmins_z && smaxs_z <= bmaxs_z;}; + +entity newmis; +void() SUB_Remove; +float JoinBestTeam(entity pl, float only_return_best); +float sv_maxspeed; + +void() Bots_Shared = +{ + local float flo; + + if (time >= 3) + { + // MauveBots + flo = cvar("bot_number"); + if (flo > bot_number) + add_MauveBot(); + else if (flo < bot_number) + remove_MauveBot(); + + // UrreBots + urrebots = cvar("urrebots"); + urrebots_strategytime = cvar("urrebots_strategytime"); + urrebots_combattime = cvar("urrebots_combattime"); + stratsearch_distance = cvar("urrebots_stratsearch_dist"); + minisearch_distance = cvar("urrebots_minisearch_dist"); + if (actualurrebots < urrebots) + { + if (loadstep == 0) + { + LoadNavNodes(); + loadstep = 1; + return; + } + else if (loadstep == 1) + { + LinkNavNodes(); + loadstep = 2; + return; + } + else if (loadstep == 2) + { + ItemEvals(); + loadstep = 3; + return; + } + UrreBotAdd(); + } + if (actualurrebots > urrebots) + UrreBotRemove(); + } + else + { + remove_MauveBot(); + UrreBotRemove(); + } +}; \ No newline at end of file diff --git a/data/qcsrc/gamec/urrebot_ai_goals.qc b/data/qcsrc/gamec/urrebot_ai_goals.qc new file mode 100644 index 000000000..5138b7155 --- /dev/null +++ b/data/qcsrc/gamec/urrebot_ai_goals.qc @@ -0,0 +1,316 @@ +/* --- Evaluation functions --- +UrreBots select items to hunt for based on distance and if the item is valuable to them +The returning float is added to the travel distance when searching +Higher numbers are crummier +-1 means definite unwanted goal*/ + +float() ShellsEval = +{ + local float f; + + f = 1000; + if (self.ammo_shells > 40) + f = f + 500; + if (self.items & IT_SHOTGUN) + f = f - 250; + if (self.ammo_shells >= 100) + f = -1; + return f; +}; + +float() NailsEval = +{ + local float f; + + f = 1000; + if (self.ammo_nails > 120) + f = f + 200; + else if (self.items & IT_UZI) + f = f - 250; + if (self.ammo_nails >= 200) + f = -1; + return f; +}; + +float() RocketsEval = +{ + local float f; + + f = 1000; + if (self.ammo_rockets > 30) + f = f + 50; + if ((self.items & IT_ROCKET_LAUNCHER) || (self.items & IT_GRENADE_LAUNCHER) || (self.items & IT_HAGAR)) + f = f - 400; + if (self.ammo_rockets >= 100) + f = -1; + return f; +}; + +float() CellsEval = +{ + local float f; + + f = 1000; + if ((self.items & IT_CRYLINK) || (self.items & IT_NEX) || (self.items & IT_ELECTRO)) + f = f - 400; + if (self.ammo_cells >= 100) + f = -1; + return f; +}; + +float() Health5Eval = +{ + local float f, f2; + + f2 = self.health * 0.01; + f = 800 * f2; + + if (f < 20) + f = 20; + return f; +}; + +float() Health25Eval = +{ + local float f, f2; + + f2 = self.health * 0.01; + f = 400 * f2; + + if (f < 20) + f = 20; + return f; +}; + +float() Health100Eval = +{ + local float f; + + f = 100; + + return f; +}; + +float() Armor5Eval = +{ + local float f; + + f = 800; + + if (self.health < 50) + f = f - 300; + return f; +}; + +float() Armor100Eval = +{ + local float f; + + f = 250; + + if (self.health < 50) + f = f - 80; + return f; +}; + +float() EEval = +{ + local float f; + + f = 250; +/* + if (deathmatch == 2 && (self.items & IT_SUPER_SHOTGUN)) + f = -1; +*/ + return f; +}; + +float() UEval = +{ + local float f; + + f = 500; +/* + if (deathmatch == 2 && (self.items & IT_SUPER_SHOTGUN)) + f = -1; +*/ + return f; +}; + +float() CLEval = +{ + local float f; + + f = 500; +/* + if (deathmatch == 2 && (self.items & IT_SUPER_SHOTGUN)) + f = -1; +*/ + return f; +}; + +float() HEval = +{ + local float f; + + f = 500; +/* + if (deathmatch == 2 && (self.items & IT_SUPER_SHOTGUN)) + f = -1; +*/ + return f; +}; + +float() NGEval = +{ + local float f; + + f = 200; +/* + if (deathmatch == 2 && (self.items & IT_NAILGUN)) + f = -1; +*/ + return f; +}; + +float() SGEval = +{ + local float f; + + f = 1000; +/* + if (deathmatch == 2 && (self.items & IT_SUPER_NAILGUN)) + f = -1; +*/ + return f; +}; + +float() GLEval = +{ + local float f; + + f = 300; +/* + if (deathmatch == 2 && (self.items & IT_GRENADE_LAUNCHER)) + f = -1; +*/ + return f; +}; + +float() RLEval = +{ + local float f; + + f = 100; +/* + if (deathmatch == 2 && (self.items & IT_ROCKET_LAUNCHER)) + f = -1; +*/ + return f; +}; + +float() InvEval = +{ + return 50; +}; + +float() StrengthEval = +{ + return 50; +}; + +float() BadEval = +{ + return -1; +}; + +/* --- ItemEvals --- +Called at load, to give all pickable items their evaluation function +Also assigns their current waybox (makes for faster evaluation)*/ + +void() ItemEvals = +{ + local entity e; + + e = findchainflags(flags, FL_ITEM); + while (e) + { + e.enemy = FindCurrentNavNode((e.absmin + e.absmax)*0.5, e.mins, e.maxs); + if (e.enemy == world) + { + dprint ("Warning: Found no box for item\n"); + e.evalfunc = BadEval; + } + else + { + if (e.netname == "shells") + e.evalfunc = ShellsEval; + else if (e.netname == "bullets") + e.evalfunc = NailsEval; + else if (e.netname == "rockets") + e.evalfunc = RocketsEval; + else if (e.netname == "cells") + e.evalfunc = CellsEval; + else if (e.netname == "5 Health") + e.evalfunc = Health5Eval; + else if (e.netname == "25 Health") + e.evalfunc = Health25Eval; + else if (e.netname == "100 Health") + e.evalfunc = Health100Eval; + else if (e.netname == "Armor Shard") + e.evalfunc = Armor5Eval; + else if (e.netname == "Armor") + e.evalfunc = Armor100Eval; + else if (e.netname == "Uzi") + e.evalfunc = UEval; + else if (e.netname == "Shotgun") + e.evalfunc = SGEval; + else if (e.netname == "Grenade Launcher") + e.evalfunc = GLEval; + else if (e.netname == "Electro") + e.evalfunc = EEval; + else if (e.netname == "Crylink") + e.evalfunc = CLEval; + else if (e.netname == "Nex Gun") + e.evalfunc = NGEval; + else if (e.netname == "Hagar") + e.evalfunc = HEval; + else if (e.netname == "Rocket Launcher") + e.evalfunc = RLEval; + else if (e.netname == "Strength Powerup") + e.evalfunc = StrengthEval; + else if (e.netname == "Invulnerability") + e.evalfunc = InvEval; + else + dprint ("Warning: Unknown item\n"); + } + e = e.chain; + } +}; + +/* --- DistEvalItems --- +This function adds the items assigned navnode's travel cost to the item +Used for goal evaluation*/ + +void() DistEvalItems = +{ + local float f; + local vector v; + local entity e; + + e = findchainflags(flags, FL_ITEM); + while (e) + { + /* + if (e.flags & FL_ONGROUND) + // if (e.classname == "BackPack") + if (!e.enemy) + e.enemy = FindCurrentNavNode((e.absmin + e.absmax)*0.5, e.mins, e.maxs); + */ + + v = e.origin + (e.mins + e.maxs) * 0.5; + f = vlen(v - e.enemy.pointl); + e.costl = e.enemy.costl + f; + e = e.chain; + } +}; \ No newline at end of file diff --git a/data/qcsrc/gamec/urrebot_ai_main.qc b/data/qcsrc/gamec/urrebot_ai_main.qc new file mode 100644 index 000000000..a99875e66 --- /dev/null +++ b/data/qcsrc/gamec/urrebot_ai_main.qc @@ -0,0 +1,724 @@ +/* --- UrreBotSetup --- +Issues a random funky name, random colors, playermodel and team to the bot*/ + +void() UrreBotSetup = +{ + local float r, shirt, pants; + local string s; + + r = random()*18; + if (r <= 1) + s = "GrooveMachine"; + else if (r <= 2) + s = "Worm"; + else if (r <= 3) + s = "ClownLock"; + else if (r <= 4) + s = "DiscO"; + else if (r <= 5) + s = "FunkyFresh"; + else if (r <= 6) + s = "DanceWithMe"; + else if (r <= 7) + s = "BodyJiggle"; + else if (r <= 8) + s = "CantSwim"; + else if (r <= 9) + s = "AtomicDog"; + else if (r <= 10) + s = "Follower"; + else if (r <= 11) + s = "BrassMonkey"; + else if (r <= 12) + s = "SirNose"; + else if (r <= 13) + s = "StarChild"; + else if (r <= 14) + s = "GeorgeC"; + else if (r <= 15) + s = "Bootsy"; + else if (r <= 16) + s = "Flashlight"; + else if (r <= 17) + s = "Bodysnatcher"; + else + s = "Boogieboy"; + + self.netname = s; + + r = random()*15; + if (r <= 1) + { + self.playermodel = "models/player/carni.zym"; + if (random() < 0.5) + self.playerskin = "0"; + else + self.playerskin = "1"; + } + else if (r <= 2) + { + self.playermodel = "models/player/crash.zym"; + self.playerskin = "0"; + } + else if (r <= 3) + { + self.playermodel = "models/player/grunt.zym"; + self.playerskin = "0"; + } + else if (r <= 4) + { + self.playermodel = "models/player/headhunter.zym"; + self.playerskin = "0"; + } + else if (r <= 5) + { + self.playermodel = "models/player/insurrectionist.zym"; + self.playerskin = "0"; + } + else if (r <= 6) + { + self.playermodel = "models/player/jeandarc.zym"; + self.playerskin = "0"; + } + else if (r <= 7) + { + self.playermodel = "models/player/lurk.zym"; + if (random() < 0.5) + self.playerskin = "0"; + else + self.playerskin = "1"; + } + else if (r <= 8) + { + self.playermodel = "models/player/lycanthrope.zym"; + self.playerskin = "0"; + } + else if (r <= 9) + { + self.playermodel = "models/player/marine.zym"; + self.playerskin = "0"; + } + else if (r <= 10) + { + self.playermodel = "models/player/nexus.zym"; + if (random() < 0.5) + self.playerskin = "0"; + else + self.playerskin = "1"; + } + else if (r <= 11) + { + self.playermodel = "models/player/pyria.zym"; + self.playerskin = "0"; + } + else if (r <= 12) + { + self.playermodel = "models/player/shock.zym"; + self.playerskin = "0"; + } + else if (r <= 13) + { + self.playermodel = "models/player/skadi.zym"; + self.playerskin = "0"; + } + else if (r <= 14) + { + self.playermodel = "models/player/specop.zym"; + self.playerskin = "0"; + } + else + { + self.playermodel = "models/player/visitant.zym"; + self.playerskin = "0"; + } + + if (teamplay) + JoinBestTeam(self, 0); + else + { + shirt = floor(random()*15); + pants = floor(random()*15); + self.clientcolors = pants + shirt * 16; + } +}; + +/* --- UrreBotInfront --- +I could've used id's infront, but as it wasn't in LordHavoc's multiplayer +only mod, I had to add a new one, named something else to not mess with people's +possible/eventual plugin-attempts*/ + +float(entity targ) UrreBotInfront = +{ + local float dot; + local vector vec; + + makevectors (self.angles); + vec = normalize (targ.origin - self.origin); + dot = vec * v_forward; + + if (dot > 0.3) + return TRUE; + return FALSE; +}; + +/* --- UrreBotEvalTargets --- +Enemies are found and lost using this function +If the bot can't see his enemy for 3 seconds, it is dropped*/ + +void() UrreBotEvalTargets = +{ + local float old, new; + local vector v1, v2; + local entity e; + + v1 = self.origin + self.view_ofs; + + if (self.enemy) + { + if (self.enemy.health >= 1 && !self.enemy.deadflag) + { + if (self.evaltime <= time) + { + self.evaltime = time + 3; + v2 = (self.enemy.absmin + self.enemy.absmax) * 0.5; + traceline(v1, v2, TRUE, self); + if (trace_fraction < 1) + self.enemy = world; + } + } + else + self.enemy = world; + } + e = findradius(v1, 1500); + while (e) + { + if (!(e.flags & FL_NOTARGET) || e.frags != -666) // -666 is spec/obs + if (e.flags & FL_CLIENT) + if (e != self) + if (!e.deadflag) + if (UrreBotInfront(e)) + if (e.health >= 1) + { + v2 = (e.absmin + e.absmax) * 0.5; + traceline(v1, v2, TRUE, self); + if (trace_fraction == 1 || trace_ent == e) + { + if (self.enemy) + { + old = vlen(self.origin - (self.enemy.absmin + self.enemy.absmax)*0.5); + new = vlen(self.origin - v2); + if (new < old) + self.enemy = e; + } + else + self.enemy = e; + } + } + e = e.chain; + } + + e = world; + if (self.goalcurrent.sflags & S_DOOR) + e = self.goalcurrent.goalentity; + else if (self.link0.sflags & S_DOOR) + e = self.link0.goalentity; + if (e.health >= 1) + self.enemy = e; +}; + +/* --- UrreBotAim --- +Very crude and simple aiming*/ + +void() UrreBotAim = +{ + local float dist, skeel; + local vector v, desiredang, testang, diffang; + + skeel = bound(1, skill, 10); + + // get the desired angles to aim at + if (self.enemy) + { + v = (self.enemy.absmin + self.enemy.absmax) * 0.5; + if (self.enemy.classname == "door") + self.aimpoint = v; + else if (self.aimtime <= time) + { + self.aimtime = time + 0.3; + traceline(self.origin + self.view_ofs, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, self); + if (trace_fraction == 1) + { + self.aimpoint = v + self.enemy.velocity*vlen(self.origin - v)*self.lead; + self.aimpoint = self.aimpoint + randomvec()*max(0, 120 - skeel*12); + } + } + desiredang = vectoangles(self.aimpoint - (self.origin + self.view_ofs)); + } + else + desiredang = vectoangles(self.velocity); + desiredang_x = 0-desiredang_x; + + if (desiredang_y <= -180) + desiredang_y = desiredang_y + 360; + else if (desiredang_y >= 180) + desiredang_y = desiredang_y - 360; + if (desiredang_x <= -180) + desiredang_x = desiredang_x + 360; + else if (desiredang_x >= 180) + desiredang_x = desiredang_x - 360; + + // calculate turn angles + testang = desiredang - self.v_angle; + testang_z = 0; + + // turn + dist = vlen(testang * ((skeel + 1) * frametime)); + if (vlen(normalize(testang) * skeel) > dist) + { + diffang = normalize(testang) * skeel; + dist = vlen(normalize(testang) * skeel); + } + else + diffang = testang * ((skeel + 1) * frametime); + if (dist > vlen(testang)) + diffang = testang; + + self.v_angle = self.v_angle + diffang; + self.angles_y = self.v_angle_y; +}; + +/* --- UrreBotMove --- +Moves towards the closest point on the next waybox in the bots list, or +towards the item he's hunting in case they are in the same waybox*/ + +void() UrreBotMove = +{ + local float f, bad; + local vector dir, tvec; + local entity plane; + + if (self.link0) + { + if (boxesoverlap(self.origin + self.mins, self.origin + self.maxs, self.link0.origin + self.link0.mins, self.link0.origin + self.link0.maxs)) + { + plane = self.link0.plane_chain; + while (plane) + { + tvec = self.maxs; + if (plane.mangle_x < 0) + tvec_x = self.mins_x; + if (plane.mangle_y < 0) + tvec_y = self.mins_y; + if (plane.mangle_z < 0) + tvec_z = self.mins_z; + tvec += self.origin; + f = tvec*plane.mangle - self.link0.origin*plane.mangle-plane.delay; + if (f > 0) + bad = TRUE; + plane = plane.list; + } + if (!bad) + { + PopRoute(); + if (self.goalcurrent.sflags & S_TELEPORT) + self.movepoint = self.goalcurrent.origin; + else + self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.link0); + } + } + else if (((self.goalcurrent.sflags & S_TOUCH) && boxesoverlap(self.origin + self.mins, self.origin + self.maxs, self.goalcurrent.origin + self.goalcurrent.mins, self.goalcurrent.origin + self.goalcurrent.maxs)) || boxenclosed(self.origin + self.mins, self.origin + self.maxs, self.goalcurrent.origin + self.goalcurrent.mins, self.goalcurrent.origin + self.goalcurrent.maxs)) + { + plane = self.goalcurrent.plane_chain; + while (plane) + { + tvec = self.maxs; + if (plane.mangle_x < 0) + tvec_x = self.mins_x; + if (plane.mangle_y < 0) + tvec_y = self.mins_y; + if (plane.mangle_z < 0) + tvec_z = self.mins_z; + tvec += self.origin; + f = tvec*plane.mangle - self.goalcurrent.origin*plane.mangle-plane.delay; + if (f > 0) + bad = TRUE; + plane = plane.list; + } + if (!bad) + { + PopRoute(); + if (self.goalcurrent.sflags & S_TELEPORT) + self.movepoint = self.goalcurrent.origin; + else + self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.goalcurrent); + if (self.movepoint == '0 0 0') + { + self.strat_me = TRUE; + UrreBotPath(minisearch_distance); + } + } + } + } + else + { + if (!self.goalcurrent || ((self.goalcurrent.flags & FL_ITEM) && !self.goalcurrent.solid)) + { + self.strat_me = TRUE; + UrreBotPath(minisearch_distance); + } + self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.goalcurrent); + } + dir = normalize(ToPointInSpace(self.goalcurrent, self.movepoint)); + dir = dir * sv_maxspeed; + makevectors(self.v_angle); + self.movement_x = dir * v_forward; + self.movement_y = dir * v_right; + self.movement_z = dir * v_up; +}; + +/* --- UrreBotImpulses --- +Returns the impulse for the best weapon in the given situation*/ + +float() UrreBotImpulses = +{ + local float dist; + local float cells, rockets, nails, shells; + local vector v; +/* + cvar("g_balance_electro_radius"); + cvar("g_balance_grenadelauncher_radius"); + cvar("g_balance_hagar_radius"); + cvar("g_balance_rocketlauncher_radius"); +*/ + if (random() < 0.5) + return 0; + + dist = 400; // we like nex and mortar + if (self.enemy) + { + v = (self.enemy.absmin + self.enemy.absmax) * 0.5; + dist = vlen(v - self.origin); + } + + cells = self.ammo_cells; + rockets = self.ammo_rockets; + nails = self.ammo_nails; + shells = self.ammo_shells; + + if (dist > 300 && cells && (self.items & IT_NEX)) + { + self.lead = 0; + return WEP_NEX; + } + else if (rockets) + { + if (dist < 500) + if (self.items & IT_GRENADE_LAUNCHER) + { + self.lead = 1 / cvar("g_balance_grenadelauncher_speed"); + return WEP_GRENADE_LAUNCHER; + } + if (self.items & IT_HAGAR) + { + self.lead = 1 / cvar("g_balance_hagar_speed"); + return WEP_HAGAR; + } + else if (self.items & IT_ROCKET_LAUNCHER) + { + self.lead = 1 / cvar("g_balance_rocketlauncher_speed"); + return WEP_ROCKET_LAUNCHER; + } + } + else if (cells) + { + if (self.items & IT_ELECTRO) + { + self.lead = 1 / cvar("g_balance_electro_speed"); + return WEP_ELECTRO; + } + else if (self.items & IT_CRYLINK) + { + self.lead = 1 / cvar("g_balance_crylink_speed"); + return WEP_CRYLINK; + } + } + else if (nails) + { + if (self.items & IT_UZI) + { + self.lead = 0; + return WEP_UZI; + } + } + else if (shells) + { + if (self.items & IT_SHOTGUN) + { + self.lead = 0; + return WEP_SHOTGUN; + } + } + self.lead = 1 / cvar("g_balance_laser_speed"); + return WEP_LASER; +}; + +float BT_LIGHTNING = 0; +float BT_BEAM = 1; + +void(float beamtype, vector bmins, vector bmaxs) BeamBox = +{ + local vector v1, v2; + + v1 = bmaxs; + v2 = bmaxs; + v2_x = bmins_x; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmaxs; + v2 = bmaxs; + v2_y = bmins_y; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmaxs; + v2 = bmaxs; + v2_z = bmins_z; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmins; + v2 = bmins; + v2_x = bmaxs_x; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmins; + v2 = bmins; + v2_y = bmaxs_y; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmins; + v2 = bmins; + v2_z = bmaxs_z; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmins; + v1_z = bmaxs_z; + v2 = bmins; + v2_x = bmaxs_x; + v2_z = bmaxs_z; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmins; + v1_z = bmaxs_z; + v2 = bmins; + v2_y = bmaxs_y; + v2_z = bmaxs_z; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmaxs; + v1_z = bmins_z; + v2 = bmaxs; + v2_x = bmins_x; + v2_z = bmins_z; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmaxs; + v1_z = bmins_z; + v2 = bmaxs; + v2_y = bmins_y; + v2_z = bmins_z; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmins; + v1_x = bmaxs_x; + v2 = bmaxs; + v2_y = bmins_y; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); + v1 = bmins; + v1_y = bmaxs_y; + v2 = bmaxs; + v2_x = bmins_x; + if (beamtype == BT_LIGHTNING) + te_lightning1(world, v1, v2); + else + te_beam(world, v1, v2); +}; + +void(float sdist) UrreBotPath = +{ + local float f, f2, f3; + local entity e, best; + + ClearRoute(); + MarkRoute(sdist); + DistEvalItems(); + f2 = 10000000; + e = findchainflags(flags, FL_ITEM); + while (e) + { + f = 0; + if (e.evalfunc) + f = e.evalfunc(); + if (f > 0) + if (e.enemy) + { + f = f + e.costl; + if (f < f2) + if (e.solid) + { + best = e; + f2 = e.costl; + } + } + e = e.chain; + } + if (best) + { + while (best) + { + PushRoute(best); + best = best.enemy; + } + } + else + { + f3 = 0; + while (f3 < 3) + { + f = 0; + f2 = 0; + f = ceil(random() * navnodes); + e = navnode_chain; + while (e) + { + if (f == f2) + if (e.enemy) + { + best = e; + f3 = 3; + } + f2 = f2 + 1; + e = e.list; + } + f3 = f3 + 1; + } + while (best) + { + PushRoute(best); + best = best.enemy; + } + } +}; + +/* --- UrreBotThink --- +In this version, UrreBot does a waybox search based on time and turn +Then it aims, moves, finds/loses stuff to shoot at, picks best weapon, +shoots, in that order +Aiming must happen before movement, because movement depends on angles +He does not yet have any combat movement*/ + +void() UrreBotThink = +{ + local float f; + local vector v; + + self.movement = '0 0 0'; + self.button0 = 0; + self.button2 = 0; + self.impulse = 0; + + if (self.deadflag) + { + ClearRoute(); + if (random() < 0.2) + self.button0 = 1; + return; + } + + if (strategytime <= time) + if (strategytoken == self) + { + strategytime = time + urrebots_strategytime; + strategytoken = self.list; + if (!strategytoken) + strategytoken = bot_chain; + if (self.strat_me) + { + self.strat_me = FALSE; + UrreBotPath(stratsearch_distance); + } + } + + UrreBotAim(); + UrreBotMove(); + + if (self.camptime <= time) + { + if (vlen(self.origin - self.campcheck) < 200) // stuckage avoidage + { + self.camptime = time + actualurrebots; + self.strat_me = TRUE; + UrreBotPath(minisearch_distance); + } + else + { + self.campcheck = self.origin; + self.camptime = time + 2; + } + } + + if (self.combattime <= time) + { + self.combattime = time + urrebots_combattime; + UrreBotEvalTargets(); + self.impulse = UrreBotImpulses(); + } + + if (self.enemy) + { + makevectors (self.v_angle); + v = self.origin + '0 0 16'; + f = vlen(v - self.aimpoint); + traceline (v, v + v_forward*f, FALSE, self); + if (vlen(trace_endpos - self.aimpoint) < 150) + { + if (skill < 3) + { + if (random() < 0.3) + self.button0 = 1; + } + else + self.button0 = 1; + } + } + if (cvar("urrebots_debug")) + { + te_lightning1(self, self.origin, self.movepoint); + if (!self.goalcurrent) + { + bprint(self.netname); + bprint(" has no goalcurrent\n"); + } + } +}; \ No newline at end of file diff --git a/data/qcsrc/gamec/urrebot_load.qc b/data/qcsrc/gamec/urrebot_load.qc new file mode 100644 index 000000000..c5513861f --- /dev/null +++ b/data/qcsrc/gamec/urrebot_load.qc @@ -0,0 +1,1205 @@ +void() UrreBotAdd = +{ + local entity ent; + + ent = self; + self = spawnclient(); + if (!self) + { + bprint("Can not add UrreBot, server full\n"); + cvar_set("bots", ftos(actualurrebots)); + self = ent; + return; + } + UrreBotSetup(); + self.list = bot_chain; + bot_chain = self; + self.bottype = BOTTYPE_URREBOT; + ClientConnect(); + PutClientInServer(); + self = ent; + + strategytoken = bot_chain; + actualurrebots = actualurrebots + 1; +}; + +void() UrreBotRemove = +{ + local entity ent, t; + + ent = findchain(classname, "player"); + while (ent) + { + if (clienttype(ent) == CLIENTTYPE_BOT) + { + if (ent.bottype == BOTTYPE_URREBOT) + { + if (bot_chain == self) + bot_chain = self.list; + else + { + t = find(world, classname, "player"); + while(t) + { + if (t.list == ent) + t.list = ent.list; + t = find(t, classname, "player"); + } + } + if (actualurrebots > 0) + actualurrebots -= 1; + dropclient(ent); + } + } + ent = ent.chain; + } +}; + +/* --- SpawnNavNode --- +The one to guess what this does gets bacon from FrikaC*/ + +entity(vector org, vector nnmins, vector nnmaxs) SpawnNavNode = +{ + newmis = spawn(); + setsize(newmis, nnmins, nnmaxs); + newmis.classname = "navnode"; + setorigin(newmis, org); + return newmis; +}; + +void(entity navn, vector normal, float dist) AddPlane = +{ + local float f; + + newmis = spawn(); + newmis.classname = "plane"; + newmis.list = navn.plane_chain; + navn.plane_chain = newmis; + newmis.mangle = normal*-1; + newmis.delay = dist; + f = (navn.origin * newmis.mangle) - ((navn.origin + (newmis.delay * newmis.mangle)) * newmis.mangle); + if (f > 0) + { + newmis.mangle = newmis.mangle*-1; + newmis.delay = newmis.delay*-1; + } +}; + +/* --- LinkNavNodes --- +Links the navnodes and gives some special attributes (like doors, teleporters)*/ + +void() LinkNavNodes = +{ + local entity e, t, tdest; + + navnode_chain = world; + navnodes = 0; + + e = findchain(classname, "navnode"); + while (e) + { + e.list = navnode_chain; + navnode_chain = e; + e.delay = navnodes; + navnodes = navnodes + 1; + e = e.chain; + } + + e = navnode_chain; + while (e) + { + if (e.link0) + { + e.link0.think = SUB_Remove; + e.link0.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link0.delay) + e.link0 = t; + t = t.list; + } + e.lflags0 = e.lflags0 | LF_REMOTE; + } + if (e.link1) + { + e.link1.think = SUB_Remove; + e.link1.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link1.delay) + e.link1 = t; + t = t.list; + } + e.lflags1 = e.lflags1 | LF_REMOTE; + } + if (e.link2) + { + e.link2.think = SUB_Remove; + e.link2.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link2.delay) + e.link2 = t; + t = t.list; + } + e.lflags2 = e.lflags2 | LF_REMOTE; + } + if (e.link3) + { + e.link3.think = SUB_Remove; + e.link3.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link3.delay) + e.link3 = t; + t = t.list; + } + e.lflags3 = e.lflags3 | LF_REMOTE; + } + if (e.link4) + { + e.link4.think = SUB_Remove; + e.link4.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link4.delay) + e.link4 = t; + t = t.list; + } + e.lflags4 = e.lflags4 | LF_REMOTE; + } + if (e.link5) + { + e.link5.think = SUB_Remove; + e.link5.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link5.delay) + e.link5 = t; + t = t.list; + } + e.lflags5 = e.lflags5 | LF_REMOTE; + } + if (e.link6) + { + e.link6.think = SUB_Remove; + e.link6.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link6.delay) + e.link6 = t; + t = t.list; + } + e.lflags6 = e.lflags6 | LF_REMOTE; + } + if (e.link7) + { + e.link7.think = SUB_Remove; + e.link7.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link7.delay) + e.link7 = t; + t = t.list; + } + e.lflags7 = e.lflags7 | LF_REMOTE; + } + if (e.link8) + { + e.link8.think = SUB_Remove; + e.link8.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link8.delay) + e.link8 = t; + t = t.list; + } + e.lflags8 = e.lflags8 | LF_REMOTE; + } + if (e.link9) + { + e.link9.think = SUB_Remove; + e.link9.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link9.delay) + e.link9 = t; + t = t.list; + } + e.lflags9 = e.lflags9 | LF_REMOTE; + } + if (e.link10) + { + e.link10.think = SUB_Remove; + e.link10.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link10.delay) + e.link10 = t; + t = t.list; + } + e.lflags10 = e.lflags10 | LF_REMOTE; + } + if (e.link11) + { + e.link11.think = SUB_Remove; + e.link11.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link11.delay) + e.link11 = t; + t = t.list; + } + e.lflags11 = e.lflags11 | LF_REMOTE; + } + if (e.link12) + { + e.link12.think = SUB_Remove; + e.link12.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link12.delay) + e.link12 = t; + t = t.list; + } + e.lflags12 = e.lflags12 | LF_REMOTE; + } + if (e.link13) + { + e.link13.think = SUB_Remove; + e.link13.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link13.delay) + e.link13 = t; + t = t.list; + } + e.lflags13 = e.lflags13 | LF_REMOTE; + } + if (e.link14) + { + e.link14.think = SUB_Remove; + e.link14.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link14.delay) + e.link14 = t; + t = t.list; + } + e.lflags14 = e.lflags14 | LF_REMOTE; + } + if (e.link15) + { + e.link15.think = SUB_Remove; + e.link15.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link15.delay) + e.link15 = t; + t = t.list; + } + e.lflags15 = e.lflags15 | LF_REMOTE; + } + if (e.link16) + { + e.link16.think = SUB_Remove; + e.link16.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link16.delay) + e.link16 = t; + t = t.list; + } + e.lflags16 = e.lflags16 | LF_REMOTE; + } + if (e.link17) + { + e.link17.think = SUB_Remove; + e.link17.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link17.delay) + e.link17 = t; + t = t.list; + } + e.lflags17 = e.lflags17 | LF_REMOTE; + } + if (e.link18) + { + e.link18.think = SUB_Remove; + e.link18.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link18.delay) + e.link18 = t; + t = t.list; + } + e.lflags18 = e.lflags18 | LF_REMOTE; + } + if (e.link19) + { + e.link19.think = SUB_Remove; + e.link19.nextthink = time; + t = navnode_chain; + while (t) + { + if (t.delay == e.link19.delay) + e.link19 = t; + t = t.list; + } + e.lflags19 = e.lflags19 | LF_REMOTE; + } + e = e.list; + } + e = navnode_chain; + while (e) + { + if (!(e.sflags & S_TELEPORT)) + { + t = navnode_chain; + while (t) + { + if (boxesoverlap(e.origin + e.mins, e.origin + e.maxs, t.origin + t.mins, t.origin + t.maxs)) + if (t != e) + if (t != e.link0) + if (t != e.link1) + if (t != e.link2) + if (t != e.link3) + if (t != e.link4) + if (t != e.link5) + if (t != e.link6) + if (t != e.link7) + if (t != e.link8) + if (t != e.link9) + if (t != e.link10) + if (t != e.link11) + if (t != e.link12) + if (t != e.link13) + if (t != e.link14) + if (t != e.link15) + if (t != e.link16) + if (t != e.link17) + if (t != e.link18) + if (t != e.link19) + { + if (!e.link0) + e.link0 = t; + else if (!e.link1) + e.link1 = t; + else if (!e.link2) + e.link2 = t; + else if (!e.link3) + e.link3 = t; + else if (!e.link4) + e.link4 = t; + else if (!e.link5) + e.link5 = t; + else if (!e.link6) + e.link6 = t; + else if (!e.link7) + e.link7 = t; + else if (!e.link8) + e.link8 = t; + else if (!e.link9) + e.link9 = t; + else if (!e.link10) + e.link10 = t; + else if (!e.link11) + e.link11 = t; + else if (!e.link12) + e.link12 = t; + else if (!e.link13) + e.link13 = t; + else if (!e.link14) + e.link14 = t; + else if (!e.link15) + e.link15 = t; + else if (!e.link16) + e.link16 = t; + else if (!e.link17) + e.link17 = t; + else if (!e.link18) + e.link18 = t; + else if (!e.link19) + e.link19 = t; + else + dprint("WARNING: Too many linking NavNodes!\n"); + } + t = t.list; + } + } + e = e.list; + } + e = navnode_chain; + while (e) + { + if (e.sflags & S_TELEPORT) + { + t = findchain(classname, "trigger_teleport"); + while (t) + { + if (boxesoverlap(t.absmin, t.absmax, e.absmin, e.absmax)) + { + tdest = find(world, targetname, t.target); + tdest = FindCurrentNavNode(tdest.origin, '0 0 0', '0 0 0'); + e.link0 = tdest; + } + t = t.chain; + } + if (!e.link0) + error("Expected navnode\n"); + } + else if (e.sflags & S_DOOR) + { + t = findchain(classname, "door"); + while (t) + { + if (boxesoverlap(t.absmin, t.absmax, e.absmin, e.absmax)) + e.goalentity = t; + t = t.chain; + } + if (!e.goalentity) + error("Expected door\n"); + } + e = e.list; + } +}; + +void() LoadNavNodes = +{ + local float f, file, length; + local float pdst; + local float sfl; + local float tfl0, tfl1, tfl2, tfl3, tfl4, tfl5, tfl6, tfl7, tfl8, tfl9; + local float tfl10, tfl11, tfl12, tfl13, tfl14, tfl15, tfl16, tfl17, tfl18, tfl19; + local string s; + local float rl0, rl1, rl2, rl3, rl4, rl5, rl6, rl7, rl8, rl9; + local float rl10, rl11, rl12, rl13, rl14, rl15, rl16, rl17, rl18, rl19; + local vector pln; + local vector wborg, wbmaxs, wbmins; + local entity e, plane_storage; + + if (navnodes) + { + dprint("NavNodes present, map needs to be reloaded to load navnode-file\n"); + return; + } + + plane_storage = spawn(); + + length = strlen(world.model); + length = length - 5; + s = substring(world.model, 5, length); + s = strcat(s, ".nnl"); + + file = fopen(s, FILE_READ); + + if (file >= 0) + { + dprint("Found navnode file\n"); + s = fgets(file); + while (s == "navnode") + { + f = 0; + pdst = -1; + pln = nullvector; + wborg = wbmaxs = wbmins = nullvector; + plane_storage.plane_chain = world; + sfl = 0; + tfl0 = tfl1 = tfl2 = tfl3 = tfl4 = tfl5 = tfl6 = tfl7 = tfl8 = tfl9 = 0; + tfl10 = tfl11 = tfl12 = tfl13 = tfl14 = tfl15 = tfl16 = tfl17 = tfl18 = tfl19 = 0; + rl0 = rl1 = rl2 = rl3 = rl4 = rl5 = rl6 = rl7 = rl8 = rl9 = -1; + rl10 = rl11 = rl12 = rl13 = rl14 = rl15 = rl16 = rl17 = rl18 = rl19 = -1; + s = fgets(file); + wborg = stov(s); + s = fgets(file); + wbmins = stov(s); + s = fgets(file); + wbmaxs = stov(s); + s = fgets(file); + while (s == "plane") + { + s = fgets(file); + pln = stov(s); + s = fgets(file); + pdst = stof(s); + AddPlane(plane_storage, pln*-1, pdst); + s = fgets(file); + } + while (substring(s, 0, 6) == "remote") + { + length = strlen(s); + f = stof(substring(s, 6, length)); + s = fgets(file); + if (!f) + rl0 = stof(s); + else if (f == 1) + rl1 = stof(s); + else if (f == 2) + rl2 = stof(s); + else if (f == 3) + rl3 = stof(s); + else if (f == 4) + rl4 = stof(s); + else if (f == 5) + rl5 = stof(s); + else if (f == 6) + rl6 = stof(s); + else if (f == 7) + rl7 = stof(s); + else if (f == 8) + rl8 = stof(s); + else if (f == 9) + rl9 = stof(s); + else if (f == 10) + rl10 = stof(s); + else if (f == 11) + rl11 = stof(s); + else if (f == 12) + rl12 = stof(s); + else if (f == 13) + rl13 = stof(s); + else if (f == 14) + rl14 = stof(s); + else if (f == 15) + rl15 = stof(s); + else if (f == 16) + rl16 = stof(s); + else if (f == 17) + rl17 = stof(s); + else if (f == 18) + rl18 = stof(s); + else if (f == 19) + rl19 = stof(s); + s = fgets(file); + } + if (s == "sflags") + { + s = fgets(file); + sfl = stof(s); + s = fgets(file); + } + while (substring(s, 0, 6) == "lflags") + { + length = strlen(s); + f = stof(substring(s, 6, length)); + s = fgets(file); + if (!f) + tfl0 = stof(s); + else if (f == 1) + tfl1 = stof(s); + else if (f == 2) + tfl2 = stof(s); + else if (f == 3) + tfl3 = stof(s); + else if (f == 4) + tfl4 = stof(s); + else if (f == 5) + tfl5 = stof(s); + else if (f == 6) + tfl6 = stof(s); + else if (f == 7) + tfl7 = stof(s); + else if (f == 8) + tfl8 = stof(s); + else if (f == 9) + tfl9 = stof(s); + else if (f == 10) + tfl10 = stof(s); + else if (f == 11) + tfl11 = stof(s); + else if (f == 12) + tfl12 = stof(s); + else if (f == 13) + tfl13 = stof(s); + else if (f == 14) + tfl14 = stof(s); + else if (f == 15) + tfl15 = stof(s); + else if (f == 16) + tfl16 = stof(s); + else if (f == 17) + tfl17 = stof(s); + else if (f == 18) + tfl18 = stof(s); + else if (f == 19) + tfl19 = stof(s); + s = fgets(file); + } + e = SpawnNavNode(wborg, wbmins, wbmaxs); + if (plane_storage.plane_chain) + e.plane_chain = plane_storage.plane_chain; + e.sflags = sfl; + e.lflags0 = tfl0; + e.lflags1 = tfl1; + e.lflags2 = tfl2; + e.lflags3 = tfl3; + e.lflags4 = tfl4; + e.lflags5 = tfl5; + e.lflags6 = tfl6; + e.lflags7 = tfl7; + e.lflags8 = tfl8; + e.lflags9 = tfl9; + e.lflags10 = tfl10; + e.lflags11 = tfl11; + e.lflags12 = tfl12; + e.lflags13 = tfl13; + e.lflags14 = tfl14; + e.lflags15 = tfl15; + e.lflags16 = tfl16; + e.lflags17 = tfl17; + e.lflags18 = tfl18; + e.lflags19 = tfl19; + if (rl0 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl0; + e.link0 = newmis; + } + if (rl1 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl1; + e.link1 = newmis; + } + if (rl2 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl2; + e.link2 = newmis; + } + if (rl3 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl3; + e.link3 = newmis; + } + if (rl4 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl4; + e.link4 = newmis; + } + if (rl5 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl5; + e.link5 = newmis; + } + if (rl6 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl6; + e.link6 = newmis; + } + if (rl7 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl7; + e.link7 = newmis; + } + if (rl8 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl8; + e.link8 = newmis; + } + if (rl9 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl9; + e.link9 = newmis; + } + if (rl10 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl10; + e.link10 = newmis; + } + if (rl11 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl11; + e.link11 = newmis; + } + if (rl12 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl12; + e.link12 = newmis; + } + if (rl13 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl13; + e.link13 = newmis; + } + if (rl14 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl14; + e.link14 = newmis; + } + if (rl15 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl15; + e.link15 = newmis; + } + if (rl16 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl16; + e.link16 = newmis; + } + if (rl17 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl17; + e.link17 = newmis; + } + if (rl18 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl18; + e.link18 = newmis; + } + if (rl19 > -1) + { + newmis = spawn(); + newmis.classname = "remotelinker"; + newmis.delay = rl19; + e.link19 = newmis; + } + } + fclose(file); + } + else + dprint("No navnode file found\n"); + remove(plane_storage); + plane_storage = world; +}; + +/* --- SpawnWayBox --- +The one to guess what this does gets a cookie from Quest*/ +/* +entity(vector org, vector wsize) SpawnWayBox = +{ + newmis = spawn(); + setsize(newmis, wsize*-1, wsize); + newmis.classname = "waybox"; + setorigin(newmis, org); + return (newmis); +}; +*/ +/* --- LinkWayBoxes --- +Links wayboxes and gives some special attributes (like doors, teleporters)*/ + +/* +void() LinkWayBoxes = +{ + local entity e, t, tdest; + + box_chain = world; + wayboxes = 0; + + e = findchain(classname, "waybox"); + while (e) + { + e.list = box_chain; + box_chain = e; + e.link0 = e.link1 = e.link2 = e.link3 = e.link4 = e.link5 = e.link6 = e.link7 = e.link8 = e.link9 = world; + e.link10 = e.link11 = e.link12 = e.link13 = e.link14 = e.link15 = e.link16 = e.link17 = e.link18 = e.link19 = world; + wayboxes = wayboxes + 1; + e = e.chain; + } + + e = box_chain; + while (e) + { + if (!(e.sflags & S_TELEPORT)) + { + t = box_chain; + while (t) + { + if (boxesoverlap(e.origin + e.mins, e.origin + e.maxs, t.origin + t.mins, t.origin + t.maxs)) + if (t != e) + if (t != e.link0) + if (t != e.link1) + if (t != e.link2) + if (t != e.link3) + if (t != e.link4) + if (t != e.link5) + if (t != e.link6) + if (t != e.link7) + if (t != e.link8) + if (t != e.link9) + if (t != e.link10) + if (t != e.link11) + if (t != e.link12) + if (t != e.link13) + if (t != e.link14) + if (t != e.link15) + if (t != e.link16) + if (t != e.link17) + if (t != e.link18) + if (t != e.link19) + { + if (!e.link0) + e.link0 = t; + else if (!e.link1) + e.link1 = t; + else if (!e.link2) + e.link2 = t; + else if (!e.link3) + e.link3 = t; + else if (!e.link4) + e.link4 = t; + else if (!e.link5) + e.link5 = t; + else if (!e.link6) + e.link6 = t; + else if (!e.link7) + e.link7 = t; + else if (!e.link8) + e.link8 = t; + else if (!e.link9) + e.link9 = t; + else if (!e.link10) + e.link10 = t; + else if (!e.link11) + e.link11 = t; + else if (!e.link12) + e.link12 = t; + else if (!e.link13) + e.link13 = t; + else if (!e.link14) + e.link14 = t; + else if (!e.link15) + e.link15 = t; + else if (!e.link16) + e.link16 = t; + else if (!e.link17) + e.link17 = t; + else if (!e.link18) + e.link18 = t; + else if (!e.link19) + e.link19 = t; + else + objerror("Too many touching wayboxes!\n"); + } + t = t.list; + } + } + e = e.list; + } + e = box_chain; + while (e) + { + if (e.sflags & S_TELEPORT) + { + t = findchain(classname, "trigger_teleport"); + while (t) + { + if (boxesoverlap(t.absmin, t.absmax, e.absmin, e.absmax)) + { + tdest = find(world, targetname, t.target); + tdest = FindCurrentWayBox(tdest.origin, '0 0 0', '0 0 0'); + e.link0 = tdest; + } + t = t.chain; + } + } + else if (e.sflags & S_DOOR) + { + t = findchain(classname, "door"); + while (t) + { + if (boxesoverlap(t.absmin, t.absmax, e.absmin, e.absmax)) + e.goalentity = t; + t = t.chain; + } + if (!e.goalentity) + error("Expected door\n"); + } + e = e.list; + } +}; +*/ + +/* --- LoadWayBoxes --- +Loads wayboxes from a file generated by the waybox-editor mod*/ + +/* +void() LoadNavs = +{ + local float f, file, length; + local float sfl; + local float tfl0, tfl1, tfl2, tfl3, tfl4, tfl5, tfl6, tfl7, tfl8, tfl9; + local float tfl10, tfl11, tfl12, tfl13, tfl14, tfl15, tfl16, tfl17, tfl18, tfl19; + local string s, nname; + local vector wborg, wbmaxs; + local entity e; + + if (navents) + { + dprint("NavEntities present, map needs to be reloaded to load navent-file\n"); + return; + } + + length = strlen(world.model); + length = length - 5; + s = substring(world.model, 5, length); + s = strcat(s, ".nel"); + + file = fopen(s, FILE_READ); + + if (file >= 0) + { + s = fgets(file); + while (s == "navent") + { + f = 0; + sfl = 0; + tfl0 = tfl1 = tfl2 = tfl3 = tfl4 = tfl5 = tfl6 = tfl7 = tfl8 = tfl9 = 0; + tfl10 = tfl11 = tfl12 = tfl13 = tfl14 = tfl15 = tfl16 = tfl17 = tfl18 = tfl19 = 0; + s = fgets(file); + if (s == "sflags") + { + s = fgets(file); + sfl = stof(s); + s = fgets(file); + } + while (substring(s, 0, 6) == "tflags") + { + f = stof(substring(s, 6, 7)); + s = fgets(file); + if (!f) + tfl0 = stof(s); + else if (f == 1) + tfl1 = stof(s); + else if (f == 2) + tfl2 = stof(s); + else if (f == 3) + tfl3 = stof(s); + else if (f == 4) + tfl4 = stof(s); + else if (f == 5) + tfl5 = stof(s); + else if (f == 6) + tfl6 = stof(s); + else if (f == 7) + tfl7 = stof(s); + else if (f == 8) + tfl8 = stof(s); + else if (f == 9) + tfl9 = stof(s); + else if (f == 10) + tfl10 = stof(s); + else if (f == 11) + tfl11 = stof(s); + else if (f == 12) + tfl12 = stof(s); + else if (f == 13) + tfl13 = stof(s); + else if (f == 14) + tfl14 = stof(s); + else if (f == 15) + tfl15 = stof(s); + else if (f == 16) + tfl16 = stof(s); + else if (f == 17) + tfl17 = stof(s); + else if (f == 18) + tfl18 = stof(s); + else if (f == 19) + tfl19 = stof(s); + s = fgets(file); + } + wborg = stov(s); + s = fgets(file); + wbmaxs = stov(s); + e = SpawnNavEnt(wborg, wbmaxs); + e.sflags = sfl; + e.tflags0 = tfl0; + e.tflags1 = tfl1; + e.tflags2 = tfl2; + e.tflags3 = tfl3; + e.tflags4 = tfl4; + e.tflags5 = tfl5; + e.tflags6 = tfl6; + e.tflags7 = tfl7; + e.tflags8 = tfl8; + e.tflags9 = tfl9; + e.tflags10 = tfl10; + e.tflags11 = tfl11; + e.tflags12 = tfl12; + e.tflags13 = tfl13; + e.tflags14 = tfl14; + e.tflags15 = tfl15; + e.tflags16 = tfl16; + e.tflags17 = tfl17; + e.tflags18 = tfl18; + e.tflags19 = tfl19; + s = fgets(file); + } + fclose(file); + } + LinkNavs(); +}; +*/ +/* +void() LoadWayBoxes = +{ + local float f, file, length; + local float sfl; + local float tfl0, tfl1, tfl2, tfl3, tfl4, tfl5, tfl6, tfl7, tfl8, tfl9; + local float tfl10, tfl11, tfl12, tfl13, tfl14, tfl15, tfl16, tfl17, tfl18, tfl19; + local string s, nname; + local vector wborg, wbmaxs; + local entity e; + + if (wayboxes) + { + dprint("Wayboxes present, map needs to be reloaded to load waybox-file\n"); + return; + } + + length = strlen(world.model); + length = length - 5; + s = substring(world.model, 5, length); + s = strcat(s, ".wbl"); + + file = fopen(s, FILE_READ); + + if (file >= 0) + { + s = fgets(file); + while (s == "waybox") + { + f = 0; + sfl = 0; + tfl0 = tfl1 = tfl2 = tfl3 = tfl4 = tfl5 = tfl6 = tfl7 = tfl8 = tfl9 = 0; + tfl10 = tfl11 = tfl12 = tfl13 = tfl14 = tfl15 = tfl16 = tfl17 = tfl18 = tfl19 = 0; + s = fgets(file); + if (s == "sflags") + { + s = fgets(file); + sfl = stof(s); + s = fgets(file); + } + while (substring(s, 0, 6) == "tflags") + { + f = stof(substring(s, 6, 7)); + s = fgets(file); + if (!f) + tfl0 = stof(s); + else if (f == 1) + tfl1 = stof(s); + else if (f == 2) + tfl2 = stof(s); + else if (f == 3) + tfl3 = stof(s); + else if (f == 4) + tfl4 = stof(s); + else if (f == 5) + tfl5 = stof(s); + else if (f == 6) + tfl6 = stof(s); + else if (f == 7) + tfl7 = stof(s); + else if (f == 8) + tfl8 = stof(s); + else if (f == 9) + tfl9 = stof(s); + else if (f == 10) + tfl10 = stof(s); + else if (f == 11) + tfl11 = stof(s); + else if (f == 12) + tfl12 = stof(s); + else if (f == 13) + tfl13 = stof(s); + else if (f == 14) + tfl14 = stof(s); + else if (f == 15) + tfl15 = stof(s); + else if (f == 16) + tfl16 = stof(s); + else if (f == 17) + tfl17 = stof(s); + else if (f == 18) + tfl18 = stof(s); + else if (f == 19) + tfl19 = stof(s); + s = fgets(file); + } + wborg = stov(s); + s = fgets(file); + wbmaxs = stov(s); + e = SpawnWayBox(wborg, wbmaxs); + e.sflags = sfl; + e.tflags0 = tfl0; + e.tflags1 = tfl1; + e.tflags2 = tfl2; + e.tflags3 = tfl3; + e.tflags4 = tfl4; + e.tflags5 = tfl5; + e.tflags6 = tfl6; + e.tflags7 = tfl7; + e.tflags8 = tfl8; + e.tflags9 = tfl9; + e.tflags10 = tfl10; + e.tflags11 = tfl11; + e.tflags12 = tfl12; + e.tflags13 = tfl13; + e.tflags14 = tfl14; + e.tflags15 = tfl15; + e.tflags16 = tfl16; + e.tflags17 = tfl17; + e.tflags18 = tfl18; + e.tflags19 = tfl19; + s = fgets(file); + } + fclose(file); + } +}; +*/ \ No newline at end of file diff --git a/data/qcsrc/gamec/urrebot_nn_search.qc b/data/qcsrc/gamec/urrebot_nn_search.qc new file mode 100644 index 000000000..f31d8a86f --- /dev/null +++ b/data/qcsrc/gamec/urrebot_nn_search.qc @@ -0,0 +1,100 @@ +/* --- CheckNavNode --- +Evaluates travel cost and wether to continue search or not in this direction*/ + +float(entity from, entity to, float lflag) CheckNavNode = +{ + local float addcost; + local vector foundpoint; + + if (!to) + return FALSE; + + if (!(lflag & LF_NOWALK)) + if (!(lflag & LF_NOLINK)) + { + if (from.sflags & S_TELEPORT) // teleporter exception + foundpoint = from.origin; + else + foundpoint = ClampPointToSpace(from.pointl, from, to); + addcost = vlen(from.pointl - foundpoint); + addcost = addcost + from.costl; + if (addcost <= search_distance) + if (to.costl > addcost) + { + to.lmark = TRUE; + to.enemy = from; + to.costl = addcost; + to.pointl = foundpoint; + } + } + return TRUE; +}; + +/* --- MarkRoute --- +Searches as far as possible, and gives all navnodes a travel cost and shortest +travel point, which is then used for goal evaluation + +Starts by clearing all navnodes, and searches breadth first from the starting navnode*/ + +void(float sdist) MarkRoute = +{ + local entity t, start; + local float searching; + + start = FindCurrentNavNode(self.origin, self.mins, self.maxs); + + if(!start) + return; + + search_distance = sdist; + + t = navnode_chain; + while (t) + { + t.enemy = world; + t.lmark = FALSE; + t.costl = 10000000; + t.pointl = '0 0 0'; + t = t.list; + } + + start.lmark = TRUE; + start.costl = 0; + start.pointl = self.origin; + + searching = TRUE; + while(searching) + { + searching = FALSE; + t = navnode_chain; + while(t) + { + if(t.lmark) + { + searching = TRUE; + t.lmark = FALSE; + if (CheckNavNode(t, t.link0, t.lflags0)) + if (CheckNavNode(t, t.link1, t.lflags1)) + if (CheckNavNode(t, t.link2, t.lflags2)) + if (CheckNavNode(t, t.link3, t.lflags3)) + if (CheckNavNode(t, t.link4, t.lflags4)) + if (CheckNavNode(t, t.link5, t.lflags5)) + if (CheckNavNode(t, t.link6, t.lflags6)) + if (CheckNavNode(t, t.link7, t.lflags7)) + if (CheckNavNode(t, t.link8, t.lflags8)) + if (CheckNavNode(t, t.link9, t.lflags9)) + if (CheckNavNode(t, t.link10, t.lflags10)) + if (CheckNavNode(t, t.link11, t.lflags11)) + if (CheckNavNode(t, t.link12, t.lflags12)) + if (CheckNavNode(t, t.link13, t.lflags13)) + if (CheckNavNode(t, t.link14, t.lflags14)) + if (CheckNavNode(t, t.link15, t.lflags15)) + if (CheckNavNode(t, t.link16, t.lflags16)) + if (CheckNavNode(t, t.link17, t.lflags17)) + if (CheckNavNode(t, t.link18, t.lflags18)) + CheckNavNode(t, t.link19, t.lflags19); + } + t = t.list; + } + } +}; \ No newline at end of file diff --git a/data/qcsrc/gamec/urrebot_nn_use.qc b/data/qcsrc/gamec/urrebot_nn_use.qc new file mode 100644 index 000000000..c8bf96a2e --- /dev/null +++ b/data/qcsrc/gamec/urrebot_nn_use.qc @@ -0,0 +1,295 @@ +/* --- ClampPointToNavNodes --- +This function lets the bot know where to go to reach the next navnode. + +This is a highly optimized version, it used to also be direction based, so it +was far more accurate. However, this accuracy was so slow, that it was a +show-stopper. Now it merely takes the shortest path to the navnode. Due to this, +navigation gets more and more inaccurate as the navnodes get larger. +A regular indoor map doesn't require navnodes of the kind of size that would +suffer from this. I hope to reinstate direction based some time in the future, +if I get it fast enough.*/ + +vector(vector point, entity current_space, entity goal_space) ClampPointToSpace = +{ + local float North, South, West, East, Up, Down; + local float f, f2; + local vector ret_point, tvec; + local entity e; + + if (!goal_space) + goal_space = current_space; + + North = min(current_space.origin_y + current_space.maxs_y, goal_space.origin_y + goal_space.maxs_y); + South = max(current_space.origin_y + current_space.mins_y, goal_space.origin_y + goal_space.mins_y); + East = min(current_space.origin_x + current_space.maxs_x, goal_space.origin_x + goal_space.maxs_x); + West = max(current_space.origin_x + current_space.mins_x, goal_space.origin_x + goal_space.mins_x); + Up = min(current_space.origin_z + current_space.maxs_z, goal_space.origin_z + goal_space.maxs_z); + Down = max(current_space.origin_z + current_space.mins_z, goal_space.origin_z + goal_space.mins_z); + + f = (East + West) * 0.5; + f2 = East - self.maxs_x; + East = max(f, f2); + f2 = West - self.mins_x; + West = min(f, f2); + f = (North + South) * 0.5; + f2 = North - self.maxs_y; + North = max(f, f2); + f2 = South - self.mins_y; + South = min(f, f2); + f = (Up + Down) * 0.5; + f2 = Up - self.maxs_z; + Up = max(f, f2); + f2 = Down - self.mins_z; + Down = min(f, f2); + + ret_point_x = bound(West, point_x, East); + ret_point_y = bound(South, point_y, North); + ret_point_z = bound(Down, point_z, Up); + + e = goal_space.plane_chain; + while (e) + { + tvec = self.maxs; + if (e.mangle_x < 0) + tvec_x = self.mins_x; + if (e.mangle_y < 0) + tvec_y = self.mins_y; + if (e.mangle_z < 0) + tvec_z = self.mins_z; + tvec += ret_point; + f = tvec*e.mangle - goal_space.origin*e.mangle-e.delay; + if (f > 0) + ret_point = ret_point - f*e.mangle; + e = e.list; + } + + return ret_point; +}; + +/* --- PopRoute --- +Traverses the bots navnode-list to get a new navnode to travel towards*/ + +void() PopRoute = +{ + self.goalcurrent = self.link0; + self.link0 = self.link1; + self.link1 = self.link2; + self.link2 = self.link3; + self.link3 = self.link4; + self.link4 = self.link5; + self.link5 = self.link6; + self.link6 = self.link7; + self.link7 = self.link8; + self.link8 = self.link9; + self.link9 = self.link10; + self.link10 = self.link11; + self.link11 = self.link12; + self.link12 = self.link13; + self.link13 = self.link14; + self.link14 = self.link15; + self.link15 = self.link16; + self.link16 = self.link17; + self.link17 = self.link18; + self.link18 = self.link19; + self.link19 = world; + if (cvar("urrebots_debug")) + { + bprint(self.goalcurrent.classname); + bprint("\n"); + } +}; + +/* --- PushRoute --- +Adds navnodes to the bots navnode-list*/ + +void(entity e) PushRoute = +{ + self.link19 = self.link18; + self.link18 = self.link17; + self.link17 = self.link16; + self.link16 = self.link15; + self.link15 = self.link14; + self.link14 = self.link13; + self.link13 = self.link12; + self.link12 = self.link11; + self.link11 = self.link10; + self.link10 = self.link9; + self.link9 = self.link8; + self.link8 = self.link7; + self.link7 = self.link6; + self.link6 = self.link5; + self.link5 = self.link4; + self.link4 = self.link3; + self.link3 = self.link2; + self.link2 = self.link1; + self.link1 = self.link0; + self.link0 = self.goalcurrent; + self.goalcurrent = e; +}; + +/* --- ClearRoute --- +Removes all navnodes from the bots navnode-list*/ + +void() ClearRoute = +{ + self.movepoint = nullvector; + self.goalcurrent = world; + self.link0 = world; + self.link1 = world; + self.link2 = world; + self.link3 = world; + self.link4 = world; + self.link5 = world; + self.link6 = world; + self.link7 = world; + self.link8 = world; + self.link9 = world; + self.link10 = world; + self.link11 = world; + self.link12 = world; + self.link13 = world; + self.link14 = world; + self.link15 = world; + self.link16 = world; + self.link17 = world; + self.link18 = world; + self.link19 = world; +}; + +/* --- FindCurrentNavNode --- +Returns the current navnode at an origin with a size*/ +// FIXME: you can do better +entity(vector org, vector minss, vector maxss) FindCurrentNavNode = +{ + local float f, f2, bad; + local vector tvec; + local entity e, plane, best; + + e = navnode_chain; + while (e) + { + bad = FALSE; + if (boxesoverlap(org, org, e.origin + e.mins, e.origin + e.maxs)) + { + plane = e.plane_chain; + while (plane) + { + f = org*plane.mangle - e.origin*plane.mangle-plane.delay; + if (f > 0) + bad = TRUE; + plane = plane.list; + } + if (!bad) + return e; + } + e = e.list; + } + e = navnode_chain; + while (e) + { + bad = FALSE; + if (boxesoverlap(org + minss, org + maxss, e.origin + e.mins, e.origin + e.maxs)) + { + plane = e.plane_chain; + while (plane) + { + tvec = maxss; + if (plane.mangle_x < 0) + tvec_x = minss_x; + if (plane.mangle_y < 0) + tvec_y = minss_y; + if (plane.mangle_z < 0) + tvec_z = minss_z; + tvec += org; + f = tvec*plane.mangle - e.origin*plane.mangle-plane.delay; + if (f > 0) + bad = TRUE; + plane = plane.list; + } + if (!bad) + return e; + } + e = e.list; + } + f2 = 10000000; + e = navnode_chain; + while (e) + { + traceline(org, e.origin, TRUE, world); + if (trace_fraction == 1) + { + f = vlen(org - e.origin); + if (f < f2) + { + best = e; + f2 = f; + } + } + e = e.list; + } + return best; +}; + +/* --- ToPointInNavNode --- +Returns a direction towards a point, inside a navnode +It prefers staying inside a navnode over going towards the point +This function exists to allow the bot to drop off a platform and run +under it, and to avoid getting stuck in corners*/ + +vector(entity space, vector point) ToPointInSpace = +{ + local float f; + vector tvec, ret_dir, intonavn, towardspoint; + local entity e; + + if (self.origin_x + self.mins_x <= space.origin_x + space.mins_x) + intonavn = '1 0 0'; + else if (self.origin_x + self.maxs_x >= space.origin_x + space.maxs_x) + intonavn = '-1 0 0'; + if (self.origin_y + self.mins_y <= space.origin_y + space.mins_y) + intonavn += '0 1 0'; + else if (self.origin_y + self.maxs_y >= space.origin_y + space.maxs_y) + intonavn += '0 -1 0'; + if (self.origin_z + self.mins_z <= space.origin_z + space.mins_z) + intonavn += '0 0 1'; + else if (self.origin_z + self.maxs_z >= space.origin_z + space.maxs_z) + intonavn += '0 0 -1'; + + e = space.plane_chain; + while (e) + { + tvec = self.maxs; + if (e.mangle_x < 0) + tvec_x = self.mins_x; + if (e.mangle_y < 0) + tvec_y = self.mins_y; + if (e.mangle_z < 0) + tvec_z = self.mins_z; + tvec += self.origin; + f = tvec*e.mangle - space.origin*e.mangle-e.delay; + if (f > 0) + intonavn = intonavn - e.mangle; + e = e.list; + } + intonavn = normalize(intonavn); + + towardspoint = normalize(point - self.origin); + if (!intonavn) + ret_dir = towardspoint; + else + { + if ((intonavn_x < 0 && towardspoint_x < 0) || (intonavn_x > 0 && towardspoint_x > 0)) + ret_dir_x = towardspoint_x; + else + ret_dir_x = intonavn_x; + if ((intonavn_y < 0 && towardspoint_y < 0) || (intonavn_y > 0 && towardspoint_y > 0)) + ret_dir_y = towardspoint_y; + else + ret_dir_y = intonavn_y; + if ((intonavn_z < 0 && towardspoint_z < 0) || (intonavn_z > 0 && towardspoint_z > 0)) + ret_dir_z = towardspoint_z; + else + ret_dir_z = intonavn_z; + } + return ret_dir; +}; \ No newline at end of file -- 2.39.2