Revert "Merge branch 'TimePath/bot_api' into 'master'\r"
authorTimePath <andrew.hardaker1995@gmail.com>
Thu, 8 Oct 2015 11:35:21 +0000 (22:35 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Thu, 8 Oct 2015 11:35:21 +0000 (22:35 +1100)
This reverts commit a39af09cb4b15ec94461af2c1f314098ffe7ce0c, reversing
changes made to 81b0f2bc5760bb652515453ac450f2822a98b725.

98 files changed:
qcsrc/client/movelib.qc [new file with mode: 0644]
qcsrc/client/movelib.qh [new file with mode: 0644]
qcsrc/client/progs.inc
qcsrc/common/monsters/monster/zombie.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/physics.qc
qcsrc/common/triggers/func/breakable.qc
qcsrc/common/turrets/turret/walker.qc
qcsrc/common/weapons/all.qh
qcsrc/dpdefs/csprogsdefs.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/aim.qc [new file with mode: 0644]
qcsrc/server/bot/aim.qh [new file with mode: 0644]
qcsrc/server/bot/api.qh [deleted file]
qcsrc/server/bot/bot.qc [new file with mode: 0644]
qcsrc/server/bot/bot.qh [new file with mode: 0644]
qcsrc/server/bot/default/aim.qc [deleted file]
qcsrc/server/bot/default/aim.qh [deleted file]
qcsrc/server/bot/default/all.inc [deleted file]
qcsrc/server/bot/default/bot.qc [deleted file]
qcsrc/server/bot/default/bot.qh [deleted file]
qcsrc/server/bot/default/defs.qh [deleted file]
qcsrc/server/bot/default/havocbot/havocbot.qc [deleted file]
qcsrc/server/bot/default/havocbot/havocbot.qh [deleted file]
qcsrc/server/bot/default/havocbot/role_keyhunt.qc [deleted file]
qcsrc/server/bot/default/havocbot/role_keyhunt.qh [deleted file]
qcsrc/server/bot/default/havocbot/roles.qc [deleted file]
qcsrc/server/bot/default/havocbot/roles.qh [deleted file]
qcsrc/server/bot/default/navigation.qc [deleted file]
qcsrc/server/bot/default/navigation.qh [deleted file]
qcsrc/server/bot/default/scripting.qc [deleted file]
qcsrc/server/bot/default/scripting.qh [deleted file]
qcsrc/server/bot/default/waypoints.qc [deleted file]
qcsrc/server/bot/default/waypoints.qh [deleted file]
qcsrc/server/bot/havocbot/havocbot.qc [new file with mode: 0644]
qcsrc/server/bot/havocbot/havocbot.qh [new file with mode: 0644]
qcsrc/server/bot/havocbot/role_keyhunt.qc [new file with mode: 0644]
qcsrc/server/bot/havocbot/role_keyhunt.qh [new file with mode: 0644]
qcsrc/server/bot/havocbot/roles.qc [new file with mode: 0644]
qcsrc/server/bot/havocbot/roles.qh [new file with mode: 0644]
qcsrc/server/bot/havocbot/scripting.qh [new file with mode: 0644]
qcsrc/server/bot/impl.qc [deleted file]
qcsrc/server/bot/lib/all.inc [deleted file]
qcsrc/server/bot/lib/movelib/all.inc [deleted file]
qcsrc/server/bot/lib/movelib/movelib.qc [deleted file]
qcsrc/server/bot/lib/movelib/movelib.qh [deleted file]
qcsrc/server/bot/lib/pathlib/all.inc [deleted file]
qcsrc/server/bot/lib/pathlib/costs.qc [deleted file]
qcsrc/server/bot/lib/pathlib/debug.qc [deleted file]
qcsrc/server/bot/lib/pathlib/expandnode.qc [deleted file]
qcsrc/server/bot/lib/pathlib/main.qc [deleted file]
qcsrc/server/bot/lib/pathlib/main.qh [deleted file]
qcsrc/server/bot/lib/pathlib/movenode.qc [deleted file]
qcsrc/server/bot/lib/pathlib/path_waypoint.qc [deleted file]
qcsrc/server/bot/lib/pathlib/pathlib.qh [deleted file]
qcsrc/server/bot/lib/pathlib/utility.qc [deleted file]
qcsrc/server/bot/lib/pathlib/utility.qh [deleted file]
qcsrc/server/bot/lib/steerlib/all.inc [deleted file]
qcsrc/server/bot/lib/steerlib/steerlib.qc [deleted file]
qcsrc/server/bot/lib/steerlib/steerlib.qh [deleted file]
qcsrc/server/bot/navigation.qc [new file with mode: 0644]
qcsrc/server/bot/navigation.qh [new file with mode: 0644]
qcsrc/server/bot/null/impl.qc [deleted file]
qcsrc/server/bot/scripting.qc [new file with mode: 0644]
qcsrc/server/bot/scripting.qh [new file with mode: 0644]
qcsrc/server/bot/waypoints.qc [new file with mode: 0644]
qcsrc/server/bot/waypoints.qh [new file with mode: 0644]
qcsrc/server/cl_client.qc
qcsrc/server/cl_impulse.qc
qcsrc/server/cl_player.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/defs.qh
qcsrc/server/g_world.qc
qcsrc/server/movelib.qc [new file with mode: 0644]
qcsrc/server/movelib.qh [new file with mode: 0644]
qcsrc/server/mutators/gamemode.qh
qcsrc/server/mutators/gamemode_assault.qh
qcsrc/server/mutators/gamemode_keyhunt.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/mutator.qh
qcsrc/server/mutators/mutators_include.qc
qcsrc/server/pathlib/costs.qc [new file with mode: 0644]
qcsrc/server/pathlib/debug.qc [new file with mode: 0644]
qcsrc/server/pathlib/expandnode.qc [new file with mode: 0644]
qcsrc/server/pathlib/main.qc [new file with mode: 0644]
qcsrc/server/pathlib/main.qh [new file with mode: 0644]
qcsrc/server/pathlib/movenode.qc [new file with mode: 0644]
qcsrc/server/pathlib/path_waypoint.qc [new file with mode: 0644]
qcsrc/server/pathlib/pathlib.qh [new file with mode: 0644]
qcsrc/server/pathlib/utility.qc [new file with mode: 0644]
qcsrc/server/pathlib/utility.qh [new file with mode: 0644]
qcsrc/server/progs.inc
qcsrc/server/race.qc
qcsrc/server/steerlib.qc [new file with mode: 0644]
qcsrc/server/steerlib.qh [new file with mode: 0644]
qcsrc/server/sv_main.qc
qcsrc/server/t_items.qc
qcsrc/server/teamplay.qc

diff --git a/qcsrc/client/movelib.qc b/qcsrc/client/movelib.qc
new file mode 100644 (file)
index 0000000..074f146
--- /dev/null
@@ -0,0 +1 @@
+#include "../server/movelib.qc"
diff --git a/qcsrc/client/movelib.qh b/qcsrc/client/movelib.qh
new file mode 100644 (file)
index 0000000..a0634f6
--- /dev/null
@@ -0,0 +1 @@
+#include "../server/movelib.qh"
index 4a10ef1..ec5c318 100644 (file)
@@ -19,6 +19,7 @@
 #include "mapvoting.qc"
 #include "miscfunctions.qc"
 #include "modeleffects.qc"
+#include "movelib.qc"
 #include "particles.qc"
 #include "player_skeleton.qc"
 #include "rubble.qc"
@@ -50,8 +51,6 @@
 #include "../common/minigames/minigames.qc"
 #include "../common/minigames/cl_minigames.qc"
 
-#include "../server/bot/lib/all.inc"
-
 #include "../common/buffs/all.qc"
 #include "../common/items/all.qc"
 #include "../common/monsters/all.qc"
index 05f4666..8425a12 100644 (file)
@@ -27,7 +27,6 @@ REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) {
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
-#include "../../../server/bot/lib/movelib/movelib.qh"
 float autocvar_g_monster_zombie_health;
 float autocvar_g_monster_zombie_damageforcescale = 0.55;
 float autocvar_g_monster_zombie_attack_melee_damage;
index 75837cf..8b05f78 100644 (file)
@@ -14,6 +14,7 @@
     #include "../../server/defs.qh"
     #include "../deathtypes.qh"
     #include "../../server/mutators/mutators_include.qh"
+       #include "../../server/steerlib.qh"
        #include "../turrets/sv_turrets.qh"
        #include "../turrets/util.qh"
     #include "../vehicles/all.qh"
index 6d63226..1a799c8 100644 (file)
@@ -1109,8 +1109,6 @@ void PM_check_frozen(void)
        }
 }
 
-.float ladder_time;
-
 void PM_check_hitground()
 {SELFPARAM();
 #ifdef SVQC
@@ -1302,8 +1300,6 @@ void PM_swim(float maxspd_mod)
        PM_ClientMovement_Move();
 }
 
-.entity ladder_entity;
-
 void PM_ladder(float maxspd_mod)
 {SELFPARAM();
        // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
index e6253b1..af9a6b4 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "../../../server/g_subs.qh"
 #include "../../../server/g_damage.qh"
+#include "../../../server/bot/bot.qh"
 #include "../../../common/csqcmodel_settings.qh"
 #include "../../../csqcmodellib/sv_model.qh"
 #include "../../../server/weapons/common.qh"
index 4a5ed2f..e629ada 100644 (file)
@@ -631,7 +631,7 @@ spawnfunc(turret_walker) { if(!turret_initialize(TUR_WALKER)) remove(self); }
 #endif // SVQC
 #ifdef CSQC
 
-#include "../../../server/bot/lib/movelib/movelib.qh"
+#include "../../../client/movelib.qh"
 
 void walker_draw(entity this)
 {
index faea45b..c96061f 100644 (file)
@@ -26,6 +26,10 @@ WepSet ReadWepSet();
 
 #include "../util.qh"
 
+#ifdef SVQC
+#include "../../server/bot/aim.qh"
+#endif
+
 REGISTRY(Weapons, 72) // Increase as needed. Can be up to 72.
 REGISTER_REGISTRY(RegisterWeapons)
 entity get_weaponinfo(int id);
index 0daa5dc..6ad2724 100644 (file)
@@ -23,8 +23,6 @@
 #undef particleeffectnum
 #undef setmodel
 
-entity(.entity fld, entity match) findchainentity = #403;
-
 #pragma noref 0
 
 #define ReadFloat() ReadCoord()
index 6a2ff9d..affd943 100644 (file)
@@ -7,6 +7,63 @@ bool autocvar__campaign_testrun;
 int autocvar__campaign_index;
 string autocvar__campaign_name;
 bool autocvar__sv_init;
+float autocvar_bot_ai_aimskill_blendrate;
+float autocvar_bot_ai_aimskill_firetolerance_distdegrees;
+float autocvar_bot_ai_aimskill_firetolerance_maxdegrees;
+float autocvar_bot_ai_aimskill_firetolerance_mindegrees;
+float autocvar_bot_ai_aimskill_fixedrate;
+float autocvar_bot_ai_aimskill_mouse;
+float autocvar_bot_ai_aimskill_offset;
+float autocvar_bot_ai_aimskill_order_filter_1st;
+float autocvar_bot_ai_aimskill_order_filter_2nd;
+float autocvar_bot_ai_aimskill_order_filter_3th;
+float autocvar_bot_ai_aimskill_order_filter_4th;
+float autocvar_bot_ai_aimskill_order_filter_5th;
+float autocvar_bot_ai_aimskill_order_mix_1st;
+float autocvar_bot_ai_aimskill_order_mix_2nd;
+float autocvar_bot_ai_aimskill_order_mix_3th;
+float autocvar_bot_ai_aimskill_order_mix_4th;
+float autocvar_bot_ai_aimskill_order_mix_5th;
+float autocvar_bot_ai_aimskill_think;
+float autocvar_bot_ai_bunnyhop_firstjumpdelay;
+float autocvar_bot_ai_bunnyhop_skilloffset;
+float autocvar_bot_ai_bunnyhop_startdistance;
+float autocvar_bot_ai_bunnyhop_stopdistance;
+float autocvar_bot_ai_chooseweaponinterval;
+string autocvar_bot_ai_custom_weapon_priority_close;
+string autocvar_bot_ai_custom_weapon_priority_distances;
+string autocvar_bot_ai_custom_weapon_priority_far;
+string autocvar_bot_ai_custom_weapon_priority_mid;
+float autocvar_bot_ai_dangerdetectioninterval;
+float autocvar_bot_ai_dangerdetectionupdates;
+float autocvar_bot_ai_enemydetectioninterval;
+float autocvar_bot_ai_enemydetectionradius;
+float autocvar_bot_ai_friends_aware_pickup_radius;
+float autocvar_bot_ai_ignoregoal_timeout;
+float autocvar_bot_ai_keyboard_distance;
+float autocvar_bot_ai_keyboard_threshold;
+float autocvar_bot_ai_navigation_jetpack;
+float autocvar_bot_ai_navigation_jetpack_mindistance;
+float autocvar_bot_ai_strategyinterval;
+float autocvar_bot_ai_thinkinterval;
+bool autocvar_bot_ai_weapon_combo;
+float autocvar_bot_ai_weapon_combo_threshold;
+string autocvar_bot_config_file;
+bool autocvar_bot_god;
+bool autocvar_bot_ignore_bots;
+bool autocvar_bot_join_empty;
+bool autocvar_bot_navigation_ignoreplayers;
+bool autocvar_bot_nofire;
+#define autocvar_bot_number cvar("bot_number")
+#define autocvar_bot_prefix cvar_string("bot_prefix")
+bool autocvar_bot_sound_monopoly;
+#define autocvar_bot_suffix cvar_string("bot_suffix")
+bool autocvar_bot_usemodelnames;
+int autocvar_bot_vs_human;
+int autocvar_bot_debug;
+bool autocvar_bot_debug_tracewalk;
+bool autocvar_bot_debug_goalstack;
+bool autocvar_bot_wander_enable;
 int autocvar_captureleadlimit_override;
 #define autocvar_capturelimit_override cvar("capturelimit_override")
 #define autocvar_developer cvar("developer")
@@ -236,6 +293,7 @@ float autocvar_g_ctf_dropped_capture_delay;
 float autocvar_g_ctf_dropped_capture_radius;
 float autocvar_g_cts_finish_kill_delay;
 bool autocvar_g_cts_selfdamage;
+bool autocvar_g_debug_bot_commands;
 int autocvar_g_domination_default_teams;
 bool autocvar_g_domination_disable_frags;
 int autocvar_g_domination_point_amt;
@@ -444,6 +502,7 @@ float autocvar_g_turrets_targetscan_maxdelay;
 float autocvar_g_turrets_targetscan_mindelay;
 bool autocvar_g_use_ammunition;
 bool autocvar_g_waypointeditor;
+int autocvar_g_waypointeditor_auto;
 bool autocvar_g_waypoints_for_items;
 float autocvar_g_weapon_charge_colormod_blue_full;
 float autocvar_g_weapon_charge_colormod_blue_half;
@@ -475,6 +534,7 @@ int autocvar_rescan_pending;
 bool autocvar_samelevel;
 string autocvar_sessionid;
 #define autocvar_skill cvar("skill")
+float autocvar_skill_auto;
 #define autocvar_slowmo cvar("slowmo")
 float autocvar_snd_soundradius;
 int autocvar_spawn_debug;
@@ -524,6 +584,7 @@ float autocvar_sv_itemstime;
 string autocvar_sv_jumpspeedcap_max;
 float autocvar_sv_jumpspeedcap_max_disable_on_ramps;
 string autocvar_sv_jumpspeedcap_min;
+float autocvar_sv_jumpvelocity;
 bool autocvar_sv_logscores_bots;
 bool autocvar_sv_logscores_console;
 bool autocvar_sv_logscores_file;
@@ -591,6 +652,7 @@ float autocvar_timelimit_overtime;
 int autocvar_timelimit_overtimes;
 float autocvar_timelimit_suddendeath;
 #define autocvar_utf8_enable cvar("utf8_enable")
+bool autocvar_waypoint_benchmark;
 float autocvar_sv_gameplayfix_gravityunaffectedbyticrate;
 bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag;
 float autocvar_g_trueaim_minrange;
diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc
new file mode 100644 (file)
index 0000000..4f750cd
--- /dev/null
@@ -0,0 +1,389 @@
+#include "aim.qh"
+#include "../_all.qh"
+
+#include "bot.qh"
+
+#include "../mutators/mutators_include.qh"
+
+// traces multiple trajectories to find one that will impact the target
+// 'end' vector is the place it aims for,
+// returns true only if it hit targ (don't target non-solid entities)
+
+float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore)
+{
+       float c, savesolid, shottime;
+       vector dir, end, v, o;
+       if (shotspeed < 1)
+               return false; // could cause division by zero if calculated
+       if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER
+               return false; // could never hit it
+       if (!tracetossent)
+               tracetossent = spawn();
+       tracetossent.owner = ignore;
+       setsize(tracetossent, m1, m2);
+       savesolid = targ.solid;
+       targ.solid = SOLID_NOT;
+       o = (targ.absmin + targ.absmax) * 0.5;
+       shottime = ((vlen(o - org) / shotspeed) + shotdelay);
+       v = targ.velocity * shottime + o;
+       tracebox(o, targ.mins, targ.maxs, v, false, targ);
+       v = trace_endpos;
+       end = v + (targ.mins + targ.maxs) * 0.5;
+       if ((vlen(end - org) / shotspeed + 0.2) > maxtime)
+       {
+               // out of range
+               targ.solid = savesolid;
+               return false;
+       }
+
+       if (!tracetossfaketarget)
+               tracetossfaketarget = spawn();
+       tracetossfaketarget.solid = savesolid;
+       tracetossfaketarget.movetype = targ.movetype;
+       _setmodel(tracetossfaketarget, targ.model); // no low precision
+       tracetossfaketarget.model = targ.model;
+       tracetossfaketarget.modelindex = targ.modelindex;
+       setsize(tracetossfaketarget, targ.mins, targ.maxs);
+       setorigin(tracetossfaketarget, v);
+
+       c = 0;
+       dir = normalize(end - org);
+       while (c < 10) // 10 traces
+       {
+               setorigin(tracetossent, org); // reset
+               tracetossent.velocity = findtrajectory_velocity = normalize(dir) * shotspeed + shotspeedupward * '0 0 1';
+               tracetoss(tracetossent, ignore); // love builtin functions...
+               if (trace_ent == tracetossfaketarget) // done
+               {
+                       targ.solid = savesolid;
+
+                       // make it disappear
+                       tracetossfaketarget.solid = SOLID_NOT;
+                       tracetossfaketarget.movetype = MOVETYPE_NONE;
+                       tracetossfaketarget.model = "";
+                       tracetossfaketarget.modelindex = 0;
+                       // relink to remove it from physics considerations
+                       setorigin(tracetossfaketarget, v);
+
+                       return true;
+               }
+               dir.z = dir.z + 0.1; // aim up a little more
+               c = c + 1;
+       }
+       targ.solid = savesolid;
+
+       // make it disappear
+       tracetossfaketarget.solid = SOLID_NOT;
+       tracetossfaketarget.movetype = MOVETYPE_NONE;
+       tracetossfaketarget.model = "";
+       tracetossfaketarget.modelindex = 0;
+       // relink to remove it from physics considerations
+       setorigin(tracetossfaketarget, v);
+
+       // leave a valid one even if it won't reach
+       findtrajectory_velocity = normalize(end - org) * shotspeed + shotspeedupward * '0 0 1';
+       return false;
+}
+
+void lag_update()
+{SELFPARAM();
+       if (self.lag1_time) if (time > self.lag1_time) {self.lag_func(self.lag1_time, self.lag1_float1, self.lag1_float2, self.lag1_entity1, self.lag1_vec1, self.lag1_vec2, self.lag1_vec3, self.lag1_vec4);self.lag1_time = 0;}
+       if (self.lag2_time) if (time > self.lag2_time) {self.lag_func(self.lag2_time, self.lag2_float1, self.lag2_float2, self.lag2_entity1, self.lag2_vec1, self.lag2_vec2, self.lag2_vec3, self.lag2_vec4);self.lag2_time = 0;}
+       if (self.lag3_time) if (time > self.lag3_time) {self.lag_func(self.lag3_time, self.lag3_float1, self.lag3_float2, self.lag3_entity1, self.lag3_vec1, self.lag3_vec2, self.lag3_vec3, self.lag3_vec4);self.lag3_time = 0;}
+       if (self.lag4_time) if (time > self.lag4_time) {self.lag_func(self.lag4_time, self.lag4_float1, self.lag4_float2, self.lag4_entity1, self.lag4_vec1, self.lag4_vec2, self.lag4_vec3, self.lag4_vec4);self.lag4_time = 0;}
+       if (self.lag5_time) if (time > self.lag5_time) {self.lag_func(self.lag5_time, self.lag5_float1, self.lag5_float2, self.lag5_entity1, self.lag5_vec1, self.lag5_vec2, self.lag5_vec3, self.lag5_vec4);self.lag5_time = 0;}
+}
+
+float lag_additem(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
+{SELFPARAM();
+       if (self.lag1_time == 0) {self.lag1_time = t;self.lag1_float1 = f1;self.lag1_float2 = f2;self.lag1_entity1 = e1;self.lag1_vec1 = v1;self.lag1_vec2 = v2;self.lag1_vec3 = v3;self.lag1_vec4 = v4;return true;}
+       if (self.lag2_time == 0) {self.lag2_time = t;self.lag2_float1 = f1;self.lag2_float2 = f2;self.lag2_entity1 = e1;self.lag2_vec1 = v1;self.lag2_vec2 = v2;self.lag2_vec3 = v3;self.lag2_vec4 = v4;return true;}
+       if (self.lag3_time == 0) {self.lag3_time = t;self.lag3_float1 = f1;self.lag3_float2 = f2;self.lag3_entity1 = e1;self.lag3_vec1 = v1;self.lag3_vec2 = v2;self.lag3_vec3 = v3;self.lag3_vec4 = v4;return true;}
+       if (self.lag4_time == 0) {self.lag4_time = t;self.lag4_float1 = f1;self.lag4_float2 = f2;self.lag4_entity1 = e1;self.lag4_vec1 = v1;self.lag4_vec2 = v2;self.lag4_vec3 = v3;self.lag4_vec4 = v4;return true;}
+       if (self.lag5_time == 0) {self.lag5_time = t;self.lag5_float1 = f1;self.lag5_float2 = f2;self.lag5_entity1 = e1;self.lag5_vec1 = v1;self.lag5_vec2 = v2;self.lag5_vec3 = v3;self.lag5_vec4 = v4;return true;}
+       // no room for it (what is the best thing to do here??)
+       return false;
+}
+
+float bot_shouldattack(entity e)
+{SELFPARAM();
+       if (e.team == self.team)
+       {
+               if (e == self)
+                       return false;
+               if (teamplay)
+               if (e.team != 0)
+                       return false;
+       }
+
+       if(e.frozen)
+               return false;
+
+       if(teamplay)
+       {
+               if(e.team==0)
+                       return false;
+       }
+       else if(bot_ignore_bots)
+               if(IS_BOT_CLIENT(e))
+                       return false;
+
+       if (!e.takedamage)
+               return false;
+       if (e.deadflag)
+               return false;
+       if (e.BUTTON_CHAT)
+               return false;
+       if(e.flags & FL_NOTARGET)
+               return false;
+
+       if(MUTATOR_CALLHOOK(BotShouldAttack, e))
+               return false;
+
+       return true;
+}
+
+void bot_lagfunc(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
+{SELFPARAM();
+       if(self.flags & FL_INWATER)
+       {
+               self.bot_aimtarg = world;
+               return;
+       }
+       self.bot_aimtarg = e1;
+       self.bot_aimlatency = self.ping; // FIXME?  Shouldn't this be in the lag item?
+       self.bot_aimselforigin = v1;
+       self.bot_aimselfvelocity = v2;
+       self.bot_aimtargorigin = v3;
+       self.bot_aimtargvelocity = v4;
+       if(skill <= 0)
+               self.bot_canfire = (random() < 0.8);
+       else if(skill <= 1)
+               self.bot_canfire = (random() < 0.9);
+       else if(skill <= 2)
+               self.bot_canfire = (random() < 0.95);
+       else
+               self.bot_canfire = 1;
+}
+
+float bot_aimdir(vector v, float maxfiredeviation)
+{SELFPARAM();
+       float dist, delta_t, blend;
+       vector desiredang, diffang;
+
+       //dprint("aim ", self.netname, ": old:", vtos(self.v_angle));
+       // make sure v_angle is sane first
+       self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360;
+       self.v_angle_z = 0;
+
+       // get the desired angles to aim at
+       //dprint(" at:", vtos(v));
+       v = normalize(v);
+       //te_lightning2(world, self.origin + self.view_ofs, self.origin + self.view_ofs + v * 200);
+       if (time >= self.bot_badaimtime)
+       {
+               self.bot_badaimtime = max(self.bot_badaimtime + 0.3, time);
+               self.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+self.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
+       }
+       desiredang = vectoangles(v) + self.bot_badaimoffset;
+       //dprint(" desired:", vtos(desiredang));
+       if (desiredang.x >= 180)
+               desiredang.x = desiredang.x - 360;
+       desiredang.x = bound(-90, 0 - desiredang.x, 90);
+       desiredang.z = self.v_angle.z;
+       //dprint(" / ", vtos(desiredang));
+
+       //// pain throws off aim
+       //if (self.bot_painintensity)
+       //{
+       //      // shake from pain
+       //      desiredang = desiredang + randomvec() * self.bot_painintensity * 0.2;
+       //}
+
+       // calculate turn angles
+       diffang = (desiredang - self.bot_olddesiredang);
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       self.bot_olddesiredang = desiredang;
+       //dprint(" diff:", vtos(diffang));
+
+       delta_t = time-self.bot_prevaimtime;
+       self.bot_prevaimtime = time;
+       // Here we will try to anticipate the comming aiming direction
+       self.bot_1st_order_aimfilter= self.bot_1st_order_aimfilter
+               + (diffang * (1 / delta_t)    - self.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1);
+       self.bot_2nd_order_aimfilter= self.bot_2nd_order_aimfilter
+               + (self.bot_1st_order_aimfilter - self.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1);
+       self.bot_3th_order_aimfilter= self.bot_3th_order_aimfilter
+               + (self.bot_2nd_order_aimfilter - self.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1);
+       self.bot_4th_order_aimfilter= self.bot_4th_order_aimfilter
+               + (self.bot_3th_order_aimfilter - self.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1);
+       self.bot_5th_order_aimfilter= self.bot_5th_order_aimfilter
+               + (self.bot_4th_order_aimfilter - self.bot_5th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_5th,1);
+
+       //blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing !
+       blend = bound(0,skill+self.bot_aimskill,10)*0.1;
+       desiredang = desiredang + blend *
+       (
+                 self.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st
+               + self.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd
+               + self.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th
+               + self.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th
+               + self.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th
+       );
+
+       // calculate turn angles
+       diffang = desiredang - self.bot_mouseaim;
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       //dprint(" diff:", vtos(diffang));
+
+       if (time >= self.bot_aimthinktime)
+       {
+               self.bot_aimthinktime = max(self.bot_aimthinktime + 0.5 - 0.05*(skill+self.bot_thinkskill), time);
+               self.bot_mouseaim = self.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+self.bot_thinkskill),10));
+       }
+
+       //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+
+       diffang = self.bot_mouseaim - desiredang;
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       desiredang = desiredang + diffang * bound(0,autocvar_bot_ai_aimskill_think,1);
+
+       // calculate turn angles
+       diffang = desiredang - self.v_angle;
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       //dprint(" diff:", vtos(diffang));
+
+       // jitter tracking
+       dist = vlen(diffang);
+       //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3)));
+
+       // turn
+       float r, fixedrate, blendrate;
+       fixedrate = autocvar_bot_ai_aimskill_fixedrate / bound(1,dist,1000);
+       blendrate = autocvar_bot_ai_aimskill_blendrate;
+       r = max(fixedrate, blendrate);
+       //self.v_angle = self.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1);
+       self.v_angle = self.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+self.bot_mouseskill,3)*0.005-random()), 1);
+       self.v_angle = self.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1);
+       //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+       //self.v_angle = self.v_angle + diffang * (1/ blendrate);
+       self.v_angle_z = 0;
+       self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360;
+       //dprint(" turn:", vtos(self.v_angle));
+
+       makevectors(self.v_angle);
+       shotorg = self.origin + self.view_ofs;
+       shotdir = v_forward;
+
+       //dprint(" dir:", vtos(v_forward));
+       //te_lightning2(world, shotorg, shotorg + shotdir * 100);
+
+       // calculate turn angles again
+       //diffang = desiredang - self.v_angle;
+       //diffang_y = diffang_y - floor(diffang_y / 360) * 360;
+       //if (diffang_y >= 180)
+       //      diffang_y = diffang_y - 360;
+
+       //dprint("e ", vtos(diffang), " < ", ftos(maxfiredeviation), "\n");
+
+       // decide whether to fire this time
+       // note the maxfiredeviation is in degrees so this has to convert to radians first
+       //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
+       if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
+       if (vlen(trace_endpos-shotorg) < 500+500*bound(0, skill+self.bot_aggresskill, 10) || random()*random()>bound(0,(skill+self.bot_aggresskill)*0.05,1))
+               self.bot_firetimer = time + bound(0.1, 0.5-(skill+self.bot_aggresskill)*0.05, 0.5);
+       //traceline(shotorg,shotorg+shotdir*1000,false,world);
+       //dprint(ftos(maxfiredeviation),"\n");
+       //dprint(" diff:", vtos(diffang), "\n");
+
+       return self.bot_canfire && (time < self.bot_firetimer);
+}
+
+vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay)
+{
+       // Try to add code here that predicts gravity effect here, no clue HOW to though ... well not yet atleast...
+       return targorigin + targvelocity * (shotdelay + vlen(targorigin - shotorg) / shotspeed);
+}
+
+float bot_aim(float shotspeed, float shotspeedupward, float maxshottime, float applygravity)
+{SELFPARAM();
+       float f, r, hf, distanceratio;
+       vector v;
+       /*
+       eprint(self);
+       dprint("bot_aim(", ftos(shotspeed));
+       dprint(", ", ftos(shotspeedupward));
+       dprint(", ", ftos(maxshottime));
+       dprint(", ", ftos(applygravity));
+       dprint(");\n");
+       */
+
+       hf = self.dphitcontentsmask;
+       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+
+       shotspeed *= W_WeaponSpeedFactor();
+       shotspeedupward *= W_WeaponSpeedFactor();
+       if (!shotspeed)
+       {
+               LOG_TRACE("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " shotspeed is zero!\n");
+               shotspeed = 1000000;
+       }
+       if (!maxshottime)
+       {
+               LOG_TRACE("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " maxshottime is zero!\n");
+               maxshottime = 1;
+       }
+       makevectors(self.v_angle);
+       shotorg = self.origin + self.view_ofs;
+       shotdir = v_forward;
+       v = bot_shotlead(self.bot_aimtargorigin, self.bot_aimtargvelocity, shotspeed, self.bot_aimlatency);
+       distanceratio = sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees;
+       distanceratio = bound(0,distanceratio,1);
+       r =  (autocvar_bot_ai_aimskill_firetolerance_maxdegrees-autocvar_bot_ai_aimskill_firetolerance_mindegrees)
+               * (1-distanceratio) + autocvar_bot_ai_aimskill_firetolerance_mindegrees;
+       if (applygravity && self.bot_aimtarg)
+       {
+               if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', self.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, self))
+               {
+                       self.dphitcontentsmask = hf;
+                       return false;
+               }
+
+               f = bot_aimdir(findtrajectory_velocity - shotspeedupward * '0 0 1', r);
+       }
+       else
+       {
+               f = bot_aimdir(v - shotorg, r);
+               //dprint("AIM: ");dprint(vtos(self.bot_aimtargorigin));dprint(" + ");dprint(vtos(self.bot_aimtargvelocity));dprint(" * ");dprint(ftos(self.bot_aimlatency + vlen(self.bot_aimtargorigin - shotorg) / shotspeed));dprint(" = ");dprint(vtos(v));dprint(" : aimdir = ");dprint(vtos(normalize(v - shotorg)));dprint(" : ");dprint(vtos(shotdir));dprint("\n");
+               //traceline(shotorg, shotorg + shotdir * 10000, false, self);
+               //if (trace_ent.takedamage)
+               //if (trace_fraction < 1)
+               //if (!bot_shouldattack(trace_ent))
+               //      return false;
+               traceline(shotorg, self.bot_aimtargorigin, false, self);
+               if (trace_fraction < 1)
+               if (trace_ent != self.enemy)
+               if (!bot_shouldattack(trace_ent))
+               {
+                       self.dphitcontentsmask = hf;
+                       return false;
+               }
+       }
+
+       //if (r > maxshottime * shotspeed)
+       //      return false;
+       self.dphitcontentsmask = hf;
+       return true;
+}
diff --git a/qcsrc/server/bot/aim.qh b/qcsrc/server/bot/aim.qh
new file mode 100644 (file)
index 0000000..d1cbd0d
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef AIM_H
+#define AIM_H
+/*
+ * Globals and Fields
+ */
+
+entity tracetossent;
+entity tracetossfaketarget;
+vector findtrajectory_velocity;
+
+
+
+vector shotorg;
+vector shotdir;
+
+// lag simulation
+// upto 5 queued messages
+.float lag1_time;
+.float lag1_float1;
+.float lag1_float2;
+.entity lag1_entity1;
+.vector lag1_vec1;
+.vector lag1_vec2;
+.vector lag1_vec3;
+.vector lag1_vec4;
+
+.float lag2_time;
+.float lag2_float1;
+.float lag2_float2;
+.entity lag2_entity1;
+.vector lag2_vec1;
+.vector lag2_vec2;
+.vector lag2_vec3;
+.vector lag2_vec4;
+
+.float lag3_time;
+.float lag3_float1;
+.float lag3_float2;
+.entity lag3_entity1;
+.vector lag3_vec1;
+.vector lag3_vec2;
+.vector lag3_vec3;
+.vector lag3_vec4;
+
+.float lag4_time;
+.float lag4_float1;
+.float lag4_float2;
+.entity lag4_entity1;
+.vector lag4_vec1;
+.vector lag4_vec2;
+.vector lag4_vec3;
+.vector lag4_vec4;
+
+.float lag5_time;
+.float lag5_float1;
+.float lag5_float2;
+.entity lag5_entity1;
+.vector lag5_vec1;
+.vector lag5_vec2;
+.vector lag5_vec3;
+.vector lag5_vec4;
+
+.float bot_badaimtime;
+.float bot_aimthinktime;
+.float bot_prevaimtime;
+.float bot_firetimer;
+.float bot_aimlatency;
+
+.vector bot_mouseaim;
+.vector bot_badaimoffset;
+.vector bot_1st_order_aimfilter;
+.vector bot_2nd_order_aimfilter;
+.vector bot_3th_order_aimfilter;
+.vector bot_4th_order_aimfilter;
+.vector bot_5th_order_aimfilter;
+.vector bot_olddesiredang;
+
+.vector bot_aimselforigin;
+.vector bot_aimselfvelocity;
+.vector bot_aimtargorigin;
+.vector bot_aimtargvelocity;
+
+.entity bot_aimtarg;
+
+/*
+ * Functions
+ */
+
+float lag_additem(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
+void lag_update();
+void bot_lagfunc(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
+
+float bot_shouldattack(entity e);
+float bot_aimdir(vector v, float maxfiredeviation);
+float bot_aim(float shotspeed, float shotspeedupward, float maxshottime, float applygravity);
+float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore);
+
+vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay);
+
+.void(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func;
+#endif
diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh
deleted file mode 100644 (file)
index 9dc20eb..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#ifndef BOT_API_H
-#define BOT_API_H
-
-#include "../defs.qh"
-#include "../autocvars.qh"
-#include "../../common/weapons/all.qh"
-#include "../../common/teams.qh"
-#include "../teamplay.qh"
-#include "../miscfunctions.qh"
-#include "../campaign.qh"
-#include "../cl_client.qh"
-#include "../mutators/mutators_include.qh"
-#include "../constants.qh"
-#include "../weapons/weaponsystem.qh"
-
-const int WAYPOINTFLAG_GENERATED = 0x800000;
-
-int autocvar_bot_vs_human;
-int autocvar_bot_number;
-bool autocvar_bot_sound_monopoly;
-float autocvar_bot_ai_strategyinterval;
-
-float bot_custom_weapon;
-float bot_weapons_close[Weapons_MAX];
-float bot_weapons_far[Weapons_MAX];
-float bot_weapons_mid[Weapons_MAX];
-float currentbots;
-float skill;
-
-.float bot_attack;
-.float bot_dodgerating;
-.float bot_dodge;
-.float bot_forced_team;
-.float bot_moveskill; // moving technique
-.float bot_pickup;
-.float(entity player, entity item) bot_pickupevalfunc;
-.float bot_strategytime;
-.string cleanname;
-.float havocbot_role_timeout;
-.float isbot; // true if this client is actually a bot
-.float lastteleporttime;
-.float navigation_hasgoals;
-.float nearestwaypointtimeout;
-.entity nearestwaypoint;
-.float speed;
-.entity wp00, wp01, wp02, wp03, wp04, wp05, wp06, wp07, wp08, wp09, wp10, wp11, wp12, wp13, wp14, wp15;
-.entity wp16, wp17, wp18, wp19, wp20, wp21, wp22, wp23, wp24, wp25, wp26, wp27, wp28, wp29, wp30, wp31;
-.float wp00mincost, wp01mincost, wp02mincost, wp03mincost, wp04mincost, wp05mincost, wp06mincost, wp07mincost;
-.float wp08mincost, wp09mincost, wp10mincost, wp11mincost, wp12mincost, wp13mincost, wp14mincost, wp15mincost;
-.float wp16mincost, wp17mincost, wp18mincost, wp19mincost, wp20mincost, wp21mincost, wp22mincost, wp23mincost;
-.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost;
-.float wpconsidered;
-.float wpcost;
-.int wpflags;
-
-bool bot_aim(float shotspeed, float shotspeedupward, float maxshottime, float applygravity);
-void bot_clientconnect();
-void bot_clientdisconnect();
-void bot_cmdhelp(string scmd);
-void bot_endgame();
-bool bot_fixcount();
-void bot_list_commands();
-void bot_queuecommand(entity bot, string cmdstring);
-void bot_relinkplayerlist();
-void bot_resetqueues();
-void bot_serverframe();
-bool bot_shouldattack(entity e);
-void bot_think();
-
-entity find_bot_by_name(string name);
-entity find_bot_by_number(float number);
-
-void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius);
-void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius);
-void havocbot_goalrating_items(float ratingscale, vector org, float sradius);
-
-entity navigation_findnearestwaypoint(entity ent, float walkfromwp);
-void navigation_goalrating_end();
-void navigation_goalrating_start();
-void navigation_markroutes(entity fixed_source_waypoint);
-void navigation_markroutes_inverted(entity fixed_source_waypoint);
-void navigation_routerating(entity e, float f, float rangebias);
-
-bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
-
-void waypoint_remove(entity e);
-void waypoint_saveall();
-void waypoint_schedulerelinkall();
-void waypoint_schedulerelink(entity wp);
-void waypoint_spawnforitem(entity e);
-void waypoint_spawnforitem_force(entity e, vector org);
-void waypoint_spawnforteleporter(entity e, vector destination, float timetaken);
-void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken);
-entity waypoint_spawn(vector m1, vector m2, float f);
-
-#endif
diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc
new file mode 100644 (file)
index 0000000..00051ca
--- /dev/null
@@ -0,0 +1,725 @@
+#include "bot.qh"
+#include "../_all.qh"
+
+#include "aim.qh"
+#include "navigation.qh"
+#include "scripting.qh"
+#include "waypoints.qh"
+
+#include "havocbot/havocbot.qh"
+#include "havocbot/scripting.qh"
+
+#include "../antilag.qh"
+#include "../autocvars.qh"
+#include "../campaign.qh"
+#include "../cl_client.qh"
+#include "../constants.qh"
+#include "../defs.qh"
+#include "../race.qh"
+#include "../t_items.qh"
+
+#include "../mutators/mutators_include.qh"
+
+#include "../weapons/accuracy.qh"
+
+#include "../../common/constants.qh"
+#include "../../common/mapinfo.qh"
+#include "../../common/teams.qh"
+#include "../../common/util.qh"
+
+#include "../../common/weapons/all.qh"
+
+#include "../../csqcmodellib/sv_model.qh"
+
+#include "../../dpdefs/dpextensions.qh"
+#include "../../dpdefs/progsdefs.qh"
+
+#include "../../warpzonelib/common.qh"
+#include "../../warpzonelib/util_server.qh"
+
+entity bot_spawn()
+{SELFPARAM();
+       entity bot = spawnclient();
+       if (bot)
+       {
+               currentbots = currentbots + 1;
+               setself(bot);
+               bot_setnameandstuff();
+               ClientConnect();
+               PutClientInServer();
+               setself(this);
+       }
+       return bot;
+}
+
+void bot_think()
+{SELFPARAM();
+       if (self.bot_nextthink > time)
+               return;
+
+       self.flags &= ~FL_GODMODE;
+       if(autocvar_bot_god)
+               self.flags |= FL_GODMODE;
+
+       self.bot_nextthink = self.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, self.bot_aiskill);
+       //if (self.bot_painintensity > 0)
+       //      self.bot_painintensity = self.bot_painintensity - (skill + 1) * 40 * frametime;
+
+       //self.bot_painintensity = self.bot_painintensity + self.bot_oldhealth - self.health;
+       //self.bot_painintensity = bound(0, self.bot_painintensity, 100);
+
+       if (!IS_PLAYER(self) || (autocvar_g_campaign && !campaign_bots_may_start))
+       {
+               self.bot_nextthink = time + 0.5;
+               return;
+       }
+
+       if (self.fixangle)
+       {
+               self.v_angle = self.angles;
+               self.v_angle_z = 0;
+               self.fixangle = false;
+       }
+
+       self.dmg_take = 0;
+       self.dmg_save = 0;
+       self.dmg_inflictor = world;
+
+       // calculate an aiming latency based on the skill setting
+       // (simulated network latency + naturally delayed reflexes)
+       //self.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think')
+       // minimum ping 20+10 random
+       self.ping = bound(0,0.07 - bound(0, (skill + self.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
+       // skill 10 = ping 0.2 (adrenaline)
+       // skill 0 = ping 0.7 (slightly drunk)
+
+       // clear buttons
+       self.BUTTON_ATCK = 0;
+       self.button1 = 0;
+       self.BUTTON_JUMP = 0;
+       self.BUTTON_ATCK2 = 0;
+       self.BUTTON_ZOOM = 0;
+       self.BUTTON_CROUCH = 0;
+       self.BUTTON_HOOK = 0;
+       self.BUTTON_INFO = 0;
+       self.button8 = 0;
+       self.BUTTON_CHAT = 0;
+       self.BUTTON_USE = 0;
+
+       if (time < game_starttime)
+       {
+               // block the bot during the countdown to game start
+               self.movement = '0 0 0';
+               self.bot_nextthink = game_starttime;
+               return;
+       }
+
+       // if dead, just wait until we can respawn
+       if (self.deadflag)
+       {
+               if (self.deadflag == DEAD_DEAD)
+               {
+                       self.BUTTON_JUMP = 1; // press jump to respawn
+                       self.bot_strategytime = 0;
+               }
+       }
+       else if(self.aistatus & AI_STATUS_STUCK)
+               navigation_unstuck();
+
+       // now call the current bot AI (havocbot for example)
+       self.bot_ai();
+}
+
+void bot_setnameandstuff()
+{SELFPARAM();
+       string readfile, s;
+       float file, tokens, prio;
+       entity p;
+
+       string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
+       string name, prefix, suffix;
+
+       if(autocvar_g_campaign)
+       {
+               prefix = "";
+               suffix = "";
+       }
+       else
+       {
+               prefix = autocvar_bot_prefix;
+               suffix = autocvar_bot_suffix;
+       }
+
+       file = fopen(autocvar_bot_config_file, FILE_READ);
+
+       if(file < 0)
+       {
+               LOG_INFO(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n"));
+               readfile = "";
+       }
+       else
+       {
+               RandomSelection_Init();
+               while((readfile = fgets(file)))
+               {
+                       if(substring(readfile, 0, 2) == "//")
+                               continue;
+                       if(substring(readfile, 0, 1) == "#")
+                               continue;
+                       tokens = tokenizebyseparator(readfile, "\t");
+                       if(tokens == 0)
+                               continue;
+                       s = argv(0);
+                       prio = 1;
+                       FOR_EACH_CLIENT(p)
+                       {
+                               if(IS_BOT_CLIENT(p))
+                               if(s == p.cleanname)
+                               {
+                                       prio = 0;
+                                       break;
+                               }
+                       }
+                       RandomSelection_Add(world, 0, readfile, 1, prio);
+               }
+               readfile = RandomSelection_chosen_string;
+               fclose(file);
+       }
+
+       tokens = tokenizebyseparator(readfile, "\t");
+       if(argv(0) != "") bot_name = argv(0);
+       else bot_name = "Bot";
+
+       if(argv(1) != "") bot_model = argv(1);
+       else bot_model = "";
+
+       if(argv(2) != "") bot_skin = argv(2);
+       else bot_skin = "0";
+
+       if(argv(3) != "" && stof(argv(3)) >= 0) bot_shirt = argv(3);
+       else bot_shirt = ftos(floor(random() * 15));
+
+       if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4);
+       else bot_pants = ftos(floor(random() * 15));
+
+       self.bot_forced_team = stof(argv(5));
+
+       prio = 6;
+
+       #define READSKILL(f,w,r) if(argv(prio) != "") self.f = stof(argv(prio)) * (w); else self.f = (!autocvar_g_campaign) * (2 * random() - 1) * (r) * (w); ++prio
+       //print(bot_name, ": ping=", argv(9), "\n");
+
+       READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill
+       READSKILL(bot_moveskill, 2, 0); // move skill
+       READSKILL(bot_dodgeskill, 2, 0); // dodge skill
+
+       READSKILL(bot_pingskill, 0.5, 0); // ping skill
+
+       READSKILL(bot_weaponskill, 2, 0); // weapon skill
+       READSKILL(bot_aggresskill, 1, 0); // aggre skill
+       READSKILL(bot_rangepreference, 1, 0); // read skill
+
+       READSKILL(bot_aimskill, 2, 0); // aim skill
+       READSKILL(bot_offsetskill, 2, 0.5); // offset skill
+       READSKILL(bot_mouseskill, 1, 0.5); // mouse skill
+
+       READSKILL(bot_thinkskill, 1, 0.5); // think skill
+       READSKILL(bot_aiskill, 2, 0); // "ai" skill
+
+       self.bot_config_loaded = true;
+
+       // this is really only a default, JoinBestTeam is called later
+       setcolor(self, stof(bot_shirt) * 16 + stof(bot_pants));
+       self.bot_preferredcolors = self.clientcolors;
+
+       // pick the name
+       if (autocvar_bot_usemodelnames)
+               name = bot_model;
+       else
+               name = bot_name;
+
+       // number bots with identical names
+       float i;
+       i = 0;
+       FOR_EACH_CLIENT(p)
+       {
+               if(IS_BOT_CLIENT(p))
+                       if(p.cleanname == name)
+                               ++i;
+       }
+       if (i)
+               self.netname = self.netname_freeme = strzone(strcat(prefix, name, "(", ftos(i), ")", suffix));
+       else
+               self.netname = self.netname_freeme = strzone(strcat(prefix, name, suffix));
+
+       self.cleanname = strzone(name);
+
+       // pick the model and skin
+       if(substring(bot_model, -4, 1) != ".")
+               bot_model = strcat(bot_model, ".iqm");
+       self.playermodel = self.playermodel_freeme = strzone(strcat("models/player/", bot_model));
+       self.playerskin = self.playerskin_freeme = strzone(bot_skin);
+
+       self.cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the world
+       self.cvar_cl_accuracy_data_receive = 0;  // don't receive any weapon accuracy data
+}
+
+void bot_custom_weapon_priority_setup()
+{
+       float tokens, i, w;
+
+       bot_custom_weapon = false;
+
+       if(     autocvar_bot_ai_custom_weapon_priority_far == "" ||
+               autocvar_bot_ai_custom_weapon_priority_mid == "" ||
+               autocvar_bot_ai_custom_weapon_priority_close == "" ||
+               autocvar_bot_ai_custom_weapon_priority_distances == ""
+       )
+               return;
+
+       // Parse distances
+       tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," ");
+
+       if (tokens!=2)
+               return;
+
+       bot_distance_far = stof(argv(0));
+       bot_distance_close = stof(argv(1));
+
+       if(bot_distance_far < bot_distance_close){
+               bot_distance_far = stof(argv(1));
+               bot_distance_close = stof(argv(0));
+       }
+
+       // Initialize list of weapons
+       bot_weapons_far[0] = -1;
+       bot_weapons_mid[0] = -1;
+       bot_weapons_close[0] = -1;
+
+       // Parse far distance weapon priorities
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," ");
+
+       int c = 0;
+       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
+               w = stof(argv(i));
+               if ( w >= WEP_FIRST && w <= WEP_LAST) {
+                       bot_weapons_far[c] = w;
+                       ++c;
+               }
+       }
+       if(c < Weapons_COUNT)
+               bot_weapons_far[c] = -1;
+
+       // Parse mid distance weapon priorities
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," ");
+
+       c = 0;
+       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
+               w = stof(argv(i));
+               if ( w >= WEP_FIRST && w <= WEP_LAST) {
+                       bot_weapons_mid[c] = w;
+                       ++c;
+               }
+       }
+       if(c < Weapons_COUNT)
+               bot_weapons_mid[c] = -1;
+
+       // Parse close distance weapon priorities
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," ");
+
+       c = 0;
+       for(i=0; i < tokens && i < Weapons_COUNT; ++i){
+               w = stof(argv(i));
+               if ( w >= WEP_FIRST && w <= WEP_LAST) {
+                       bot_weapons_close[c] = w;
+                       ++c;
+               }
+       }
+       if(c < Weapons_COUNT)
+               bot_weapons_close[c] = -1;
+
+       bot_custom_weapon = true;
+}
+
+void bot_endgame()
+{
+       entity e;
+       //dprint("bot_endgame\n");
+       e = bot_list;
+       while (e)
+       {
+               setcolor(e, e.bot_preferredcolors);
+               e = e.nextbot;
+       }
+       // if dynamic waypoints are ever implemented, save them here
+}
+
+void bot_relinkplayerlist()
+{
+       entity e;
+       entity prevbot;
+       player_count = 0;
+       currentbots = 0;
+       player_list = e = findchainflags(flags, FL_CLIENT);
+       bot_list = world;
+       prevbot = world;
+       while (e)
+       {
+               player_count = player_count + 1;
+               e.nextplayer = e.chain;
+               if (IS_BOT_CLIENT(e))
+               {
+                       if (prevbot)
+                               prevbot.nextbot = e;
+                       else
+                       {
+                               bot_list = e;
+                               bot_list.nextbot = world;
+                       }
+                       prevbot = e;
+                       currentbots = currentbots + 1;
+               }
+               e = e.chain;
+       }
+       LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
+       bot_strategytoken = bot_list;
+       bot_strategytoken_taken = true;
+}
+
+void bot_clientdisconnect()
+{SELFPARAM();
+       if (!IS_BOT_CLIENT(self))
+               return;
+       bot_clearqueue(self);
+       if(self.cleanname)
+               strunzone(self.cleanname);
+       if(self.netname_freeme)
+               strunzone(self.netname_freeme);
+       if(self.playermodel_freeme)
+               strunzone(self.playermodel_freeme);
+       if(self.playerskin_freeme)
+               strunzone(self.playerskin_freeme);
+       self.cleanname = string_null;
+       self.netname_freeme = string_null;
+       self.playermodel_freeme = string_null;
+       self.playerskin_freeme = string_null;
+       if(self.bot_cmd_current)
+               remove(self.bot_cmd_current);
+       if(bot_waypoint_queue_owner==self)
+               bot_waypoint_queue_owner = world;
+}
+
+void bot_clientconnect()
+{SELFPARAM();
+       if (!IS_BOT_CLIENT(self))
+               return;
+       self.bot_preferredcolors = self.clientcolors;
+       self.bot_nextthink = time - random();
+       self.lag_func = bot_lagfunc;
+       self.isbot = true;
+       self.createdtime = self.bot_nextthink;
+
+       if(!self.bot_config_loaded) // This is needed so team overrider doesn't break between matches
+               bot_setnameandstuff();
+
+       if(self.bot_forced_team==1)
+               self.team = NUM_TEAM_1;
+       else if(self.bot_forced_team==2)
+               self.team = NUM_TEAM_2;
+       else if(self.bot_forced_team==3)
+               self.team = NUM_TEAM_3;
+       else if(self.bot_forced_team==4)
+               self.team = NUM_TEAM_4;
+       else
+               JoinBestTeam(self, false, true);
+
+       havocbot_setupbot();
+}
+
+void bot_removefromlargestteam()
+{
+       float besttime, bestcount, thiscount;
+       entity best, head;
+       CheckAllowedTeams(world);
+       GetTeamCounts(world);
+       head = findchainfloat(isbot, true);
+       if (!head)
+               return;
+       best = head;
+       besttime = head.createdtime;
+       bestcount = 0;
+       while (head)
+       {
+               if(head.team == NUM_TEAM_1)
+                       thiscount = c1;
+               else if(head.team == NUM_TEAM_2)
+                       thiscount = c2;
+               else if(head.team == NUM_TEAM_3)
+                       thiscount = c3;
+               else if(head.team == NUM_TEAM_4)
+                       thiscount = c4;
+               else
+                       thiscount = 0;
+               if (thiscount > bestcount)
+               {
+                       bestcount = thiscount;
+                       besttime = head.createdtime;
+                       best = head;
+               }
+               else if (thiscount == bestcount && besttime < head.createdtime)
+               {
+                       besttime = head.createdtime;
+                       best = head;
+               }
+               head = head.chain;
+       }
+       currentbots = currentbots - 1;
+       dropclient(best);
+}
+
+void bot_removenewest()
+{
+       float besttime;
+       entity best, head;
+
+       if(teamplay)
+       {
+               bot_removefromlargestteam();
+               return;
+       }
+
+       head = findchainfloat(isbot, true);
+       if (!head)
+               return;
+       best = head;
+       besttime = head.createdtime;
+       while (head)
+       {
+               if (besttime < head.createdtime)
+               {
+                       besttime = head.createdtime;
+                       best = head;
+               }
+               head = head.chain;
+       }
+       currentbots = currentbots - 1;
+       dropclient(best);
+}
+
+void autoskill(float factor)
+{
+       float bestbot;
+       float bestplayer;
+       entity head;
+
+       bestbot = -1;
+       bestplayer = -1;
+       FOR_EACH_PLAYER(head)
+       {
+               if(IS_REAL_CLIENT(head))
+                       bestplayer = max(bestplayer, head.totalfrags - head.totalfrags_lastcheck);
+               else
+                       bestbot = max(bestbot, head.totalfrags - head.totalfrags_lastcheck);
+       }
+
+       LOG_TRACE("autoskill: best player got ", ftos(bestplayer), ", ");
+       LOG_TRACE("best bot got ", ftos(bestbot), "; ");
+       if(bestbot < 0 || bestplayer < 0)
+       {
+               LOG_TRACE("not doing anything\n");
+               // don't return, let it reset all counters below
+       }
+       else if(bestbot <= bestplayer * factor - 2)
+       {
+               if(autocvar_skill < 17)
+               {
+                       LOG_TRACE("2 frags difference, increasing skill\n");
+                       cvar_set("skill", ftos(autocvar_skill + 1));
+                       bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
+               }
+       }
+       else if(bestbot >= bestplayer * factor + 2)
+       {
+               if(autocvar_skill > 0)
+               {
+                       LOG_TRACE("2 frags difference, decreasing skill\n");
+                       cvar_set("skill", ftos(autocvar_skill - 1));
+                       bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
+               }
+       }
+       else
+       {
+               LOG_TRACE("not doing anything\n");
+               return;
+               // don't reset counters, wait for them to accumulate
+       }
+
+       FOR_EACH_PLAYER(head)
+               head.totalfrags_lastcheck = head.totalfrags;
+}
+
+void bot_calculate_stepheightvec(void)
+{
+       stepheightvec = autocvar_sv_stepheight * '0 0 1';
+       jumpstepheightvec = stepheightvec +
+               ((autocvar_sv_jumpvelocity * autocvar_sv_jumpvelocity) / (2 * autocvar_sv_gravity)) * '0 0 0.85';
+               // 0.75 factor is for safety to make the jumps easy
+}
+
+float bot_fixcount()
+{
+       entity head;
+       float realplayers, bots, activerealplayers;
+
+       activerealplayers = 0;
+       realplayers = 0;
+
+       FOR_EACH_REALCLIENT(head)
+       {
+               if(IS_PLAYER(head) || g_lms || head.caplayer == 1)
+                       ++activerealplayers;
+               ++realplayers;
+       }
+
+       // add/remove bots if needed to make sure there are at least
+       // minplayers+bot_number, or remove all bots if no one is playing
+       // But don't remove bots immediately on level change, as the real players
+       // usually haven't rejoined yet
+       bots_would_leave = false;
+       if (teamplay && autocvar_bot_vs_human && (c3==-1 && c4==-1))
+               bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers);
+       else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5)))
+       {
+               float realminplayers, minplayers;
+               realminplayers = autocvar_minplayers;
+               minplayers = max(0, floor(realminplayers));
+
+               float realminbots, minbots;
+               realminbots = autocvar_bot_number;
+               minbots = max(0, floor(realminbots));
+
+               bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers);
+               if(bots > minbots)
+                       bots_would_leave = true;
+       }
+       else
+       {
+               // if there are no players, remove bots
+               bots = 0;
+       }
+
+       // only add one bot per frame to avoid utter chaos
+       if(time > botframe_nextthink)
+       {
+               //dprint(ftos(bots), " ? ", ftos(currentbots), "\n");
+               while (currentbots < bots)
+               {
+                       if (bot_spawn() == world)
+                       {
+                               bprint("Can not add bot, server full.\n");
+                               return false;
+                       }
+               }
+               while (currentbots > bots)
+                       bot_removenewest();
+       }
+
+       return true;
+}
+
+void bot_serverframe()
+{
+       if (intermission_running)
+               return;
+
+       if (time < 2)
+               return;
+
+       bot_calculate_stepheightvec();
+       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
+
+       if(time > autoskill_nextthink)
+       {
+               float a;
+               a = autocvar_skill_auto;
+               if(a)
+                       autoskill(a);
+               autoskill_nextthink = time + 5;
+       }
+
+       if(time > botframe_nextthink)
+       {
+               if(!bot_fixcount())
+                       botframe_nextthink = time + 10;
+       }
+
+       bot_ignore_bots = autocvar_bot_ignore_bots;
+
+       if(botframe_spawnedwaypoints)
+       {
+               if(autocvar_waypoint_benchmark)
+                       localcmd("quit\n");
+       }
+
+       if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto)
+       if (botframe_spawnedwaypoints)
+       {
+               if(botframe_cachedwaypointlinks)
+               {
+                       if(!botframe_loadedforcedlinks)
+                               waypoint_load_links_hardwired();
+               }
+               else
+               {
+                       // TODO: Make this check cleaner
+                       entity wp = findchain(classname, "waypoint");
+                       if(time - wp.nextthink > 10)
+                               waypoint_save_links();
+               }
+       }
+       else
+       {
+               botframe_spawnedwaypoints = true;
+               waypoint_loadall();
+               if(!waypoint_load_links())
+                       waypoint_schedulerelinkall();
+       }
+
+       if (bot_list)
+       {
+               // cycle the goal token from one bot to the next each frame
+               // (this prevents them from all doing spawnfunc_waypoint searches on the same
+               //  frame, which causes choppy framerates)
+               if (bot_strategytoken_taken)
+               {
+                       bot_strategytoken_taken = false;
+                       if (bot_strategytoken)
+                               bot_strategytoken = bot_strategytoken.nextbot;
+                       if (!bot_strategytoken)
+                               bot_strategytoken = bot_list;
+               }
+
+               if (botframe_nextdangertime < time)
+               {
+                       float interval;
+                       interval = autocvar_bot_ai_dangerdetectioninterval;
+                       if (botframe_nextdangertime < time - interval * 1.5)
+                               botframe_nextdangertime = time;
+                       botframe_nextdangertime = botframe_nextdangertime + interval;
+                       botframe_updatedangerousobjects(autocvar_bot_ai_dangerdetectionupdates);
+               }
+       }
+
+       if (autocvar_g_waypointeditor)
+               botframe_showwaypointlinks();
+
+       if (autocvar_g_waypointeditor_auto)
+               botframe_autowaypoints();
+
+       if(time > bot_cvar_nextthink)
+       {
+               if(currentbots>0)
+                       bot_custom_weapon_priority_setup();
+               bot_cvar_nextthink = time + 5;
+       }
+}
diff --git a/qcsrc/server/bot/bot.qh b/qcsrc/server/bot/bot.qh
new file mode 100644 (file)
index 0000000..d5a2794
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef BOT_H
+#define BOT_H
+/*
+ * Globals and Fields
+ */
+
+const int AI_STATUS_ROAMING                                            = 1;    // Bot is just crawling the map. No enemies at sight
+const int AI_STATUS_ATTACKING                                  = 2;    // There are enemies at sight
+const int AI_STATUS_RUNNING                                            = 4;    // Bot is bunny hopping
+const int AI_STATUS_DANGER_AHEAD                               = 8;    // There is lava/slime/trigger_hurt ahead
+const int AI_STATUS_OUT_JUMPPAD                                        = 16;   // Trying to get out of a "vertical" jump pad
+const int AI_STATUS_OUT_WATER                                  = 32;   // Trying to get out of water
+const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = 64;   // Waiting for the personal waypoint to be linked
+const int AI_STATUS_WAYPOINT_PERSONAL_GOING            = 128;  // Going to a personal waypoint
+const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = 256;  // Personal waypoint reached
+const int AI_STATUS_JETPACK_FLYING                             = 512;
+const int AI_STATUS_JETPACK_LANDING                            = 1024;
+const int AI_STATUS_STUCK                                              = 2048; // Cannot reach any goal
+
+.float isbot; // true if this client is actually a bot
+.int aistatus;
+
+// Skill system
+float skill;
+float autoskill_nextthink;
+
+// havocbot_keyboardskill // keyboard movement
+.float bot_moveskill; // moving technique
+.float bot_dodgeskill; // dodging
+
+.float bot_pingskill; // ping offset
+
+.float bot_weaponskill; // weapon usage skill (combos, e.g.)
+.float bot_aggresskill; // aggressivity, controls "think before fire" behaviour
+.float bot_rangepreference; // weapon choice offset for range (>0 = prefer long range earlier "sniper", <0 = prefer short range "spammer")
+
+.float bot_aimskill; // aim accuracy
+.float bot_offsetskill; // aim breakage
+.float bot_mouseskill; // mouse "speed"
+
+.float bot_thinkskill; // target choice
+.float bot_aiskill; // strategy choice
+
+.float totalfrags_lastcheck;
+
+// Custom weapon priorities
+float bot_custom_weapon;
+float bot_distance_far;
+float bot_distance_close;
+
+float bot_weapons_far[Weapons_MAX];
+float bot_weapons_mid[Weapons_MAX];
+float bot_weapons_close[Weapons_MAX];
+
+entity bot_list;
+entity player_list;
+.entity nextbot;
+.entity nextplayer;
+.string cleanname;
+.string netname_freeme;
+.string playermodel_freeme;
+.string playerskin_freeme;
+
+.float bot_nextthink;
+
+.float createdtime;
+.float bot_preferredcolors;
+.float bot_attack;
+.float bot_dodge;
+.float bot_dodgerating;
+
+.float bot_pickup;
+.float bot_pickupbasevalue;
+.float bot_canfire;
+.float bot_strategytime;
+
+.float bot_forced_team;
+.float bot_config_loaded;
+
+float bot_strategytoken_taken;
+entity bot_strategytoken;
+
+float botframe_spawnedwaypoints;
+float botframe_nextthink;
+float botframe_nextdangertime;
+float bot_cvar_nextthink;
+float bot_ignore_bots; // let bots not attack other bots (only works in non-teamplay)
+
+/*
+ * Functions
+ */
+
+entity bot_spawn();
+float bot_fixcount();
+
+void bot_think();
+void bot_setnameandstuff();
+void bot_custom_weapon_priority_setup();
+void bot_endgame();
+void bot_relinkplayerlist();
+void bot_clientdisconnect();
+void bot_clientconnect();
+void bot_removefromlargestteam();
+void bot_removenewest();
+void autoskill(float factor);
+void bot_serverframe();
+
+.void() bot_ai;
+.float(entity player, entity item) bot_pickupevalfunc;
+
+/*
+ * Imports
+ */
+
+void() havocbot_setupbot;
+
+void bot_calculate_stepheightvec(void);
+#endif
diff --git a/qcsrc/server/bot/default/aim.qc b/qcsrc/server/bot/default/aim.qc
deleted file mode 100644 (file)
index 6353d1e..0000000
+++ /dev/null
@@ -1,384 +0,0 @@
-#include "aim.qh"
-
-// traces multiple trajectories to find one that will impact the target
-// 'end' vector is the place it aims for,
-// returns true only if it hit targ (don't target non-solid entities)
-
-float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore)
-{
-       float c, savesolid, shottime;
-       vector dir, end, v, o;
-       if (shotspeed < 1)
-               return false; // could cause division by zero if calculated
-       if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER
-               return false; // could never hit it
-       if (!tracetossent)
-               tracetossent = spawn();
-       tracetossent.owner = ignore;
-       setsize(tracetossent, m1, m2);
-       savesolid = targ.solid;
-       targ.solid = SOLID_NOT;
-       o = (targ.absmin + targ.absmax) * 0.5;
-       shottime = ((vlen(o - org) / shotspeed) + shotdelay);
-       v = targ.velocity * shottime + o;
-       tracebox(o, targ.mins, targ.maxs, v, false, targ);
-       v = trace_endpos;
-       end = v + (targ.mins + targ.maxs) * 0.5;
-       if ((vlen(end - org) / shotspeed + 0.2) > maxtime)
-       {
-               // out of range
-               targ.solid = savesolid;
-               return false;
-       }
-
-       if (!tracetossfaketarget)
-               tracetossfaketarget = spawn();
-       tracetossfaketarget.solid = savesolid;
-       tracetossfaketarget.movetype = targ.movetype;
-       _setmodel(tracetossfaketarget, targ.model); // no low precision
-       tracetossfaketarget.model = targ.model;
-       tracetossfaketarget.modelindex = targ.modelindex;
-       setsize(tracetossfaketarget, targ.mins, targ.maxs);
-       setorigin(tracetossfaketarget, v);
-
-       c = 0;
-       dir = normalize(end - org);
-       while (c < 10) // 10 traces
-       {
-               setorigin(tracetossent, org); // reset
-               tracetossent.velocity = findtrajectory_velocity = normalize(dir) * shotspeed + shotspeedupward * '0 0 1';
-               tracetoss(tracetossent, ignore); // love builtin functions...
-               if (trace_ent == tracetossfaketarget) // done
-               {
-                       targ.solid = savesolid;
-
-                       // make it disappear
-                       tracetossfaketarget.solid = SOLID_NOT;
-                       tracetossfaketarget.movetype = MOVETYPE_NONE;
-                       tracetossfaketarget.model = "";
-                       tracetossfaketarget.modelindex = 0;
-                       // relink to remove it from physics considerations
-                       setorigin(tracetossfaketarget, v);
-
-                       return true;
-               }
-               dir.z = dir.z + 0.1; // aim up a little more
-               c = c + 1;
-       }
-       targ.solid = savesolid;
-
-       // make it disappear
-       tracetossfaketarget.solid = SOLID_NOT;
-       tracetossfaketarget.movetype = MOVETYPE_NONE;
-       tracetossfaketarget.model = "";
-       tracetossfaketarget.modelindex = 0;
-       // relink to remove it from physics considerations
-       setorigin(tracetossfaketarget, v);
-
-       // leave a valid one even if it won't reach
-       findtrajectory_velocity = normalize(end - org) * shotspeed + shotspeedupward * '0 0 1';
-       return false;
-}
-
-void lag_update()
-{SELFPARAM();
-       if (self.lag1_time) if (time > self.lag1_time) {self.lag_func(self.lag1_time, self.lag1_float1, self.lag1_float2, self.lag1_entity1, self.lag1_vec1, self.lag1_vec2, self.lag1_vec3, self.lag1_vec4);self.lag1_time = 0;}
-       if (self.lag2_time) if (time > self.lag2_time) {self.lag_func(self.lag2_time, self.lag2_float1, self.lag2_float2, self.lag2_entity1, self.lag2_vec1, self.lag2_vec2, self.lag2_vec3, self.lag2_vec4);self.lag2_time = 0;}
-       if (self.lag3_time) if (time > self.lag3_time) {self.lag_func(self.lag3_time, self.lag3_float1, self.lag3_float2, self.lag3_entity1, self.lag3_vec1, self.lag3_vec2, self.lag3_vec3, self.lag3_vec4);self.lag3_time = 0;}
-       if (self.lag4_time) if (time > self.lag4_time) {self.lag_func(self.lag4_time, self.lag4_float1, self.lag4_float2, self.lag4_entity1, self.lag4_vec1, self.lag4_vec2, self.lag4_vec3, self.lag4_vec4);self.lag4_time = 0;}
-       if (self.lag5_time) if (time > self.lag5_time) {self.lag_func(self.lag5_time, self.lag5_float1, self.lag5_float2, self.lag5_entity1, self.lag5_vec1, self.lag5_vec2, self.lag5_vec3, self.lag5_vec4);self.lag5_time = 0;}
-}
-
-float lag_additem(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
-{SELFPARAM();
-       if (self.lag1_time == 0) {self.lag1_time = t;self.lag1_float1 = f1;self.lag1_float2 = f2;self.lag1_entity1 = e1;self.lag1_vec1 = v1;self.lag1_vec2 = v2;self.lag1_vec3 = v3;self.lag1_vec4 = v4;return true;}
-       if (self.lag2_time == 0) {self.lag2_time = t;self.lag2_float1 = f1;self.lag2_float2 = f2;self.lag2_entity1 = e1;self.lag2_vec1 = v1;self.lag2_vec2 = v2;self.lag2_vec3 = v3;self.lag2_vec4 = v4;return true;}
-       if (self.lag3_time == 0) {self.lag3_time = t;self.lag3_float1 = f1;self.lag3_float2 = f2;self.lag3_entity1 = e1;self.lag3_vec1 = v1;self.lag3_vec2 = v2;self.lag3_vec3 = v3;self.lag3_vec4 = v4;return true;}
-       if (self.lag4_time == 0) {self.lag4_time = t;self.lag4_float1 = f1;self.lag4_float2 = f2;self.lag4_entity1 = e1;self.lag4_vec1 = v1;self.lag4_vec2 = v2;self.lag4_vec3 = v3;self.lag4_vec4 = v4;return true;}
-       if (self.lag5_time == 0) {self.lag5_time = t;self.lag5_float1 = f1;self.lag5_float2 = f2;self.lag5_entity1 = e1;self.lag5_vec1 = v1;self.lag5_vec2 = v2;self.lag5_vec3 = v3;self.lag5_vec4 = v4;return true;}
-       // no room for it (what is the best thing to do here??)
-       return false;
-}
-
-float bot_shouldattack(entity e)
-{SELFPARAM();
-       if (e.team == self.team)
-       {
-               if (e == self)
-                       return false;
-               if (teamplay)
-               if (e.team != 0)
-                       return false;
-       }
-
-       if(e.frozen)
-               return false;
-
-       if(teamplay)
-       {
-               if(e.team==0)
-                       return false;
-       }
-       else if(bot_ignore_bots)
-               if(IS_BOT_CLIENT(e))
-                       return false;
-
-       if (!e.takedamage)
-               return false;
-       if (e.deadflag)
-               return false;
-       if (e.BUTTON_CHAT)
-               return false;
-       if(e.flags & FL_NOTARGET)
-               return false;
-
-       if(MUTATOR_CALLHOOK(BotShouldAttack, e))
-               return false;
-
-       return true;
-}
-
-void bot_lagfunc(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
-{SELFPARAM();
-       if(self.flags & FL_INWATER)
-       {
-               self.bot_aimtarg = world;
-               return;
-       }
-       self.bot_aimtarg = e1;
-       self.bot_aimlatency = self.ping; // FIXME?  Shouldn't this be in the lag item?
-       self.bot_aimselforigin = v1;
-       self.bot_aimselfvelocity = v2;
-       self.bot_aimtargorigin = v3;
-       self.bot_aimtargvelocity = v4;
-       if(skill <= 0)
-               self.bot_canfire = (random() < 0.8);
-       else if(skill <= 1)
-               self.bot_canfire = (random() < 0.9);
-       else if(skill <= 2)
-               self.bot_canfire = (random() < 0.95);
-       else
-               self.bot_canfire = 1;
-}
-
-float bot_aimdir(vector v, float maxfiredeviation)
-{SELFPARAM();
-       float dist, delta_t, blend;
-       vector desiredang, diffang;
-
-       //dprint("aim ", self.netname, ": old:", vtos(self.v_angle));
-       // make sure v_angle is sane first
-       self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360;
-       self.v_angle_z = 0;
-
-       // get the desired angles to aim at
-       //dprint(" at:", vtos(v));
-       v = normalize(v);
-       //te_lightning2(world, self.origin + self.view_ofs, self.origin + self.view_ofs + v * 200);
-       if (time >= self.bot_badaimtime)
-       {
-               self.bot_badaimtime = max(self.bot_badaimtime + 0.3, time);
-               self.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+self.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
-       }
-       desiredang = vectoangles(v) + self.bot_badaimoffset;
-       //dprint(" desired:", vtos(desiredang));
-       if (desiredang.x >= 180)
-               desiredang.x = desiredang.x - 360;
-       desiredang.x = bound(-90, 0 - desiredang.x, 90);
-       desiredang.z = self.v_angle.z;
-       //dprint(" / ", vtos(desiredang));
-
-       //// pain throws off aim
-       //if (self.bot_painintensity)
-       //{
-       //      // shake from pain
-       //      desiredang = desiredang + randomvec() * self.bot_painintensity * 0.2;
-       //}
-
-       // calculate turn angles
-       diffang = (desiredang - self.bot_olddesiredang);
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       self.bot_olddesiredang = desiredang;
-       //dprint(" diff:", vtos(diffang));
-
-       delta_t = time-self.bot_prevaimtime;
-       self.bot_prevaimtime = time;
-       // Here we will try to anticipate the comming aiming direction
-       self.bot_1st_order_aimfilter= self.bot_1st_order_aimfilter
-               + (diffang * (1 / delta_t)    - self.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1);
-       self.bot_2nd_order_aimfilter= self.bot_2nd_order_aimfilter
-               + (self.bot_1st_order_aimfilter - self.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1);
-       self.bot_3th_order_aimfilter= self.bot_3th_order_aimfilter
-               + (self.bot_2nd_order_aimfilter - self.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1);
-       self.bot_4th_order_aimfilter= self.bot_4th_order_aimfilter
-               + (self.bot_3th_order_aimfilter - self.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1);
-       self.bot_5th_order_aimfilter= self.bot_5th_order_aimfilter
-               + (self.bot_4th_order_aimfilter - self.bot_5th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_5th,1);
-
-       //blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing !
-       blend = bound(0,skill+self.bot_aimskill,10)*0.1;
-       desiredang = desiredang + blend *
-       (
-                 self.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st
-               + self.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd
-               + self.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th
-               + self.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th
-               + self.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th
-       );
-
-       // calculate turn angles
-       diffang = desiredang - self.bot_mouseaim;
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       //dprint(" diff:", vtos(diffang));
-
-       if (time >= self.bot_aimthinktime)
-       {
-               self.bot_aimthinktime = max(self.bot_aimthinktime + 0.5 - 0.05*(skill+self.bot_thinkskill), time);
-               self.bot_mouseaim = self.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+self.bot_thinkskill),10));
-       }
-
-       //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
-
-       diffang = self.bot_mouseaim - desiredang;
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       desiredang = desiredang + diffang * bound(0,autocvar_bot_ai_aimskill_think,1);
-
-       // calculate turn angles
-       diffang = desiredang - self.v_angle;
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       //dprint(" diff:", vtos(diffang));
-
-       // jitter tracking
-       dist = vlen(diffang);
-       //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3)));
-
-       // turn
-       float r, fixedrate, blendrate;
-       fixedrate = autocvar_bot_ai_aimskill_fixedrate / bound(1,dist,1000);
-       blendrate = autocvar_bot_ai_aimskill_blendrate;
-       r = max(fixedrate, blendrate);
-       //self.v_angle = self.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1);
-       self.v_angle = self.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+self.bot_mouseskill,3)*0.005-random()), 1);
-       self.v_angle = self.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1);
-       //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
-       //self.v_angle = self.v_angle + diffang * (1/ blendrate);
-       self.v_angle_z = 0;
-       self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360;
-       //dprint(" turn:", vtos(self.v_angle));
-
-       makevectors(self.v_angle);
-       shotorg = self.origin + self.view_ofs;
-       shotdir = v_forward;
-
-       //dprint(" dir:", vtos(v_forward));
-       //te_lightning2(world, shotorg, shotorg + shotdir * 100);
-
-       // calculate turn angles again
-       //diffang = desiredang - self.v_angle;
-       //diffang_y = diffang_y - floor(diffang_y / 360) * 360;
-       //if (diffang_y >= 180)
-       //      diffang_y = diffang_y - 360;
-
-       //dprint("e ", vtos(diffang), " < ", ftos(maxfiredeviation), "\n");
-
-       // decide whether to fire this time
-       // note the maxfiredeviation is in degrees so this has to convert to radians first
-       //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
-       if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
-       if (vlen(trace_endpos-shotorg) < 500+500*bound(0, skill+self.bot_aggresskill, 10) || random()*random()>bound(0,(skill+self.bot_aggresskill)*0.05,1))
-               self.bot_firetimer = time + bound(0.1, 0.5-(skill+self.bot_aggresskill)*0.05, 0.5);
-       //traceline(shotorg,shotorg+shotdir*1000,false,world);
-       //dprint(ftos(maxfiredeviation),"\n");
-       //dprint(" diff:", vtos(diffang), "\n");
-
-       return self.bot_canfire && (time < self.bot_firetimer);
-}
-
-vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay)
-{
-       // Try to add code here that predicts gravity effect here, no clue HOW to though ... well not yet atleast...
-       return targorigin + targvelocity * (shotdelay + vlen(targorigin - shotorg) / shotspeed);
-}
-
-float bot_aim(float shotspeed, float shotspeedupward, float maxshottime, float applygravity)
-{SELFPARAM();
-       float f, r, hf, distanceratio;
-       vector v;
-       /*
-       eprint(self);
-       dprint("bot_aim(", ftos(shotspeed));
-       dprint(", ", ftos(shotspeedupward));
-       dprint(", ", ftos(maxshottime));
-       dprint(", ", ftos(applygravity));
-       dprint(");\n");
-       */
-
-       hf = self.dphitcontentsmask;
-       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-
-       shotspeed *= W_WeaponSpeedFactor();
-       shotspeedupward *= W_WeaponSpeedFactor();
-       if (!shotspeed)
-       {
-               LOG_TRACE("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " shotspeed is zero!\n");
-               shotspeed = 1000000;
-       }
-       if (!maxshottime)
-       {
-               LOG_TRACE("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " maxshottime is zero!\n");
-               maxshottime = 1;
-       }
-       makevectors(self.v_angle);
-       shotorg = self.origin + self.view_ofs;
-       shotdir = v_forward;
-       v = bot_shotlead(self.bot_aimtargorigin, self.bot_aimtargvelocity, shotspeed, self.bot_aimlatency);
-       distanceratio = sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees;
-       distanceratio = bound(0,distanceratio,1);
-       r =  (autocvar_bot_ai_aimskill_firetolerance_maxdegrees-autocvar_bot_ai_aimskill_firetolerance_mindegrees)
-               * (1-distanceratio) + autocvar_bot_ai_aimskill_firetolerance_mindegrees;
-       if (applygravity && self.bot_aimtarg)
-       {
-               if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', self.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, self))
-               {
-                       self.dphitcontentsmask = hf;
-                       return false;
-               }
-
-               f = bot_aimdir(findtrajectory_velocity - shotspeedupward * '0 0 1', r);
-       }
-       else
-       {
-               f = bot_aimdir(v - shotorg, r);
-               //dprint("AIM: ");dprint(vtos(self.bot_aimtargorigin));dprint(" + ");dprint(vtos(self.bot_aimtargvelocity));dprint(" * ");dprint(ftos(self.bot_aimlatency + vlen(self.bot_aimtargorigin - shotorg) / shotspeed));dprint(" = ");dprint(vtos(v));dprint(" : aimdir = ");dprint(vtos(normalize(v - shotorg)));dprint(" : ");dprint(vtos(shotdir));dprint("\n");
-               //traceline(shotorg, shotorg + shotdir * 10000, false, self);
-               //if (trace_ent.takedamage)
-               //if (trace_fraction < 1)
-               //if (!bot_shouldattack(trace_ent))
-               //      return false;
-               traceline(shotorg, self.bot_aimtargorigin, false, self);
-               if (trace_fraction < 1)
-               if (trace_ent != self.enemy)
-               if (!bot_shouldattack(trace_ent))
-               {
-                       self.dphitcontentsmask = hf;
-                       return false;
-               }
-       }
-
-       //if (r > maxshottime * shotspeed)
-       //      return false;
-       self.dphitcontentsmask = hf;
-       return true;
-}
diff --git a/qcsrc/server/bot/default/aim.qh b/qcsrc/server/bot/default/aim.qh
deleted file mode 100644 (file)
index d1cbd0d..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#ifndef AIM_H
-#define AIM_H
-/*
- * Globals and Fields
- */
-
-entity tracetossent;
-entity tracetossfaketarget;
-vector findtrajectory_velocity;
-
-
-
-vector shotorg;
-vector shotdir;
-
-// lag simulation
-// upto 5 queued messages
-.float lag1_time;
-.float lag1_float1;
-.float lag1_float2;
-.entity lag1_entity1;
-.vector lag1_vec1;
-.vector lag1_vec2;
-.vector lag1_vec3;
-.vector lag1_vec4;
-
-.float lag2_time;
-.float lag2_float1;
-.float lag2_float2;
-.entity lag2_entity1;
-.vector lag2_vec1;
-.vector lag2_vec2;
-.vector lag2_vec3;
-.vector lag2_vec4;
-
-.float lag3_time;
-.float lag3_float1;
-.float lag3_float2;
-.entity lag3_entity1;
-.vector lag3_vec1;
-.vector lag3_vec2;
-.vector lag3_vec3;
-.vector lag3_vec4;
-
-.float lag4_time;
-.float lag4_float1;
-.float lag4_float2;
-.entity lag4_entity1;
-.vector lag4_vec1;
-.vector lag4_vec2;
-.vector lag4_vec3;
-.vector lag4_vec4;
-
-.float lag5_time;
-.float lag5_float1;
-.float lag5_float2;
-.entity lag5_entity1;
-.vector lag5_vec1;
-.vector lag5_vec2;
-.vector lag5_vec3;
-.vector lag5_vec4;
-
-.float bot_badaimtime;
-.float bot_aimthinktime;
-.float bot_prevaimtime;
-.float bot_firetimer;
-.float bot_aimlatency;
-
-.vector bot_mouseaim;
-.vector bot_badaimoffset;
-.vector bot_1st_order_aimfilter;
-.vector bot_2nd_order_aimfilter;
-.vector bot_3th_order_aimfilter;
-.vector bot_4th_order_aimfilter;
-.vector bot_5th_order_aimfilter;
-.vector bot_olddesiredang;
-
-.vector bot_aimselforigin;
-.vector bot_aimselfvelocity;
-.vector bot_aimtargorigin;
-.vector bot_aimtargvelocity;
-
-.entity bot_aimtarg;
-
-/*
- * Functions
- */
-
-float lag_additem(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
-void lag_update();
-void bot_lagfunc(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
-
-float bot_shouldattack(entity e);
-float bot_aimdir(vector v, float maxfiredeviation);
-float bot_aim(float shotspeed, float shotspeedupward, float maxshottime, float applygravity);
-float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore);
-
-vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay);
-
-.void(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func;
-#endif
diff --git a/qcsrc/server/bot/default/all.inc b/qcsrc/server/bot/default/all.inc
deleted file mode 100644 (file)
index a613a39..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "defs.qh"
-
-#include "bot.qc"
-#include "aim.qc"
-#include "navigation.qc"
-#include "scripting.qc"
-#include "waypoints.qc"
-
-#include "havocbot/havocbot.qc"
-#include "havocbot/role_keyhunt.qc"
-#include "havocbot/roles.qc"
diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc
deleted file mode 100644 (file)
index 7eb438e..0000000
+++ /dev/null
@@ -1,697 +0,0 @@
-#include "bot.qh"
-
-#include "aim.qh"
-#include "navigation.qh"
-#include "scripting.qh"
-#include "waypoints.qh"
-
-#include "havocbot/havocbot.qh"
-
-#include "../../weapons/accuracy.qh"
-
-entity bot_spawn()
-{SELFPARAM();
-       entity bot = spawnclient();
-       if (bot)
-       {
-               currentbots = currentbots + 1;
-               setself(bot);
-               bot_setnameandstuff();
-               ClientConnect();
-               PutClientInServer();
-               setself(this);
-       }
-       return bot;
-}
-
-void bot_think()
-{SELFPARAM();
-       if (self.bot_nextthink > time)
-               return;
-
-       self.flags &= ~FL_GODMODE;
-       if(autocvar_bot_god)
-               self.flags |= FL_GODMODE;
-
-       self.bot_nextthink = self.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, self.bot_aiskill);
-       //if (self.bot_painintensity > 0)
-       //      self.bot_painintensity = self.bot_painintensity - (skill + 1) * 40 * frametime;
-
-       //self.bot_painintensity = self.bot_painintensity + self.bot_oldhealth - self.health;
-       //self.bot_painintensity = bound(0, self.bot_painintensity, 100);
-
-       if (!IS_PLAYER(self) || (autocvar_g_campaign && !campaign_bots_may_start))
-       {
-               self.bot_nextthink = time + 0.5;
-               return;
-       }
-
-       if (self.fixangle)
-       {
-               self.v_angle = self.angles;
-               self.v_angle_z = 0;
-               self.fixangle = false;
-       }
-
-       self.dmg_take = 0;
-       self.dmg_save = 0;
-       self.dmg_inflictor = world;
-
-       // calculate an aiming latency based on the skill setting
-       // (simulated network latency + naturally delayed reflexes)
-       //self.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think')
-       // minimum ping 20+10 random
-       self.ping = bound(0,0.07 - bound(0, (skill + self.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
-       // skill 10 = ping 0.2 (adrenaline)
-       // skill 0 = ping 0.7 (slightly drunk)
-
-       // clear buttons
-       self.BUTTON_ATCK = 0;
-       self.button1 = 0;
-       self.BUTTON_JUMP = 0;
-       self.BUTTON_ATCK2 = 0;
-       self.BUTTON_ZOOM = 0;
-       self.BUTTON_CROUCH = 0;
-       self.BUTTON_HOOK = 0;
-       self.BUTTON_INFO = 0;
-       self.button8 = 0;
-       self.BUTTON_CHAT = 0;
-       self.BUTTON_USE = 0;
-
-       if (time < game_starttime)
-       {
-               // block the bot during the countdown to game start
-               self.movement = '0 0 0';
-               self.bot_nextthink = game_starttime;
-               return;
-       }
-
-       // if dead, just wait until we can respawn
-       if (self.deadflag)
-       {
-               if (self.deadflag == DEAD_DEAD)
-               {
-                       self.BUTTON_JUMP = 1; // press jump to respawn
-                       self.bot_strategytime = 0;
-               }
-       }
-       else if(self.aistatus & AI_STATUS_STUCK)
-               navigation_unstuck();
-
-       // now call the current bot AI (havocbot for example)
-       self.bot_ai();
-}
-
-void bot_setnameandstuff()
-{SELFPARAM();
-       string readfile, s;
-       float file, tokens, prio;
-       entity p;
-
-       string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
-       string name, prefix, suffix;
-
-       if(autocvar_g_campaign)
-       {
-               prefix = "";
-               suffix = "";
-       }
-       else
-       {
-               prefix = autocvar_bot_prefix;
-               suffix = autocvar_bot_suffix;
-       }
-
-       file = fopen(autocvar_bot_config_file, FILE_READ);
-
-       if(file < 0)
-       {
-               LOG_INFO(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n"));
-               readfile = "";
-       }
-       else
-       {
-               RandomSelection_Init();
-               while((readfile = fgets(file)))
-               {
-                       if(substring(readfile, 0, 2) == "//")
-                               continue;
-                       if(substring(readfile, 0, 1) == "#")
-                               continue;
-                       tokens = tokenizebyseparator(readfile, "\t");
-                       if(tokens == 0)
-                               continue;
-                       s = argv(0);
-                       prio = 1;
-                       FOR_EACH_CLIENT(p)
-                       {
-                               if(IS_BOT_CLIENT(p))
-                               if(s == p.cleanname)
-                               {
-                                       prio = 0;
-                                       break;
-                               }
-                       }
-                       RandomSelection_Add(world, 0, readfile, 1, prio);
-               }
-               readfile = RandomSelection_chosen_string;
-               fclose(file);
-       }
-
-       tokens = tokenizebyseparator(readfile, "\t");
-       if(argv(0) != "") bot_name = argv(0);
-       else bot_name = "Bot";
-
-       if(argv(1) != "") bot_model = argv(1);
-       else bot_model = "";
-
-       if(argv(2) != "") bot_skin = argv(2);
-       else bot_skin = "0";
-
-       if(argv(3) != "" && stof(argv(3)) >= 0) bot_shirt = argv(3);
-       else bot_shirt = ftos(floor(random() * 15));
-
-       if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4);
-       else bot_pants = ftos(floor(random() * 15));
-
-       self.bot_forced_team = stof(argv(5));
-
-       prio = 6;
-
-       #define READSKILL(f,w,r) if(argv(prio) != "") self.f = stof(argv(prio)) * (w); else self.f = (!autocvar_g_campaign) * (2 * random() - 1) * (r) * (w); ++prio
-       //print(bot_name, ": ping=", argv(9), "\n");
-
-       READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill
-       READSKILL(bot_moveskill, 2, 0); // move skill
-       READSKILL(bot_dodgeskill, 2, 0); // dodge skill
-
-       READSKILL(bot_pingskill, 0.5, 0); // ping skill
-
-       READSKILL(bot_weaponskill, 2, 0); // weapon skill
-       READSKILL(bot_aggresskill, 1, 0); // aggre skill
-       READSKILL(bot_rangepreference, 1, 0); // read skill
-
-       READSKILL(bot_aimskill, 2, 0); // aim skill
-       READSKILL(bot_offsetskill, 2, 0.5); // offset skill
-       READSKILL(bot_mouseskill, 1, 0.5); // mouse skill
-
-       READSKILL(bot_thinkskill, 1, 0.5); // think skill
-       READSKILL(bot_aiskill, 2, 0); // "ai" skill
-
-       self.bot_config_loaded = true;
-
-       // this is really only a default, JoinBestTeam is called later
-       setcolor(self, stof(bot_shirt) * 16 + stof(bot_pants));
-       self.bot_preferredcolors = self.clientcolors;
-
-       // pick the name
-       if (autocvar_bot_usemodelnames)
-               name = bot_model;
-       else
-               name = bot_name;
-
-       // number bots with identical names
-       float i;
-       i = 0;
-       FOR_EACH_CLIENT(p)
-       {
-               if(IS_BOT_CLIENT(p))
-                       if(p.cleanname == name)
-                               ++i;
-       }
-       if (i)
-               self.netname = self.netname_freeme = strzone(strcat(prefix, name, "(", ftos(i), ")", suffix));
-       else
-               self.netname = self.netname_freeme = strzone(strcat(prefix, name, suffix));
-
-       self.cleanname = strzone(name);
-
-       // pick the model and skin
-       if(substring(bot_model, -4, 1) != ".")
-               bot_model = strcat(bot_model, ".iqm");
-       self.playermodel = self.playermodel_freeme = strzone(strcat("models/player/", bot_model));
-       self.playerskin = self.playerskin_freeme = strzone(bot_skin);
-
-       self.cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the world
-       self.cvar_cl_accuracy_data_receive = 0;  // don't receive any weapon accuracy data
-}
-
-void bot_custom_weapon_priority_setup()
-{
-       float tokens, i, w;
-
-       bot_custom_weapon = false;
-
-       if(     autocvar_bot_ai_custom_weapon_priority_far == "" ||
-               autocvar_bot_ai_custom_weapon_priority_mid == "" ||
-               autocvar_bot_ai_custom_weapon_priority_close == "" ||
-               autocvar_bot_ai_custom_weapon_priority_distances == ""
-       )
-               return;
-
-       // Parse distances
-       tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," ");
-
-       if (tokens!=2)
-               return;
-
-       bot_distance_far = stof(argv(0));
-       bot_distance_close = stof(argv(1));
-
-       if(bot_distance_far < bot_distance_close){
-               bot_distance_far = stof(argv(1));
-               bot_distance_close = stof(argv(0));
-       }
-
-       // Initialize list of weapons
-       bot_weapons_far[0] = -1;
-       bot_weapons_mid[0] = -1;
-       bot_weapons_close[0] = -1;
-
-       // Parse far distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," ");
-
-       int c = 0;
-       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
-               w = stof(argv(i));
-               if ( w >= WEP_FIRST && w <= WEP_LAST) {
-                       bot_weapons_far[c] = w;
-                       ++c;
-               }
-       }
-       if(c < Weapons_COUNT)
-               bot_weapons_far[c] = -1;
-
-       // Parse mid distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," ");
-
-       c = 0;
-       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
-               w = stof(argv(i));
-               if ( w >= WEP_FIRST && w <= WEP_LAST) {
-                       bot_weapons_mid[c] = w;
-                       ++c;
-               }
-       }
-       if(c < Weapons_COUNT)
-               bot_weapons_mid[c] = -1;
-
-       // Parse close distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," ");
-
-       c = 0;
-       for(i=0; i < tokens && i < Weapons_COUNT; ++i){
-               w = stof(argv(i));
-               if ( w >= WEP_FIRST && w <= WEP_LAST) {
-                       bot_weapons_close[c] = w;
-                       ++c;
-               }
-       }
-       if(c < Weapons_COUNT)
-               bot_weapons_close[c] = -1;
-
-       bot_custom_weapon = true;
-}
-
-void bot_endgame()
-{
-       entity e;
-       //dprint("bot_endgame\n");
-       e = bot_list;
-       while (e)
-       {
-               setcolor(e, e.bot_preferredcolors);
-               e = e.nextbot;
-       }
-       // if dynamic waypoints are ever implemented, save them here
-}
-
-void bot_relinkplayerlist()
-{
-       entity e;
-       entity prevbot;
-       player_count = 0;
-       currentbots = 0;
-       player_list = e = findchainflags(flags, FL_CLIENT);
-       bot_list = world;
-       prevbot = world;
-       while (e)
-       {
-               player_count = player_count + 1;
-               e.nextplayer = e.chain;
-               if (IS_BOT_CLIENT(e))
-               {
-                       if (prevbot)
-                               prevbot.nextbot = e;
-                       else
-                       {
-                               bot_list = e;
-                               bot_list.nextbot = world;
-                       }
-                       prevbot = e;
-                       currentbots = currentbots + 1;
-               }
-               e = e.chain;
-       }
-       LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
-       bot_strategytoken = bot_list;
-       bot_strategytoken_taken = true;
-}
-
-void bot_clientdisconnect()
-{SELFPARAM();
-       if (!IS_BOT_CLIENT(self))
-               return;
-       bot_clearqueue(self);
-       if(self.cleanname)
-               strunzone(self.cleanname);
-       if(self.netname_freeme)
-               strunzone(self.netname_freeme);
-       if(self.playermodel_freeme)
-               strunzone(self.playermodel_freeme);
-       if(self.playerskin_freeme)
-               strunzone(self.playerskin_freeme);
-       self.cleanname = string_null;
-       self.netname_freeme = string_null;
-       self.playermodel_freeme = string_null;
-       self.playerskin_freeme = string_null;
-       if(self.bot_cmd_current)
-               remove(self.bot_cmd_current);
-       if(bot_waypoint_queue_owner==self)
-               bot_waypoint_queue_owner = world;
-}
-
-void bot_clientconnect()
-{SELFPARAM();
-       if (!IS_BOT_CLIENT(self))
-               return;
-       self.bot_preferredcolors = self.clientcolors;
-       self.bot_nextthink = time - random();
-       self.lag_func = bot_lagfunc;
-       self.isbot = true;
-       self.createdtime = self.bot_nextthink;
-
-       if(!self.bot_config_loaded) // This is needed so team overrider doesn't break between matches
-               bot_setnameandstuff();
-
-       if(self.bot_forced_team==1)
-               self.team = NUM_TEAM_1;
-       else if(self.bot_forced_team==2)
-               self.team = NUM_TEAM_2;
-       else if(self.bot_forced_team==3)
-               self.team = NUM_TEAM_3;
-       else if(self.bot_forced_team==4)
-               self.team = NUM_TEAM_4;
-       else
-               JoinBestTeam(self, false, true);
-
-       havocbot_setupbot();
-}
-
-void bot_removefromlargestteam()
-{
-       float besttime, bestcount, thiscount;
-       entity best, head;
-       CheckAllowedTeams(world);
-       GetTeamCounts(world);
-       head = findchainfloat(isbot, true);
-       if (!head)
-               return;
-       best = head;
-       besttime = head.createdtime;
-       bestcount = 0;
-       while (head)
-       {
-               if(head.team == NUM_TEAM_1)
-                       thiscount = c1;
-               else if(head.team == NUM_TEAM_2)
-                       thiscount = c2;
-               else if(head.team == NUM_TEAM_3)
-                       thiscount = c3;
-               else if(head.team == NUM_TEAM_4)
-                       thiscount = c4;
-               else
-                       thiscount = 0;
-               if (thiscount > bestcount)
-               {
-                       bestcount = thiscount;
-                       besttime = head.createdtime;
-                       best = head;
-               }
-               else if (thiscount == bestcount && besttime < head.createdtime)
-               {
-                       besttime = head.createdtime;
-                       best = head;
-               }
-               head = head.chain;
-       }
-       currentbots = currentbots - 1;
-       dropclient(best);
-}
-
-void bot_removenewest()
-{
-       float besttime;
-       entity best, head;
-
-       if(teamplay)
-       {
-               bot_removefromlargestteam();
-               return;
-       }
-
-       head = findchainfloat(isbot, true);
-       if (!head)
-               return;
-       best = head;
-       besttime = head.createdtime;
-       while (head)
-       {
-               if (besttime < head.createdtime)
-               {
-                       besttime = head.createdtime;
-                       best = head;
-               }
-               head = head.chain;
-       }
-       currentbots = currentbots - 1;
-       dropclient(best);
-}
-
-void autoskill(float factor)
-{
-       float bestbot;
-       float bestplayer;
-       entity head;
-
-       bestbot = -1;
-       bestplayer = -1;
-       FOR_EACH_PLAYER(head)
-       {
-               if(IS_REAL_CLIENT(head))
-                       bestplayer = max(bestplayer, head.totalfrags - head.totalfrags_lastcheck);
-               else
-                       bestbot = max(bestbot, head.totalfrags - head.totalfrags_lastcheck);
-       }
-
-       LOG_TRACE("autoskill: best player got ", ftos(bestplayer), ", ");
-       LOG_TRACE("best bot got ", ftos(bestbot), "; ");
-       if(bestbot < 0 || bestplayer < 0)
-       {
-               LOG_TRACE("not doing anything\n");
-               // don't return, let it reset all counters below
-       }
-       else if(bestbot <= bestplayer * factor - 2)
-       {
-               if(autocvar_skill < 17)
-               {
-                       LOG_TRACE("2 frags difference, increasing skill\n");
-                       cvar_set("skill", ftos(autocvar_skill + 1));
-                       bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
-               }
-       }
-       else if(bestbot >= bestplayer * factor + 2)
-       {
-               if(autocvar_skill > 0)
-               {
-                       LOG_TRACE("2 frags difference, decreasing skill\n");
-                       cvar_set("skill", ftos(autocvar_skill - 1));
-                       bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
-               }
-       }
-       else
-       {
-               LOG_TRACE("not doing anything\n");
-               return;
-               // don't reset counters, wait for them to accumulate
-       }
-
-       FOR_EACH_PLAYER(head)
-               head.totalfrags_lastcheck = head.totalfrags;
-}
-
-void bot_calculate_stepheightvec(void)
-{
-       stepheightvec = autocvar_sv_stepheight * '0 0 1';
-       jumpstepheightvec = stepheightvec +
-               ((autocvar_sv_jumpvelocity * autocvar_sv_jumpvelocity) / (2 * autocvar_sv_gravity)) * '0 0 0.85';
-               // 0.75 factor is for safety to make the jumps easy
-}
-
-float bot_fixcount()
-{
-       entity head;
-       float realplayers, bots, activerealplayers;
-
-       activerealplayers = 0;
-       realplayers = 0;
-
-       FOR_EACH_REALCLIENT(head)
-       {
-               if(IS_PLAYER(head) || g_lms || head.caplayer == 1)
-                       ++activerealplayers;
-               ++realplayers;
-       }
-
-       // add/remove bots if needed to make sure there are at least
-       // minplayers+bot_number, or remove all bots if no one is playing
-       // But don't remove bots immediately on level change, as the real players
-       // usually haven't rejoined yet
-       bots_would_leave = false;
-       if (teamplay && autocvar_bot_vs_human && (c3==-1 && c4==-1))
-               bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers);
-       else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5)))
-       {
-               float realminplayers, minplayers;
-               realminplayers = autocvar_minplayers;
-               minplayers = max(0, floor(realminplayers));
-
-               float realminbots, minbots;
-               realminbots = autocvar_bot_number;
-               minbots = max(0, floor(realminbots));
-
-               bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers);
-               if(bots > minbots)
-                       bots_would_leave = true;
-       }
-       else
-       {
-               // if there are no players, remove bots
-               bots = 0;
-       }
-
-       // only add one bot per frame to avoid utter chaos
-       if(time > botframe_nextthink)
-       {
-               //dprint(ftos(bots), " ? ", ftos(currentbots), "\n");
-               while (currentbots < bots)
-               {
-                       if (bot_spawn() == world)
-                       {
-                               bprint("Can not add bot, server full.\n");
-                               return false;
-                       }
-               }
-               while (currentbots > bots)
-                       bot_removenewest();
-       }
-
-       return true;
-}
-
-void bot_serverframe()
-{
-       if (intermission_running)
-               return;
-
-       if (time < 2)
-               return;
-
-       bot_calculate_stepheightvec();
-       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
-
-       if(time > autoskill_nextthink)
-       {
-               float a;
-               a = autocvar_skill_auto;
-               if(a)
-                       autoskill(a);
-               autoskill_nextthink = time + 5;
-       }
-
-       if(time > botframe_nextthink)
-       {
-               if(!bot_fixcount())
-                       botframe_nextthink = time + 10;
-       }
-
-       bot_ignore_bots = autocvar_bot_ignore_bots;
-
-       if(botframe_spawnedwaypoints)
-       {
-               if(autocvar_waypoint_benchmark)
-                       localcmd("quit\n");
-       }
-
-       if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto)
-       if (botframe_spawnedwaypoints)
-       {
-               if(botframe_cachedwaypointlinks)
-               {
-                       if(!botframe_loadedforcedlinks)
-                               waypoint_load_links_hardwired();
-               }
-               else
-               {
-                       // TODO: Make this check cleaner
-                       entity wp = findchain(classname, "waypoint");
-                       if(time - wp.nextthink > 10)
-                               waypoint_save_links();
-               }
-       }
-       else
-       {
-               botframe_spawnedwaypoints = true;
-               waypoint_loadall();
-               if(!waypoint_load_links())
-                       waypoint_schedulerelinkall();
-       }
-
-       if (bot_list)
-       {
-               // cycle the goal token from one bot to the next each frame
-               // (this prevents them from all doing spawnfunc_waypoint searches on the same
-               //  frame, which causes choppy framerates)
-               if (bot_strategytoken_taken)
-               {
-                       bot_strategytoken_taken = false;
-                       if (bot_strategytoken)
-                               bot_strategytoken = bot_strategytoken.nextbot;
-                       if (!bot_strategytoken)
-                               bot_strategytoken = bot_list;
-               }
-
-               if (botframe_nextdangertime < time)
-               {
-                       float interval;
-                       interval = autocvar_bot_ai_dangerdetectioninterval;
-                       if (botframe_nextdangertime < time - interval * 1.5)
-                               botframe_nextdangertime = time;
-                       botframe_nextdangertime = botframe_nextdangertime + interval;
-                       botframe_updatedangerousobjects(autocvar_bot_ai_dangerdetectionupdates);
-               }
-       }
-
-       if (autocvar_g_waypointeditor)
-               botframe_showwaypointlinks();
-
-       if (autocvar_g_waypointeditor_auto)
-               botframe_autowaypoints();
-
-       if(time > bot_cvar_nextthink)
-       {
-               if(currentbots>0)
-                       bot_custom_weapon_priority_setup();
-               bot_cvar_nextthink = time + 5;
-       }
-}
diff --git a/qcsrc/server/bot/default/bot.qh b/qcsrc/server/bot/default/bot.qh
deleted file mode 100644 (file)
index b3c628d..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef BOT_H
-#define BOT_H
-/*
- * Globals and Fields
- */
-
-const int AI_STATUS_ROAMING                                            = 1;    // Bot is just crawling the map. No enemies at sight
-const int AI_STATUS_ATTACKING                                  = 2;    // There are enemies at sight
-const int AI_STATUS_RUNNING                                            = 4;    // Bot is bunny hopping
-const int AI_STATUS_DANGER_AHEAD                               = 8;    // There is lava/slime/trigger_hurt ahead
-const int AI_STATUS_OUT_JUMPPAD                                        = 16;   // Trying to get out of a "vertical" jump pad
-const int AI_STATUS_OUT_WATER                                  = 32;   // Trying to get out of water
-const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = 64;   // Waiting for the personal waypoint to be linked
-const int AI_STATUS_WAYPOINT_PERSONAL_GOING            = 128;  // Going to a personal waypoint
-const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = 256;  // Personal waypoint reached
-const int AI_STATUS_JETPACK_FLYING                             = 512;
-const int AI_STATUS_JETPACK_LANDING                            = 1024;
-const int AI_STATUS_STUCK                                              = 2048; // Cannot reach any goal
-
-.float isbot; // true if this client is actually a bot
-.int aistatus;
-
-// Skill system
-float autoskill_nextthink;
-
-// havocbot_keyboardskill // keyboard movement
-.float bot_moveskill; // moving technique
-.float bot_dodgeskill; // dodging
-
-.float bot_pingskill; // ping offset
-
-.float bot_weaponskill; // weapon usage skill (combos, e.g.)
-.float bot_aggresskill; // aggressivity, controls "think before fire" behaviour
-.float bot_rangepreference; // weapon choice offset for range (>0 = prefer long range earlier "sniper", <0 = prefer short range "spammer")
-
-.float bot_aimskill; // aim accuracy
-.float bot_offsetskill; // aim breakage
-.float bot_mouseskill; // mouse "speed"
-
-.float bot_thinkskill; // target choice
-.float bot_aiskill; // strategy choice
-
-.float totalfrags_lastcheck;
-
-
-
-entity bot_list;
-entity player_list;
-.entity nextbot;
-.entity nextplayer;
-.string cleanname;
-.string netname_freeme;
-.string playermodel_freeme;
-.string playerskin_freeme;
-
-.float bot_nextthink;
-
-.float createdtime;
-.float bot_preferredcolors;
-.float bot_attack;
-.float bot_dodge;
-.float bot_dodgerating;
-
-.float bot_pickup;
-.float bot_pickupbasevalue;
-.float bot_canfire;
-.float bot_strategytime;
-
-.float bot_forced_team;
-.float bot_config_loaded;
-
-float bot_strategytoken_taken;
-entity bot_strategytoken;
-
-float botframe_spawnedwaypoints;
-float botframe_nextthink;
-float botframe_nextdangertime;
-float bot_cvar_nextthink;
-
-/*
- * Functions
- */
-
-entity bot_spawn();
-float bot_fixcount();
-
-void bot_think();
-void bot_setnameandstuff();
-void bot_custom_weapon_priority_setup();
-void bot_endgame();
-void bot_relinkplayerlist();
-void bot_clientdisconnect();
-void bot_clientconnect();
-void bot_removefromlargestteam();
-void bot_removenewest();
-void autoskill(float factor);
-void bot_serverframe();
-
-.void() bot_ai;
-.float(entity player, entity item) bot_pickupevalfunc;
-
-/*
- * Imports
- */
-
-void() havocbot_setupbot;
-
-void bot_calculate_stepheightvec(void);
-#endif
diff --git a/qcsrc/server/bot/default/defs.qh b/qcsrc/server/bot/default/defs.qh
deleted file mode 100644 (file)
index 2d7c499..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-float bot_distance_close;
-float bot_distance_far;
-float bot_ignore_bots; // let bots not attack other bots (only works in non-teamplay)
-
-int autocvar_g_waypointeditor_auto;
-float autocvar_skill_auto;
-float autocvar_sv_jumpvelocity;
-bool autocvar_waypoint_benchmark;
-bool autocvar_bot_god;
-float autocvar_bot_ai_thinkinterval;
-float autocvar_bot_ai_aimskill_blendrate;
-float autocvar_bot_ai_aimskill_firetolerance_distdegrees;
-float autocvar_bot_ai_aimskill_firetolerance_maxdegrees;
-float autocvar_bot_ai_aimskill_firetolerance_mindegrees;
-float autocvar_bot_ai_aimskill_fixedrate;
-float autocvar_bot_ai_aimskill_mouse;
-float autocvar_bot_ai_aimskill_offset;
-float autocvar_bot_ai_aimskill_order_filter_1st;
-float autocvar_bot_ai_aimskill_order_filter_2nd;
-float autocvar_bot_ai_aimskill_order_filter_3th;
-float autocvar_bot_ai_aimskill_order_filter_4th;
-float autocvar_bot_ai_aimskill_order_filter_5th;
-float autocvar_bot_ai_aimskill_order_mix_1st;
-float autocvar_bot_ai_aimskill_order_mix_2nd;
-float autocvar_bot_ai_aimskill_order_mix_3th;
-float autocvar_bot_ai_aimskill_order_mix_4th;
-float autocvar_bot_ai_aimskill_order_mix_5th;
-float autocvar_bot_ai_aimskill_think;
-float autocvar_bot_ai_bunnyhop_firstjumpdelay;
-float autocvar_bot_ai_bunnyhop_skilloffset;
-float autocvar_bot_ai_bunnyhop_startdistance;
-float autocvar_bot_ai_bunnyhop_stopdistance;
-float autocvar_bot_ai_chooseweaponinterval;
-string autocvar_bot_ai_custom_weapon_priority_close;
-string autocvar_bot_ai_custom_weapon_priority_distances;
-string autocvar_bot_ai_custom_weapon_priority_far;
-string autocvar_bot_ai_custom_weapon_priority_mid;
-float autocvar_bot_ai_dangerdetectioninterval;
-float autocvar_bot_ai_dangerdetectionupdates;
-float autocvar_bot_ai_enemydetectioninterval;
-float autocvar_bot_ai_enemydetectionradius;
-float autocvar_bot_ai_friends_aware_pickup_radius;
-float autocvar_bot_ai_ignoregoal_timeout;
-float autocvar_bot_ai_keyboard_distance;
-float autocvar_bot_ai_keyboard_threshold;
-float autocvar_bot_ai_navigation_jetpack;
-float autocvar_bot_ai_navigation_jetpack_mindistance;
-bool autocvar_bot_ai_weapon_combo;
-float autocvar_bot_ai_weapon_combo_threshold;
-string autocvar_bot_config_file;
-bool autocvar_bot_ignore_bots;
-bool autocvar_bot_join_empty;
-bool autocvar_bot_navigation_ignoreplayers;
-bool autocvar_bot_nofire;
-#define autocvar_bot_prefix cvar_string("bot_prefix")
-#define autocvar_bot_suffix cvar_string("bot_suffix")
-bool autocvar_bot_usemodelnames;
-int autocvar_bot_debug;
-bool autocvar_bot_debug_tracewalk;
-bool autocvar_bot_debug_goalstack;
-bool autocvar_bot_wander_enable;
diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc
deleted file mode 100644 (file)
index ae5314a..0000000
+++ /dev/null
@@ -1,1311 +0,0 @@
-#include "havocbot.qh"
-
-void havocbot_ai()
-{SELFPARAM();
-       if(self.draggedby)
-               return;
-
-       if(bot_execute_commands())
-               return;
-
-       if (bot_strategytoken == self)
-       if (!bot_strategytoken_taken)
-       {
-               if(self.havocbot_blockhead)
-               {
-                       self.havocbot_blockhead = false;
-               }
-               else
-               {
-                       if (!self.jumppadcount)
-                               self.havocbot_role();
-               }
-
-               // TODO: tracewalk() should take care of this job (better path finding under water)
-               // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
-               if(self.deadflag != DEAD_NO)
-               if(self.goalcurrent==world)
-               if(self.waterlevel==WATERLEVEL_SWIMMING || (self.aistatus & AI_STATUS_OUT_WATER))
-               {
-                       // Look for the closest waypoint out of water
-                       entity newgoal, head;
-                       float bestdistance, distance;
-
-                       newgoal = world;
-                       bestdistance = 10000;
-                       for (head = findchain(classname, "waypoint"); head; head = head.chain)
-                       {
-                               distance = vlen(head.origin - self.origin);
-                               if(distance>10000)
-                                       continue;
-
-                               if(head.origin.z < self.origin.z)
-                                       continue;
-
-                               if(head.origin.z - self.origin.z - self.view_ofs.z > 100)
-                                       continue;
-
-                               if (pointcontents(head.origin + head.maxs + '0 0 1') != CONTENT_EMPTY)
-                                       continue;
-
-                               traceline(self.origin + self.view_ofs , head.origin, true, head);
-
-                               if(trace_fraction<1)
-                                       continue;
-
-                               if(distance<bestdistance)
-                               {
-                                       newgoal = head;
-                                       bestdistance = distance;
-                               }
-                       }
-
-                       if(newgoal)
-                       {
-                       //      te_wizspike(newgoal.origin);
-                               navigation_pushroute(newgoal);
-                       }
-               }
-
-               // token has been used this frame
-               bot_strategytoken_taken = true;
-       }
-
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       havocbot_chooseenemy();
-       if (self.bot_chooseweapontime < time )
-       {
-               self.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
-               havocbot_chooseweapon();
-       }
-       havocbot_aim();
-       lag_update();
-       if (self.bot_aimtarg)
-       {
-               self.aistatus |= AI_STATUS_ATTACKING;
-               self.aistatus &= ~AI_STATUS_ROAMING;
-
-               if(self.weapons)
-               {
-                       Weapon w = get_weaponinfo(self.weapon);
-                       w.wr_aim(w);
-                       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
-                       {
-                               self.BUTTON_ATCK = false;
-                               self.BUTTON_ATCK2 = false;
-                       }
-                       else
-                       {
-                               if(self.BUTTON_ATCK||self.BUTTON_ATCK2)
-                                       self.lastfiredweapon = self.weapon;
-                       }
-               }
-               else
-               {
-                       if(IS_PLAYER(self.bot_aimtarg))
-                               bot_aimdir(self.bot_aimtarg.origin + self.bot_aimtarg.view_ofs - self.origin - self.view_ofs , -1);
-               }
-       }
-       else if (self.goalcurrent)
-       {
-               self.aistatus |= AI_STATUS_ROAMING;
-               self.aistatus &= ~AI_STATUS_ATTACKING;
-
-               vector now,v,next;//,heading;
-               float aimdistance,skillblend,distanceblend,blend;
-               next = now = ( (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5) - (self.origin + self.view_ofs);
-               aimdistance = vlen(now);
-               //heading = self.velocity;
-               //dprint(self.goalstack01.classname,etos(self.goalstack01),"\n");
-               if(
-                       self.goalstack01 != self && self.goalstack01 != world && ((self.aistatus & AI_STATUS_RUNNING) == 0) &&
-                       !(self.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
-               )
-                       next = ((self.goalstack01.absmin + self.goalstack01.absmax) * 0.5) - (self.origin + self.view_ofs);
-
-               skillblend=bound(0,(skill+self.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn
-               distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1);
-               blend = skillblend * (1-distanceblend);
-               //v = (now * (distanceblend) + next * (1-distanceblend)) * (skillblend) + now * (1-skillblend);
-               //v = now * (distanceblend) * (skillblend) + next * (1-distanceblend) * (skillblend) + now * (1-skillblend);
-               //v = now * ((1-skillblend) + (distanceblend) * (skillblend)) + next * (1-distanceblend) * (skillblend);
-               v = now + blend * (next - now);
-               //dprint(etos(self), " ");
-               //dprint(vtos(now), ":", vtos(next), "=", vtos(v), " (blend ", ftos(blend), ")\n");
-               //v = now * (distanceblend) + next * (1-distanceblend);
-               if (self.waterlevel < WATERLEVEL_SWIMMING)
-                       v.z = 0;
-               //dprint("walk at:", vtos(v), "\n");
-               //te_lightning2(world, self.origin, self.goalcurrent.origin);
-               bot_aimdir(v, -1);
-       }
-       havocbot_movetogoal();
-
-       // if the bot is not attacking, consider reloading weapons
-       if (!(self.aistatus & AI_STATUS_ATTACKING))
-       {
-               // we are currently holding a weapon that's not fully loaded, reload it
-               if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
-               if(self.clip_load < self.clip_size)
-                       self.impulse = 20; // "press" the reload button, not sure if this is done right
-
-               // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
-               // the code above executes next frame, starting the reloading then
-               if(skill >= 5) // bots can only look for unloaded weapons past this skill
-               if(self.clip_load >= 0) // only if we're not reloading a weapon already
-               {
-                       for (int i = WEP_FIRST; i <= WEP_LAST; ++i)
-                       {
-                               entity e = get_weaponinfo(i);
-                               if ((self.weapons & WepSet_FromWeapon(i)) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < e.reloading_ammo))
-                                       self.switchweapon = i;
-                       }
-               }
-       }
-}
-
-void havocbot_keyboard_movement(vector destorg)
-{SELFPARAM();
-       vector keyboard;
-       float blend, maxspeed;
-       float sk;
-
-       sk = skill + self.bot_moveskill;
-
-       maxspeed = autocvar_sv_maxspeed;
-
-       if (time < self.havocbot_keyboardtime)
-               return;
-
-       self.havocbot_keyboardtime =
-               max(
-                       self.havocbot_keyboardtime
-                               + 0.05/max(1, sk+self.havocbot_keyboardskill)
-                               + random()*0.025/max(0.00025, skill+self.havocbot_keyboardskill)
-               , time);
-       keyboard = self.movement * (1.0 / maxspeed);
-
-       float trigger, trigger1;
-       blend = bound(0,sk*0.1,1);
-       trigger = autocvar_bot_ai_keyboard_threshold;
-       trigger1 = 0 - trigger;
-
-       // categorize forward movement
-       // at skill < 1.5 only forward
-       // at skill < 2.5 only individual directions
-       // at skill < 4.5 only individual directions, and forward diagonals
-       // at skill >= 4.5, all cases allowed
-       if (keyboard.x > trigger)
-       {
-               keyboard.x = 1;
-               if (sk < 2.5)
-                       keyboard.y = 0;
-       }
-       else if (keyboard.x < trigger1 && sk > 1.5)
-       {
-               keyboard.x = -1;
-               if (sk < 4.5)
-                       keyboard.y = 0;
-       }
-       else
-       {
-               keyboard.x = 0;
-               if (sk < 1.5)
-                       keyboard.y = 0;
-       }
-       if (sk < 4.5)
-               keyboard.z = 0;
-
-       if (keyboard.y > trigger)
-               keyboard.y = 1;
-       else if (keyboard.y < trigger1)
-               keyboard.y = -1;
-       else
-               keyboard.y = 0;
-
-       if (keyboard.z > trigger)
-               keyboard.z = 1;
-       else if (keyboard.z < trigger1)
-               keyboard.z = -1;
-       else
-               keyboard.z = 0;
-
-       self.havocbot_keyboard = keyboard * maxspeed;
-       if (self.havocbot_ducktime>time) self.BUTTON_CROUCH=true;
-
-       keyboard = self.havocbot_keyboard;
-       blend = bound(0,vlen(destorg-self.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree
-       //dprint("movement ", vtos(self.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
-       self.movement = self.movement + (keyboard - self.movement) * blend;
-}
-
-void havocbot_bunnyhop(vector dir)
-{SELFPARAM();
-       float bunnyhopdistance;
-       vector deviation;
-       float maxspeed;
-       vector gco, gno;
-
-       // Don't jump when attacking
-       if(self.aistatus & AI_STATUS_ATTACKING)
-               return;
-
-       if(IS_PLAYER(self.goalcurrent))
-               return;
-
-       maxspeed = autocvar_sv_maxspeed;
-
-       if(self.aistatus & AI_STATUS_DANGER_AHEAD)
-       {
-               self.aistatus &= ~AI_STATUS_RUNNING;
-               self.BUTTON_JUMP = false;
-               self.bot_canruntogoal = 0;
-               self.bot_timelastseengoal = 0;
-               return;
-       }
-
-       if(self.waterlevel > WATERLEVEL_WETFEET)
-       {
-               self.aistatus &= ~AI_STATUS_RUNNING;
-               return;
-       }
-
-       if(self.bot_lastseengoal != self.goalcurrent && !(self.aistatus & AI_STATUS_RUNNING))
-       {
-               self.bot_canruntogoal = 0;
-               self.bot_timelastseengoal = 0;
-       }
-
-       gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
-       bunnyhopdistance = vlen(self.origin - gco);
-
-       // Run only to visible goals
-       if(self.flags & FL_ONGROUND)
-       if(self.speed==maxspeed)
-       if(checkpvs(self.origin + self.view_ofs, self.goalcurrent))
-       {
-                       self.bot_lastseengoal = self.goalcurrent;
-
-                       // seen it before
-                       if(self.bot_timelastseengoal)
-                       {
-                               // for a period of time
-                               if(time - self.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
-                               {
-                                       float checkdistance;
-                                       checkdistance = true;
-
-                                       // don't run if it is too close
-                                       if(self.bot_canruntogoal==0)
-                                       {
-                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance)
-                                                       self.bot_canruntogoal = 1;
-                                               else
-                                                       self.bot_canruntogoal = -1;
-                                       }
-
-                                       if(self.bot_canruntogoal != 1)
-                                               return;
-
-                                       if(self.aistatus & AI_STATUS_ROAMING)
-                                       if(self.goalcurrent.classname=="waypoint")
-                                       if (!(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
-                                       if(fabs(gco.z - self.origin.z) < self.maxs.z - self.mins.z)
-                                       if(self.goalstack01!=world)
-                                       {
-                                               gno = (self.goalstack01.absmin + self.goalstack01.absmax) * 0.5;
-                                               deviation = vectoangles(gno - self.origin) - vectoangles(gco - self.origin);
-                                               while (deviation.y < -180) deviation.y = deviation.y + 360;
-                                               while (deviation.y > 180) deviation.y = deviation.y - 360;
-
-                                               if(fabs(deviation.y) < 20)
-                                               if(bunnyhopdistance < vlen(self.origin - gno))
-                                               if(fabs(gno.z - gco.z) < self.maxs.z - self.mins.z)
-                                               {
-                                                       if(vlen(gco - gno) > autocvar_bot_ai_bunnyhop_startdistance)
-                                                       if(checkpvs(self.origin + self.view_ofs, self.goalstack01))
-                                                       {
-                                                               checkdistance = false;
-                                                       }
-                                               }
-                                       }
-
-                                       if(checkdistance)
-                                       {
-                                               self.aistatus &= ~AI_STATUS_RUNNING;
-                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
-                                                       self.BUTTON_JUMP = true;
-                                       }
-                                       else
-                                       {
-                                               self.aistatus |= AI_STATUS_RUNNING;
-                                               self.BUTTON_JUMP = true;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               self.bot_timelastseengoal = time;
-                       }
-       }
-       else
-       {
-               self.bot_timelastseengoal = 0;
-       }
-
-#if 0
-       // Release jump button
-       if(!cvar("sv_pogostick"))
-       if((self.flags & FL_ONGROUND) == 0)
-       {
-               if(self.velocity.z < 0 || vlen(self.velocity)<maxspeed)
-                       self.BUTTON_JUMP = false;
-
-               // Strafe
-               if(self.aistatus & AI_STATUS_RUNNING)
-               if(vlen(self.velocity)>maxspeed)
-               {
-                       deviation = vectoangles(dir) - vectoangles(self.velocity);
-                       while (deviation.y < -180) deviation.y = deviation.y + 360;
-                       while (deviation.y > 180) deviation.y = deviation.y - 360;
-
-                       if(fabs(deviation.y)>10)
-                               self.movement_x = 0;
-
-                       if(deviation.y>10)
-                               self.movement_y = maxspeed * -1;
-                       else if(deviation.y<10)
-                               self.movement_y = maxspeed;
-
-               }
-       }
-#endif
-}
-
-void havocbot_movetogoal()
-{SELFPARAM();
-       vector destorg;
-       vector diff;
-       vector dir;
-       vector flatdir;
-       vector m1;
-       vector m2;
-       vector evadeobstacle;
-       vector evadelava;
-       float s;
-       float maxspeed;
-       vector gco;
-       //float dist;
-       vector dodge;
-       //if (self.goalentity)
-       //      te_lightning2(self, self.origin, (self.goalentity.absmin + self.goalentity.absmax) * 0.5);
-       self.movement = '0 0 0';
-       maxspeed = autocvar_sv_maxspeed;
-
-       // Jetpack navigation
-       if(self.goalcurrent)
-       if(self.navigation_jetpack_goal)
-       if(self.goalcurrent==self.navigation_jetpack_goal)
-       if(self.ammo_fuel)
-       {
-               if(autocvar_bot_debug_goalstack)
-               {
-                       debuggoalstack();
-                       te_wizspike(self.navigation_jetpack_point);
-               }
-
-               // Take off
-               if (!(self.aistatus & AI_STATUS_JETPACK_FLYING))
-               {
-                       // Brake almost completely so it can get a good direction
-                       if(vlen(self.velocity)>10)
-                               return;
-                       self.aistatus |= AI_STATUS_JETPACK_FLYING;
-               }
-
-               makevectors(self.v_angle.y * '0 1 0');
-               dir = normalize(self.navigation_jetpack_point - self.origin);
-
-               // Landing
-               if(self.aistatus & AI_STATUS_JETPACK_LANDING)
-               {
-                       // Calculate brake distance in xy
-                       float db, v, d;
-                       vector dxy;
-
-                       dxy = self.origin - ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ); dxy.z = 0;
-                       d = vlen(dxy);
-                       v = vlen(self.velocity -  self.velocity.z * '0 0 1');
-                       db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100;
-               //      dprint("distance ", ftos(ceil(d)), " velocity ", ftos(ceil(v)), " brake at ", ftos(ceil(db)), "\n");
-                       if(d < db || d < 500)
-                       {
-                               // Brake
-                               if(fabs(self.velocity.x)>maxspeed*0.3)
-                               {
-                                       self.movement_x = dir * v_forward * -maxspeed;
-                                       return;
-                               }
-                               // Switch to normal mode
-                               self.navigation_jetpack_goal = world;
-                               self.aistatus &= ~AI_STATUS_JETPACK_LANDING;
-                               self.aistatus &= ~AI_STATUS_JETPACK_FLYING;
-                               return;
-                       }
-               }
-               else if(checkpvs(self.origin,self.goalcurrent))
-               {
-                       // If I can see the goal switch to landing code
-                       self.aistatus &= ~AI_STATUS_JETPACK_FLYING;
-                       self.aistatus |= AI_STATUS_JETPACK_LANDING;
-                       return;
-               }
-
-               // Flying
-               self.BUTTON_HOOK = true;
-               if(self.navigation_jetpack_point.z - PL_MAX.z + PL_MIN.z < self.origin.z)
-               {
-                       self.movement_x = dir * v_forward * maxspeed;
-                       self.movement_y = dir * v_right * maxspeed;
-               }
-               return;
-       }
-
-       // Handling of jump pads
-       if(self.jumppadcount)
-       {
-               // If got stuck on the jump pad try to reach the farthest visible waypoint
-               if(self.aistatus & AI_STATUS_OUT_JUMPPAD)
-               {
-                       if(fabs(self.velocity.z)<50)
-                       {
-                               entity head, newgoal = world;
-                               float distance, bestdistance = 0;
-
-                               for (head = findchain(classname, "waypoint"); head; head = head.chain)
-                               {
-
-                                       distance = vlen(head.origin - self.origin);
-                                       if(distance>1000)
-                                               continue;
-
-                                       traceline(self.origin + self.view_ofs , ( ( head.absmin + head.absmax ) * 0.5 ), true, world);
-
-                                       if(trace_fraction<1)
-                                               continue;
-
-                                       if(distance>bestdistance)
-                                       {
-                                               newgoal = head;
-                                               bestdistance = distance;
-                                       }
-                               }
-
-                               if(newgoal)
-                               {
-                                       self.ignoregoal = self.goalcurrent;
-                                       self.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
-                                       navigation_clearroute();
-                                       navigation_routetogoal(newgoal, self.origin);
-                                       self.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
-                               }
-                       }
-                       else
-                               return;
-               }
-               else
-               {
-                       if(self.velocity.z>0)
-                       {
-                               float threshold, sxy;
-                               vector velxy = self.velocity; velxy_z = 0;
-                               sxy = vlen(velxy);
-                               threshold = maxspeed * 0.2;
-                               if(sxy < threshold)
-                               {
-                                       LOG_TRACE("Warning: ", self.netname, " got stuck on a jumppad (velocity in xy is ", ftos(sxy), "), trying to get out of it now\n");
-                                       self.aistatus |= AI_STATUS_OUT_JUMPPAD;
-                               }
-                               return;
-                       }
-
-                       // Don't chase players while using a jump pad
-                       if(IS_PLAYER(self.goalcurrent) || IS_PLAYER(self.goalstack01))
-                               return;
-               }
-       }
-       else if(self.aistatus & AI_STATUS_OUT_JUMPPAD)
-               self.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
-
-       // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump
-       if(skill>6)
-       if (!(self.flags & FL_ONGROUND))
-       {
-               tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 -65536', MOVE_NOMONSTERS, self);
-               if(tracebox_hits_trigger_hurt(self.origin, self.mins, self.maxs, trace_endpos ))
-               if(self.items & IT_JETPACK)
-               {
-                       tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 65536', MOVE_NOMONSTERS, self);
-                       if(tracebox_hits_trigger_hurt(self.origin, self.mins, self.maxs, trace_endpos + '0 0 1' ))
-                       {
-                               if(self.velocity.z<0)
-                               {
-                                       self.BUTTON_HOOK = true;
-                               }
-                       }
-                       else
-                               self.BUTTON_HOOK = true;
-
-                       // If there is no goal try to move forward
-
-                       if(self.goalcurrent==world)
-                               dir = v_forward;
-                       else
-                               dir = normalize(( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - self.origin);
-
-                       vector xyvelocity = self.velocity; xyvelocity_z = 0;
-                       float xyspeed = xyvelocity * dir;
-
-                       if(xyspeed < (maxspeed / 2))
-                       {
-                               makevectors(self.v_angle.y * '0 1 0');
-                               tracebox(self.origin, self.mins, self.maxs, self.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, self);
-                               if(trace_fraction==1)
-                               {
-                                       self.movement_x = dir * v_forward * maxspeed;
-                                       self.movement_y = dir * v_right * maxspeed;
-                                       if (skill < 10)
-                                               havocbot_keyboard_movement(self.origin + dir * 100);
-                               }
-                       }
-
-                       self.havocbot_blockhead = true;
-
-                       return;
-               }
-               else if(self.health>WEP_CVAR(devastator, damage)*0.5)
-               {
-                       if(self.velocity.z < 0)
-                       if(client_hasweapon(self, WEP_DEVASTATOR.m_id, true, false))
-                       {
-                               self.movement_x = maxspeed;
-
-                               if(self.rocketjumptime)
-                               {
-                                       if(time > self.rocketjumptime)
-                                       {
-                                               self.BUTTON_ATCK2 = true;
-                                               self.rocketjumptime = 0;
-                                       }
-                                       return;
-                               }
-
-                               self.switchweapon = WEP_DEVASTATOR.m_id;
-                               self.v_angle_x = 90;
-                               self.BUTTON_ATCK = true;
-                               self.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
-                               return;
-                       }
-               }
-               else
-               {
-                       // If there is no goal try to move forward
-                       if(self.goalcurrent==world)
-                               self.movement_x = maxspeed;
-               }
-       }
-
-       // If we are under water with no goals, swim up
-       if(self.waterlevel)
-       if(self.goalcurrent==world)
-       {
-               dir = '0 0 0';
-               if(self.waterlevel>WATERLEVEL_SWIMMING)
-                       dir.z = 1;
-               else if(self.velocity.z >= 0 && !(self.waterlevel == WATERLEVEL_WETFEET && self.watertype == CONTENT_WATER))
-                       self.BUTTON_JUMP = true;
-               else
-                       self.BUTTON_JUMP = false;
-               makevectors(self.v_angle.y * '0 1 0');
-               self.movement_x = dir * v_forward * maxspeed;
-               self.movement_y = dir * v_right * maxspeed;
-               self.movement_z = dir * v_up * maxspeed;
-       }
-
-       // if there is nowhere to go, exit
-       if (self.goalcurrent == world)
-               return;
-
-       if (self.goalcurrent)
-               navigation_poptouchedgoals();
-
-       // if ran out of goals try to use an alternative goal or get a new strategy asap
-       if(self.goalcurrent == world)
-       {
-               self.bot_strategytime = 0;
-               return;
-       }
-
-
-       if(autocvar_bot_debug_goalstack)
-               debuggoalstack();
-
-       m1 = self.goalcurrent.origin + self.goalcurrent.mins;
-       m2 = self.goalcurrent.origin + self.goalcurrent.maxs;
-       destorg = self.origin;
-       destorg.x = bound(m1_x, destorg.x, m2_x);
-       destorg.y = bound(m1_y, destorg.y, m2_y);
-       destorg.z = bound(m1_z, destorg.z, m2_z);
-       diff = destorg - self.origin;
-       //dist = vlen(diff);
-       dir = normalize(diff);
-       flatdir = diff;flatdir.z = 0;
-       flatdir = normalize(flatdir);
-       gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
-
-       //if (self.bot_dodgevector_time < time)
-       {
-       //      self.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval");
-       //      self.bot_dodgevector_jumpbutton = 1;
-               evadeobstacle = '0 0 0';
-               evadelava = '0 0 0';
-
-               if (self.waterlevel)
-               {
-                       if(self.waterlevel>WATERLEVEL_SWIMMING)
-                       {
-                       //      flatdir_z = 1;
-                               self.aistatus |= AI_STATUS_OUT_WATER;
-                       }
-                       else
-                       {
-                               if(self.velocity.z >= 0 && !(self.watertype == CONTENT_WATER && gco.z < self.origin.z) &&
-                                       ( !(self.waterlevel == WATERLEVEL_WETFEET && self.watertype == CONTENT_WATER) || self.aistatus & AI_STATUS_OUT_WATER))
-                                       self.BUTTON_JUMP = true;
-                               else
-                                       self.BUTTON_JUMP = false;
-                       }
-                       dir = normalize(flatdir);
-                       makevectors(self.v_angle.y * '0 1 0');
-               }
-               else
-               {
-                       if(self.aistatus & AI_STATUS_OUT_WATER)
-                               self.aistatus &= ~AI_STATUS_OUT_WATER;
-
-                       // jump if going toward an obstacle that doesn't look like stairs we
-                       // can walk up directly
-                       tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.2, false, self);
-                       if (trace_fraction < 1)
-                       if (trace_plane_normal.z < 0.7)
-                       {
-                               s = trace_fraction;
-                               tracebox(self.origin + stepheightvec, self.mins, self.maxs, self.origin + self.velocity * 0.2 + stepheightvec, false, self);
-                               if (trace_fraction < s + 0.01)
-                               if (trace_plane_normal.z < 0.7)
-                               {
-                                       s = trace_fraction;
-                                       tracebox(self.origin + jumpstepheightvec, self.mins, self.maxs, self.origin + self.velocity * 0.2 + jumpstepheightvec, false, self);
-                                       if (trace_fraction > s)
-                                               self.BUTTON_JUMP = 1;
-                               }
-                       }
-
-                       // avoiding dangers and obstacles
-                       vector dst_ahead, dst_down;
-                       makevectors(self.v_angle.y * '0 1 0');
-                       dst_ahead = self.origin + self.view_ofs + (self.velocity * 0.4) + (v_forward * 32 * 3);
-                       dst_down = dst_ahead - '0 0 1500';
-
-                       // Look ahead
-                       traceline(self.origin + self.view_ofs, dst_ahead, true, world);
-
-                       // Check head-banging against walls
-                       if(vlen(self.origin + self.view_ofs - trace_endpos) < 25 && !(self.aistatus & AI_STATUS_OUT_WATER))
-                       {
-                               self.BUTTON_JUMP = true;
-                               if(self.facingwalltime && time > self.facingwalltime)
-                               {
-                                       self.ignoregoal = self.goalcurrent;
-                                       self.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
-                                       self.bot_strategytime = 0;
-                                       return;
-                               }
-                               else
-                               {
-                                       self.facingwalltime = time + 0.05;
-                               }
-                       }
-                       else
-                       {
-                               self.facingwalltime = 0;
-
-                               if(self.ignoregoal != world && time > self.ignoregoaltime)
-                               {
-                                       self.ignoregoal = world;
-                                       self.ignoregoaltime = 0;
-                               }
-                       }
-
-                       // Check for water/slime/lava and dangerous edges
-                       // (only when the bot is on the ground or jumping intentionally)
-                       self.aistatus &= ~AI_STATUS_DANGER_AHEAD;
-
-                       if(trace_fraction == 1 && self.jumppadcount == 0 && !self.goalcurrent.wphardwired )
-                       if((self.flags & FL_ONGROUND) || (self.aistatus & AI_STATUS_RUNNING) || self.BUTTON_JUMP == true)
-                       {
-                               // Look downwards
-                               traceline(dst_ahead , dst_down, true, world);
-                       //      te_lightning2(world, self.origin, dst_ahead);   // Draw "ahead" look
-                       //      te_lightning2(world, dst_ahead, dst_down);              // Draw "downwards" look
-                               if(trace_endpos.z < self.origin.z + self.mins.z)
-                               {
-                                       s = pointcontents(trace_endpos + '0 0 1');
-                                       if (s != CONTENT_SOLID)
-                                       if (s == CONTENT_LAVA || s == CONTENT_SLIME)
-                                               evadelava = normalize(self.velocity) * -1;
-                                       else if (s == CONTENT_SKY)
-                                               evadeobstacle = normalize(self.velocity) * -1;
-                                       else if (!boxesoverlap(dst_ahead - self.view_ofs + self.mins, dst_ahead - self.view_ofs + self.maxs,
-                                                               self.goalcurrent.absmin, self.goalcurrent.absmax))
-                                       {
-                                               // if ain't a safe goal with "holes" (like the jumpad on soylent)
-                                               // and there is a trigger_hurt below
-                                               if(tracebox_hits_trigger_hurt(dst_ahead, self.mins, self.maxs, trace_endpos))
-                                               {
-                                                       // Remove dangerous dynamic goals from stack
-                                                       LOG_TRACE("bot ", self.netname, " avoided the goal ", self.goalcurrent.classname, " ", etos(self.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
-                                                       navigation_clearroute();
-                                                       return;
-                                               }
-                                       }
-                               }
-                       }
-
-                       dir = flatdir;
-                       evadeobstacle.z = 0;
-                       evadelava.z = 0;
-                       makevectors(self.v_angle.y * '0 1 0');
-
-                       if(evadeobstacle!='0 0 0'||evadelava!='0 0 0')
-                               self.aistatus |= AI_STATUS_DANGER_AHEAD;
-               }
-
-               dodge = havocbot_dodge();
-               dodge = dodge * bound(0,0.5+(skill+self.bot_dodgeskill)*0.1,1);
-               evadelava = evadelava * bound(1,3-(skill+self.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it
-               traceline(self.origin, ( ( self.enemy.absmin + self.enemy.absmax ) * 0.5 ), true, world);
-               if(IS_PLAYER(trace_ent))
-                       dir = dir * bound(0,(skill+self.bot_dodgeskill)/7,1);
-
-               dir = normalize(dir + dodge + evadeobstacle + evadelava);
-       //      self.bot_dodgevector = dir;
-       //      self.bot_dodgevector_jumpbutton = self.BUTTON_JUMP;
-       }
-
-       if(time < self.ladder_time)
-       {
-               if(self.goalcurrent.origin.z + self.goalcurrent.mins.z > self.origin.z + self.mins.z)
-               {
-                       if(self.origin.z + self.mins.z  < self.ladder_entity.origin.z + self.ladder_entity.maxs.z)
-                               dir.z = 1;
-               }
-               else
-               {
-                       if(self.origin.z + self.mins.z  > self.ladder_entity.origin.z + self.ladder_entity.mins.z)
-                               dir.z = -1;
-               }
-       }
-
-       //dir = self.bot_dodgevector;
-       //if (self.bot_dodgevector_jumpbutton)
-       //      self.BUTTON_JUMP = 1;
-       self.movement_x = dir * v_forward * maxspeed;
-       self.movement_y = dir * v_right * maxspeed;
-       self.movement_z = dir * v_up * maxspeed;
-
-       // Emulate keyboard interface
-       if (skill < 10)
-               havocbot_keyboard_movement(destorg);
-
-       // Bunnyhop!
-//     if(self.aistatus & AI_STATUS_ROAMING)
-       if(self.goalcurrent)
-       if(skill+self.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
-               havocbot_bunnyhop(dir);
-
-       if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (self.flags & FL_ONGROUND)) self.BUTTON_JUMP=1;
-       if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.BUTTON_JUMP=true;
-       if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.havocbot_ducktime=time+0.3/bound(0.1,skill+self.bot_dodgeskill,10);
-}
-
-void havocbot_chooseenemy()
-{SELFPARAM();
-       entity head, best, head2;
-       float rating, bestrating, hf;
-       vector eye, v;
-       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
-       {
-               self.enemy = world;
-               return;
-       }
-       if (self.enemy)
-       {
-               if (!bot_shouldattack(self.enemy))
-               {
-                       // enemy died or something, find a new target
-                       self.enemy = world;
-                       self.havocbot_chooseenemy_finished = time;
-               }
-               else if (self.havocbot_stickenemy)
-               {
-                       // tracking last chosen enemy
-                       // if enemy is visible
-                       // and not really really far away
-                       // and we're not severely injured
-                       // then keep tracking for a half second into the future
-                       traceline(self.origin+self.view_ofs, ( self.enemy.absmin + self.enemy.absmax ) * 0.5,false,world);
-                       if (trace_ent == self.enemy || trace_fraction == 1)
-                       if (vlen((( self.enemy.absmin + self.enemy.absmax ) * 0.5) - self.origin) < 1000)
-                       if (self.health > 30)
-                       {
-                               // remain tracking him for a shot while (case he went after a small corner or pilar
-                               self.havocbot_chooseenemy_finished = time + 0.5;
-                               return;
-                       }
-                       // enemy isn't visible, or is far away, or we're injured severely
-                       // so stop preferring this enemy
-                       // (it will still take a half second until a new one is chosen)
-                       self.havocbot_stickenemy = 0;
-               }
-       }
-       if (time < self.havocbot_chooseenemy_finished)
-               return;
-       self.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval;
-       eye = self.origin + self.view_ofs;
-       best = world;
-       bestrating = 100000000;
-       head = head2 = findchainfloat(bot_attack, true);
-
-       // Backup hit flags
-       hf = self.dphitcontentsmask;
-
-       // Search for enemies, if no enemy can be seen directly try to look through transparent objects
-
-       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-
-       bool scan_transparent = false;
-       bool scan_secondary_targets = false;
-       bool have_secondary_targets = false;
-       while(true)
-       {
-               scan_secondary_targets = false;
-               :scan_targets
-               for( ; head; head = head.chain)
-               {
-                       if(!scan_secondary_targets)
-                       {
-                               if(head.classname == "misc_breakablemodel")
-                               {
-                                       have_secondary_targets = true;
-                                       continue;
-                               }
-                       }
-                       else
-                       {
-                               if(head.classname != "misc_breakablemodel")
-                                       continue;
-                       }
-
-                       v = (head.absmin + head.absmax) * 0.5;
-                       rating = vlen(v - eye);
-                       if (rating<autocvar_bot_ai_enemydetectionradius)
-                       if (bestrating > rating)
-                       if (bot_shouldattack(head))
-                       {
-                               traceline(eye, v, true, self);
-                               if (trace_ent == head || trace_fraction >= 1)
-                               {
-                                       best = head;
-                                       bestrating = rating;
-                               }
-                       }
-               }
-
-               if(!best && have_secondary_targets && !scan_secondary_targets)
-               {
-                       scan_secondary_targets = true;
-                       // restart the loop
-                       head = head2;
-                       bestrating = 100000000;
-                       goto scan_targets;
-               }
-
-               // I want to do a second scan if no enemy was found or I don't have weapons
-               // TODO: Perform the scan when using the rifle (requires changes on the rifle code)
-               if(best || self.weapons) // || self.weapon == WEP_RIFLE.m_id
-                       break;
-               if(scan_transparent)
-                       break;
-
-               // Set flags to see through transparent objects
-               self.dphitcontentsmask |= DPCONTENTS_OPAQUE;
-
-               head = head2;
-               scan_transparent = true;
-       }
-
-       // Restore hit flags
-       self.dphitcontentsmask = hf;
-
-       self.enemy = best;
-       self.havocbot_stickenemy = true;
-       if(best && best.classname == "misc_breakablemodel")
-               self.havocbot_stickenemy = false;
-}
-
-float havocbot_chooseweapon_checkreload(int new_weapon)
-{SELFPARAM();
-       // bots under this skill cannot find unloaded weapons to reload idly when not in combat,
-       // so skip this for them, or they'll never get to reload their weapons at all.
-       // this also allows bots under this skill to be more stupid, and reload more often during combat :)
-       if(skill < 5)
-               return false;
-
-       // if this weapon is scheduled for reloading, don't switch to it during combat
-       if (self.weapon_load[new_weapon] < 0)
-       {
-               float i, other_weapon_available = false;
-               for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-               {
-                       Weapon w = get_weaponinfo(i);
-                       // if we are out of ammo for all other weapons, it's an emergency to switch to anything else
-                       if (w.wr_checkammo1(w) + w.wr_checkammo2(w))
-                               other_weapon_available = true;
-               }
-               if(other_weapon_available)
-                       return true;
-       }
-
-       return false;
-}
-
-void havocbot_chooseweapon()
-{SELFPARAM();
-       int i;
-
-       // ;)
-       if(g_weaponarena_weapons == WEPSET(TUBA))
-       {
-               self.switchweapon = WEP_TUBA.m_id;
-               return;
-       }
-
-       // TODO: clean this up by moving it to weapon code
-       if(self.enemy==world)
-       {
-               // If no weapon was chosen get the first available weapon
-               if(self.weapon==0)
-               for(i = WEP_FIRST; i <= WEP_LAST; ++i) if(i != WEP_BLASTER.m_id)
-               {
-                       if(client_hasweapon(self, i, true, false))
-                       {
-                               self.switchweapon = i;
-                               return;
-                       }
-               }
-               return;
-       }
-
-       // Do not change weapon during the next second after a combo
-       float f = time - self.lastcombotime;
-       if(f < 1)
-               return;
-
-       float w;
-       float distance; distance=bound(10,vlen(self.origin-self.enemy.origin)-200,10000);
-
-       // Should it do a weapon combo?
-       float af, ct, combo_time, combo;
-
-       af = ATTACK_FINISHED(self);
-       ct = autocvar_bot_ai_weapon_combo_threshold;
-
-       // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos
-       // Ideally this 4 should be calculated as longest_weapon_refire / bot_ai_weapon_combo_threshold
-       combo_time = time + ct + (ct * ((-0.3*(skill+self.bot_weaponskill))+3));
-
-       combo = false;
-
-       if(autocvar_bot_ai_weapon_combo)
-       if(self.weapon == self.lastfiredweapon)
-       if(af > combo_time)
-       {
-               combo = true;
-               self.lastcombotime = time;
-       }
-
-       distance *= pow(2, self.bot_rangepreference);
-
-       // Custom weapon list based on distance to the enemy
-       if(bot_custom_weapon){
-
-               // Choose weapons for far distance
-               if ( distance > bot_distance_far ) {
-                       for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){
-                               w = bot_weapons_far[i];
-                               if ( client_hasweapon(self, w, true, false) )
-                               {
-                                       if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
-                                               continue;
-                                       self.switchweapon = w;
-                                       return;
-                               }
-                       }
-               }
-
-               // Choose weapons for mid distance
-               if ( distance > bot_distance_close) {
-                       for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){
-                               w = bot_weapons_mid[i];
-                               if ( client_hasweapon(self, w, true, false) )
-                               {
-                                       if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
-                                               continue;
-                                       self.switchweapon = w;
-                                       return;
-                               }
-                       }
-               }
-
-               // Choose weapons for close distance
-               for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){
-                       w = bot_weapons_close[i];
-                       if ( client_hasweapon(self, w, true, false) )
-                       {
-                               if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
-                                       continue;
-                               self.switchweapon = w;
-                               return;
-                       }
-               }
-       }
-}
-
-void havocbot_aim()
-{SELFPARAM();
-       vector selfvel, enemyvel;
-//     if(self.flags & FL_INWATER)
-//             return;
-       if (time < self.nextaim)
-               return;
-       self.nextaim = time + 0.1;
-       selfvel = self.velocity;
-       if (!self.waterlevel)
-               selfvel.z = 0;
-       if (self.enemy)
-       {
-               enemyvel = self.enemy.velocity;
-               if (!self.enemy.waterlevel)
-                       enemyvel.z = 0;
-               lag_additem(time + self.ping, 0, 0, self.enemy, self.origin, selfvel, (self.enemy.absmin + self.enemy.absmax) * 0.5, enemyvel);
-       }
-       else
-               lag_additem(time + self.ping, 0, 0, world, self.origin, selfvel, ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5, '0 0 0');
-}
-
-float havocbot_moveto_refresh_route()
-{SELFPARAM();
-       // Refresh path to goal if necessary
-       entity wp;
-       wp = self.havocbot_personal_waypoint;
-       navigation_goalrating_start();
-       navigation_routerating(wp, 10000, 10000);
-       navigation_goalrating_end();
-       return self.navigation_hasgoals;
-}
-
-float havocbot_moveto(vector pos)
-{SELFPARAM();
-       entity wp;
-
-       if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-       {
-               // Step 4: Move to waypoint
-               if(self.havocbot_personal_waypoint==world)
-               {
-                       LOG_TRACE("Error: ", self.netname, " trying to walk to a non existent personal waypoint\n");
-                       self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                       return CMD_STATUS_ERROR;
-               }
-
-               if (!bot_strategytoken_taken)
-               if(self.havocbot_personal_waypoint_searchtime<time)
-               {
-                       bot_strategytoken_taken = true;
-                       if(havocbot_moveto_refresh_route())
-                       {
-                               LOG_TRACE(self.netname, " walking to its personal waypoint (after ", ftos(self.havocbot_personal_waypoint_failcounter), " failed attempts)\n");
-                               self.havocbot_personal_waypoint_searchtime = time + 10;
-                               self.havocbot_personal_waypoint_failcounter = 0;
-                       }
-                       else
-                       {
-                               self.havocbot_personal_waypoint_failcounter += 1;
-                               self.havocbot_personal_waypoint_searchtime = time + 2;
-                               if(self.havocbot_personal_waypoint_failcounter >= 30)
-                               {
-                                       LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(self.havocbot_personal_waypoint.origin),"\n");
-                                       self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-                                       remove(self.havocbot_personal_waypoint);
-                                       return CMD_STATUS_ERROR;
-                               }
-                               else
-                                       LOG_TRACE(self.netname, " can't walk to its personal waypoint (after ", ftos(self.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n");
-                       }
-               }
-
-               if(autocvar_bot_debug_goalstack)
-                       debuggoalstack();
-
-               // Heading
-               vector dir = ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - (self.origin + self.view_ofs);
-               dir.z = 0;
-               bot_aimdir(dir, -1);
-
-               // Go!
-               havocbot_movetogoal();
-
-               if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED)
-               {
-                       // Step 5: Waypoint reached
-                       LOG_TRACE(self.netname, "'s personal waypoint reached\n");
-                       remove(self.havocbot_personal_waypoint);
-                       self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                       return CMD_STATUS_FINISHED;
-               }
-
-               return CMD_STATUS_EXECUTING;
-       }
-
-       // Step 2: Linking waypoint
-       if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_LINKING)
-       {
-               // Wait until it is linked
-               if(!self.havocbot_personal_waypoint.wplinked)
-               {
-                       LOG_TRACE(self.netname, " waiting for personal waypoint to be linked\n");
-                       return CMD_STATUS_EXECUTING;
-               }
-
-               self.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame
-               self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-               self.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
-
-               // Step 3: Route to waypoint
-               LOG_TRACE(self.netname, " walking to its personal waypoint\n");
-
-               return CMD_STATUS_EXECUTING;
-       }
-
-       // Step 1: Spawning waypoint
-       wp = waypoint_spawnpersonal(pos);
-       if(wp==world)
-       {
-               LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos),"\n");
-               return CMD_STATUS_ERROR;
-       }
-
-       self.havocbot_personal_waypoint = wp;
-       self.havocbot_personal_waypoint_failcounter = 0;
-       self.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-
-       // if pos is inside a teleport, then let's mark it as teleport waypoint
-       entity head;
-       for(head = world; (head = find(head, classname, "trigger_teleport")); )
-       {
-               if(WarpZoneLib_BoxTouchesBrush(pos, pos, head, world))
-               {
-                       wp.wpflags |= WAYPOINTFLAG_TELEPORT;
-                       self.lastteleporttime = 0;
-               }
-       }
-
-/*
-       if(wp.wpflags & WAYPOINTFLAG_TELEPORT)
-               print("routing to a teleporter\n");
-       else
-               print("routing to a non-teleporter\n");
-*/
-
-       return CMD_STATUS_EXECUTING;
-}
-
-float havocbot_resetgoal()
-{
-       navigation_clearroute();
-       return CMD_STATUS_FINISHED;
-}
-
-void havocbot_setupbot()
-{SELFPARAM();
-       self.bot_ai = havocbot_ai;
-       self.cmd_moveto = havocbot_moveto;
-       self.cmd_resetgoal = havocbot_resetgoal;
-
-       havocbot_chooserole();
-}
-
-vector havocbot_dodge()
-{SELFPARAM();
-       // LordHavoc: disabled because this is too expensive
-       return '0 0 0';
-#if 0
-       entity head;
-       vector dodge, v, n;
-       float danger, bestdanger, vl, d;
-       dodge = '0 0 0';
-       bestdanger = -20;
-       // check for dangerous objects near bot or approaching bot
-       head = findchainfloat(bot_dodge, true);
-       while(head)
-       {
-               if (head.owner != self)
-               {
-                       vl = vlen(head.velocity);
-                       if (vl > autocvar_sv_maxspeed * 0.3)
-                       {
-                               n = normalize(head.velocity);
-                               v = self.origin - head.origin;
-                               d = v * n;
-                               if (d > (0 - head.bot_dodgerating))
-                               if (d < (vl * 0.2 + head.bot_dodgerating))
-                               {
-                                       // calculate direction and distance from the flight path, by removing the forward axis
-                                       v = v - (n * (v * n));
-                                       danger = head.bot_dodgerating - vlen(v);
-                                       if (bestdanger < danger)
-                                       {
-                                               bestdanger = danger;
-                                               // dodge to the side of the object
-                                               dodge = normalize(v);
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               danger = head.bot_dodgerating - vlen(head.origin - self.origin);
-                               if (bestdanger < danger)
-                               {
-                                       bestdanger = danger;
-                                       dodge = normalize(self.origin - head.origin);
-                               }
-                       }
-               }
-               head = head.chain;
-       }
-       return dodge;
-#endif
-}
diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qh b/qcsrc/server/bot/default/havocbot/havocbot.qh
deleted file mode 100644 (file)
index 2d3d329..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef HAVOCBOT_H
-#define HAVOCBOT_H
-
-/*
- * Globals and Fields
- */
-
-.float havocbot_keyboardskill;
-.float facingwalltime, ignoregoaltime;
-.float lastfiredweapon;
-.float lastcombotime;
-.float havocbot_blockhead;
-
-.float havocbot_keyboardtime;
-.float havocbot_ducktime;
-.float bot_timelastseengoal;
-.float bot_canruntogoal;
-.float bot_chooseweapontime;
-.float rocketjumptime;
-.float nextaim;
-.float havocbot_personal_waypoint_searchtime;
-.float havocbot_personal_waypoint_failcounter;
-.float havocbot_chooseenemy_finished;
-.float havocbot_stickenemy;
-.float havocbot_role_timeout;
-
-.entity ignoregoal;
-.entity bot_lastseengoal;
-.entity havocbot_personal_waypoint;
-
-.vector havocbot_keyboard;
-
-/*
- * Functions
- */
-
-void havocbot_ai();
-void havocbot_aim();
-void havocbot_setupbot();
-void havocbot_movetogoal();
-void havocbot_chooserole();
-void havocbot_chooseenemy();
-void havocbot_chooseweapon();
-void havocbot_bunnyhop(vector dir);
-void havocbot_keyboard_movement(vector destorg);
-
-float havocbot_resetgoal();
-float havocbot_moveto(vector pos);
-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
- */
-
-.entity draggedby;
-.float ladder_time;
-.entity ladder_entity;
-#endif
diff --git a/qcsrc/server/bot/default/havocbot/role_keyhunt.qc b/qcsrc/server/bot/default/havocbot/role_keyhunt.qc
deleted file mode 100644 (file)
index a260685..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-#include "role_keyhunt.qh"
-
-void() havocbot_role_kh_carrier;
-void() havocbot_role_kh_defense;
-void() havocbot_role_kh_offense;
-void() havocbot_role_kh_freelancer;
-
-
-void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
-{SELFPARAM();
-       entity head;
-       for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
-       {
-               if(head.owner == self)
-                       continue;
-               if(!kh_tracking_enabled)
-               {
-                       // if it's carried by our team we know about it
-                       // otherwise we have to see it to know about it
-                       if(!head.owner || head.team != self.team)
-                       {
-                               traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
-                               if (trace_fraction < 1 && trace_ent != head)
-                                       continue; // skip what I can't see
-                       }
-               }
-               if(!head.owner)
-                       navigation_routerating(head, ratingscale_dropped * BOT_PICKUP_RATING_HIGH, 100000);
-               else if(head.team == self.team)
-                       navigation_routerating(head.owner, ratingscale_team * BOT_PICKUP_RATING_HIGH, 100000);
-               else
-                       navigation_routerating(head.owner, ratingscale_enemy * BOT_PICKUP_RATING_HIGH, 100000);
-       }
-
-       havocbot_goalrating_items(1, self.origin, 10000);
-}
-
-void havocbot_role_kh_carrier()
-{SELFPARAM();
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (!(self.kh_next))
-       {
-               LOG_TRACE("changing role to freelancer\n");
-               self.havocbot_role = havocbot_role_kh_freelancer;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-
-               if(kh_Key_AllOwnedByWhichTeam() == self.team)
-                       havocbot_goalrating_kh(10, 0.1, 0.1); // bring home
-               else
-                       havocbot_goalrating_kh(4, 4, 1); // play defensively
-
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_role_kh_defense()
-{SELFPARAM();
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (self.kh_next)
-       {
-               LOG_TRACE("changing role to carrier\n");
-               self.havocbot_role = havocbot_role_kh_carrier;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + random() * 10 + 20;
-       if (time > self.havocbot_role_timeout)
-       {
-               LOG_TRACE("changing role to freelancer\n");
-               self.havocbot_role = havocbot_role_kh_freelancer;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               float key_owner_team;
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-
-               key_owner_team = kh_Key_AllOwnedByWhichTeam();
-               if(key_owner_team == self.team)
-                       havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers
-               else if(key_owner_team == -1)
-                       havocbot_goalrating_kh(4, 1, 0.1); // play defensively
-               else
-                       havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
-
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_role_kh_offense()
-{SELFPARAM();
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (self.kh_next)
-       {
-               LOG_TRACE("changing role to carrier\n");
-               self.havocbot_role = havocbot_role_kh_carrier;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + random() * 10 + 20;
-       if (time > self.havocbot_role_timeout)
-       {
-               LOG_TRACE("changing role to freelancer\n");
-               self.havocbot_role = havocbot_role_kh_freelancer;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               float key_owner_team;
-
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-
-               key_owner_team = kh_Key_AllOwnedByWhichTeam();
-               if(key_owner_team == self.team)
-                       havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
-               else if(key_owner_team == -1)
-                       havocbot_goalrating_kh(0.1, 1, 4); // play offensively
-               else
-                       havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK! EMERGENCY!
-
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_role_kh_freelancer()
-{SELFPARAM();
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (self.kh_next)
-       {
-               LOG_TRACE("changing role to carrier\n");
-               self.havocbot_role = havocbot_role_kh_carrier;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + random() * 10 + 10;
-       if (time > self.havocbot_role_timeout)
-       {
-               if (random() < 0.5)
-               {
-                       LOG_TRACE("changing role to offense\n");
-                       self.havocbot_role = havocbot_role_kh_offense;
-               }
-               else
-               {
-                       LOG_TRACE("changing role to defense\n");
-                       self.havocbot_role = havocbot_role_kh_defense;
-               }
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               float key_owner_team;
-
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-
-               key_owner_team = kh_Key_AllOwnedByWhichTeam();
-               if(key_owner_team == self.team)
-                       havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
-               else if(key_owner_team == -1)
-                       havocbot_goalrating_kh(1, 10, 4); // prefer dropped keys
-               else
-                       havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
-
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_chooserole_kh()
-{SELFPARAM();
-       float r;
-
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       r = random() * 3;
-       if (r < 1)
-               self.havocbot_role = havocbot_role_kh_offense;
-       else if (r < 2)
-               self.havocbot_role = havocbot_role_kh_defense;
-       else
-               self.havocbot_role = havocbot_role_kh_freelancer;
-}
diff --git a/qcsrc/server/bot/default/havocbot/role_keyhunt.qh b/qcsrc/server/bot/default/havocbot/role_keyhunt.qh
deleted file mode 100644 (file)
index ac3c77e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef ROLE_KEYHUNT_H
-#define ROLE_KEYHUNT_H
-
-void havocbot_chooserole_kh();
-#endif
diff --git a/qcsrc/server/bot/default/havocbot/roles.qc b/qcsrc/server/bot/default/havocbot/roles.qc
deleted file mode 100644 (file)
index c520ab3..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-#include "roles.qh"
-
-.float max_armorvalue;
-.float havocbot_role_timeout;
-
-.void() havocbot_previous_role;
-.void() havocbot_role;
-
-void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
-{SELFPARAM();
-       entity head;
-       entity player;
-       float rating, d, discard, distance, friend_distance, enemy_distance;
-       vector o;
-       ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
-       head = findchainfloat(bot_pickup, true);
-
-       while (head)
-       {
-               o = (head.absmin + head.absmax) * 0.5;
-               distance = vlen(o - org);
-               friend_distance = 10000; enemy_distance = 10000;
-               rating = 0;
-
-               if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
-               {
-                       head = head.chain;
-                       continue;
-               }
-
-               // Check if the item can be picked up safely
-               if(head.classname == "droppedweapon")
-               {
-                       traceline(o, o + '0 0 -1500', true, world);
-
-                       d = pointcontents(trace_endpos + '0 0 1');
-                       if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
-                       {
-                               head = head.chain;
-                               continue;
-                       }
-                       if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
-                       {
-                               head = head.chain;
-                               continue;
-                       }
-               }
-               else
-               {
-                       // Ignore items under water
-                       traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
-                       if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
-                       {
-                               head = head.chain;
-                               continue;
-                       }
-               }
-
-               if(teamplay)
-               {
-                       discard = false;
-
-                       FOR_EACH_PLAYER(player)
-                       {
-
-                               if ( self == player || player.deadflag )
-                                       continue;
-
-                               d = vlen(player.origin - o); // distance between player and item
-
-                               if ( player.team == self.team )
-                               {
-                                       if ( !IS_REAL_CLIENT(player) || discard )
-                                               continue;
-
-                                       if( d > friend_distance)
-                                               continue;
-
-                                       friend_distance = d;
-
-                                       discard = true;
-
-                                       if( head.health && player.health > self.health )
-                                               continue;
-
-                                       if( head.armorvalue && player.armorvalue > self.armorvalue)
-                                               continue;
-
-                                       if( head.weapons )
-                                       if( head.weapons & ~player.weapons )
-                                               continue;
-
-                                       if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
-                                               continue;
-
-                                       if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
-                                               continue;
-
-                                       if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
-                                               continue;
-
-                                       if (head.ammo_cells && player.ammo_cells > self.ammo_cells)
-                                               continue;
-
-                                       if (head.ammo_plasma && player.ammo_plasma > self.ammo_plasma)
-                                               continue;
-
-                                       discard = false;
-                               }
-                               else
-                               {
-                                       // If enemy only track distances
-                                       // TODO: track only if visible ?
-                                       if( d < enemy_distance )
-                                               enemy_distance = d;
-                               }
-                       }
-
-                       // Rate the item only if no one needs it, or if an enemy is closer to it
-                       if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
-                               (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
-                               rating = head.bot_pickupevalfunc(self, head);
-
-               }
-               else
-                       rating = head.bot_pickupevalfunc(self, head);
-
-               if(rating > 0)
-                       navigation_routerating(head, rating * ratingscale, 2000);
-               head = head.chain;
-       }
-}
-
-void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
-{SELFPARAM();
-       entity head;
-       head = findchain(classname, "dom_controlpoint");
-       while (head)
-       {
-               if (vlen(( ( head.absmin + head.absmax ) * 0.5 ) - org) < sradius)
-               {
-                       if(head.cnt > -1) // this is just being fought for
-                               navigation_routerating(head, ratingscale, 5000);
-                       else if(head.goalentity.cnt == 0) // unclaimed point
-                               navigation_routerating(head, ratingscale * 0.5, 5000);
-                       else if(head.goalentity.team != self.team) // other team's point
-                               navigation_routerating(head, ratingscale * 0.2, 5000);
-               }
-               head = head.chain;
-       }
-}
-
-void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
-{SELFPARAM();
-       entity head;
-       int t;
-       float distance;
-       noref bool noteam = ((self.team == 0) || !teamplay);
-
-       if (autocvar_bot_nofire)
-               return;
-
-       // don't chase players if we're under water
-       if(self.waterlevel>WATERLEVEL_WETFEET)
-               return;
-
-       FOR_EACH_PLAYER(head)
-       {
-               // TODO: Merge this logic with the bot_shouldattack function
-               if(bot_shouldattack(head))
-               {
-                       distance = vlen(head.origin - org);
-                       if (distance < 100 || distance > sradius)
-                               continue;
-
-                       // rate only visible enemies
-                       /*
-                       traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
-                       if (trace_fraction < 1 || trace_ent != head)
-                               continue;
-                       */
-
-                       if((head.flags & FL_INWATER) || (head.flags & FL_PARTIALGROUND))
-                               continue;
-
-                       // not falling
-                       if((head.flags & FL_ONGROUND) == 0)
-                       {
-                               traceline(head.origin, head.origin + '0 0 -1500', true, world);
-                               t = pointcontents(trace_endpos + '0 0 1');
-                               if( t != CONTENT_SOLID )
-                               if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
-                                       continue;
-                               if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
-                                       continue;
-                       }
-
-                       // TODO: rate waypoints near the targetted player at that moment, instead of the player itself
-                       //               adding a player as a goal seems to be quite dangerous, especially on space maps
-                       //               remove hack in navigation_poptouchedgoals() after performing this change
-
-                       t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
-                       navigation_routerating(head, t * ratingscale, 2000);
-               }
-       }
-}
-
-// legacy bot role for standard gamemodes
-// go to best items
-void havocbot_role_generic()
-{SELFPARAM();
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (self.bot_strategytime < time)
-       {
-               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();
-       }
-}
-
-void havocbot_chooserole_generic()
-{SELFPARAM();
-       self.havocbot_role = havocbot_role_generic;
-}
-
-void havocbot_chooserole()
-{SELFPARAM();
-       LOG_TRACE("choosing a role...\n");
-       self.bot_strategytime = 0;
-       if (MUTATOR_CALLHOOK(HavocBot_ChooseRole, self))
-               return;
-       else if (g_keyhunt)
-               havocbot_chooserole_kh();
-       else
-               havocbot_chooserole_generic();
-}
diff --git a/qcsrc/server/bot/default/havocbot/roles.qh b/qcsrc/server/bot/default/havocbot/roles.qh
deleted file mode 100644 (file)
index cfabf05..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef ROLES_H
-#define ROLES_H
-void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius);
-#endif
diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc
deleted file mode 100644 (file)
index 8898b71..0000000
+++ /dev/null
@@ -1,1259 +0,0 @@
-#include "navigation.qh"
-
-#include "../../../common/triggers/trigger/jumppads.qh"
-
-void bot_debug(string input)
-{
-       switch(autocvar_bot_debug)
-       {
-               case 1: LOG_TRACE(input); break;
-               case 2: LOG_INFO(input); break;
-       }
-}
-
-// rough simulation of walking from one point to another to test if a path
-// can be traveled, used for waypoint linking and havocbot
-
-float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode)
-{SELFPARAM();
-       vector org;
-       vector move;
-       vector dir;
-       float dist;
-       float totaldist;
-       float stepdist;
-       float yaw;
-       float ignorehazards;
-       float swimming;
-
-       if(autocvar_bot_debug_tracewalk)
-       {
-               debugresetnodes();
-               debugnode(start);
-       }
-
-       move = end - start;
-       move.z = 0;
-       org = start;
-       dist = totaldist = vlen(move);
-       dir = normalize(move);
-       stepdist = 32;
-       ignorehazards = false;
-       swimming = false;
-
-       // Analyze starting point
-       traceline(start, start, MOVE_NORMAL, e);
-       if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
-               ignorehazards = true;
-       else
-       {
-               traceline( start, start + '0 0 -65536', MOVE_NORMAL, e);
-               if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
-               {
-                       ignorehazards = true;
-                       swimming = true;
-               }
-       }
-       tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e);
-       if (trace_startsolid)
-       {
-               // Bad start
-               if(autocvar_bot_debug_tracewalk)
-                       debugnodestatus(start, DEBUG_NODE_FAIL);
-
-               //print("tracewalk: ", vtos(start), " is a bad start\n");
-               return false;
-       }
-
-       // Movement loop
-       yaw = vectoyaw(move);
-       move = end - org;
-       for (;;)
-       {
-               if (boxesoverlap(end, end, org + m1 + '-1 -1 -1', org + m2 + '1 1 1'))
-               {
-                       // Succeeded
-                       if(autocvar_bot_debug_tracewalk)
-                               debugnodestatus(org, DEBUG_NODE_SUCCESS);
-
-                       //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
-                       return true;
-               }
-               if(autocvar_bot_debug_tracewalk)
-                       debugnode(org);
-
-               if (dist <= 0)
-                       break;
-               if (stepdist > dist)
-                       stepdist = dist;
-               dist = dist - stepdist;
-               traceline(org, org, MOVE_NORMAL, e);
-               if (!ignorehazards)
-               {
-                       if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
-                       {
-                               // hazards blocking path
-                               if(autocvar_bot_debug_tracewalk)
-                                       debugnodestatus(org, DEBUG_NODE_FAIL);
-
-                               //print("tracewalk: ", vtos(start), " hits a hazard when trying to reach ", vtos(end), "\n");
-                               return false;
-                       }
-               }
-               if (trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
-               {
-                       move = normalize(end - org);
-                       tracebox(org, m1, m2, org + move * stepdist, movemode, e);
-
-                       if(autocvar_bot_debug_tracewalk)
-                               debugnode(trace_endpos);
-
-                       if (trace_fraction < 1)
-                       {
-                               swimming = true;
-                               org = trace_endpos - normalize(org - trace_endpos) * stepdist;
-                               for (; org.z < end.z + self.maxs.z; org.z += stepdist)
-                               {
-                                               if(autocvar_bot_debug_tracewalk)
-                                                       debugnode(org);
-
-                                       if(pointcontents(org) == CONTENT_EMPTY)
-                                                       break;
-                               }
-
-                               if(pointcontents(org + '0 0 1') != CONTENT_EMPTY)
-                               {
-                                       if(autocvar_bot_debug_tracewalk)
-                                               debugnodestatus(org, DEBUG_NODE_FAIL);
-
-                                       return false;
-                                       //print("tracewalk: ", vtos(start), " failed under water\n");
-                               }
-                               continue;
-
-                       }
-                       else
-                               org = trace_endpos;
-               }
-               else
-               {
-                       move = dir * stepdist + org;
-                       tracebox(org, m1, m2, move, movemode, e);
-
-                       if(autocvar_bot_debug_tracewalk)
-                               debugnode(trace_endpos);
-
-                       // hit something
-                       if (trace_fraction < 1)
-                       {
-                               // check if we can walk over this obstacle, possibly by jumpstepping
-                               tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
-                               if (trace_fraction < 1 || trace_startsolid)
-                               {
-                                       tracebox(org + jumpstepheightvec, m1, m2, move + jumpstepheightvec, movemode, e);
-                                       if (trace_fraction < 1 || trace_startsolid)
-                                       {
-                                               if(autocvar_bot_debug_tracewalk)
-                                                       debugnodestatus(trace_endpos, DEBUG_NODE_WARNING);
-
-                                               // check for doors
-                                               traceline( org, move, movemode, e);
-                                               if ( trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
-                                               {
-                                                       vector nextmove;
-                                                       move = trace_endpos;
-                                                       while(trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
-                                                       {
-                                                               nextmove = move + (dir * stepdist);
-                                                               traceline( move, nextmove, movemode, e);
-                                                               move = nextmove;
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       if(autocvar_bot_debug_tracewalk)
-                                                               debugnodestatus(trace_endpos, DEBUG_NODE_FAIL);
-
-                                                       //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
-                                                       //te_explosion(trace_endpos);
-                                                       //print(ftos(e.dphitcontentsmask), "\n");
-                                                       return false; // failed
-                                               }
-                                       }
-                                       else
-                                               move = trace_endpos;
-                               }
-                               else
-                                       move = trace_endpos;
-                       }
-                       else
-                               move = trace_endpos;
-
-                       // trace down from stepheight as far as possible and move there,
-                       // if this starts in solid we try again without the stepup, and
-                       // if that also fails we assume it is a wall
-                       // (this is the same logic as the Quake walkmove function used)
-                       tracebox(move, m1, m2, move + '0 0 -65536', movemode, e);
-
-                       // moved successfully
-                       if(swimming)
-                       {
-                               float c;
-                               c = pointcontents(org + '0 0 1');
-                               if (!(c == CONTENT_WATER || c == CONTENT_LAVA || c == CONTENT_SLIME))
-                                       swimming = false;
-                               else
-                                       continue;
-                       }
-
-                       org = trace_endpos;
-               }
-       }
-
-       //print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n");
-
-       // moved but didn't arrive at the intended destination
-       if(autocvar_bot_debug_tracewalk)
-               debugnodestatus(org, DEBUG_NODE_FAIL);
-
-       return false;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// goal stack
-/////////////////////////////////////////////////////////////////////////////
-
-// completely empty the goal stack, used when deciding where to go
-void navigation_clearroute()
-{SELFPARAM();
-       //print("bot ", etos(self), " clear\n");
-       self.navigation_hasgoals = false;
-       self.goalcurrent = world;
-       self.goalstack01 = world;
-       self.goalstack02 = world;
-       self.goalstack03 = world;
-       self.goalstack04 = world;
-       self.goalstack05 = world;
-       self.goalstack06 = world;
-       self.goalstack07 = world;
-       self.goalstack08 = world;
-       self.goalstack09 = world;
-       self.goalstack10 = world;
-       self.goalstack11 = world;
-       self.goalstack12 = world;
-       self.goalstack13 = world;
-       self.goalstack14 = world;
-       self.goalstack15 = world;
-       self.goalstack16 = world;
-       self.goalstack17 = world;
-       self.goalstack18 = world;
-       self.goalstack19 = world;
-       self.goalstack20 = world;
-       self.goalstack21 = world;
-       self.goalstack22 = world;
-       self.goalstack23 = world;
-       self.goalstack24 = world;
-       self.goalstack25 = world;
-       self.goalstack26 = world;
-       self.goalstack27 = world;
-       self.goalstack28 = world;
-       self.goalstack29 = world;
-       self.goalstack30 = world;
-       self.goalstack31 = world;
-}
-
-// add a new goal at the beginning of the stack
-// (in other words: add a new prerequisite before going to the later goals)
-// NOTE: when a waypoint is added, the WP gets pushed first, then the
-// next-closest WP on the shortest path to the WP
-// That means, if the stack overflows, the bot will know how to do the FIRST 32
-// steps to the goal, and then recalculate the path.
-void navigation_pushroute(entity e)
-{SELFPARAM();
-       //print("bot ", etos(self), " push ", etos(e), "\n");
-       self.goalstack31 = self.goalstack30;
-       self.goalstack30 = self.goalstack29;
-       self.goalstack29 = self.goalstack28;
-       self.goalstack28 = self.goalstack27;
-       self.goalstack27 = self.goalstack26;
-       self.goalstack26 = self.goalstack25;
-       self.goalstack25 = self.goalstack24;
-       self.goalstack24 = self.goalstack23;
-       self.goalstack23 = self.goalstack22;
-       self.goalstack22 = self.goalstack21;
-       self.goalstack21 = self.goalstack20;
-       self.goalstack20 = self.goalstack19;
-       self.goalstack19 = self.goalstack18;
-       self.goalstack18 = self.goalstack17;
-       self.goalstack17 = self.goalstack16;
-       self.goalstack16 = self.goalstack15;
-       self.goalstack15 = self.goalstack14;
-       self.goalstack14 = self.goalstack13;
-       self.goalstack13 = self.goalstack12;
-       self.goalstack12 = self.goalstack11;
-       self.goalstack11 = self.goalstack10;
-       self.goalstack10 = self.goalstack09;
-       self.goalstack09 = self.goalstack08;
-       self.goalstack08 = self.goalstack07;
-       self.goalstack07 = self.goalstack06;
-       self.goalstack06 = self.goalstack05;
-       self.goalstack05 = self.goalstack04;
-       self.goalstack04 = self.goalstack03;
-       self.goalstack03 = self.goalstack02;
-       self.goalstack02 = self.goalstack01;
-       self.goalstack01 = self.goalcurrent;
-       self.goalcurrent = e;
-}
-
-// remove first goal from stack
-// (in other words: remove a prerequisite for reaching the later goals)
-// (used when a spawnfunc_waypoint is reached)
-void navigation_poproute()
-{SELFPARAM();
-       //print("bot ", etos(self), " pop\n");
-       self.goalcurrent = self.goalstack01;
-       self.goalstack01 = self.goalstack02;
-       self.goalstack02 = self.goalstack03;
-       self.goalstack03 = self.goalstack04;
-       self.goalstack04 = self.goalstack05;
-       self.goalstack05 = self.goalstack06;
-       self.goalstack06 = self.goalstack07;
-       self.goalstack07 = self.goalstack08;
-       self.goalstack08 = self.goalstack09;
-       self.goalstack09 = self.goalstack10;
-       self.goalstack10 = self.goalstack11;
-       self.goalstack11 = self.goalstack12;
-       self.goalstack12 = self.goalstack13;
-       self.goalstack13 = self.goalstack14;
-       self.goalstack14 = self.goalstack15;
-       self.goalstack15 = self.goalstack16;
-       self.goalstack16 = self.goalstack17;
-       self.goalstack17 = self.goalstack18;
-       self.goalstack18 = self.goalstack19;
-       self.goalstack19 = self.goalstack20;
-       self.goalstack20 = self.goalstack21;
-       self.goalstack21 = self.goalstack22;
-       self.goalstack22 = self.goalstack23;
-       self.goalstack23 = self.goalstack24;
-       self.goalstack24 = self.goalstack25;
-       self.goalstack25 = self.goalstack26;
-       self.goalstack26 = self.goalstack27;
-       self.goalstack27 = self.goalstack28;
-       self.goalstack28 = self.goalstack29;
-       self.goalstack29 = self.goalstack30;
-       self.goalstack30 = self.goalstack31;
-       self.goalstack31 = world;
-}
-
-float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist)
-{
-       float dist;
-       dist = vlen(v - org);
-       if (bestdist > dist)
-       {
-               traceline(v, org, true, ent);
-               if (trace_fraction == 1)
-               {
-                       if (walkfromwp)
-                       {
-                               if (tracewalk(ent, v, PL_MIN, PL_MAX, org, bot_navigation_movemode))
-                                       return true;
-                       }
-                       else
-                       {
-                               if (tracewalk(ent, org, PL_MIN, PL_MAX, v, bot_navigation_movemode))
-                                       return true;
-                       }
-               }
-       }
-       return false;
-}
-
-// find the spawnfunc_waypoint near a dynamic goal such as a dropped weapon
-entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfromwp, float bestdist, entity except)
-{
-       entity waylist, w, best;
-       vector v, org, pm1, pm2;
-
-       pm1 = ent.origin + ent.mins;
-       pm2 = ent.origin + ent.maxs;
-       waylist = findchain(classname, "waypoint");
-
-       // do two scans, because box test is cheaper
-       w = waylist;
-       while (w)
-       {
-               // if object is touching spawnfunc_waypoint
-               if(w != ent && w != except)
-                       if (boxesoverlap(pm1, pm2, w.absmin, w.absmax))
-                               return w;
-               w = w.chain;
-       }
-
-       org = ent.origin + 0.5 * (ent.mins + ent.maxs);
-       org.z = ent.origin.z + ent.mins.z - PL_MIN.z; // player height
-       // TODO possibly make other code have the same support for bboxes
-       if(ent.tag_entity)
-               org = org + ent.tag_entity.origin;
-       if (navigation_testtracewalk)
-               te_plasmaburn(org);
-
-       best = world;
-
-       // box check failed, try walk
-       w = waylist;
-       while (w)
-       {
-               // if object can walk from spawnfunc_waypoint
-               if(w != ent)
-               {
-                       if (w.wpisbox)
-                       {
-                               vector wm1, wm2;
-                               wm1 = w.origin + w.mins;
-                               wm2 = w.origin + w.maxs;
-                               v.x = bound(wm1_x, org.x, wm2_x);
-                               v.y = bound(wm1_y, org.y, wm2_y);
-                               v.z = bound(wm1_z, org.z, wm2_z);
-                       }
-                       else
-                               v = w.origin;
-                       if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist))
-                       {
-                               bestdist = vlen(v - org);
-                               best = w;
-                       }
-               }
-               w = w.chain;
-       }
-       return best;
-}
-entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
-{
-       entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, world);
-       if (autocvar_g_waypointeditor_auto)
-       {
-               entity wp2 = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, wp);
-               if (wp && !wp2)
-                       wp.wpflags |= WAYPOINTFLAG_PROTECTED;
-       }
-       return wp;
-}
-
-// finds the waypoints near the bot initiating a navigation query
-float navigation_markroutes_nearestwaypoints(entity waylist, float maxdist)
-{SELFPARAM();
-       entity head;
-       vector v, m1, m2, diff;
-       float c;
-//     navigation_testtracewalk = true;
-       c = 0;
-       head = waylist;
-       while (head)
-       {
-               if (!head.wpconsidered)
-               {
-                       if (head.wpisbox)
-                       {
-                               m1 = head.origin + head.mins;
-                               m2 = head.origin + head.maxs;
-                               v = self.origin;
-                               v.x = bound(m1_x, v.x, m2_x);
-                               v.y = bound(m1_y, v.y, m2_y);
-                               v.z = bound(m1_z, v.z, m2_z);
-                       }
-                       else
-                               v = head.origin;
-                       diff = v - self.origin;
-                       diff.z = max(0, diff.z);
-                       if (vlen(diff) < maxdist)
-                       {
-                               head.wpconsidered = true;
-                               if (tracewalk(self, self.origin, self.mins, self.maxs, v, bot_navigation_movemode))
-                               {
-                                       head.wpnearestpoint = v;
-                                       head.wpcost = vlen(v - self.origin) + head.dmg;
-                                       head.wpfire = 1;
-                                       head.enemy = world;
-                                       c = c + 1;
-                               }
-                       }
-               }
-               head = head.chain;
-       }
-       //navigation_testtracewalk = false;
-       return c;
-}
-
-// updates a path link if a spawnfunc_waypoint link is better than the current one
-void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p)
-{
-       vector m1;
-       vector m2;
-       vector v;
-       if (wp.wpisbox)
-       {
-               m1 = wp.absmin;
-               m2 = wp.absmax;
-               v.x = bound(m1_x, p.x, m2_x);
-               v.y = bound(m1_y, p.y, m2_y);
-               v.z = bound(m1_z, p.z, m2_z);
-       }
-       else
-               v = wp.origin;
-       cost2 = cost2 + vlen(v - p);
-       if (wp.wpcost > cost2)
-       {
-               wp.wpcost = cost2;
-               wp.enemy = w;
-               wp.wpfire = 1;
-               wp.wpnearestpoint = v;
-       }
-}
-
-// queries the entire spawnfunc_waypoint network for pathes leading away from the bot
-void navigation_markroutes(entity fixed_source_waypoint)
-{SELFPARAM();
-       entity w, wp, waylist;
-       float searching, cost, cost2;
-       vector p;
-       w = waylist = findchain(classname, "waypoint");
-       while (w)
-       {
-               w.wpconsidered = false;
-               w.wpnearestpoint = '0 0 0';
-               w.wpcost = 10000000;
-               w.wpfire = 0;
-               w.enemy = world;
-               w = w.chain;
-       }
-
-       if(fixed_source_waypoint)
-       {
-               fixed_source_waypoint.wpconsidered = true;
-               fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
-               fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg;
-               fixed_source_waypoint.wpfire = 1;
-               fixed_source_waypoint.enemy = world;
-       }
-       else
-       {
-               // try a short range search for the nearest waypoints, and expand the search repeatedly if none are found
-               // as this search is expensive we will use lower values if the bot is on the air
-               float i, increment, maxdistance;
-               if(self.flags & FL_ONGROUND)
-               {
-                       increment = 750;
-                       maxdistance = 50000;
-               }
-               else
-               {
-                       increment = 500;
-                       maxdistance = 1500;
-               }
-
-               for(i=increment;!navigation_markroutes_nearestwaypoints(waylist, i)&&i<maxdistance;i+=increment);
-       }
-
-       searching = true;
-       while (searching)
-       {
-               searching = false;
-               w = waylist;
-               while (w)
-               {
-                       if (w.wpfire)
-                       {
-                               searching = true;
-                               w.wpfire = 0;
-                               cost = w.wpcost;
-                               p = w.wpnearestpoint;
-                               wp = w.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp00mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp01mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp02mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp03mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp04mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp05mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp06mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp07mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp08mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp09mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp10mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp11mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp12mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp13mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp14mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp15mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp16mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp17mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp18mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp19mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp20mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp21mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp22mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp23mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp24mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp25mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp26mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp27mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp28mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp29mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp30mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp31mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
-                       }
-                       w = w.chain;
-               }
-       }
-}
-
-// queries the entire spawnfunc_waypoint network for pathes leading to the bot
-void navigation_markroutes_inverted(entity fixed_source_waypoint)
-{
-       entity w, wp, waylist;
-       float searching, cost, cost2;
-       vector p;
-       w = waylist = findchain(classname, "waypoint");
-       while (w)
-       {
-               w.wpconsidered = false;
-               w.wpnearestpoint = '0 0 0';
-               w.wpcost = 10000000;
-               w.wpfire = 0;
-               w.enemy = world;
-               w = w.chain;
-       }
-
-       if(fixed_source_waypoint)
-       {
-               fixed_source_waypoint.wpconsidered = true;
-               fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
-               fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; // the cost to get from X to fixed_source_waypoint
-               fixed_source_waypoint.wpfire = 1;
-               fixed_source_waypoint.enemy = world;
-       }
-       else
-       {
-               error("need to start with a waypoint\n");
-       }
-
-       searching = true;
-       while (searching)
-       {
-               searching = false;
-               w = waylist;
-               while (w)
-               {
-                       if (w.wpfire)
-                       {
-                               searching = true;
-                               w.wpfire = 0;
-                               cost = w.wpcost; // cost to walk from w to home
-                               p = w.wpnearestpoint;
-                               for(wp = waylist; wp; wp = wp.chain)
-                               {
-                                       if(w != wp.wp00) if(w != wp.wp01) if(w != wp.wp02) if(w != wp.wp03)
-                                       if(w != wp.wp04) if(w != wp.wp05) if(w != wp.wp06) if(w != wp.wp07)
-                                       if(w != wp.wp08) if(w != wp.wp09) if(w != wp.wp10) if(w != wp.wp11)
-                                       if(w != wp.wp12) if(w != wp.wp13) if(w != wp.wp14) if(w != wp.wp15)
-                                       if(w != wp.wp16) if(w != wp.wp17) if(w != wp.wp18) if(w != wp.wp19)
-                                       if(w != wp.wp20) if(w != wp.wp21) if(w != wp.wp22) if(w != wp.wp23)
-                                       if(w != wp.wp24) if(w != wp.wp25) if(w != wp.wp26) if(w != wp.wp27)
-                                       if(w != wp.wp28) if(w != wp.wp29) if(w != wp.wp30) if(w != wp.wp31)
-                                               continue;
-                                       cost2 = cost + wp.dmg;
-                                       navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               }
-                       }
-                       w = w.chain;
-               }
-       }
-}
-
-// updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item
-void navigation_routerating(entity e, float f, float rangebias)
-{SELFPARAM();
-       entity nwp;
-       vector o;
-       if (!e)
-               return;
-
-       if(e.blacklisted)
-               return;
-
-       o = (e.absmin + e.absmax) * 0.5;
-
-       //print("routerating ", etos(e), " = ", ftos(f), " - ", ftos(rangebias), "\n");
-
-       // Evaluate path using jetpack
-       if(g_jetpack)
-       if(self.items & IT_JETPACK)
-       if(autocvar_bot_ai_navigation_jetpack)
-       if(vlen(self.origin - o) > autocvar_bot_ai_navigation_jetpack_mindistance)
-       {
-               vector pointa, pointb;
-
-               bot_debug(strcat("jetpack ai: evaluating path for ", e.classname, "\n"));
-
-               // Point A
-               traceline(self.origin, self.origin + '0 0 65535', MOVE_NORMAL, self);
-               pointa = trace_endpos - '0 0 1';
-
-               // Point B
-               traceline(o, o + '0 0 65535', MOVE_NORMAL, e);
-               pointb = trace_endpos - '0 0 1';
-
-               // Can I see these two points from the sky?
-               traceline(pointa, pointb, MOVE_NORMAL, self);
-
-               if(trace_fraction==1)
-               {
-                       bot_debug("jetpack ai: can bridge these two points\n");
-
-                       // Lower the altitude of these points as much as possible
-                       float zdistance, xydistance, cost, t, fuel;
-                       vector down, npa, npb;
-
-                       down = '0 0 -1' * (PL_MAX.z - PL_MIN.z) * 10;
-
-                       do{
-                               npa = pointa + down;
-                               npb = pointb + down;
-
-                               if(npa.z<=self.absmax.z)
-                                       break;
-
-                               if(npb.z<=e.absmax.z)
-                                       break;
-
-                               traceline(npa, npb, MOVE_NORMAL, self);
-                               if(trace_fraction==1)
-                               {
-                                       pointa = npa;
-                                       pointb = npb;
-                               }
-                       }
-                       while(trace_fraction == 1);
-
-
-                       // Rough estimation of fuel consumption
-                       // (ignores acceleration and current xyz velocity)
-                       xydistance = vlen(pointa - pointb);
-                       zdistance = fabs(pointa.z - self.origin.z);
-
-                       t = zdistance / autocvar_g_jetpack_maxspeed_up;
-                       t += xydistance / autocvar_g_jetpack_maxspeed_side;
-                       fuel = t * autocvar_g_jetpack_fuel * 0.8;
-
-                       bot_debug(strcat("jetpack ai: required fuel ", ftos(fuel), " self.ammo_fuel ", ftos(self.ammo_fuel), "\n"));
-
-                       // enough fuel ?
-                       if(self.ammo_fuel>fuel)
-                       {
-                               // Estimate cost
-                               // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship
-                               //  - between air and ground speeds)
-
-                               cost = xydistance / (autocvar_g_jetpack_maxspeed_side/autocvar_sv_maxspeed);
-                               cost += zdistance / (autocvar_g_jetpack_maxspeed_up/autocvar_sv_maxspeed);
-                               cost *= 1.5;
-
-                               // Compare against other goals
-                               f = f * rangebias / (rangebias + cost);
-
-                               if (navigation_bestrating < f)
-                               {
-                                       bot_debug(strcat("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")\n"));
-                                       navigation_bestrating = f;
-                                       navigation_bestgoal = e;
-                                       self.navigation_jetpack_goal = e;
-                                       self.navigation_jetpack_point = pointb;
-                               }
-                               return;
-                       }
-               }
-       }
-
-       //te_wizspike(e.origin);
-       //bprint(etos(e));
-       //bprint("\n");
-       // update the cached spawnfunc_waypoint link on a dynamic item entity
-       if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL))
-       {
-               nwp = e;
-       }
-       else
-       {
-               float search;
-
-               search = true;
-
-               if(e.flags & FL_ITEM)
-               {
-                       if (!(e.flags & FL_WEAPON))
-                       if(e.nearestwaypoint)
-                               search = false;
-               }
-               else if (e.flags & FL_WEAPON)
-               {
-                       if(e.classname != "droppedweapon")
-                       if(e.nearestwaypoint)
-                               search = false;
-               }
-
-               if(search)
-               if (time > e.nearestwaypointtimeout)
-               {
-                       nwp = navigation_findnearestwaypoint(e, true);
-                       if(nwp)
-                               e.nearestwaypoint = nwp;
-                       else
-                       {
-                               bot_debug(strcat("FAILED to find a nearest waypoint to '", e.classname, "' #", etos(e), "\n"));
-
-                               if(e.flags & FL_ITEM)
-                                       e.blacklisted = true;
-                               else if (e.flags & FL_WEAPON)
-                               {
-                                       if(e.classname != "droppedweapon")
-                                               e.blacklisted = true;
-                               }
-
-                               if(e.blacklisted)
-                               {
-                                       bot_debug(strcat("The entity '", e.classname, "' is going to be excluded from path finding during this match\n"));
-                                       return;
-                               }
-                       }
-
-                       // TODO: Cleaner solution, probably handling this timeout from ctf.qc
-                       if(e.classname=="item_flag_team")
-                               e.nearestwaypointtimeout = time + 2;
-                       else
-                               e.nearestwaypointtimeout = time + random() * 3 + 5;
-               }
-               nwp = e.nearestwaypoint;
-       }
-
-       bot_debug(strcat("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")\n"));
-       if (nwp)
-       if (nwp.wpcost < 10000000)
-       {
-               //te_wizspike(nwp.wpnearestpoint);
-               bot_debug(strcat(e.classname, " ", ftos(f), "/(1+", ftos((nwp.wpcost + vlen(e.origin - nwp.wpnearestpoint))), "/", ftos(rangebias), ") = "));
-               f = f * rangebias / (rangebias + (nwp.wpcost + vlen(o - nwp.wpnearestpoint)));
-               bot_debug(strcat("considering ", e.classname, " (with rating ", ftos(f), ")\n"));
-               if (navigation_bestrating < f)
-               {
-                       bot_debug(strcat("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")\n"));
-                       navigation_bestrating = f;
-                       navigation_bestgoal = e;
-               }
-       }
-}
-
-// adds an item to the the goal stack with the path to a given item
-float navigation_routetogoal(entity e, vector startposition)
-{SELFPARAM();
-       self.goalentity = e;
-
-       // if there is no goal, just exit
-       if (!e)
-               return false;
-
-       self.navigation_hasgoals = true;
-
-       // put the entity on the goal stack
-       //print("routetogoal ", etos(e), "\n");
-       navigation_pushroute(e);
-
-       if(g_jetpack)
-       if(e==self.navigation_jetpack_goal)
-               return true;
-
-       // if it can reach the goal there is nothing more to do
-       if (tracewalk(self, startposition, PL_MIN, PL_MAX, (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
-               return true;
-
-       // see if there are waypoints describing a path to the item
-       if(e.classname != "waypoint" || (e.wpflags & WAYPOINTFLAG_PERSONAL))
-               e = e.nearestwaypoint;
-       else
-               e = e.enemy; // we already have added it, so...
-
-       if(e == world)
-               return false;
-
-       for (;;)
-       {
-               // add the spawnfunc_waypoint to the path
-               navigation_pushroute(e);
-               e = e.enemy;
-
-               if(e==world)
-                       break;
-       }
-
-       return false;
-}
-
-// removes any currently touching waypoints from the goal stack
-// (this is how bots detect if they reached a goal)
-void navigation_poptouchedgoals()
-{SELFPARAM();
-       vector org, m1, m2;
-       org = self.origin;
-       m1 = org + self.mins;
-       m2 = org + self.maxs;
-
-       if(self.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
-       {
-               if(self.lastteleporttime>0)
-               if(time-self.lastteleporttime<(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15)
-               {
-                       if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-                       if(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && self.goalcurrent.owner==self)
-                       {
-                               self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                               self.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                       }
-                       navigation_poproute();
-                       return;
-               }
-       }
-
-       // If for some reason the bot is closer to the next goal, pop the current one
-       if(self.goalstack01)
-       if(vlen(self.goalcurrent.origin - self.origin) > vlen(self.goalstack01.origin - self.origin))
-       if(checkpvs(self.origin + self.view_ofs, self.goalstack01))
-       if(tracewalk(self, self.origin, self.mins, self.maxs, (self.goalstack01.absmin + self.goalstack01.absmax) * 0.5, bot_navigation_movemode))
-       {
-               bot_debug(strcat("path optimized for ", self.netname, ", removed a goal from the queue\n"));
-               navigation_poproute();
-               // TODO this may also be a nice idea to do "early" (e.g. by
-               // manipulating the vlen() comparisons) to shorten paths in
-               // general - this would make bots walk more "on rails" than
-               // "zigzagging" which they currently do with sufficiently
-               // random-like waypoints, and thus can make a nice bot
-               // personality property
-       }
-
-       // HACK: remove players/bots as goals, they can lead a bot to unexpected places (cliffs, lava, etc)
-       // TODO: rate waypoints near the targetted player at that moment, instead of the player itself
-       if(IS_PLAYER(self.goalcurrent))
-               navigation_poproute();
-
-       // aid for detecting jump pads better (distance based check fails sometimes)
-       if(self.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT && self.jumppadcount > 0 )
-               navigation_poproute();
-
-       // Loose goal touching check when running
-       if(self.aistatus & AI_STATUS_RUNNING)
-       if(self.speed >= autocvar_sv_maxspeed) // if -really- running
-       if(self.goalcurrent.classname=="waypoint")
-       {
-               if(vlen(self.origin - self.goalcurrent.origin)<150)
-               {
-                       traceline(self.origin + self.view_ofs , self.goalcurrent.origin, true, world);
-                       if(trace_fraction==1)
-                       {
-                               // Detect personal waypoints
-                               if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-                               if(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && self.goalcurrent.owner==self)
-                               {
-                                       self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                                       self.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                               }
-
-                               navigation_poproute();
-                       }
-               }
-       }
-
-       while (self.goalcurrent && boxesoverlap(m1, m2, self.goalcurrent.absmin, self.goalcurrent.absmax))
-       {
-               // Detect personal waypoints
-               if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-               if(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && self.goalcurrent.owner==self)
-               {
-                       self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                       self.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-               }
-
-               navigation_poproute();
-       }
-}
-
-// begin a goal selection session (queries spawnfunc_waypoint network)
-void navigation_goalrating_start()
-{SELFPARAM();
-       if(self.aistatus & AI_STATUS_STUCK)
-               return;
-
-       self.navigation_jetpack_goal = world;
-       navigation_bestrating = -1;
-       self.navigation_hasgoals = false;
-       navigation_clearroute();
-       navigation_bestgoal = world;
-       navigation_markroutes(world);
-}
-
-// ends a goal selection session (updates goal stack to the best goal)
-void navigation_goalrating_end()
-{SELFPARAM();
-       if(self.aistatus & AI_STATUS_STUCK)
-               return;
-
-       navigation_routetogoal(navigation_bestgoal, self.origin);
-       bot_debug(strcat("best goal ", self.goalcurrent.classname , "\n"));
-
-       // If the bot got stuck then try to reach the farthest waypoint
-       if (!self.navigation_hasgoals)
-       if (autocvar_bot_wander_enable)
-       {
-               if (!(self.aistatus & AI_STATUS_STUCK))
-               {
-                       bot_debug(strcat(self.netname, " cannot walk to any goal\n"));
-                       self.aistatus |= AI_STATUS_STUCK;
-               }
-
-               self.navigation_hasgoals = false; // Reset this value
-       }
-}
-
-void botframe_updatedangerousobjects(float maxupdate)
-{
-       entity head, bot_dodgelist;
-       vector m1, m2, v, o;
-       float c, d, danger;
-       c = 0;
-       bot_dodgelist = findchainfloat(bot_dodge, true);
-       botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint");
-       while (botframe_dangerwaypoint != world)
-       {
-               danger = 0;
-               m1 = botframe_dangerwaypoint.mins;
-               m2 = botframe_dangerwaypoint.maxs;
-               head = bot_dodgelist;
-               while (head)
-               {
-                       v = head.origin;
-                       v.x = bound(m1_x, v.x, m2_x);
-                       v.y = bound(m1_y, v.y, m2_y);
-                       v.z = bound(m1_z, v.z, m2_z);
-                       o = (head.absmin + head.absmax) * 0.5;
-                       d = head.bot_dodgerating - vlen(o - v);
-                       if (d > 0)
-                       {
-                               traceline(o, v, true, world);
-                               if (trace_fraction == 1)
-                                       danger = danger + d;
-                       }
-                       head = head.chain;
-               }
-               botframe_dangerwaypoint.dmg = danger;
-               c = c + 1;
-               if (c >= maxupdate)
-                       break;
-               botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint");
-       }
-}
-
-void navigation_unstuck()
-{SELFPARAM();
-       float search_radius = 1000;
-
-       if (!autocvar_bot_wander_enable)
-               return;
-
-       if (!bot_waypoint_queue_owner)
-       {
-               bot_debug(strcat(self.netname, " sutck, taking over the waypoints queue\n"));
-               bot_waypoint_queue_owner = self;
-               bot_waypoint_queue_bestgoal = world;
-               bot_waypoint_queue_bestgoalrating = 0;
-       }
-
-       if(bot_waypoint_queue_owner!=self)
-               return;
-
-       if (bot_waypoint_queue_goal)
-       {
-               // evaluate the next goal on the queue
-               float d = vlen(self.origin - bot_waypoint_queue_goal.origin);
-               bot_debug(strcat(self.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d), "\n"));
-               if(tracewalk(bot_waypoint_queue_goal, self.origin, PL_MIN, PL_MAX, bot_waypoint_queue_goal.origin, bot_navigation_movemode))
-               {
-                       if( d > bot_waypoint_queue_bestgoalrating)
-                       {
-                               bot_waypoint_queue_bestgoalrating = d;
-                               bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal;
-                       }
-               }
-               bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal;
-
-               if (!bot_waypoint_queue_goal)
-               {
-                       if (bot_waypoint_queue_bestgoal)
-                       {
-                               bot_debug(strcat(self.netname, " stuck, reachable waypoint found, heading to it\n"));
-                               navigation_routetogoal(bot_waypoint_queue_bestgoal, self.origin);
-                               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-                               self.aistatus &= ~AI_STATUS_STUCK;
-                       }
-                       else
-                       {
-                               bot_debug(strcat(self.netname, " stuck, cannot walk to any waypoint at all\n"));
-                       }
-
-                       bot_waypoint_queue_owner = world;
-               }
-       }
-       else
-       {
-               if(bot_strategytoken!=self)
-                       return;
-
-               // build a new queue
-               bot_debug(strcat(self.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu\n"));
-
-               entity head, first;
-
-               first = world;
-               head = findradius(self.origin, search_radius);
-
-               while(head)
-               {
-                       if(head.classname=="waypoint")
-               //      if(!(head.wpflags & WAYPOINTFLAG_GENERATED))
-                       {
-                               if(bot_waypoint_queue_goal)
-                                       bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = head;
-                               else
-                                       first = head;
-
-                               bot_waypoint_queue_goal = head;
-                               bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = world;
-                       }
-
-                       head = head.chain;
-               }
-
-               if (first)
-                       bot_waypoint_queue_goal = first;
-               else
-               {
-                       bot_debug(strcat(self.netname, " stuck, cannot walk to any waypoint at all\n"));
-                       bot_waypoint_queue_owner = world;
-               }
-       }
-}
-
-// Support for debugging tracewalk visually
-
-void debugresetnodes()
-{
-       debuglastnode = '0 0 0';
-}
-
-void debugnode(vector node)
-{SELFPARAM();
-       if (!IS_PLAYER(self))
-               return;
-
-       if(debuglastnode=='0 0 0')
-       {
-               debuglastnode = node;
-               return;
-       }
-
-       te_lightning2(world, node, debuglastnode);
-       debuglastnode = node;
-}
-
-void debugnodestatus(vector position, float status)
-{
-       vector c;
-
-       switch (status)
-       {
-               case DEBUG_NODE_SUCCESS:
-                       c = '0 15 0';
-                       break;
-               case DEBUG_NODE_WARNING:
-                       c = '15 15 0';
-                       break;
-               case DEBUG_NODE_FAIL:
-                       c = '15 0 0';
-                       break;
-               default:
-                       c = '15 15 15';
-       }
-
-       te_customflash(position, 40,  2, c);
-}
-
-// Support for debugging the goal stack visually
-
-.float goalcounter;
-.vector lastposition;
-
-// Debug the goal stack visually
-void debuggoalstack()
-{SELFPARAM();
-       entity goal;
-       vector org, go;
-
-       if(self.goalcounter==0)goal=self.goalcurrent;
-       else if(self.goalcounter==1)goal=self.goalstack01;
-       else if(self.goalcounter==2)goal=self.goalstack02;
-       else if(self.goalcounter==3)goal=self.goalstack03;
-       else if(self.goalcounter==4)goal=self.goalstack04;
-       else if(self.goalcounter==5)goal=self.goalstack05;
-       else if(self.goalcounter==6)goal=self.goalstack06;
-       else if(self.goalcounter==7)goal=self.goalstack07;
-       else if(self.goalcounter==8)goal=self.goalstack08;
-       else if(self.goalcounter==9)goal=self.goalstack09;
-       else if(self.goalcounter==10)goal=self.goalstack10;
-       else if(self.goalcounter==11)goal=self.goalstack11;
-       else if(self.goalcounter==12)goal=self.goalstack12;
-       else if(self.goalcounter==13)goal=self.goalstack13;
-       else if(self.goalcounter==14)goal=self.goalstack14;
-       else if(self.goalcounter==15)goal=self.goalstack15;
-       else if(self.goalcounter==16)goal=self.goalstack16;
-       else if(self.goalcounter==17)goal=self.goalstack17;
-       else if(self.goalcounter==18)goal=self.goalstack18;
-       else if(self.goalcounter==19)goal=self.goalstack19;
-       else if(self.goalcounter==20)goal=self.goalstack20;
-       else if(self.goalcounter==21)goal=self.goalstack21;
-       else if(self.goalcounter==22)goal=self.goalstack22;
-       else if(self.goalcounter==23)goal=self.goalstack23;
-       else if(self.goalcounter==24)goal=self.goalstack24;
-       else if(self.goalcounter==25)goal=self.goalstack25;
-       else if(self.goalcounter==26)goal=self.goalstack26;
-       else if(self.goalcounter==27)goal=self.goalstack27;
-       else if(self.goalcounter==28)goal=self.goalstack28;
-       else if(self.goalcounter==29)goal=self.goalstack29;
-       else if(self.goalcounter==30)goal=self.goalstack30;
-       else if(self.goalcounter==31)goal=self.goalstack31;
-       else goal=world;
-
-       if(goal==world)
-       {
-               self.goalcounter = 0;
-               self.lastposition='0 0 0';
-               return;
-       }
-
-       if(self.lastposition=='0 0 0')
-               org = self.origin;
-       else
-               org = self.lastposition;
-
-
-       go = ( goal.absmin + goal.absmax ) * 0.5;
-       te_lightning2(world, org, go);
-       self.lastposition = go;
-
-       self.goalcounter++;
-}
diff --git a/qcsrc/server/bot/default/navigation.qh b/qcsrc/server/bot/default/navigation.qh
deleted file mode 100644 (file)
index cf4a5ce..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef NAVIGATION_H
-#define NAVIGATION_H
-/*
- * Globals and Fields
- */
-
-float navigation_bestrating;
-float bot_navigation_movemode;
-float navigation_testtracewalk;
-
-vector jumpstepheightvec;
-vector stepheightvec;
-
-entity botframe_dangerwaypoint;
-entity navigation_bestgoal;
-
-// stack of current goals (the last one of which may be an item or other
-// desirable object, the rest are typically waypoints to reach it)
-.entity goalcurrent, goalstack01, goalstack02, goalstack03;
-.entity goalstack04, goalstack05, goalstack06, goalstack07;
-.entity goalstack08, goalstack09, goalstack10, goalstack11;
-.entity goalstack12, goalstack13, goalstack14, goalstack15;
-.entity goalstack16, goalstack17, goalstack18, goalstack19;
-.entity goalstack20, goalstack21, goalstack22, goalstack23;
-.entity goalstack24, goalstack25, goalstack26, goalstack27;
-.entity goalstack28, goalstack29, goalstack30, goalstack31;
-.entity nearestwaypoint;
-
-.float nearestwaypointtimeout;
-.float navigation_hasgoals;
-.float lastteleporttime;
-
-.float blacklisted;
-
-.entity navigation_jetpack_goal;
-.vector navigation_jetpack_point;
-
-const float DEBUG_NODE_SUCCESS        = 1;
-const float DEBUG_NODE_WARNING        = 2;
-const float DEBUG_NODE_FAIL           = 3;
-vector debuglastnode;
-
-entity bot_waypoint_queue_owner;       // Owner of the temporary list of goals
-entity bot_waypoint_queue_goal;                // Head of the temporary list of goals
-.entity bot_waypoint_queue_nextgoal;
-entity bot_waypoint_queue_bestgoal;
-float bot_waypoint_queue_bestgoalrating;
-
-/*
- * Functions
- */
-
-void debugresetnodes();
-void debugnode(vector node);
-void debugnodestatus(vector position, float status);
-
-void debuggoalstack();
-
-float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
-
-float navigation_markroutes_nearestwaypoints(entity waylist, float maxdist);
-float navigation_routetogoal(entity e, vector startposition);
-
-void navigation_clearroute();
-void navigation_pushroute(entity e);
-void navigation_poproute();
-void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p);
-void navigation_markroutes(entity fixed_source_waypoint);
-void navigation_markroutes_inverted(entity fixed_source_waypoint);
-void navigation_routerating(entity e, float f, float rangebias);
-void navigation_poptouchedgoals();
-void navigation_goalrating_start();
-void navigation_goalrating_end();
-void navigation_unstuck();
-
-void botframe_updatedangerousobjects(float maxupdate);
-
-entity navigation_findnearestwaypoint(entity ent, float walkfromwp);
-float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist);
-#endif
diff --git a/qcsrc/server/bot/default/scripting.qc b/qcsrc/server/bot/default/scripting.qc
deleted file mode 100644 (file)
index 3f53d2b..0000000
+++ /dev/null
@@ -1,1346 +0,0 @@
-#include "scripting.qh"
-
-.float bot_cmdqueuebuf_allocated;
-.float bot_cmdqueuebuf;
-.float bot_cmdqueuebuf_start;
-.float bot_cmdqueuebuf_end;
-
-void bot_clearqueue(entity bot)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-               return;
-       buf_del(bot.bot_cmdqueuebuf);
-       bot.bot_cmdqueuebuf_allocated = false;
-       LOG_TRACE("bot ", bot.netname, " queue cleared\n");
-}
-
-void bot_queuecommand(entity bot, string cmdstring)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-       {
-               bot.bot_cmdqueuebuf = buf_create();
-               bot.bot_cmdqueuebuf_allocated = true;
-               bot.bot_cmdqueuebuf_start = 0;
-               bot.bot_cmdqueuebuf_end = 0;
-       }
-
-       bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
-
-       // if the command was a "sound" command, precache the sound NOW
-       // this prevents lagging!
-       {
-               float sp;
-               string parm;
-               string cmdstr;
-
-               sp = strstrofs(cmdstring, " ", 0);
-               if(sp >= 0)
-               {
-                       parm = substring(cmdstring, sp + 1, -1);
-                       cmdstr = substring(cmdstring, 0, sp);
-                       if(cmdstr == "sound")
-                       {
-                               // find the LAST word
-                               for (;;)
-                               {
-                                       sp = strstrofs(parm, " ", 0);
-                                       if(sp < 0)
-                                               break;
-                                       parm = substring(parm, sp + 1, -1);
-                               }
-                               precache_sound(parm);
-                       }
-               }
-       }
-
-       bot.bot_cmdqueuebuf_end += 1;
-}
-
-void bot_dequeuecommand(entity bot, float idx)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-               error("dequeuecommand but no queue allocated");
-       if(idx < bot.bot_cmdqueuebuf_start)
-               error("dequeueing a command in the past");
-       if(idx >= bot.bot_cmdqueuebuf_end)
-               error("dequeueing a command in the future");
-       bufstr_set(bot.bot_cmdqueuebuf, idx, "");
-       if(idx == bot.bot_cmdqueuebuf_start)
-               bot.bot_cmdqueuebuf_start += 1;
-       if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
-               bot_clearqueue(bot);
-}
-
-string bot_readcommand(entity bot, float idx)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-               error("readcommand but no queue allocated");
-       if(idx < bot.bot_cmdqueuebuf_start)
-               error("reading a command in the past");
-       if(idx >= bot.bot_cmdqueuebuf_end)
-               error("reading a command in the future");
-       return bufstr_get(bot.bot_cmdqueuebuf, idx);
-}
-
-float bot_havecommand(entity bot, float idx)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-               return 0;
-       if(idx < bot.bot_cmdqueuebuf_start)
-               return 0;
-       if(idx >= bot.bot_cmdqueuebuf_end)
-               return 0;
-       return 1;
-}
-
-const int MAX_BOT_PLACES = 4;
-.float bot_places_count;
-.entity bot_places[MAX_BOT_PLACES];
-.string bot_placenames[MAX_BOT_PLACES];
-entity bot_getplace(string placename)
-{SELFPARAM();
-       entity e;
-       if(substring(placename, 0, 1) == "@")
-       {
-               int i, p;
-               placename = substring(placename, 1, -1);
-               string s, s2;
-               for(i = 0; i < self.bot_places_count; ++i)
-                       if(self.(bot_placenames[i]) == placename)
-                               return self.(bot_places[i]);
-               // now: i == self.bot_places_count
-               s = s2 = cvar_string(placename);
-               p = strstrofs(s2, " ", 0);
-               if(p >= 0)
-               {
-                       s = substring(s2, 0, p);
-                       //print("places: ", placename, " -> ", cvar_string(placename), "\n");
-                       cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
-                       //print("places: ", placename, " := ", cvar_string(placename), "\n");
-               }
-               e = find(world, targetname, s);
-               if(!e)
-                       LOG_INFO("invalid place ", s, "\n");
-               if(i < MAX_BOT_PLACES)
-               {
-                       self.(bot_placenames[i]) = strzone(placename);
-                       self.(bot_places[i]) = e;
-                       self.bot_places_count += 1;
-               }
-               return e;
-       }
-       else
-       {
-               e = find(world, targetname, placename);
-               if(!e)
-                       LOG_INFO("invalid place ", placename, "\n");
-               return e;
-       }
-}
-
-
-// Initialize global commands list
-// NOTE: New commands should be initialized here
-void bot_commands_init()
-{
-       bot_cmd_string[BOT_CMD_NULL] = "";
-       bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_PAUSE] = "pause";
-       bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
-       bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_WAIT] = "wait";
-       bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_TURN] = "turn";
-       bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
-       bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
-
-       bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
-       bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
-       bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_CC] = "cc";
-       bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_IF] = "if";
-       bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_ELSE] = "else";
-       bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_FI] = "fi";
-       bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
-       bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_AIM] = "aim";
-       bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
-       bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
-       bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
-       bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
-       bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
-       bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
-       bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
-       bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_CONSOLE] = "console";
-       bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_SOUND] = "sound";
-       bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
-       bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmds_initialized = true;
-}
-
-// Returns first bot with matching name
-entity find_bot_by_name(string name)
-{
-       entity bot;
-
-       bot = findchainflags(flags, FL_CLIENT);
-       while (bot)
-       {
-               if(IS_BOT_CLIENT(bot))
-               if(bot.netname==name)
-                       return bot;
-
-               bot = bot.chain;
-       }
-
-       return world;
-}
-
-// Returns a bot by number on list
-entity find_bot_by_number(float number)
-{
-       entity bot;
-       float c = 0;
-
-       if(!number)
-               return world;
-
-       bot = findchainflags(flags, FL_CLIENT);
-       while (bot)
-       {
-               if(IS_BOT_CLIENT(bot))
-               {
-                       if(++c==number)
-                               return bot;
-               }
-               bot = bot.chain;
-       }
-
-       return world;
-}
-
-float bot_decodecommand(string cmdstring)
-{
-       float cmd_parm_type;
-       float sp;
-       string parm;
-
-       sp = strstrofs(cmdstring, " ", 0);
-       if(sp < 0)
-       {
-               parm = "";
-       }
-       else
-       {
-               parm = substring(cmdstring, sp + 1, -1);
-               cmdstring = substring(cmdstring, 0, sp);
-       }
-
-       if(!bot_cmds_initialized)
-               bot_commands_init();
-
-       int i;
-       for(i=1;i<BOT_CMD_COUNTER;++i)
-       {
-               if(bot_cmd_string[i]!=cmdstring)
-                       continue;
-
-               cmd_parm_type = bot_cmd_parm_type[i];
-
-               if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
-               {
-                       LOG_INFO("ERROR: A parameter is required for this command\n");
-                       return 0;
-               }
-
-               // Load command into queue
-               bot_cmd.bot_cmd_type = i;
-
-               // Attach parameter
-               switch(cmd_parm_type)
-               {
-                       case BOT_CMD_PARAMETER_FLOAT:
-                               bot_cmd.bot_cmd_parm_float = stof(parm);
-                               break;
-                       case BOT_CMD_PARAMETER_STRING:
-                               if(bot_cmd.bot_cmd_parm_string)
-                                       strunzone(bot_cmd.bot_cmd_parm_string);
-                               bot_cmd.bot_cmd_parm_string = strzone(parm);
-                               break;
-                       case BOT_CMD_PARAMETER_VECTOR:
-                               bot_cmd.bot_cmd_parm_vector = stov(parm);
-                               break;
-                       default:
-                               break;
-               }
-               return 1;
-       }
-       LOG_INFO("ERROR: No such command '", cmdstring, "'\n");
-       return 0;
-}
-
-void bot_cmdhelp(string scmd)
-{
-       int i, ntype;
-       string stype;
-
-       if(!bot_cmds_initialized)
-               bot_commands_init();
-
-       for(i=1;i<BOT_CMD_COUNTER;++i)
-       {
-               if(bot_cmd_string[i]!=scmd)
-                       continue;
-
-               ntype = bot_cmd_parm_type[i];
-
-               switch(ntype)
-               {
-                       case BOT_CMD_PARAMETER_FLOAT:
-                               stype = "float number";
-                               break;
-                       case BOT_CMD_PARAMETER_STRING:
-                               stype = "string";
-                               break;
-                       case BOT_CMD_PARAMETER_VECTOR:
-                               stype = "vector";
-                               break;
-                       default:
-                               stype = "none";
-                               break;
-               }
-
-               LOG_INFO(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
-
-               LOG_INFO("Description: ");
-               switch(i)
-               {
-                       case BOT_CMD_PAUSE:
-                               LOG_INFO("Stops the bot completely. Any command other than 'continue' will be ignored.");
-                               break;
-                       case BOT_CMD_CONTINUE:
-                               LOG_INFO("Disable paused status");
-                               break;
-                       case BOT_CMD_WAIT:
-                               LOG_INFO("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
-                               break;
-                       case BOT_CMD_WAIT_UNTIL:
-                               LOG_INFO("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
-                               break;
-                       case BOT_CMD_BARRIER:
-                               LOG_INFO("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
-                               break;
-                       case BOT_CMD_TURN:
-                               LOG_INFO("Look to the right or left N degrees. For turning to the left use positive numbers.");
-                               break;
-                       case BOT_CMD_MOVETO:
-                               LOG_INFO("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
-                               break;
-                       case BOT_CMD_MOVETOTARGET:
-                               LOG_INFO("Walk to the specific target on the map");
-                               break;
-                       case BOT_CMD_RESETGOAL:
-                               LOG_INFO("Resets the goal stack");
-                               break;
-                       case BOT_CMD_CC:
-                               LOG_INFO("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
-                               break;
-                       case BOT_CMD_IF:
-                               LOG_INFO("Perform simple conditional execution.\n");
-                               LOG_INFO("Syntax: \n");
-                               LOG_INFO("        sv_cmd .. if \"condition\"\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if true>\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if true>\n");
-                               LOG_INFO("        sv_cmd .. else\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if false>\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if false>\n");
-                               LOG_INFO("        sv_cmd .. fi\n");
-                               LOG_INFO("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
-                               LOG_INFO("            Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
-                               LOG_INFO("Fields: health, speed, flagcarrier\n");
-                               LOG_INFO("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
-                               break;
-                       case BOT_CMD_RESETAIM:
-                               LOG_INFO("Points the aim to the coordinates x,y 0,0");
-                               break;
-                       case BOT_CMD_AIM:
-                               LOG_INFO("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
-                               LOG_INFO("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
-                               LOG_INFO("Examples: aim \"90 0\"        // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
-                               LOG_INFO("          aim \"0 90 2\"      // Will gradually look to the sky in the next two seconds");
-                               break;
-                       case BOT_CMD_AIMTARGET:
-                               LOG_INFO("Points the aim to given target");
-                               break;
-                       case BOT_CMD_PRESSKEY:
-                               LOG_INFO("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
-                               LOG_INFO("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
-                               LOG_INFO("Note: The script will not return the control to the bot ai until all keys are released");
-                               break;
-                       case BOT_CMD_RELEASEKEY:
-                               LOG_INFO("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
-                               break;
-                       case BOT_CMD_SOUND:
-                               LOG_INFO("play sound file at bot location");
-                               break;
-                       case BOT_CMD_DEBUG_ASSERT_CANFIRE:
-                               LOG_INFO("verify the state of the weapon entity");
-                               break;
-                       default:
-                               LOG_INFO("This command has no description yet.");
-                               break;
-               }
-               LOG_INFO("\n");
-       }
-}
-
-void bot_list_commands()
-{
-       int i;
-       string ptype;
-
-       if(!bot_cmds_initialized)
-               bot_commands_init();
-
-       LOG_INFO("List of all available commands:\n");
-       LOG_INFO("  Command - Parameter Type\n");
-
-       for(i=1;i<BOT_CMD_COUNTER;++i)
-       {
-               switch(bot_cmd_parm_type[i])
-               {
-                       case BOT_CMD_PARAMETER_FLOAT:
-                               ptype = "float number";
-                               break;
-                       case BOT_CMD_PARAMETER_STRING:
-                               ptype = "string";
-                               break;
-                       case BOT_CMD_PARAMETER_VECTOR:
-                               ptype = "vector";
-                               break;
-                       default:
-                               ptype = "none";
-                               break;
-               }
-               LOG_INFO(strcat("  ",bot_cmd_string[i]," - <",ptype,"> \n"));
-       }
-}
-
-// Commands code
-.int bot_exec_status;
-
-void SV_ParseClientCommand(string s);
-float bot_cmd_cc()
-{
-       SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_impulse()
-{SELFPARAM();
-       self.impulse = bot_cmd.bot_cmd_parm_float;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_continue()
-{SELFPARAM();
-       self.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
-       return CMD_STATUS_FINISHED;
-}
-
-.float bot_cmd_wait_time;
-float bot_cmd_wait()
-{SELFPARAM();
-       if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
-       {
-               if(time>=self.bot_cmd_wait_time)
-               {
-                       self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
-                       return CMD_STATUS_FINISHED;
-               }
-               else
-                       return CMD_STATUS_EXECUTING;
-       }
-
-       self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
-       self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
-       return CMD_STATUS_EXECUTING;
-}
-
-float bot_cmd_wait_until()
-{SELFPARAM();
-       if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
-       {
-               self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
-               return CMD_STATUS_EXECUTING;
-       }
-       self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_barrier()
-{SELFPARAM();
-       entity cl;
-
-       // 0 = no barrier, 1 = waiting, 2 = waiting finished
-
-       if(self.bot_barrier == 0) // initialization
-       {
-               self.bot_barrier = 1;
-
-               //self.colormod = '4 4 0';
-       }
-
-       if(self.bot_barrier == 1) // find other bots
-       {
-               FOR_EACH_CLIENT(cl) if(cl.isbot)
-               {
-                       if(cl.bot_cmdqueuebuf_allocated)
-                               if(cl.bot_barrier != 1)
-                                       return CMD_STATUS_EXECUTING; // not all are at the barrier yet
-               }
-
-               // all bots hit the barrier!
-               FOR_EACH_CLIENT(cl) if(cl.isbot)
-               {
-                       cl.bot_barrier = 2; // acknowledge barrier
-               }
-
-               bot_barriertime = time;
-       }
-
-       // if we get here, the barrier is finished
-       // so end it...
-       self.bot_barrier = 0;
-       //self.colormod = '0 0 0';
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_turn()
-{SELFPARAM();
-       self.v_angle_y = self.v_angle.y + bot_cmd.bot_cmd_parm_float;
-       self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_select_weapon()
-{SELFPARAM();
-       float id;
-
-       id = bot_cmd.bot_cmd_parm_float;
-
-       if(id < WEP_FIRST || id > WEP_LAST)
-               return CMD_STATUS_ERROR;
-
-       if(client_hasweapon(self, id, true, false))
-               self.switchweapon = id;
-       else
-               return CMD_STATUS_ERROR;
-
-       return CMD_STATUS_FINISHED;
-}
-
-.int bot_cmd_condition_status;
-
-const int CMD_CONDITION_NONE = 0;
-const int CMD_CONDITION_true = 1;
-const int CMD_CONDITION_false = 2;
-const int CMD_CONDITION_true_BLOCK = 4;
-const int CMD_CONDITION_false_BLOCK = 8;
-
-float bot_cmd_eval(string expr)
-{SELFPARAM();
-       // Search for numbers
-       if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
-       {
-               return stof(expr);
-       }
-
-       // Search for cvars
-       if(substring(expr, 0, 5)=="cvar.")
-       {
-               return cvar(substring(expr, 5, strlen(expr)));
-       }
-
-       // Search for fields
-       switch(expr)
-       {
-               case "health":
-                       return self.health;
-               case "speed":
-                       return vlen(self.velocity);
-               case "flagcarrier":
-                       return ((self.flagcarried!=world));
-       }
-
-       LOG_INFO(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
-       return 0;
-}
-
-float bot_cmd_if()
-{SELFPARAM();
-       string expr, val_a, val_b;
-       float cmpofs;
-
-       if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
-       {
-               // Only one "if" block is allowed at time
-               LOG_INFO("ERROR: Only one conditional block can be processed at time");
-               bot_clearqueue(self);
-               return CMD_STATUS_ERROR;
-       }
-
-       self.bot_cmd_condition_status |= CMD_CONDITION_true_BLOCK;
-
-       // search for operators
-       expr = bot_cmd.bot_cmd_parm_string;
-
-       cmpofs = strstrofs(expr,"=",0);
-
-       if(cmpofs>0)
-       {
-               val_a = substring(expr,0,cmpofs);
-               val_b = substring(expr,cmpofs+1,strlen(expr));
-
-               if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
-                       self.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       self.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-               return CMD_STATUS_FINISHED;
-       }
-
-       cmpofs = strstrofs(expr,">",0);
-
-       if(cmpofs>0)
-       {
-               val_a = substring(expr,0,cmpofs);
-               val_b = substring(expr,cmpofs+1,strlen(expr));
-
-               if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
-                       self.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       self.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-               return CMD_STATUS_FINISHED;
-       }
-
-       cmpofs = strstrofs(expr,"<",0);
-
-       if(cmpofs>0)
-       {
-               val_a = substring(expr,0,cmpofs);
-               val_b = substring(expr,cmpofs+1,strlen(expr));
-
-               if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
-                       self.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       self.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-               return CMD_STATUS_FINISHED;
-       }
-
-       if(bot_cmd_eval(expr))
-               self.bot_cmd_condition_status |= CMD_CONDITION_true;
-       else
-               self.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_else()
-{SELFPARAM();
-       self.bot_cmd_condition_status &= ~CMD_CONDITION_true_BLOCK;
-       self.bot_cmd_condition_status |= CMD_CONDITION_false_BLOCK;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_fi()
-{SELFPARAM();
-       self.bot_cmd_condition_status = CMD_CONDITION_NONE;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_resetaim()
-{SELFPARAM();
-       self.v_angle = '0 0 0';
-       return CMD_STATUS_FINISHED;
-}
-
-.float bot_cmd_aim_begintime;
-.float bot_cmd_aim_endtime;
-.vector bot_cmd_aim_begin;
-.vector bot_cmd_aim_end;
-
-float bot_cmd_aim()
-{SELFPARAM();
-       // Current direction
-       if(self.bot_cmd_aim_endtime)
-       {
-               float progress;
-
-               progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
-               self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
-
-               if(time>=self.bot_cmd_aim_endtime)
-               {
-                       self.bot_cmd_aim_endtime = 0;
-                       return CMD_STATUS_FINISHED;
-               }
-               else
-                       return CMD_STATUS_EXECUTING;
-       }
-
-       // New aiming direction
-       string parms;
-       float tokens, step;
-
-       parms = bot_cmd.bot_cmd_parm_string;
-
-       tokens = tokenizebyseparator(parms, " ");
-
-       if(tokens<2||tokens>3)
-               return CMD_STATUS_ERROR;
-
-       step = (tokens == 3) ? stof(argv(2)) : 0;
-
-       if(step == 0)
-       {
-               self.v_angle_x -= stof(argv(1));
-      &nb