]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into terencehill/scoreboard_panel_2
authorterencehill <piuntn@gmail.com>
Wed, 10 Aug 2016 13:40:54 +0000 (15:40 +0200)
committerterencehill <piuntn@gmail.com>
Wed, 10 Aug 2016 13:40:54 +0000 (15:40 +0200)
231 files changed:
.gitlab-ci.yml
qcsrc/client/announcer.qc
qcsrc/client/hud/panel/quickmenu.qc
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/client/mapvoting.qc
qcsrc/common/anim.qh
qcsrc/common/animdecide.qh
qcsrc/common/campaign_common.qh
qcsrc/common/command/all.qh
qcsrc/common/command/command.qh
qcsrc/common/command/generic.qh
qcsrc/common/command/markup.qh
qcsrc/common/command/rpn.qh
qcsrc/common/constants.qh
qcsrc/common/csqcmodel_settings.qh
qcsrc/common/deathtypes/all.qh
qcsrc/common/effects/all.qh
qcsrc/common/effects/effect.qh
qcsrc/common/effects/effectinfo.qc
qcsrc/common/effects/qc/all.qh
qcsrc/common/effects/qc/globalsound.qc
qcsrc/common/effects/qc/globalsound.qh
qcsrc/common/effects/qc/rubble.qh
qcsrc/common/ent_cs.qh
qcsrc/common/impulses/all.qh
qcsrc/common/items/all.qh
qcsrc/common/items/inventory.qh
qcsrc/common/items/item.qh
qcsrc/common/items/item/ammo.qh
qcsrc/common/items/item/armor.qh
qcsrc/common/items/item/health.qh
qcsrc/common/items/item/pickup.qh
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/common/minigames/cl_minigames.qh
qcsrc/common/minigames/cl_minigames_hud.qh
qcsrc/common/minigames/minigames.qh
qcsrc/common/minigames/sv_minigames.qh
qcsrc/common/models/all.qh
qcsrc/common/models/model.qh
qcsrc/common/monsters/all.qh
qcsrc/common/monsters/monster.qh
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/spawn.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/monsters/sv_monsters.qh
qcsrc/common/mutators/base.qh
qcsrc/common/mutators/events.qh
qcsrc/common/mutators/mutator/buffs/buffs.qc
qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc
qcsrc/common/mutators/mutator/campcheck/campcheck.qc
qcsrc/common/mutators/mutator/midair/midair.qc
qcsrc/common/mutators/mutator/multijump/multijump.qc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/pinata/pinata.qc
qcsrc/common/net_notice.qh
qcsrc/common/notifications/all.qc
qcsrc/common/notifications/all.qh
qcsrc/common/physics/movelib.qh
qcsrc/common/physics/movetypes/movetypes.qh
qcsrc/common/physics/player.qc
qcsrc/common/physics/player.qh
qcsrc/common/playerstats.qh
qcsrc/common/sounds/all.qh
qcsrc/common/sounds/sound.qh
qcsrc/common/state.qc
qcsrc/common/stats.qh
qcsrc/common/t_items.qc
qcsrc/common/t_items.qh
qcsrc/common/teams.qh
qcsrc/common/triggers/func/breakable.qc
qcsrc/common/triggers/func/breakable.qh
qcsrc/common/triggers/func/include.qh
qcsrc/common/triggers/include.qh
qcsrc/common/triggers/platforms.qh
qcsrc/common/triggers/subs.qh
qcsrc/common/triggers/target/include.qh
qcsrc/common/triggers/target/music.qh
qcsrc/common/triggers/teleporters.qh
qcsrc/common/triggers/trigger/impulse.qh
qcsrc/common/triggers/trigger/include.qh
qcsrc/common/triggers/trigger/jumppads.qh
qcsrc/common/triggers/trigger/secret.qh
qcsrc/common/triggers/trigger/swamp.qh
qcsrc/common/triggers/trigger/viewloc.qh
qcsrc/common/triggers/triggers.qh
qcsrc/common/turrets/all.qh
qcsrc/common/turrets/cl_turrets.qc
qcsrc/common/turrets/config.qh
qcsrc/common/turrets/sv_turrets.qh
qcsrc/common/turrets/turret.qh
qcsrc/common/turrets/util.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/common/vehicles/all.qh
qcsrc/common/vehicles/cl_vehicles.qh
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/vehicles/sv_vehicles.qh
qcsrc/common/vehicles/vehicle.qh
qcsrc/common/vehicles/vehicle/bumblebee.qc
qcsrc/common/vehicles/vehicle/bumblebee.qh
qcsrc/common/vehicles/vehicle/racer.qc
qcsrc/common/vehicles/vehicle/raptor.qc
qcsrc/common/vehicles/vehicle/raptor.qh
qcsrc/common/vehicles/vehicle/spiderbot.qc
qcsrc/common/viewloc.qh
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/all.qh
qcsrc/common/weapons/calculations.qc
qcsrc/common/weapons/calculations.qh
qcsrc/common/weapons/config.qh
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/crylink.qc
qcsrc/common/weapons/weapon/porto.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/tuba.qc
qcsrc/dpdefs/csprogsdefs.qh
qcsrc/dpdefs/dpextensions.qh
qcsrc/dpdefs/keycodes.qh
qcsrc/dpdefs/menudefs.qh
qcsrc/dpdefs/progsdefs.qh
qcsrc/ecs/systems/cl_physics.qc
qcsrc/ecs/systems/physics.qc
qcsrc/ecs/systems/physics.qh
qcsrc/ecs/systems/sv_physics.qc
qcsrc/lib/_all.inc
qcsrc/lib/csqcmodel/cl_model.qh
qcsrc/lib/csqcmodel/cl_player.qh
qcsrc/lib/csqcmodel/common.qh
qcsrc/lib/csqcmodel/interpolate.qh
qcsrc/lib/csqcmodel/settings.qh
qcsrc/lib/csqcmodel/sv_model.qh
qcsrc/lib/intrusivelist.qh
qcsrc/lib/log.qh
qcsrc/lib/map.qh
qcsrc/lib/matrix/matrix.qc
qcsrc/lib/net.qh
qcsrc/lib/p99.qh
qcsrc/lib/spawnfunc.qh
qcsrc/lib/test.qh
qcsrc/lib/warpzone/anglestransform.qh
qcsrc/lib/warpzone/client.qh
qcsrc/lib/warpzone/common.qh
qcsrc/lib/warpzone/mathlib.qh
qcsrc/lib/warpzone/server.qh
qcsrc/lib/warpzone/util_server.qh
qcsrc/menu/command/all.qc
qcsrc/menu/command/all.qh [new file with mode: 0644]
qcsrc/menu/matrix.qc
qcsrc/menu/matrix.qh [new file with mode: 0644]
qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc
qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc
qcsrc/menu/xonotic/gametypelist.qc
qcsrc/menu/xonotic/maplist.qc
qcsrc/menu/xonotic/maplist.qh
qcsrc/menu/xonotic/serverlist.qc
qcsrc/menu/xonotic/util.qc
qcsrc/menu/xonotic/util.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/_mod.inc
qcsrc/server/bot/_mod.qh
qcsrc/server/bot/aim.qc [deleted file]
qcsrc/server/bot/aim.qh [deleted file]
qcsrc/server/bot/api.qc [new file with mode: 0644]
qcsrc/server/bot/api.qh [new file with mode: 0644]
qcsrc/server/bot/bot.qc [deleted file]
qcsrc/server/bot/bot.qh [deleted file]
qcsrc/server/bot/default/_mod.inc [new file with mode: 0644]
qcsrc/server/bot/default/_mod.qh [new file with mode: 0644]
qcsrc/server/bot/default/aim.qc [new file with mode: 0644]
qcsrc/server/bot/default/aim.qh [new file with mode: 0644]
qcsrc/server/bot/default/bot.qc [new file with mode: 0644]
qcsrc/server/bot/default/bot.qh [new file with mode: 0644]
qcsrc/server/bot/default/cvars.qc [new file with mode: 0644]
qcsrc/server/bot/default/cvars.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/_mod.inc [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/_mod.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/havocbot.qc [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/havocbot.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/roles.qc [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/roles.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/scripting.qh [new file with mode: 0644]
qcsrc/server/bot/default/navigation.qc [new file with mode: 0644]
qcsrc/server/bot/default/navigation.qh [new file with mode: 0644]
qcsrc/server/bot/default/scripting.qc [new file with mode: 0644]
qcsrc/server/bot/default/scripting.qh [new file with mode: 0644]
qcsrc/server/bot/default/waypoints.qc [new file with mode: 0644]
qcsrc/server/bot/default/waypoints.qh [new file with mode: 0644]
qcsrc/server/bot/havocbot/_mod.inc [deleted file]
qcsrc/server/bot/havocbot/_mod.qh [deleted file]
qcsrc/server/bot/havocbot/havocbot.qc [deleted file]
qcsrc/server/bot/havocbot/havocbot.qh [deleted file]
qcsrc/server/bot/havocbot/roles.qc [deleted file]
qcsrc/server/bot/havocbot/roles.qh [deleted file]
qcsrc/server/bot/havocbot/scripting.qh [deleted file]
qcsrc/server/bot/navigation.qc [deleted file]
qcsrc/server/bot/navigation.qh [deleted file]
qcsrc/server/bot/scripting.qc [deleted file]
qcsrc/server/bot/scripting.qh [deleted file]
qcsrc/server/bot/waypoints.qc [deleted file]
qcsrc/server/bot/waypoints.qh [deleted file]
qcsrc/server/cl_client.qc
qcsrc/server/cl_impulse.qc
qcsrc/server/cl_player.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/mapvoting.qc
qcsrc/server/mutators/all.qc
qcsrc/server/mutators/gamemode.qh
qcsrc/server/mutators/mutator.qh
qcsrc/server/mutators/mutator/gamemode_ctf.qc
qcsrc/server/mutators/mutator/gamemode_cts.qc
qcsrc/server/mutators/mutator/gamemode_keepaway.qc
qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
qcsrc/server/mutators/mutator/gamemode_lms.qc
qcsrc/server/mutators/mutator/gamemode_race.qc
qcsrc/server/pathlib/path_waypoint.qc
qcsrc/server/progs.inc
qcsrc/server/race.qc
qcsrc/server/scores.qc
qcsrc/server/sv_main.qc
qcsrc/server/teamplay.qc
qcsrc/server/weapons/selection.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/tools/cloc.txt [new file with mode: 0644]
qcsrc/tools/headerstyle.sh
qcsrc/tools/whitespace.sh

index f826484fe47d3f21861f142a09124d7754bb235a..83a84305a7615708203d7e34a15b3b573b61fb17 100644 (file)
@@ -5,6 +5,12 @@ before_script:
   - cd gmqcc && make -j $(nproc) && export QCC="$PWD/gmqcc"
   - cd ..
 
+report_cloc:
+  stage: test
+  script:
+    - cloc --force-lang-def=qcsrc/tools/cloc.txt --sql 1 --sql-project xonotic qcsrc | sqlite3 code.db
+    - sqlite3 code.db 'select file,nCode from t where nCode > 1000 order by nBlank+nComment+nCode desc'
+
 test_compilation_units:
   stage: test
   script:
index 9107add6c87c1b694998ba858717ebb242328362..62b732bec25034e773801113d86aa9f268bfcbf4 100644 (file)
@@ -58,7 +58,7 @@ void Announcer_Countdown(entity this)
                {
                        Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
                        Notification annce_num = Announcer_PickNumber(CNT_GAMESTART, countdown_rounded);
-                       if(annce_num != NULL)           
+                       if(annce_num != NULL)
                                Local_Notification(MSG_ANNCE, annce_num);
                }
 
index 4cb5c9fd4e752fbabea639ce94c0658c854cc061..470701966379d7b1cbfec4188f47cda6776eb968 100644 (file)
@@ -156,7 +156,7 @@ bool QuickMenu_Open(string mode, string submenu)
        }
        else
        {
-               LOG_WARNINGF("Unrecognized mode %s\n", mode);
+               LOG_WARNF("Unrecognized mode %s\n", mode);
                return false;
        }
 
@@ -292,7 +292,7 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page)
                        // printf("^1 skipping %s\n", s);
                }
                if(QuickMenu_Buffer_Index == QuickMenu_Buffer_Size)
-                       LOG_WARNINGF("Couldn't find submenu \"%s\"\n", z_submenu);
+                       LOG_WARNF("Couldn't find submenu \"%s\"\n", z_submenu);
        }
 
        // only the last page can contain up to QUICKMENU_MAXLINES entries
@@ -866,9 +866,9 @@ void QuickMenu_Default(string target_submenu)
 
        if(target_submenu != "" && !target_submenu_found)
        {
-               LOG_WARNINGF("Couldn't find submenu \"%s\"\n", target_submenu);
+               LOG_WARNF("Couldn't find submenu \"%s\"\n", target_submenu);
                if(prvm_language != "en")
-                       LOG_WARNINGF("^3Warning: submenu must be in English\n", target_submenu);
+                       LOG_WARNF("^3Warning: submenu must be in English\n", target_submenu);
                QuickMenu_Buffer_Size = 0;
        }
 }
index b5c841a36bea5a9585408cd48847e763ff6f1550..517a09549fa124381d5ffa84ab27bb5ec0d4f3e5 100644 (file)
@@ -108,7 +108,6 @@ void CSQC_Init()
        binddb = db_create();
        tempdb = db_create();
        ClientProgsDB = db_load("client.db");
-       compressShortVector_init();
 
        draw_endBoldFont();
 
@@ -131,7 +130,7 @@ void CSQC_Init()
 
        registercvar("cl_spawn_near_teammate", "1");
 
-       gametype = 0;
+       gametype = NULL;
 
        // sbt_fields uses strunzone on the titles!
        for(int i = 0; i < MAX_SBT_FIELDS; ++i)
@@ -880,7 +879,7 @@ void CSQC_Ent_Remove(entity this)
        if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype);
        if (wasfreed(this))
        {
-               LOG_WARNING("CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
+               LOG_WARN("CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
                return;
        }
        if (this.enttype) Ent_Remove(this);
@@ -950,7 +949,7 @@ void Gamemode_Init();
 NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew)
 {
        make_pure(this);
-       gametype = ReadInt24_t();
+       gametype = ReadRegistered(Gametypes);
        HUD_ModIcons_SetFunc();
        FOREACH(Scores, true, {
                if (scores_label(it)) strunzone(scores_label(it));
index e57f9754284e4ef4014c5db629c9c112e342009b..afd0b5e38c96be69d22b400022a0be49429a786a 100644 (file)
@@ -19,7 +19,7 @@ string minimapname;
 // General stuff
 
 float postinit;
-float gametype;
+entity gametype;
 
 //float sorted_players;
 //float sorted_teams;
index adcd2a7b6e057c142e5469d37d572a1a4d82e042..19c128a7da6db459c14e59c4b3737dd6cb965a36 100644 (file)
@@ -627,7 +627,7 @@ void GameTypeVote_ReadOption(int i)
        }
        else
        {
-               int type = MapInfo_Type_FromString(gt);
+               Gametype type = MapInfo_Type_FromString(gt);
                mv_pk3[i] = strzone(MapInfo_Type_ToText(type));
                mv_desc[i] = MapInfo_Type_Description(type);
        }
index acf6735f37924df3ed97fb5602150d56ffa51831..84d59720d467c199bc2593aee5ce540ec4ad0903 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ANIM_H
-#define ANIM_H
+#pragma once
 
 // begin engine fields
 
@@ -46,5 +45,3 @@ void anim_set(entity e, vector anim, bool looping, bool override, bool restart);
 #define setanim(...) anim_set(__VA_ARGS__)
 void anim_update(entity e);
 #define updateanim(...) anim_update(__VA_ARGS__)
-
-#endif
index 39ca54ff2f7783514755385f4e3af49b557d54c9..ebdba4e5d4aaab64df58f2c36f82f32b7800f611 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ANIMDECIDE_H
-#define ANIMDECIDE_H
+#pragma once
 
 // must be called at least once to initialize, or when modelindex is changed
 void animdecide_load_if_needed(entity e);
@@ -145,4 +144,3 @@ const int ANIMACTION_PAIN2 = 3; // pain
 const int ANIMACTION_SHOOT = 4; // shoot
 const int ANIMACTION_TAUNT = 5; // taunt
 const int ANIMACTION_MELEE = 6; // melee
-#endif
index 3f494a4639b723a310a26682d7d743e05971eaa6..3bdc8725c2803b5c9c27f6f0f30c9841d6ea8294 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CAMPAIGN_COMMON_H
-#define CAMPAIGN_COMMON_H
+#pragma once
 
 #ifndef CAMPAIGN_MAX_ENTRIES
 #define CAMPAIGN_MAX_ENTRIES 64
@@ -31,4 +30,3 @@ void CampaignFile_Unload();
 // Sets up the campaign for the n-th array item (meaning: campaign_offset+nth
 // level) using localcmd()
 void CampaignSetup(float n);
-#endif
index 129090d3ab442b0bfeaf468434a778fbe4f7147a..15285b92ecac7e33144040d0ffbbb26b58ba992e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMON_COMMANDS_ALL_H
-#define COMMON_COMMANDS_ALL_H
+#pragma once
 
 #include "command.qh"
 REGISTRY(GENERIC_COMMANDS, BITS(7))
@@ -22,5 +21,3 @@ STATIC_INIT(GENERIC_COMMANDS_aliases) {
 #include "generic.qh"
 #include "markup.qh"
 #include "rpn.qh"
-
-#endif
index 72eaa18dabb3189c7185ced9e66eeb686ddab832..0b3741a06cd2976007bc34dfd38da44d1eab8a0e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMAND_H
-#define COMMAND_H
+#pragma once
 
 const int CMD_REQUEST_COMMAND = 1;
 const int CMD_REQUEST_USAGE = 2;
@@ -12,5 +11,3 @@ CLASS(Command, Object)
         TC(Command, this);
        }
 ENDCLASS(Command)
-
-#endif
index f8139aaf47e4a894bb803645628683b1168d5c96..b39c79901457de6a77b18d42ed93ac5113d71d61 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMAND_GENERIC_H
-#define COMMAND_GENERIC_H
+#pragma once
 
 #include <common/constants.qh>
 
@@ -39,4 +38,3 @@ void Curl_URI_Get_Callback(int id, float status, string data);
 int curl_uri_get_pos;
 float curl_uri_get_exec[URI_GET_CURL_END - URI_GET_CURL + 1];
 string curl_uri_get_cvar[URI_GET_CURL_END - URI_GET_CURL + 1];
-#endif
index bccc78d0bcd3537d43c4aaa1c91410d47fad7a26..7a1b3876af07e726b3d120678562214f399e2f3e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMAND_MARKUP_H
-#define COMMAND_MARKUP_H
+#pragma once
 
 // ==========================================================
 //  Declarations for markup command code, reworked by Samual
@@ -12,4 +11,3 @@ string markup_from[NUM_MARKUPS];
 string markup_to[NUM_MARKUPS];
 
 string GenericCommand_markup(string s2);
-#endif
index 3c5a8019b2a0cfee52c3ab8e9c500edde24563fa..ba028e248431806131679777bf116d8430d4f07a 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMAND_RPN_H
-#define COMMAND_RPN_H
+#pragma once
 
 // =========================================================
 //  Declarations for RPN command code, written by divVerent
@@ -13,5 +12,3 @@ int rpn_sp;
 string rpn_stack[MAX_RPN_STACK];
 
 void GenericCommand_rpn(float request, float argc, string command);
-
-#endif
index e6e5596939dc38b5f7269af128c87fe4d0820ace..6c775560394259816e14622b1584cb2843b34d59 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CONSTANTS_H
-#define CONSTANTS_H
+#pragma once
 
 REGISTER_NET_TEMP(TE_CSQC_PICTURE)
 REGISTER_NET_TEMP(TE_CSQC_RACE)
@@ -326,4 +325,3 @@ const int SPAWN_PRIO_GOOD_DISTANCE = 10;
 const int GTV_FORBIDDEN = 0; // Cannot be voted
 const int GTV_AVAILABLE = 1; // Can be voted
 const int GTV_CUSTOM    = 2; // Custom entry
-#endif
index 39cec43225ea9ff430422133ea0d7ae91df4a844..9c8dd42917a9e85de2a97c863230273cacd26c2c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CSQCMODEL_SETTINGS_H
-#define CSQCMODEL_SETTINGS_H
+#pragma once
 
 // define this if svqc code wants to use .frame2 and .lerpfrac
 //#define CSQCMODEL_HAVE_TWO_FRAMES
@@ -94,4 +93,3 @@
 #endif
 
 #define CSQCMODEL_EF_RESPAWNGHOST EF_SELECTABLE
-#endif
index 903087947416dfe6978a24900a72020fa3af3354..0466c230ab3b709f40343ce3d2bba3c2c3442894 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef DEATHTYPES_ALL_H
-#define DEATHTYPES_ALL_H
+#pragma once
 
 #include <common/notifications/all.qh>
 
@@ -47,5 +46,3 @@ const int DT_FIRST = BIT(13);
 string Deathtype_Name(int deathtype);
 
 #include "all.inc"
-
-#endif
index e0e9a3ca869d16b85734356e49802dba3fc706b9..7581618375bd5bfc0b949b0b023435db4bebdd8e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef EFFECTS_ALL_H
-#define EFFECTS_ALL_H
+#pragma once
 
 #include "effect.qh"
 
@@ -17,5 +16,3 @@ REGISTRY_CHECK(Effects)
 
 EFFECT(0, Null, string_null)
 #include "all.inc"
-
-#endif
index e58d42ca7b4ba55f9ed82af6b623bd1d21a6b883..7802f0a91be3f3ad464e41b69ad62d1a2b09c0da 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef EFFECT_H
-#define EFFECT_H
+#pragma once
 
 #define particleeffectnum(e) \
        _particleeffectnum(e.eent_eff_name)
@@ -32,5 +31,3 @@ entity Create_Effect_Entity(string eff_name, bool eff_trail)
        this.eent_eff_trail = eff_trail;
        return this;
 }
-
-#endif
index 07d76c480a07627cddc8b284c1e614c89e804e16..67312d90545bf530d46535d0afabb080250b49af 100644 (file)
@@ -253,7 +253,7 @@ void effectinfo_read()
             #undef p
             #undef MY
             default:
-                LOG_WARNINGF("Unknown property '%s'\n", k);
+                LOG_WARNF("Unknown property '%s'\n", k);
                 break;
         }
     }
@@ -306,7 +306,7 @@ GENERIC_COMMAND(dumpeffectinfo, "Dump all effectinfo to effectinfo_dump.txt")
                                LOG_INFOF("Reload with ^2cl_particles_reloadeffects data/%s^7.\n", filename);
                 fclose(fh);
             } else {
-                LOG_WARNINGF("Could not open file '%s'!\n", filename);
+                LOG_WARNF("Could not open file '%s'!\n", filename);
             }
             return;
         }
index 5b9b364313cea105d933bd2516bb6abdaaaf9b9a..54cf6b6ff007b674b5b0225f74b054e61863fe43 100644 (file)
@@ -1,4 +1,3 @@
-#ifndef EFFECTS_QC
-#define EFFECTS_QC
+#pragma once
+
 #include "all.inc"
-#endif
index 8c1ed1ca8305759ef85ee3a7cb691975c51ca7f7..c2fa827fa9d6831b1a9db8ece9a15bc8d378a87d 100644 (file)
                        else
                        {
                                // Can this happen?
-                               LOG_WARNINGF("Missing entcs data for player %d\n", who);
+                               LOG_WARNF("Missing entcs data for player %d\n", who);
                                sound8(e, o, chan, sample, vol, atten, 0, 0);
                        }
                        return true;
                        else
                        {
                                // Can this happen?
-                               LOG_WARNINGF("Missing entcs data for player %d\n", who);
+                               LOG_WARNF("Missing entcs data for player %d\n", who);
                                sound8(e, o, chan, sample, vol, atten, 0, 0);
                        }
                        return true;
                int fh = fopen(f, FILE_READ);
                if (fh < 0)
                {
-                       LOG_WARNINGF("Player sound file not found: %s\n", f);
+                       LOG_WARNF("Player sound file not found: %s\n", f);
                        return;
                }
                for (string s; (s = fgets(fh)); )
                        int n = tokenize_console(s);
                        if (n != 3)
                        {
-                               if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s);
+                               if (n != 0) LOG_WARNF("Invalid sound info line: %s\n", s);
                                continue;
                        }
                        string file = argv(1);
                        int fh = fopen(f, FILE_READ);
                        if (fh < 0)
                        {
-                               if (strict) LOG_WARNINGF("Player sound file not found: %s\n", f);
+                               if (strict) LOG_WARNF("Player sound file not found: %s\n", f);
                                return false;
                        }
                        for (string s; (s = fgets(fh)); )
                                int n = tokenize_console(s);
                                if (n != 3)
                                {
-                                       if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s);
+                                       if (n != 0) LOG_WARNF("Invalid sound info line: %s\n", s);
                                        continue;
                                }
                                string key = argv(0);
index 640d330f1c0cc139d46266e77b05115507c7cc9d..1df0b1a42b1145eeeea104092fec1a98d5b5b25d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef GLOBALSOUND_H
-#define GLOBALSOUND_H
+#pragma once
 
 #ifdef SVQC
        /** Use new sound handling. TODO: use when sounds play correctly on clients */
@@ -148,5 +147,3 @@ STATIC_INIT(allvoicesamples)
     FOREACH(PlayerSounds, it.instanceOfVoiceMessage, allvoicesamples = strcat(allvoicesamples, " ", it.m_playersoundstr));
     allvoicesamples = strzone(substring(allvoicesamples, 1, -1));
 }
-
-#endif
index 1665e4ccef38e8c37db1d984dacea596214ff6b2..83a6941213d627dbeca64e0174cc9e662696310e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef RUBBLE_H
-#define RUBBLE_H
+#pragma once
 
 #ifdef CSQC
 
@@ -46,5 +45,3 @@ entity RubbleNew(string cname)
 }
 
 #endif
-
-#endif
index 1c72b351f7ff7e4e65b48fd9e5bf599f86c11a40..fdaaab2d57c0f37be4345a824a3892a3fdd90e51 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ENT_CS_H
-#define ENT_CS_H
+#pragma once
 
 REGISTER_NET_LINKED(ENT_CLIENT_ENTCS)
 REGISTER_NET_TEMP(CLIENT_ENTCS)
@@ -141,5 +140,3 @@ REGISTER_NET_TEMP(CLIENT_ENTCS)
        }
 
 #endif
-
-#endif
index 6780ac16d0fa1ddd83c766319d05f2d9289b2c01..45a8f1323b9f142bd02b5eae8ad61202a273ae63 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef IMPULSES_ALL_H
-#define IMPULSES_ALL_H
+#pragma once
 
 REGISTRY(IMPULSES, 255)
 REGISTER_REGISTRY(IMPULSES)
@@ -216,5 +215,3 @@ CHIMPULSE(SPEEDRUN, 141)
 CHIMPULSE(CLONE_STANDING, 142)
 CHIMPULSE(TELEPORT, 143)
 CHIMPULSE(R00T, 148)
-
-#endif
index 18cc5ae4f3b707dfd11caea3774cc2473d2587f2..ff54a3a299f8a3e614fc0e7c62992591b0c8777d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ITEMS_ALL_H
-#define ITEMS_ALL_H
+#pragma once
 
 #include <common/command/all.qh>
 
@@ -33,5 +32,3 @@ GENERIC_COMMAND(dumpitems, "Dump all items to the console") {
 #ifndef MENUQC
 string Item_Model(string item_mdl);
 #endif
-
-#endif
index c7f602e653835d6b519843bea740acc2ca0caf10..4ca11c268664f1b33a69b5809cda3dafdd60cb29 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef INVENTORY_H
-#define INVENTORY_H
+#pragma once
 
 #include "all.qh"
 #include "item/pickup.qh"
@@ -74,5 +73,3 @@ void Inventory_new(entity e)
 void Inventory_delete(entity e) { delete(e.inventory.inventory); delete(e.inventory); }
 void Inventory_update(entity e) { e.inventory.SendFlags = 0xFFFFFF; }
 #endif
-
-#endif
index 4ab7af4f2317341ebeb4d1f625dfb8659cecb3d8..ed33169fcdb2b7e97714fb8fe9b3737e08d88e47 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef GAMEITEM_H
-#define GAMEITEM_H
+#pragma once
 
 const int IT_UNLIMITED_WEAPON_AMMO             =  BIT(0); // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
 const int IT_UNLIMITED_SUPERWEAPONS            =  BIT(1); // when this bit is set, superweapons don't expire. Checkpoints can give this powerup.
@@ -58,5 +57,3 @@ CLASS(GameItem, Object)
     }
     void ITEM_HANDLE(Show, GameItem this) { this.show(this); }
 ENDCLASS(GameItem)
-
-#endif
index 84f20483e4f2902d65cc325ee8d0f0ca31d0509b..ed2ada7cf4ad36a5be5714a650a58a53c1a53051 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef AMMO_H
-#define AMMO_H
+#pragma once
+
 #include "pickup.qh"
 CLASS(Ammo, Pickup)
 #ifdef SVQC
@@ -8,4 +8,3 @@ CLASS(Ammo, Pickup)
     ATTRIB(Ammo, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_ammo))
 #endif
 ENDCLASS(Ammo)
-#endif
index adb93e7c94eb8010c2ca8de8da34e6da1e7b7f2f..6e79a2200a7ced73104807c1bbe83a5631f943b3 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef ARMOR_H
-#define ARMOR_H
+#pragma once
+
 #include "pickup.qh"
 CLASS(Armor, Pickup)
 #ifdef SVQC
@@ -8,4 +8,3 @@ CLASS(Armor, Pickup)
     ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc)
 #endif
 ENDCLASS(Armor)
-#endif
index f7bfb5c9386d48e3d0d972fd04db2bc714769dd9..3a0249d5132084ac1f7a1157e5ff22ebaa716ca9 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef HEALTH_H
-#define HEALTH_H
+#pragma once
+
 #include "pickup.qh"
 CLASS(Health, Pickup)
 #ifdef SVQC
@@ -8,4 +8,3 @@ CLASS(Health, Pickup)
     ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc)
 #endif
 ENDCLASS(Health)
-#endif
index 6d914948182482ea6267a1a49352231188dd6c3b..5949a71edace0f8767e8bf937e31b6d3936cd3ce 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef PICKUP_H
-#define PICKUP_H
+#pragma once
+
 #include <common/items/inventory.qh>
 #include <common/items/item.qh>
 CLASS(Pickup, GameItem)
@@ -38,5 +38,3 @@ CLASS(Pickup, GameItem)
     bool ITEM_HANDLE(Pickup, Pickup this, entity item, entity player);
 #endif
 ENDCLASS(Pickup)
-
-#endif
index 2ec902f68556d121bdceb8122adbef84f4edc999..a63f6613b5abe13604045b4e76bbc880e6d40478 100644 (file)
@@ -150,7 +150,11 @@ float _MapInfo_FilterList_cmp(float i, float j, entity pass)
        return strcasecmp(a, b);
 }
 
-float MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
+float MapInfo_FilterGametype(Gametype pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
+{
+       return _MapInfo_FilterGametype(pGametype.m_flags, pFeatures, pFlagsRequired, pFlagsForbidden, pAbortOnGenerate);
+}
+float _MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
 {
        float i, j;
        if (!_MapInfo_filtered_allocated)
@@ -161,7 +165,7 @@ float MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, i
        MapInfo_count = 0;
        for(i = 0, j = -1; i < _MapInfo_globcount; ++i)
        {
-               if(MapInfo_Get_ByName(_MapInfo_GlobItem(i), 1, 0) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame.
+               if(MapInfo_Get_ByName(_MapInfo_GlobItem(i), 1, NULL) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame.
                        if(pAbortOnGenerate)
                        {
                                LOG_TRACE("Autogenerated a .mapinfo, doing the rest later.\n");
@@ -249,7 +253,7 @@ string unquote(string s)
 
 float MapInfo_Get_ByID(float i)
 {
-       if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, 0))
+       if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, NULL))
                return 1;
        return 0;
 }
@@ -346,19 +350,19 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                        else if(k == "classname")
                        {
                                if(v == "dom_controlpoint")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION.m_flags;
                                else if(v == "item_flag_team2")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF.m_flags;
                                else if(v == "team_CTF_blueflag")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF.m_flags;
                                else if(v == "invasion_spawnpoint")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_INVASION;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_INVASION.m_flags;
                                else if(v == "target_assault_roundend")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT.m_flags;
                                else if(v == "onslaught_generator")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT.m_flags;
                                else if(substring(v, 0, 8) == "nexball_" || substring(v, 0, 4) == "ball")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_NEXBALL;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_NEXBALL.m_flags;
                                else if(v == "info_player_team1")
                                        ++spawnpoints;
                                else if(v == "info_player_team2")
@@ -368,9 +372,9 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                                else if(v == "info_player_deathmatch")
                                        ++spawnpoints;
                                else if(v == "trigger_race_checkpoint")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RACE;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RACE.m_flags;
                                else if(v == "target_startTimer")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS;
+                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS.m_flags;
                                else if(v == "weapon_nex")
                                        { }
                                else if(v == "weapon_railgun")
@@ -395,7 +399,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
        }
        diameter = vlen(mapMaxs - mapMins);
 
-       twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF | MAPINFO_TYPE_ASSAULT | MAPINFO_TYPE_RACE | MAPINFO_TYPE_NEXBALL);
+       twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF.m_flags | MAPINFO_TYPE_ASSAULT.m_flags | MAPINFO_TYPE_RACE.m_flags | MAPINFO_TYPE_NEXBALL.m_flags);
        if(twoBaseModes && (MapInfo_Map_supportedGametypes == twoBaseModes))
        {
                // we have a CTF-only or Assault-only map. Don't add other modes then,
@@ -403,24 +407,24 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
        }
        else
        {
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;      // DM always works
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;             // LMS always works
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEEPAWAY;                // Keepaway always works
+               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH.m_flags;      // DM always works
+               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS.m_flags;             // LMS always works
+               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEEPAWAY.m_flags;                // Keepaway always works
 
                if(spawnpoints >= 8  && diameter > 4096) {
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_FREEZETAG;
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA;
+                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH.m_flags;
+                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_FREEZETAG.m_flags;
+                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA.m_flags;
                }
                if(spawnpoints >= 12 && diameter > 5120)
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
+                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT.m_flags;
        }
 
-       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE)
+       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE.m_flags)
        if(!spawnplaces)
        {
-               MapInfo_Map_supportedGametypes &= ~MAPINFO_TYPE_RACE;
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS;
+               MapInfo_Map_supportedGametypes &= ~MAPINFO_TYPE_RACE.m_flags;
+               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS.m_flags;
        }
 
        LOG_TRACE("-> diameter ",    ftos(diameter));
@@ -447,7 +451,7 @@ void _MapInfo_Map_Reset()
        MapInfo_Map_maxs = '0 0 0';
 }
 
-string _MapInfo_GetDefault(float t)
+string _MapInfo_GetDefault(Gametype t)
 {
        switch(t)
        {
@@ -472,11 +476,11 @@ string _MapInfo_GetDefault(float t)
        }
 }
 
-void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int load_default)
+void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisType, int load_default)
 {
        string sa;
-       MapInfo_Map_supportedGametypes |= pThisType;
-       if(!(pThisType & pWantedType))
+       MapInfo_Map_supportedGametypes |= pThisType.m_flags;
+       if(!(pThisType.m_flags & pWantedType.m_flags))
                return;
 
        if(load_default)
@@ -595,22 +599,20 @@ void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int lo
        }
 }
 
-string _MapInfo_GetDefaultEx(float t)
+string _MapInfo_GetDefaultEx(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.model2);
-       return "";
+       return t ? t.model2 : "";
 }
 
-float _MapInfo_GetTeamPlayBool(float t)
+float _MapInfo_GetTeamPlayBool(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.team);
-       return false;
+       return t ? t.team : false;
 }
 
-void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
+void _MapInfo_Map_ApplyGametypeEx(string s, Gametype pWantedType, Gametype pThisType)
 {
-       MapInfo_Map_supportedGametypes |= pThisType;
-       if (!(pThisType & pWantedType))
+       MapInfo_Map_supportedGametypes |= pThisType.m_flags;
+       if (!(pThisType.m_flags & pWantedType.m_flags))
                return;
 
        // reset all the cvars to their defaults
@@ -683,13 +685,7 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
        }
 }
 
-Gametype MapInfo_Type(int t)
-{
-       FOREACH(Gametypes, it.items == t, return it);
-       return NULL;
-}
-
-int MapInfo_Type_FromString(string t)
+Gametype MapInfo_Type_FromString(string t)
 {
 #define deprecate(from, to) MACRO_BEGIN { \
        if (t == #from) { \
@@ -704,31 +700,25 @@ int MapInfo_Type_FromString(string t)
        deprecate(invasion, inv);
        deprecate(assault, as);
        deprecate(race, rc);
-       if (t == "all") return MAPINFO_TYPE_ALL;
-       FOREACH(Gametypes, it.mdl == t, return it.items);
-       return 0;
+       FOREACH(Gametypes, it.mdl == t, return it);
+       return NULL;
 #undef deprecate
 }
 
-string MapInfo_Type_Description(float t)
+string MapInfo_Type_Description(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.gametype_description);
-       return "";
+       return t ? t.gametype_description : "";
 }
 
-string MapInfo_Type_ToString(float t)
+string MapInfo_Type_ToString(Gametype t)
 {
-       if(t == MAPINFO_TYPE_ALL)
-               return "all";
-       FOREACH(Gametypes, it.items == t, return it.mdl);
-       return "";
+       return t ? t.mdl : "";
 }
 
-string MapInfo_Type_ToText(float t)
+string MapInfo_Type_ToText(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.message);
        /* xgettext:no-c-format */
-       return _("@!#%'n Tuba Throwing");
+       return t ? t.message : _("@!#%'n Tuba Throwing");
 }
 
 void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse)
@@ -857,7 +847,7 @@ float MapInfo_isRedundant(string fn, string t)
 }
 
 // load info about a map by name into the MapInfo_Map_* globals
-float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int pGametypeToSet)
+float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gametype pGametypeToSet)
 {
        string fn;
        string s, t;
@@ -874,7 +864,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                return 0;
        }
 
-       if(pGametypeToSet == 0)
+       if(pGametypeToSet == NULL)
                if(MapInfo_Cache_Retrieve(pFilename))
                        return 1;
 
@@ -939,9 +929,9 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                        if(MapInfo_Map_flags & MAPINFO_FLAG_FRUSTRATING)
                                fputs(fh, "frustrating\n");
 
-                       for(i = 1; i <= MapInfo_Map_supportedGametypes; i *= 2)
-                               if(MapInfo_Map_supportedGametypes & i)
-                                       fputs(fh, sprintf("gametype %s // defaults: %s\n", MapInfo_Type_ToString(i), _MapInfo_GetDefaultEx(i)));
+                       FOREACH(Gametypes, MapInfo_Map_supportedGametypes & it.m_flags, {
+                               fputs(fh, sprintf("gametype %s // defaults: %s\n", MapInfo_Type_ToString(it), _MapInfo_GetDefaultEx(it)));
+                       });
 
                        if(fexists(strcat("scripts/", pFilename, ".arena")))
                                fputs(fh, "settemp_for_type all sv_q3acompat_machineshotgunswap 1\n");
@@ -1025,7 +1015,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                else if(t == "type")
                {
                        t = car(s); s = cdr(s);
-                       f = MapInfo_Type_FromString(t);
+                       Gametype f = MapInfo_Type_FromString(t);
                        LOG_MAPWARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.\n");
                        if(f)
                                _MapInfo_Map_ApplyGametype (s, pGametypeToSet, f, true);
@@ -1035,7 +1025,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                else if(t == "gametype")
                {
                        t = car(s); s = cdr(s);
-                       f = MapInfo_Type_FromString(t);
+                       Gametype f = MapInfo_Type_FromString(t);
                        if(f)
                                _MapInfo_Map_ApplyGametypeEx (s, pGametypeToSet, f);
                        else
@@ -1075,9 +1065,10 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                else if(t == "settemp_for_type")
                {
                        t = car(s); s = cdr(s);
+                       Gametype f;
                        if((f = MapInfo_Type_FromString(t)))
                        {
-                               if(f & pGametypeToSet)
+                               if(f.m_flags & pGametypeToSet.m_flags)
                                {
                                        _MapInfo_Parse_Settemp(pFilename, acl, 0, s, 1);
                                }
@@ -1090,9 +1081,10 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                else if(t == "clientsettemp_for_type")
                {
                        t = car(s); s = cdr(s);
+                       Gametype f;
                        if((f = MapInfo_Type_FromString(t)))
                        {
-                               if(f & pGametypeToSet)
+                               if(f.m_flags & pGametypeToSet.m_flags)
                                {
                                        _MapInfo_Parse_Settemp(pFilename, acl, 1, s, 1);
                                }
@@ -1146,21 +1138,21 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
        LOG_MAPWARN("Map ", pFilename, " supports no game types, ignored\n");
        return 0;
 }
-float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, int pGametypeToSet)
+int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametypeToSet)
 {
-       float r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet);
+       int r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet);
 
        if(cvar("g_tdm_on_dm_maps"))
        {
                // if this is set, all DM maps support TDM too
-               if (!(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH))
-                       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH)
+               if (!(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH.m_flags))
+                       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags)
                                _MapInfo_Map_ApplyGametypeEx ("", pGametypeToSet, MAPINFO_TYPE_TEAM_DEATHMATCH);
        }
 
        if(pGametypeToSet)
        {
-               if(!(MapInfo_Map_supportedGametypes & pGametypeToSet))
+               if(!(MapInfo_Map_supportedGametypes & pGametypeToSet.m_flags))
                {
                        error("Can't select the requested game type. This should never happen as the caller should prevent it!\n");
                        //_MapInfo_Map_ApplyGametypeEx("", pGametypeToSet, MAPINFO_TYPE_DEATHMATCH);
@@ -1233,19 +1225,18 @@ int MapInfo_CurrentFeatures()
        return req;
 }
 
-int MapInfo_CurrentGametype()
+Gametype MapInfo_CurrentGametype()
 {
-       int prev = cvar("gamecfg");
-       FOREACH(Gametypes, cvar(it.netname) && it.items != prev, return it.items);
-       if (prev) return prev;
-       return MAPINFO_TYPE_DEATHMATCH;
+       Gametype prev = Gametypes_from(cvar("gamecfg"));
+       FOREACH(Gametypes, cvar(it.netname) && it != prev, return it);
+       return prev ? prev : MAPINFO_TYPE_DEATHMATCH;
 }
 
 float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise
 {
-       if(!MapInfo_Get_ByName(s, 1, 0))
+       if(!MapInfo_Get_ByName(s, 1, NULL))
                return 0;
-       if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0)
+       if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype().m_flags) == 0)
                return 0;
        if((MapInfo_Map_supportedFeatures & MapInfo_CurrentFeatures()) != MapInfo_CurrentFeatures())
                return 0;
@@ -1260,9 +1251,9 @@ float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with th
        return r;
 }
 
-void MapInfo_SwitchGameType(int t)
+void MapInfo_SwitchGameType(Gametype t)
 {
-       FOREACH(Gametypes, true, cvar_set(it.netname, (it.items == t) ? "1" : "0"));
+       FOREACH(Gametypes, true, cvar_set(it.netname, (it == t) ? "1" : "0"));
 }
 
 void MapInfo_LoadMap(string s, float reinit)
@@ -1272,7 +1263,7 @@ void MapInfo_LoadMap(string s, float reinit)
        //if(!MapInfo_CheckMap(s))
        //{
        //      print("EMERGENCY: can't play the selected map in the given game mode. Falling back to DM.\n");
-       //      MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH);
+       //      MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH.m_flags);
        //}
 
        cvar_settemp_restore();
@@ -1282,7 +1273,7 @@ void MapInfo_LoadMap(string s, float reinit)
                localcmd(strcat("\nchangelevel ", s, "\n"));
 }
 
-string MapInfo_ListAllowedMaps(float type, float pRequiredFlags, float pForbiddenFlags)
+string MapInfo_ListAllowedMaps(Gametype type, float pRequiredFlags, float pForbiddenFlags)
 {
        string out;
        float i;
@@ -1304,7 +1295,7 @@ string MapInfo_ListAllAllowedMaps(float pRequiredFlags, float pForbiddenFlags)
 
        // to make absolutely sure:
        MapInfo_Enumerate();
-       MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, pRequiredFlags, pForbiddenFlags, 0);
+       _MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, pRequiredFlags, pForbiddenFlags, 0);
 
        out = "";
        for(i = 0; i < MapInfo_count; ++i)
@@ -1315,18 +1306,16 @@ string MapInfo_ListAllAllowedMaps(float pRequiredFlags, float pForbiddenFlags)
        return substring(out, 1, strlen(out) - 1);
 }
 
-void MapInfo_LoadMapSettings_SaveGameType(float t)
+void MapInfo_LoadMapSettings_SaveGameType(Gametype t)
 {
        MapInfo_SwitchGameType(t);
-       cvar_set("gamecfg", ftos(t));
+       cvar_set("gamecfg", ftos(t.m_id));
        MapInfo_LoadedGametype = t;
 }
 
 void MapInfo_LoadMapSettings(string s) // to be called from worldspawn
 {
-       float t;
-
-       t = MapInfo_CurrentGametype();
+       Gametype t = MapInfo_CurrentGametype();
        MapInfo_LoadMapSettings_SaveGameType(t);
 
        if(!_MapInfo_CheckMap(s)) // with underscore, it keeps temps
@@ -1341,21 +1330,22 @@ void MapInfo_LoadMapSettings(string s) // to be called from worldspawn
                if(MapInfo_Map_supportedGametypes == 0)
                {
                        LOG_SEVERE("Mapinfo system is not functional at all. Assuming deathmatch.\n");
-                       MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH;
+                       MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH.m_flags;
                        MapInfo_LoadMapSettings_SaveGameType(MAPINFO_TYPE_DEATHMATCH);
                        _MapInfo_Map_ApplyGametypeEx("", MAPINFO_TYPE_DEATHMATCH, MAPINFO_TYPE_DEATHMATCH);
                        return; // do not call Get_ByName!
                }
 
-               t = 1;
+               int _t = 1;
                while(!(MapInfo_Map_supportedGametypes & 1))
                {
-                       t *= 2;
-                       MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes / 2);
+                       _t <<= 1;
+                       MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes >> 1);
                }
+               FOREACH(Gametypes, it.m_flags == _t, { t = it; break; });
 
                // t is now a supported mode!
-               LOG_WARNING("can't play the selected map in the given game mode. Falling back to a supported mode.\n");
+               LOG_WARN("can't play the selected map in the given game mode. Falling back to a supported mode.\n");
                MapInfo_LoadMapSettings_SaveGameType(t);
        }
        MapInfo_Get_ByName(s, 1, t);
index b7115ef552522fd2786a3749166430e9a7ae8ea8..34fa0ee35cfa610d1723a15464b8666f4e8523a3 100644 (file)
@@ -1,10 +1,9 @@
-#ifndef MAPINFO_H
-#define MAPINFO_H
+#pragma once
 
 bool autocvar_developer_mapper;
 
-#define LOG_MAPWARN(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNING(__VA_ARGS__); } MACRO_END
-#define LOG_MAPWARNF(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNINGF(__VA_ARGS__); } MACRO_END
+#define LOG_MAPWARN(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARN(__VA_ARGS__); } MACRO_END
+#define LOG_MAPWARNF(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNF(__VA_ARGS__); } MACRO_END
 
 #include "util.qh"
 
@@ -53,20 +52,20 @@ CLASS(Gametype, Object)
     }
 ENDCLASS(Gametype)
 
-REGISTRY(Gametypes, BITS(4))
+REGISTRY(Gametypes, 24)
 #define Gametypes_from(i) _Gametypes_from(i, NULL)
 REGISTER_REGISTRY(Gametypes)
 REGISTRY_CHECK(Gametypes)
 int MAPINFO_TYPE_ALL;
+.int m_flags;
 #define REGISTER_GAMETYPE(hname, sname, g_name, NAME, gteamplay, mutators, defaults, gdescription)          \
-    int MAPINFO_TYPE_##NAME;                                                                                \
     bool NAME##_mapinfo(string k, string v) { return = false; }                                             \
-    REGISTER(Gametypes, MAPINFO_TYPE, g_name, m_id,                                      \
+    REGISTER(Gametypes, MAPINFO_TYPE, NAME, m_id,                                                           \
         NEW(Gametype, hname, #sname, #g_name, gteamplay, #sname " " mutators, defaults, gdescription)       \
     ) {                                                                                                     \
         /* same as `1 << m_id` */                                                                           \
-        MAPINFO_TYPE_##NAME = MAPINFO_TYPE_ALL + 1; MAPINFO_TYPE_ALL |= MAPINFO_TYPE_##NAME;                \
-        this.items = MAPINFO_TYPE_##NAME;                                                                   \
+        int MAPINFO_TYPE_##NAME = MAPINFO_TYPE_ALL + 1; MAPINFO_TYPE_ALL |= MAPINFO_TYPE_##NAME;            \
+        this.items = this.m_flags = MAPINFO_TYPE_##NAME;                                                    \
         this.m_parse_mapinfo = NAME##_mapinfo;                                                              \
     }                                                                                                       \
     [[accumulate]] bool NAME##_mapinfo(string k, string v)
@@ -217,10 +216,11 @@ void MapInfo_Enumerate();
 
 // filter the info by game type mask (updates MapInfo_count)
 float MapInfo_progress;
-float MapInfo_FilterGametype(float gametype, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
+float MapInfo_FilterGametype(Gametype gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
+float _MapInfo_FilterGametype(int gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
 void MapInfo_FilterString(string sf); // filter _MapInfo_filtered (created by MapInfo_FilterGametype) with keyword
 int MapInfo_CurrentFeatures(); // retrieves currently required features from cvars
-int MapInfo_CurrentGametype(); // retrieves current gametype from cvars
+Gametype MapInfo_CurrentGametype(); // retrieves current gametype from cvars
 int MapInfo_ForbiddenFlags(); // retrieves current flags from cvars
 int MapInfo_RequiredFlags(); // retrieves current flags from cvars
 
@@ -229,7 +229,7 @@ float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure
 string MapInfo_BSPName_ByID(float i);
 
 // load info about a map by name into the MapInfo_Map_* globals
-float MapInfo_Get_ByName(string s, float allowGenerate, float gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file
+int MapInfo_Get_ByName(string s, float allowGenerate, Gametype gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file
 
 // look for a map by a prefix, returns the actual map name on success, string_null on failure or ambigous match
 string MapInfo_FindName_match; // the name of the map that was found
@@ -242,23 +242,22 @@ float MapInfo_CheckMap(string s); // returns 0 if the map can't be played with t
 void MapInfo_LoadMap(string s, float reinit);
 
 // list all maps for the current game type
-string MapInfo_ListAllowedMaps(float type, float pFlagsRequired, float pFlagsForbidden);
+string MapInfo_ListAllowedMaps(Gametype type, float pFlagsRequired, float pFlagsForbidden);
 // list all allowed maps (for any game type)
 string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
 
 // gets a gametype from a string
-string _MapInfo_GetDefaultEx(float t);
-float _MapInfo_GetTeamPlayBool(float t);
-Gametype MapInfo_Type(int t);
-float MapInfo_Type_FromString(string t);
-string MapInfo_Type_Description(float t);
-string MapInfo_Type_ToString(float t);
-string MapInfo_Type_ToText(float t);
-void MapInfo_SwitchGameType(int t);
+string _MapInfo_GetDefaultEx(Gametype t);
+float _MapInfo_GetTeamPlayBool(Gametype t);
+Gametype MapInfo_Type_FromString(string t);
+string MapInfo_Type_Description(Gametype t);
+string MapInfo_Type_ToString(Gametype t);
+string MapInfo_Type_ToText(Gametype t);
+void MapInfo_SwitchGameType(Gametype t);
 
 // to be called from worldspawn to set up cvars
 void MapInfo_LoadMapSettings(string s);
-float MapInfo_LoadedGametype; // game type that was active during map load
+Gametype MapInfo_LoadedGametype; // game type that was active during map load
 
 void MapInfo_Cache_Destroy(); // disable caching
 void MapInfo_Cache_Create(); // enable caching
@@ -270,4 +269,3 @@ void MapInfo_Shutdown(); // call this in the shutdown handler
 
 #define MAPINFO_SETTEMP_ACL_USER cvar_string("g_mapinfo_settemp_acl")
 #define MAPINFO_SETTEMP_ACL_SYSTEM "-g_mapinfo_* -rcon_* -_* -g_ban* +*"
-#endif
index cc24d4bf0c69256717525a81ced1e28360ac5941..602c7973585c1510e3903b4d6fe1d5d6d696ebaa 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CL_MINIGAMES_H
-#define CL_MINIGAMES_H
+#pragma once
 
 // Get a square in the center of the avaliable area
 // \note macro to pass by reference pos and mySize
@@ -128,5 +127,3 @@ REGISTRY_CHECK(Minigames)
                this.minigame_event = name##_client_event; \
     } \
     REGISTER_INIT(MINIGAME_##name)
-
-#endif
index f170c590dea2037745df88de968f9c5ca75ad092..ef44ea025f5633635496415635e9ce6665293075 100644 (file)
@@ -1,7 +1,4 @@
-#ifndef CL_MINIGAMES_HUD_H
-#define CL_MINIGAMES_HUD_H
+#pragma once
 
 float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary);
 void HUD_Minigame_Mouse();
-
-#endif
index 7425149f389d9b484c38d089ebef97c2b750f6df..284001a0a22e97997b107a386601d9f75207345f 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MINIGAMES_H
-#define MINIGAMES_H
+#pragma once
 
 // previous node in a doubly linked list
 .entity list_prev;
@@ -121,5 +120,3 @@ entity msle_spawn(entity minigame_session, string class_name);
 
 int msle_id(string class_name);
 string msle_classname(int id);
-
-#endif
index de9e3f69610d41b3a42b3eeb5ac1fffc01c99914..b5015a1f40fd3e452d83ce2d23ed5a510ce63c4c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SV_MINIGAMES_H
-#define SV_MINIGAMES_H
+#pragma once
 
 /// Create a new minigame session
 /// \return minigame session entity
@@ -59,5 +58,3 @@ REGISTRY_CHECK(Minigames)
                this.minigame_event = name##_server_event; \
     } \
     REGISTER_INIT(MINIGAME_##name)
-
-#endif
index f3bfd64c7532a833757eed9545782933b72272aa..ce98629411fd0cd6c3ce798a813acf5dd0fd602f 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MODELS_ALL_H
-#define MODELS_ALL_H
+#pragma once
 
 #include "model.qh"
 
@@ -22,5 +21,3 @@ PRECACHE(Models) {
 
 MODEL(Null, "null");
 #include "all.inc"
-
-#endif
index 91fb278ae0a5ec975c1580d4f7e023f8b96793e2..1610240a79f88c98a5ad65c3f02ed7222254b3bb 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MODEL_H
-#define MODEL_H
+#pragma once
 
 #define setmodel(e, m) _setmodel((e), (m).model_str())
 
@@ -16,12 +15,10 @@ CLASS(Model, Object)
         TC(Model, this);
         string s = this.model_str();
         if (s != "" && s != "null" && !fexists(s)) {
-            LOG_WARNINGF("Missing model: \"%s\"\n", s);
+            LOG_WARNF("Missing model: \"%s\"\n", s);
             return;
         }
         profile(sprintf("precache_model(\"%s\")\n", s));
         precache_model(s);
     }
 ENDCLASS(Model)
-
-#endif
index 1e23f3287940218b3c8a882033d3f12b42c88fd2..84f7a0d78f6d19d4121334681655c170e19f2569 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MONSTERS_ALL_H
-#define MONSTERS_ALL_H
+#pragma once
 
 #include "monster.qh"
 
@@ -18,5 +17,3 @@ REGISTER_MONSTER(Null, NEW(Monster));
 
 
 #include "monster/_mod.inc"
-
-#endif
index de24caa01a2ded2092bfd8689b2a4924f849fe4d..b345fa6fc155cc4039b47bcc122c91b1365fe062 100644 (file)
@@ -1,10 +1,9 @@
-#ifndef MONSTER_H
-#define MONSTER_H
+#pragma once
 
 #ifdef SVQC
 #include "sv_monsters.qh"
 #include <server/g_damage.qh>
-#include <server/bot/bot.qh>
+#include <server/bot/api.qh>
 #include <server/weapons/common.qh>
 #include <server/weapons/tracing.qh>
 #include <server/weapons/weaponsystem.qh>
@@ -74,5 +73,3 @@ CLASS(Monster, Object)
     METHOD(Monster, mr_anim, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
 
 ENDCLASS(Monster)
-
-#endif
index 3018741b09be4b64e1bba83abebcc1065e896311..eb615fb55483d7e5665fe7f698d8472acbb206dd 100644 (file)
@@ -91,7 +91,7 @@ void M_Shambler_Attack_Lightning_Explode(entity this, entity directhitentity)
        if(this.move_movetype == MOVETYPE_NONE)
                this.velocity = this.oldvelocity;
 
-       RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius), 
+       RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius),
                                        NULL, NULL, (autocvar_g_monster_shambler_attack_lightning_force), this.projectiledeathtype, directhitentity);
 
        FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_shambler_attack_lightning_radius_zap, it != this.realowner && it.takedamage,
index 2ebdcc54e80dadee46142c9435e10007147f9b05..0aba5c19d4326f2220c89c576468d022263be4f3 100644 (file)
@@ -1,5 +1,3 @@
-#ifndef SPAWN_H
-#define SPAWN_H
+#pragma once
 
 entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag);
-#endif
index 8508d57089f85f453cddbbb8bb52b56ab80c92e7..f03a51368628b06b9d41d7dbf5002934ad6decaf 100644 (file)
@@ -685,6 +685,8 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra
        //this.angles = vectoangles(this.velocity);
 }
 
+.entity draggedby;
+
 void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
 {
        if(this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
index ffade7b6a2accc4ccbfeb0fac9c82a1ebb5e7a0e..56509cf66130287530503eed396bd3a32fc2d35b 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SV_MONSTERS_H
-#define SV_MONSTERS_H
+#pragma once
 
 // stats networking
 .int stat_monsters_killed;
@@ -113,5 +112,3 @@ ALLMONSTERSOUNDS
 #undef _MSOUND
 
 float GetMonsterSoundSampleField_notFound;
-
-#endif
index c1f7501c9bc5528536590c5e3b9cd4082e2afd9c..a6e367178a93478a12bc90efdf98fe8613355c11 100644 (file)
@@ -203,7 +203,7 @@ NET_HANDLE(Mutator, bool isNew)
         WITH(bool, mutator_log, true, LAMBDA(
             FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
         ));
-        if (added > 1) LOG_WARNINGF("Added more than one mutator for %s\n", s);
+        if (added > 1) LOG_WARNF("Added more than one mutator for %s\n", s);
     }
 }
 #endif
index 75fc8d31e1508a7037037c1db71e1b7db1c77e8a..0dbc9ea21857cffe72d144a258fbddc25e5dc837 100644 (file)
@@ -75,6 +75,7 @@ MUTATOR_HOOKABLE(WP_Format, EV_WP_Format);
  */
 #define EV_PlayerPhysics(i, o) \
     /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** ticrate*/ i(float, MUTATOR_ARGV_1_float) \
     /**/
 MUTATOR_HOOKABLE(PlayerPhysics, EV_PlayerPhysics);
 
@@ -92,6 +93,7 @@ MUTATOR_HOOKABLE(PlayerJump, EV_PlayerJump);
 #define EV_PM_Physics(i, o) \
     /** player */       i(entity, MUTATOR_ARGV_0_entity) \
     /** maxspeed_mod */ i(float, MUTATOR_ARGV_1_float) \
+    /** tick rate */    i(float, MUTATOR_ARGV_2_float) \
     /**/
 MUTATOR_HOOKABLE(PM_Physics, EV_PM_Physics);
 
index e27fa3d7f4f82537570caa3f1ea7082ffe3748d4..29a0a39256ed85d45404a8f097603f0744efda2f 100644 (file)
@@ -681,7 +681,7 @@ MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
 MUTATOR_HOOKFUNCTION(buffs, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        if(frag_target.buffs)
        {
                int buffid = buff_FirstFromFlags(frag_target.buffs).m_id;
index fbbe6035043af2fbfe6bc1a087d38f8e56f85961..f636696e64fc2506cedd588e63afc99a5a31d80d 100644 (file)
@@ -60,7 +60,7 @@ void bugrigs_SetVars()
 
 #endif
 
-void RaceCarPhysics(entity this)
+void RaceCarPhysics(entity this, float dt)
 {
        // using this move type for "big rigs"
        // the engine does not push the entity!
@@ -114,35 +114,35 @@ void RaceCarPhysics(entity this)
                {
                        if (myspeed > 0)
                        {
-                               myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
+                               myspeed = max(0, myspeed - dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
                        }
                        else
                        {
                                if (!PHYS_BUGRIGS_REVERSE_SPEEDING(this))
-                                       myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR(this));
+                                       myspeed = min(0, myspeed + dt * PHYS_BUGRIGS_FRICTION_FLOOR(this));
                        }
                }
                else
                {
                        if (myspeed >= 0)
                        {
-                               myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR(this));
+                               myspeed = max(0, myspeed - dt * PHYS_BUGRIGS_FRICTION_FLOOR(this));
                        }
                        else
                        {
                                if (PHYS_BUGRIGS_REVERSE_STOPPING(this))
                                        myspeed = 0;
                                else
-                                       myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
+                                       myspeed = min(0, myspeed + dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
                        }
                }
                // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec
                //MAXIMA: friction(v) := PHYS_BUGRIGS_FRICTION_FLOOR(this);
 
-               this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
+               this.angles_y += steer * dt * steerfactor; // apply steering
                makevectors(this.angles); // new forward direction!
 
-               myspeed += accel * accelfactor * PHYS_INPUT_TIMELENGTH;
+               myspeed += accel * accelfactor * dt;
 
                rigvel = myspeed * v_forward + '0 0 1' * upspeed;
        }
@@ -153,13 +153,13 @@ void RaceCarPhysics(entity this)
                // responsiveness factor for steering and acceleration
                float f = 1 / (1 + pow(max(0, myspeed / PHYS_BUGRIGS_SPEED_REF(this)), PHYS_BUGRIGS_SPEED_POW(this)));
                float steerfactor = -myspeed * f;
-               this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
+               this.angles_y += steer * dt * steerfactor; // apply steering
 
                rigvel = this.velocity;
                makevectors(this.angles); // new forward direction!
        }
 
-       rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * PHYS_INPUT_TIMELENGTH);
+       rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * dt);
        //MAXIMA: airfriction(v) := v * v * PHYS_BUGRIGS_FRICTION_AIR(this);
        //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v);
        //MAXIMA: solve(total_acceleration(v) = 0, v);
@@ -169,7 +169,7 @@ void RaceCarPhysics(entity this)
                vector rigvel_xy, neworigin, up;
                float mt;
 
-               rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY(this); // 4x gravity plays better
+               rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better
                rigvel_xy = vec2(rigvel);
 
                if (PHYS_BUGRIGS_CAR_JUMPING(this))
@@ -182,10 +182,10 @@ void RaceCarPhysics(entity this)
 
                // BUG RIGS: align the move to the surface instead of doing collision testing
                // can we move?
-               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * PHYS_INPUT_TIMELENGTH, mt, this);
+               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * dt, mt, this);
 
                // align to surface
-               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * PHYS_INPUT_TIMELENGTH, mt, this);
+               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * dt, mt, this);
 
                if (trace_fraction < 0.5)
                {
@@ -213,12 +213,12 @@ void RaceCarPhysics(entity this)
                        UNSET_ONGROUND(this);
                }
 
-               this.velocity = (neworigin - this.origin) * (1.0 / PHYS_INPUT_TIMELENGTH);
+               this.velocity = (neworigin - this.origin) * (1.0 / dt);
                set_movetype(this, MOVETYPE_NOCLIP);
        }
        else
        {
-               rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY(this); // 4x gravity plays better
+               rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better
                this.velocity = rigvel;
                set_movetype(this, MOVETYPE_FLY);
        }
@@ -251,7 +251,7 @@ void RaceCarPhysics(entity this)
        // smooth the angles
        vector vf1, vu1, smoothangles;
        makevectors(this.angles);
-       float f = bound(0, PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_ANGLE_SMOOTHING(this), 1);
+       float f = bound(0, dt * PHYS_BUGRIGS_ANGLE_SMOOTHING(this), 1);
        if (f == 0)
                f = 1;
        vf1 = v_forward * f;
@@ -270,6 +270,7 @@ void RaceCarPhysics(entity this)
 MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics)
 {
     entity player = M_ARGV(0, entity);
+    float dt = M_ARGV(2, float);
 
        if(!PHYS_BUGRIGS(player) || !IS_PLAYER(player)) { return; }
 
@@ -277,7 +278,7 @@ MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics)
        player.angles = player.bugrigs_prevangles;
 #endif
 
-       RaceCarPhysics(player);
+       RaceCarPhysics(player, dt);
        return true;
 }
 
index 191f61681583ab70092cde44e2eb49ff2fb9a18e..b39a2c1650003d616e95ac73e62e851add50645a 100644 (file)
@@ -11,7 +11,7 @@ REGISTER_MUTATOR(campcheck, cvar("g_campcheck"));
 MUTATOR_HOOKFUNCTION(campcheck, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK);
 }
 
index cda6b4c8282d290ff00083d1e76fb4c2ec3531d1..1fa69a60e147929437e2b1e02aa8a4816f649738 100644 (file)
@@ -10,7 +10,7 @@ MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
-       
+
        if(IS_PLAYER(frag_attacker))
        if(IS_PLAYER(frag_target))
        if(time < frag_target.midair_shieldtime)
index 780836d4986166d4c6f6cce4457dbc2a0e90d1a0..d6dc30cee4ec16d0ce8d4a3e899376ab6b429575 100644 (file)
@@ -60,7 +60,7 @@ MUTATOR_HOOKFUNCTION(multijump, PlayerJump)
 
        int phys_multijump = PHYS_MULTIJUMP(player);
 
-       if(!M_ARGV(2, bool) && player.multijump_ready && (PHYS_MULTIJUMP_COUNT(player) < phys_multijump || phys_multijump == -1) && player.velocity_z > PHYS_MULTIJUMP_SPEED(player) && 
+       if(!M_ARGV(2, bool) && player.multijump_ready && (PHYS_MULTIJUMP_COUNT(player) < phys_multijump || phys_multijump == -1) && player.velocity_z > PHYS_MULTIJUMP_SPEED(player) &&
                (!PHYS_MULTIJUMP_MAXSPEED(player) || vdist(player.velocity, <=, PHYS_MULTIJUMP_MAXSPEED(player))))
        {
                if (PHYS_MULTIJUMP(player))
index 14e2b01ac089706b6cd01f2ed8f4647f0e56692e..fdde998566e54479fcee22b2473339b23bfcd652 100644 (file)
@@ -1429,7 +1429,7 @@ MUTATOR_HOOKFUNCTION(nades, MonsterDies)
 MUTATOR_HOOKFUNCTION(nades, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        if(frag_target.nade)
                toss_nade(frag_target, true, '0 0 0', time + 0.05);
 }
index 10866a2f169bf19a7a5843922b67f2d6eb2761ab..acdf1718b04cab3dca0702a3d6b5d68bdf2b2d7c 100644 (file)
@@ -4,7 +4,7 @@ REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_ove
 MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        FOREACH(Weapons, it != WEP_Null, LAMBDA(
                if(frag_target.weapons & WepSet_FromWeapon(it))
                if(PS(frag_target).m_switchweapon != it)
index b36f631fe2a17676c452e272f8176290d09d29eb..1134205185474bda2be0142568448d3e711ac494 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef NET_NOTICE_H
-#define NET_NOTICE_H
+#pragma once
 
 #ifdef SVQC
 string autocvar_sv_join_notices;
@@ -13,5 +12,3 @@ void sv_notice_join(entity _to);
 #ifdef CSQC
 void cl_notice_read();
 #endif
-
-#endif
index d1aa1dbf96deaabea08599cdad263c8ae8c3c8a9..2b3dbdfbe640627abc8a49d59319d032fef56102 100644 (file)
@@ -572,7 +572,7 @@ void Create_Notification_Entity_InfoCenter(entity notif,
                                }
                                else if(icon != "")
                                {
-                                       LOG_WARNINGF(
+                                       LOG_WARNF(
                                                (
                                                        "^1NOTIFICATION HAS ICON BUT NO HUDARGS: "
                                                        "^7net_type = %s, net_name = %s.\n"
@@ -589,7 +589,7 @@ void Create_Notification_Entity_InfoCenter(entity notif,
 
                                        if (cpid == CPID_Null && durcnt != "0 0")
                                        {
-                                               LOG_WARNINGF(
+                                               LOG_WARNF(
                                                        (
                                                                "Notification has durcnt but no cpid: "
                                                                "net_type = %s, net_name = %s."
@@ -1210,7 +1210,7 @@ void Local_Notification(MSG net_type, Notification net_name, ...count)
                        Get_Notif_TypeName(net_type)
                ));
                #endif
-               LOG_WARNINGF("Incorrect usage of Local_Notification: %s\n", "Null notification");
+               LOG_WARNF("Incorrect usage of Local_Notification: %s\n", "Null notification");
                return;
        }
 
@@ -1523,7 +1523,7 @@ void Kill_Notification(
        #endif
 
        string checkargs = Notification_CheckArgs(broadcast, client);
-       if (checkargs != "") { LOG_WARNINGF("Incorrect usage of Kill_Notification: %s", checkargs); return; }
+       if (checkargs != "") { LOG_WARNF("Incorrect usage of Kill_Notification: %s", checkargs); return; }
 
        entity net_notif = new_pure(net_kill_notification);
        net_notif.nent_broadcast = broadcast;
@@ -1561,7 +1561,7 @@ void Send_Notification(
 
        if (!notif)
        {
-               LOG_WARNING("Send_Notification: Could not find notification entity!");
+               LOG_WARN("Send_Notification: Could not find notification entity!");
                return;
        }
 
@@ -1570,7 +1570,7 @@ void Send_Notification(
     if (!net_name) { checkargs = sprintf("No notification provided! %s", checkargs); }
        if (checkargs != "")
        {
-               LOG_WARNINGF("Incorrect usage of Send_Notification: %s", checkargs);
+               LOG_WARNF("Incorrect usage of Send_Notification: %s", checkargs);
                return;
        }
 
@@ -1594,7 +1594,7 @@ void Send_Notification(
 
        if ((notif.nent_stringcount + notif.nent_floatcount) != count)
        {
-               LOG_WARNINGF(
+               LOG_WARNF(
                        "Argument mismatch for Send_Notification(%s, ...)! "
                        "stringcount(%d) + floatcount(%d) != count(%d)\n"
                        "Check the definition and function call for accuracy...?\n",
index 4ff276c78b416c6f2460c24d43a60c8ff523200d..c7b1ec8fc999127d010de913c6c6729ae82d792a 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef NOTIFICATIONS_H
-#define NOTIFICATIONS_H
+#pragma once
 
 #include <common/command/all.qh>
 
@@ -33,7 +32,7 @@ string Get_Notif_TypeName(MSG net_type)
                case MSG_MULTI: return "MSG_MULTI";
                case MSG_CHOICE: return "MSG_CHOICE";
        }
-       LOG_WARNINGF("Get_Notif_TypeName(%d): Improper net type!\n", ORDINAL(net_type));
+       LOG_WARNF("Get_Notif_TypeName(%d): Improper net type!\n", ORDINAL(net_type));
        return "";
 }
 
@@ -253,7 +252,7 @@ string Get_Notif_BroadcastName(NOTIF broadcast)
                case NOTIF_TEAM: return "NOTIF_TEAM";
                case NOTIF_TEAM_EXCEPT: return "NOTIF_TEAM_EXCEPT";
        }
-       LOG_WARNINGF("Get_Notif_BroadcastName(%d): Improper broadcast!\n", broadcast);
+       LOG_WARNF("Get_Notif_BroadcastName(%d): Improper broadcast!\n", broadcast);
        return "";
 }
 
@@ -685,7 +684,7 @@ Notification Get_Notif_Ent(MSG net_type, int net_name)
 {
        Notification it = _Notifications_from(net_name, NULL);
        if (it.nent_type != net_type) {
-               LOG_WARNINGF("Get_Notif_Ent(%s (%d), %s (%d)): Improper net type '%s'!\n",
+               LOG_WARNF("Get_Notif_Ent(%s (%d), %s (%d)): Improper net type '%s'!\n",
                        Get_Notif_TypeName(net_type), net_type,
                        it.registered_id, net_name,
                        Get_Notif_TypeName(it.nent_type)
@@ -800,5 +799,3 @@ REGISTRY_END(Notifications)
 }
 
 #include "all.inc"
-
-#endif
index 250c3ba2b28e121d7e4568a4a87baf134d5ac45b..e0659a40c197ffcbeeffb775a974682974466212 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MOVELIB_H
-#define MOVELIB_H
+#pragma once
 
 #ifdef SVQC
 .vector moveto;
@@ -49,5 +48,3 @@ Yed need to set v_up and v_forward (generally by calling makevectors) before cal
 #endif
 
 void movelib_groundalign4point(entity this, float spring_length, float spring_up, float blendrate, float _max);
-
-#endif
index 35a73d3331755d5f24b04ef175ce3f3208bf7420..db5d29cefda9ec18af34da81e6e57708630c53a4 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MOVETYPES_H
-#define MOVETYPES_H
+#pragma once
 
 #define IS_ONGROUND(s)                      boolean((s).flags & FL_ONGROUND)
 #define SET_ONGROUND(s)                     ((s).flags |= FL_ONGROUND)
@@ -90,5 +89,3 @@ const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2);
 #ifdef CSQC
 #define moveflags STAT(MOVEFLAGS)
 #endif
-
-#endif
index 74a44252fe8bd6d7fc8a11a177ca7ad9c1dfd8ee..6823c29631a18146a561cd4ecbbc25baa513d641 100644 (file)
@@ -134,10 +134,6 @@ void PM_ClientMovement_UpdateStatus(entity this)
                do_crouch = false;
        if(STAT(FROZEN, this))
                do_crouch = false;
-       if((activeweapon.spawnflags & WEP_TYPE_MELEE_PRI) && viewmodel.animstate_startframe == viewmodel.anim_fire1_x && time < viewmodel.weapon_nextthink)
-               do_crouch = false;
-       if((activeweapon.spawnflags & WEP_TYPE_MELEE_SEC) && viewmodel.animstate_startframe == viewmodel.anim_fire2_x && time < viewmodel.weapon_nextthink)
-               do_crouch = false;
 
        if (do_crouch)
        {
@@ -159,7 +155,7 @@ void PM_ClientMovement_UpdateStatus(entity this)
 #endif
 }
 
-void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
+void CPM_PM_Aircontrol(entity this, float dt, vector wishdir, float wishspeed)
 {
        float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1);
        if (k <= 0)
@@ -176,7 +172,7 @@ void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
 
        if (dot > 0) // we can't change direction while slowing down
        {
-               k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH;
+               k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * dt;
                xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32);
                k *= PHYS_AIRCONTROL(this);
                this.velocity = normalize(this.velocity * xyspeed + wishdir * k);
@@ -196,7 +192,7 @@ float AdjustAirAccelQW(float accelqw, float factor)
 //   sv_airaccel_sideways_friction 0
 //   prvm_globalset server speedclamp_mode 1
 //     (or 2)
-void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
+void PM_Accelerate(entity this, float dt, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
 {
        float speedclamp = stretchfactor > 0 ? stretchfactor
        : accelqw < 0 ? 1 // full clamping, no stretch
@@ -212,7 +208,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        vector vel_xy = vec2(this.velocity);
        vector vel_perpend = vel_xy - vel_straight * wishdir;
 
-       float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+       float step = accel * dt * wishspeed0;
 
        float vel_xy_current  = vlen(vel_xy);
        if (speedlimit)
@@ -225,7 +221,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        if (sidefric < 0 && (vel_perpend*vel_perpend))
                // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
        {
-               float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+               float f = max(0, 1 + dt * wishspeed * sidefric);
                float themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend);
                // assume: themin > 1
                // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
@@ -241,7 +237,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
                }
        }
        else
-               vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+               vel_perpend *= max(0, 1 - dt * wishspeed * sidefric);
 
        vel_xy = vel_straight * wishdir + vel_perpend;
 
@@ -260,7 +256,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        this.velocity = vel_xy + vel_z * '0 0 1';
 }
 
-void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
+void PM_AirAccelerate(entity this, float dt, vector wishdir, float wishspeed)
 {
        if (wishspeed == 0)
                return;
@@ -270,18 +266,18 @@ void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
        float curspeed = vlen(curvel);
 
        if (wishspeed > curspeed * 1.01)
-               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * dt);
        else
        {
                float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this)));
-               wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH;
+               wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * dt;
        }
        vector wishvel = wishdir * wishspeed;
        vector acceldir = wishvel - curvel;
        float addspeed = vlen(acceldir);
        acceldir = normalize(acceldir);
 
-       float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+       float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * dt);
 
        if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1)
        {
@@ -579,12 +575,12 @@ void PM_check_nickspam(entity this)
 #endif
 }
 
-void PM_check_punch(entity this)
+void PM_check_punch(entity this, float dt)
 {
 #ifdef SVQC
        if (this.punchangle != '0 0 0')
        {
-               float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
+               float f = vlen(this.punchangle) - 10 * dt;
                if (f > 0)
                        this.punchangle = normalize(this.punchangle) * f;
                else
@@ -593,7 +589,7 @@ void PM_check_punch(entity this)
 
        if (this.punchvector != '0 0 0')
        {
-               float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
+               float f = vlen(this.punchvector) - 30 * dt;
                if (f > 0)
                        this.punchvector = normalize(this.punchvector) * f;
                else
@@ -683,7 +679,7 @@ void PM_check_blocked(entity this)
 
 .vector oldmovement;
 
-void PM_jetpack(entity this, float maxspd_mod)
+void PM_jetpack(entity this, float maxspd_mod, float dt)
 {
        //makevectors(this.v_angle.y * '0 1 0');
        makevectors(this.v_angle);
@@ -768,7 +764,7 @@ void PM_jetpack(entity this, float maxspd_mod)
 
        fvel = min(1, vlen(wishvel) / best);
        if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
-               f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel));
+               f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * dt * fvel));
        else
                f = 1;
 
@@ -776,12 +772,12 @@ void PM_jetpack(entity this, float maxspd_mod)
 
        if (f > 0 && wishvel != '0 0 0')
        {
-               this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
+               this.velocity = this.velocity + wishvel * f * dt;
                UNSET_ONGROUND(this);
 
 #ifdef SVQC
                if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
-                       this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f;
+                       this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * dt * fvel * f;
 
                ITEMS_STAT(this) |= IT_USING_JETPACK;
 
index f120e9fe6cff8ebe5866250663aa8c9f314d6dff..9e7b799d027257e72d1201f6f013fcbffe57eb99 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMON_PHYSICS_H
-#define COMMON_PHYSICS_H
+#pragma once
 
 // Client/server mappings
 
@@ -25,7 +24,7 @@
 .vector v_angle_old;
 .string lastclassname;
 
-.float(entity) PlayerPhysplug;
+.float(entity,float) PlayerPhysplug;
 float AdjustAirAccelQW(float accelqw, float factor);
 
 bool IsFlying(entity a);
@@ -325,5 +324,3 @@ NET_HANDLE(setpause, bool)
        return true;
 }
 #endif
-
-#endif
index ec309c4f4c191fc9d46500e57ff8de3d0571e2ab..73496ba7639bd1b168b54c60fce009e2dbdd9916 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef PLAYERSTATS_H
-#define PLAYERSTATS_H
+#pragma once
 
 #ifdef SVQC
 //float PS_PM_IN_DB = -1;   // playerstats_prematch_in_db      // db for info COLLECTED at the beginning of a match
@@ -116,4 +115,3 @@ void PlayerStats_PlayerDetail();
 void PlayerStats_PlayerDetail_CheckUpdate();
 void PlayerStats_PlayerDetail_Handler(entity fh, entity p, float status);
 #endif
-#endif
index a7fe25f17d1e324d07069f46e16057efbd22ba96..763aae67cfac58bb0b48fb599823b58500635d51 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SOUNDS_ALL_H
-#define SOUNDS_ALL_H
+#pragma once
 
 #include "sound.qh"
 
@@ -21,4 +20,3 @@ PRECACHE(Sounds) {
 SOUND(Null, "misc/null");
 #include "all.inc"
 #include "all.qc"
-#endif
index 519f910b0d3fee4721a698c549a59316841d658b..73442509c78f28cbc9fa77e47489383df1da07ad 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SOUND_H
-#define SOUND_H
+#pragma once
 
 // negative = SVQC autochannels
 // positive = one per entity
@@ -115,7 +114,7 @@ CLASS(Sound, Object)
                        /**/
                #define tryext(ext) { string s = strcat(base, "." #ext); if (fexists(strcat("sound/", s))) return s; }
                extensions(tryext);
-               LOG_WARNINGF("Missing sound: \"%s\"\n", strcat("sound/", base));
+               LOG_WARNF("Missing sound: \"%s\"\n", strcat("sound/", base));
                #undef tryext
                #undef extensions
                return string_null;
@@ -130,5 +129,3 @@ CLASS(Sound, Object)
                precache_sound(s);
        }
 ENDCLASS(Sound)
-
-#endif
index eaa96892f64aba3e9e1cb38f5b2e34af51358ed3..891461c6a4dd24f92886c1c89522af11e67f2093 100644 (file)
@@ -16,12 +16,12 @@ void PlayerState_detach(entity this)
        if (!ps) return;  // initial connect
        PS(this) = NULL;
 
+       if (ps.m_client != this) return;  // don't own state, spectator
        ps.m_switchweapon = WEP_Null;
        ps.m_weapon = WEP_Null;
        ps.m_switchingweapon = WEP_Null;
        ps.ps_push(ps, this);
-
-       if (ps.m_client != this) return;  // don't own state, spectator
+       
        FOREACH_CLIENT(PS(it) == ps, { PS(it) = NULL; });
        delete(ps);
 
index c7fc313a8802b082147098b3c1e391570ebff0ab..a24243bb6d4ec659532b39018fe458f7fc4078ea 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef STATS_H
-#define STATS_H
+#pragma once
 
 #ifdef SVQC
 #include <server/cl_client.qh>
@@ -323,6 +322,3 @@ REGISTER_STAT(GUNALIGN, int, this.cvar_cl_gunalign)
 #ifdef SVQC
 SPECTATE_COPYFIELD(_STAT(GUNALIGN))
 #endif
-
-
-#endif
index f7c81629544dc15e91b2c2781fc097f6fdca9a9c..e53963e2f0bf40a839c47027849bd5370eb86e80 100644 (file)
@@ -4,8 +4,7 @@
 
 #if defined(SVQC)
 
-    #include "../server/bot/bot.qh"
-    #include "../server/bot/waypoints.qh"
+    #include "../server/bot/api.qh"
 
     #include <server/mutators/all.qh>
 
@@ -35,6 +34,7 @@ REGISTER_NET_LINKED(ENT_CLIENT_ITEM)
 
 #ifdef CSQC
 bool autocvar_cl_ghost_items_vehicle = true;
+.vector item_glowmod;
 void Item_SetAlpha(entity this)
 {
        bool veh_hud = (hud && autocvar_cl_ghost_items_vehicle);
@@ -42,7 +42,8 @@ void Item_SetAlpha(entity this)
        if(!veh_hud && (this.ItemStatus & ITS_AVAILABLE))
        {
                this.alpha = 1;
-               this.colormod = this.glowmod = '1 1 1';
+               this.colormod = '1 1 1';
+               this.glowmod = this.item_glowmod;
        }
        else
        {
@@ -247,7 +248,12 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
     }
 
     if(sf & ISF_COLORMAP)
+    {
         this.colormap = ReadShort();
+        this.item_glowmod_x = ReadByte() / 255.0;
+        this.item_glowmod_y = ReadByte() / 255.0;
+        this.item_glowmod_z = ReadByte() / 255.0;
+    }
 
     if(sf & ISF_DROP)
     {
@@ -328,14 +334,19 @@ bool ItemSend(entity this, entity to, int sf)
                WriteShort(MSG_ENTITY, this.fade_start);
 
                if(this.mdl == "")
-                       LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "exspect a crash just aboute now\n");
+                       LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "expect a crash just about now\n");
 
                WriteString(MSG_ENTITY, this.mdl);
        }
 
 
        if(sf & ISF_COLORMAP)
+       {
                WriteShort(MSG_ENTITY, this.colormap);
+               WriteByte(MSG_ENTITY, this.glowmod.x * 255.0);
+        WriteByte(MSG_ENTITY, this.glowmod.y * 255.0);
+        WriteByte(MSG_ENTITY, this.glowmod.z * 255.0);
+       }
 
        if(sf & ISF_DROP)
        {
@@ -447,7 +458,7 @@ void Item_Show (entity e, float mode)
                //setmodel(e, "null");
                e.solid = SOLID_NOT;
                e.colormod = '0 0 0';
-               e.glowmod = e.colormod;
+               //e.glowmod = e.colormod;
                e.spawnshieldtime = 1;
                e.ItemStatus &= ~ITS_AVAILABLE;
        }}
@@ -1205,7 +1216,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                        this.gravity = 1;
                if (!(this.spawnflags & 1024))
                        this.ItemStatus |= ITS_ANIMATE1;
-               this.ItemStatus |= ISF_COLORMAP;
+               this.SendFlags |= ISF_COLORMAP;
        }
 
        this.state = 0;
index 1ed807c7c1ecea93a1059a50eb96f42bf577beed..1c65e806af7e97544ef801904c7f9c1aeb0e741d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef T_ITEMS_H
-#define T_ITEMS_H
+#pragma once
 
 const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
 
@@ -133,4 +132,3 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa
 
 float GiveItems(entity e, float beginarg, float endarg);
 #endif
-#endif
index aeaf078dce4b564b6c2a77df146821ff5a77c935..1f8a603dd01744b1dde6a4f13f98b5b32bb61d03 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TEAMS_H
-#define TEAMS_H
+#pragma once
 
 #ifdef TEAMNUMBERS_THAT_ARENT_STUPID
 const int NUM_TEAM_1 = 1;  // red
@@ -177,5 +176,3 @@ float Team_TeamToNumber(float teamid)
 // safe team comparisons
 #define SAME_TEAM(a,b) (teamplay ? (a.team == b.team) : (a == b))
 #define DIFF_TEAM(a,b) (teamplay ? (a.team != b.team) : (a != b))
-
-#endif
index 6763701d31fa91d52468af4e71f657d0986313e7..29d6c6a269ec3d4181a3ae8a9bb168b7e8682c50 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <server/g_subs.qh>
 #include <server/g_damage.qh>
-#include <server/bot/bot.qh>
+#include <server/bot/api.qh>
 #include <common/csqcmodel_settings.qh>
 #include <lib/csqcmodel/sv_model.qh>
 #include <server/weapons/common.qh>
index 75cfb94c690cf09d51c5ad8eaa1211bf5012043c..761a2c7a98f350fe53629bc13f74e3874eb19673 100644 (file)
@@ -1,8 +1,5 @@
-#ifndef TRIGGERS_FUNC_BREAKABLE_H
-#define TRIGGERS_FUNC_BREAKABLE_H
+#pragma once
 
 #ifdef SVQC
 spawnfunc(func_breakable);
 #endif
-
-#endif
index 628355cb79aa0280f2173848d03c0bca5387e419..4e8b94cd1c88779b56d0ec81fc99b429bc0bcf55 100644 (file)
@@ -1,8 +1,5 @@
-#ifndef TRIGGERS_FUNC_INCLUDE_H
-#define TRIGGERS_FUNC_INCLUDE_H
+#pragma once
 
 #include "door.qh"
 #include "ladder.qh"
 #include "train.qh"
-
-#endif
index 25688e3ecb692e7114c6ec739d5dbf83236d7aa9..f3f81738a19f7a73106863b91a90a0bee7c47c3d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TRIGGERS_INCLUDE_H
-#define TRIGGERS_INCLUDE_H
+#pragma once
 
 // some required common stuff
 #ifdef CSQC
@@ -18,5 +17,3 @@
 
 // trigger
 #include "trigger/include.qh"
-
-#endif
index f0727be3ca1fe59cf57b8779d60e20178ffc533b..c40467494f367adf9c68af7390dbd55ee6ec120c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef PLATFORMS_H
-#define PLATFORMS_H
+#pragma once
 
 .float dmgtime2;
 
@@ -12,5 +11,3 @@ void plat_crush(entity this, entity blocker);
 const float PLAT_LOW_TRIGGER = 1;
 
 .float dmg;
-
-#endif
index 096022c848644ac499c5c674e4f0fe1c709079e1..4f98d5ae15cd92335c4b76282814566358ba1fa7 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SUBS_H
-#define SUBS_H
+#pragma once
 
 #define SUB_ANGLES(s)          (s).angles
 #define SUB_VELOCITY           velocity
@@ -69,5 +68,3 @@ float STATE_DOWN              = 3;
 
 .float         max_health;             // players maximum health is stored here
 #endif
-
-#endif
index 4e44b9762b901a762436b82ba5f507ebb840cfb4..c0f7cad443a0fd62f5cfad147ba3f207e0b662b5 100644 (file)
@@ -1,6 +1,3 @@
-#ifndef TRIGGERS_TARGET_INCLUDE_H
-#define TRIGGERS_TARGET_INCLUDE_H
+#pragma once
 
 #include "music.qh"
-
-#endif
index a9232107ebbbed9407716c1e112acfab553a35b8..80b3684a4e9a768b8ef17e4ea596f463d9cc886d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TARGET_MUSIC_H
-#define TARGET_MUSIC_H
+#pragma once
 
 .float lifetime;
 
@@ -25,5 +24,3 @@ void Ent_TriggerMusic_Remove(entity this);
 #elif defined(SVQC)
 void target_music_kill();
 #endif
-
-#endif
index c0f45f263d588087d724d1e52a59e590275f6b38..513ed3e26928460e636b36940fa0d009c5213241 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef T_TELEPORTERS_H
-#define T_TELEPORTERS_H
+#pragma once
 
 .entity pusher;
 const float TELEPORT_FLAG_SOUND = 1;
@@ -92,5 +91,3 @@ void WarpZone_PostTeleportPlayer_Callback(entity pl);
                        if(head.isplayermodel) \
                                if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax))
 #endif
-
-#endif
index 67d6361fbed2ea61c76c4bafb8e2952d6f06d485..a6961f5d2e7693920f56dac39df1d78011b88f91 100644 (file)
@@ -1,10 +1,7 @@
-#ifndef TRIGGER_IMPULSE_H
-#define TRIGGER_IMPULSE_H
+#pragma once
 
 // tZorks trigger impulse / gravity
 .float radius;
 .float falloff;
 .float strength;
 .float lastpushtime;
-
-#endif
index 63cdb019017254c0fdeba69e347dc8bc21d7256c..8aa6b2b1729244d8f548102d7d8ebd8b6c3a4a34 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TRIGGERS_TRIGGER_INCLUDE_H
-#define TRIGGERS_TRIGGER_INCLUDE_H
+#pragma once
 
 #include "multi.qh"
 #include "jumppads.qh"
@@ -8,5 +7,3 @@
 #include "keylock.qh"
 #include "impulse.qh"
 #include "viewloc.qh"
-
-#endif
index e03f14d335a2bf9aad0db26bbc11e1bcf7b9e832..76d244da55b2d0b8e814b1d3574b53f293c01ff6 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef T_JUMPPADS_H
-#define T_JUMPPADS_H
+#pragma once
 
 const float PUSH_ONCE          = 1;
 const float PUSH_SILENT                = 2;
@@ -59,4 +58,3 @@ spawnfunc(target_push);
 spawnfunc(info_notnull);
 spawnfunc(target_position);
 #endif
-#endif
index c2a155ce72e0114e790e092d56dceacca0f25c74..e483316a4086da58721cea32794b1d5f5de3453c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SECRET_H
-#define SECRET_H
+#pragma once
 #ifdef SVQC
 
 /**
@@ -21,4 +20,3 @@ float secrets_found;
  */
 void secrets_setstatus(entity this);
 #endif
-#endif
index 7ae50bac42a9b7d4bbc7fc6ba5582b6fd2fd4993..f4df98378d544659b1c22f0c663919fb95f90c17 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TRIGGER_SWAMP_H
-#define TRIGGER_SWAMP_H
+#pragma once
 
 .float swamp_interval; //Hurt players in swamp with this interval
 .float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
@@ -7,5 +6,3 @@
 
 .float in_swamp;              // bool
 .entity swampslug;            // Uses this to release from swamp ("untouch" fix)
-
-#endif
index 881197a3b82fb57b7c96bc4f16ed3c14fd527b04..167fc218f7707c5f00b2f70d612d654f21ee1999 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef T_VIEWLOC_H
-#define T_VIEWLOC_H
+#pragma once
 
 .entity viewloc;
 
@@ -8,5 +7,3 @@
 .entity enemy;
 .vector movedir;
 #endif
-
-#endif
index 82c0916c19228c2dd6605b24a6f503d6b3c07061..8a8eb566c19b2ed844035390e6236e78fd1e18b3 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TRIGGERS_H
-#define TRIGGERS_H
+#pragma once
 
 const float SF_TRIGGER_INIT = 1;
 const float SF_TRIGGER_UPDATE = 2;
@@ -50,5 +49,3 @@ const int ACTIVE_IDLE                 = 2;
 const int ACTIVE_BUSY          = 2;
 const int ACTIVE_TOGGLE                = 3;
 #endif
-
-#endif
index a993b9218a22cf77ae2df4f80870d2115b3b9fde..476da2d18e0423d7c242963fc14408ff3e7d7ad1 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TURRETS_ALL_H
-#define TURRETS_ALL_H
+#pragma once
 
 #include <common/command/all.qh>
 #include "config.qh"
@@ -75,5 +74,3 @@ const int TUR_FIRST = 1;
 REGISTER_TURRET(Null, NEW(Turret));
 
 #include "turret/_mod.inc"
-
-#endif
index 79f1f216fff93bcf18ad7ded7fae17cb5c9ced43..29658b56b459ade61ef0f89a2e8a3f58fed3d2ec 100644 (file)
@@ -202,7 +202,7 @@ void turret_draw2d(entity this)
                        );
 }
 
-void turret_construct(entity this)
+void turret_construct(entity this, bool isnew)
 {
        entity tur = get_turretinfo(this.m_id);
 
@@ -238,11 +238,16 @@ void turret_construct(entity this)
        this.tur_head.drawmask                  = MASK_NORMAL;
        this.anim_start_time                    = 0;
        this.draw2d = turret_draw2d;
-       IL_PUSH(g_drawables_2d, this);
        this.maxdistance = autocvar_g_waypointsprite_turrets_maxdist;
        this.teamradar_color = '1 0 0';
        this.alpha = 1;
 
+       if(isnew)
+       {
+               IL_PUSH(g_drawables, this);
+               IL_PUSH(g_drawables_2d, this);
+       }
+
        tur.tr_setup(tur, this);
 }
 
@@ -370,7 +375,7 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew)
                this.angles_x = ReadAngle();
                this.angles_y = ReadAngle();
 
-               turret_construct(this);
+               turret_construct(this, isnew);
                this.colormap = 1024;
                this.glowmod = '0 1 1';
                this.tur_head.colormap = this.colormap;
index bb2a81b847eb740251b5ba126335dfd8295e5e49..caa68a86487dcd3780eb9c277f93aab8eacaa635 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TURRETS_CONFIG_H
-#define TURRETS_CONFIG_H
+#pragma once
 
 #ifdef SVQC
 
@@ -17,5 +16,3 @@ string tur_config_queue[MAX_TUR_CONFIG];
 
 
 #endif
-
-#endif
index 29d08c6290fd6a0699204197e2484283b591c669..c5a3728d134cf20b1e2f068dc803ff943b647cad 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SV_TURRETS_H
-#define SV_TURRETS_H
+#pragma once
 
 entity turret_projectile(entity actor, Sound _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim);
 void turret_projectile_explode(entity this);
@@ -119,5 +118,3 @@ vector tvt_tadv; // turret angle diff vector, updated by a successful call to tu
 float tvt_thadf; // turret head angle diff float, updated by a successful call to turret_validate_target
 float tvt_tadf; // turret angle diff float, updated by a successful call to turret_validate_target
 float tvt_dist; // turret distance, updated by a successful call to turret_validate_target
-
-#endif
index a66952833a10fe877c7ba2a806cc474da50be5ba..dc84acc7401b7457508fcb85429f696204c595d6 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TURRET_H
-#define TURRET_H
+#pragma once
 
 #include <common/weapons/all.qh>
 
@@ -176,5 +175,3 @@ const int TNSF_MOVE         = 64;
 const int TNSF_ANIM         = 128;
 
 const int TNSF_FULL_UPDATE  = 16777215;
-
-#endif
index d5c948d573e85fb46b2cc28de60d413989766392..5f52695bafe7b2df5d9c26e8c6f74ffaa78b73b0 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TURRETS_UTIL_H
-#define TURRETS_UTIL_H
+#pragma once
 
 float shortangle_f(float ang1, float ang2);
 float anglemods(float v);
@@ -8,5 +7,3 @@ vector shortangle_vxy(vector ang1, vector ang2);
 vector angleofs(entity from, entity to);
 vector angleofs3(vector from, vector from_a, entity to);
 void FireImoBeam(entity this, vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype);
-
-#endif
index 70ecc3340e0b2fc3681ea4ab20a1af6d88f37bbb..6f15936c4d9fccedde0bc3398582a212f680f2b5 100644 (file)
@@ -325,7 +325,7 @@ float compressShortVector(vector vec)
        return (p * 0x1000) + (y * 0x80) + len;
 }
 
-void compressShortVector_init()
+STATIC_INIT(compressShortVector)
 {
        float l = 1;
        float f = pow(2, 1/8);
@@ -472,7 +472,7 @@ void get_mi_min_max(float mode)
 
        mi_min = mi;
        mi_max = ma;
-       MapInfo_Get_ByName(mi_shortname, 0, 0);
+       MapInfo_Get_ByName(mi_shortname, 0, NULL);
        if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x)
        {
                mi_min = MapInfo_Map_mins;
@@ -932,7 +932,7 @@ string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_
                return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
 }
 
-float isGametypeInFilter(float gt, float tp, float ts, string pattern)
+float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
 {
        string subpattern, subpattern2, subpattern3, subpattern4;
        subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
index fa17a06a328af4414350a1a9a29632f5114cd34b..81acde950eba2fc63cd450375f0bcebb9c1e7b16 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMON_UTIL_H
-#define COMMON_UTIL_H
+#pragma once
 
 #ifndef MENUQC
 
@@ -64,7 +63,6 @@ const float TIME_FACTOR = 100;
 
 string ScoreString(float vflags, float value);
 
-void compressShortVector_init();
 vector decompressShortVector(float data);
 float compressShortVector(vector vec);
 
@@ -126,7 +124,7 @@ string getWrappedLine_remaining;
 string getWrappedLine(float w, vector size, textLengthUpToWidth_widthFunction_t tw);
 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw);
 
-float isGametypeInFilter(float gt, float tp, float ts, string pattern);
+float isGametypeInFilter(entity gt, float tp, float ts, string pattern);
 
 string swapwords(string str, float i, float j);
 string shufflewords(string str);
@@ -297,4 +295,3 @@ vector bezier_quadratic_getderivative(vector a, vector p, vector b, float t);
 
 // Returns the correct difference between two always increasing numbers
 #define COMPARE_INCREASING(to,from) (to < from ? from + to + 2 : to - from)
-#endif
index b58389d4abb7955feb7c3c80934583fadd4f6f1b..158492f661e7e1326cc248283c3b4841bb64c76a 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef VEHICLES_ALL_H
-#define VEHICLES_ALL_H
+#pragma once
 
 #include "vehicle.qh"
 
@@ -22,5 +21,3 @@ const int VEH_FIRST = 1;
 REGISTER_VEHICLE(Null, NEW(Vehicle));
 
 #include "vehicle/_mod.inc"
-
-#endif
index 5293030c19fe9ecda1aad3c8e733edabc29eda72..327074b98641e72631bbb3089ecc3829b008dca5 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CL_VEHICLES_H
-#define CL_VEHICLES_H
+#pragma once
 
 vector vehicleHud_Size;
 vector vehicleHud_Pos;
@@ -8,5 +7,3 @@ void RaptorCBShellfragDraw(entity this);
 void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang);
 
 #define weapon2mode STAT(VEHICLESTAT_W2MODE)
-
-#endif
index 9f02cf21bcecfd5992ca151f81613d5ef6006416..f7c5a55741fe1bbf6d87b1a65fdcf31b7c487b32 100644 (file)
@@ -339,7 +339,7 @@ bool vehicle_addplayerslot( entity _owner,
                                                                entity _slot,
                                                                int _hud,
                                                                Model _hud_model,
-                                                               bool(entity) _framefunc,
+                                                               bool(entity,float) _framefunc,
                                                                void(entity,bool) _exitfunc, float(entity, entity) _enterfunc)
 {
        if(!(_owner.vehicle_flags & VHF_MULTISLOT))
index e593922fcb0f4beb788cc77ad65d1b0bf754cca2..158191f654eed354806d07cf2140d7a6db1176b4 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef VEHICLES_DEF_H
-#define VEHICLES_DEF_H
+#pragma once
 #ifdef SVQC
 
 #include <common/turrets/sv_turrets.qh>
@@ -110,4 +109,3 @@ bool vehicles_crushable(entity e);
 float vehicle_altitude(entity this, float amax);
 
 #endif
-#endif
index 927465ea6b933ee0d98cf4bbdffc553567d56f7d..1578e0a7d549832e8aa33f548dabd213c49b50d9 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef VEHICLE_H
-#define VEHICLE_H
+#pragma once
 
 CLASS(Vehicle, Object)
     ATTRIB(Vehicle, vehicleid, int, 0)
@@ -24,7 +23,7 @@ CLASS(Vehicle, Object)
     /** cockpit model tag */
     ATTRIB(Vehicle, tag_view, string, string_null)
     /** player physics mod */
-    ATTRIB(Vehicle, PlayerPhysplug, bool(entity), func_null)
+    ATTRIB(Vehicle, PlayerPhysplug, bool(entity,float), func_null)
     /**  */
     ATTRIB(Vehicle, spawnflags, int, 0)
     /** vehicle hitbox size */
@@ -76,5 +75,3 @@ const int VHF_PLAYERSLOT              = BIT(14); /// This ent is a player slot on a multi-pe
 
 // fields:
 .entity tur_head;
-
-#endif
index 91964a9d598cc6d81ab23c97bf7565ea58a5019a..3c0ccaabf708ad9b34870848c715f333266027df 100644 (file)
@@ -99,7 +99,7 @@ vector autocvar_g_vehicle_bumblebee_bouncepain = '1 100 200';
 
 bool autocvar_g_vehicle_bumblebee = true;
 
-bool bumblebee_gunner_frame(entity this)
+bool bumblebee_gunner_frame(entity this, float dt)
 {
        entity vehic = this.vehicle.owner;
        entity gun = this.vehicle;
@@ -394,28 +394,28 @@ void bumblebee_touch(entity this, entity toucher)
        vehicles_touch(this, toucher);
 }
 
-void bumblebee_regen(entity this)
+void bumblebee_regen(entity this, float dt)
 {
        if(this.gun1.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
                this.gun1.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
-                                                                          this.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
+                                                                          this.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * dt);
 
        if(this.gun2.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
                this.gun2.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
-                                                                          this.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
+                                                                          this.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * dt);
 
        if(this.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(this, this.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, frametime, true);
+               vehicles_regen(this, this.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, dt, true);
 
        if(this.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(this, this.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, frametime, false);
+               vehicles_regen(this, this.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, dt, false);
 
        if(this.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(this, this.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, frametime, false);
+               vehicles_regen(this, this.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, dt, false);
 
 }
 
-bool bumblebee_pilot_frame(entity this)
+bool bumblebee_pilot_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
@@ -435,7 +435,7 @@ bool bumblebee_pilot_frame(entity this)
                return;
        }
 
-       bumblebee_regen(vehic);
+       bumblebee_regen(vehic, dt);
 
        crosshair_trace(this);
 
@@ -486,7 +486,7 @@ bool bumblebee_pilot_frame(entity this)
                else if(this.movement.y > 0)
                        newvel += v_right * autocvar_g_vehicle_bumblebee_speed_strafe;
                ftmp = newvel * v_right;
-               ftmp *= frametime * 0.1;
+               ftmp *= dt * 0.1;
                vehic.angles_z = bound(-15, vehic.angles.z + ftmp, 15);
        }
        else
@@ -501,7 +501,7 @@ bool bumblebee_pilot_frame(entity this)
        else if(PHYS_INPUT_BUTTON_JUMP(this))
                newvel +=  v_up * autocvar_g_vehicle_bumblebee_speed_up;
 
-       vehic.velocity  += newvel * frametime;
+       vehic.velocity  += newvel * dt;
        this.velocity = this.movement  = vehic.velocity;
 
 
@@ -542,7 +542,7 @@ bool bumblebee_pilot_frame(entity this)
                                          autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1,  autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides,  autocvar_g_vehicle_bumblebee_raygun_turnspeed);
 
        if(!forbidWeaponUse(this))
-       if((PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime || autocvar_g_vehicle_bumblebee_raygun == 0))
+       if((PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * PHYS_INPUT_FRAMETIME || autocvar_g_vehicle_bumblebee_raygun == 0))
        {
                vehic.gun3.enemy.realowner = this;
                vehic.gun3.enemy.effects &= ~EF_NODRAW;
@@ -556,8 +556,8 @@ bool bumblebee_pilot_frame(entity this)
                {
                        if(autocvar_g_vehicle_bumblebee_raygun)
                        {
-                               Damage(trace_ent, vehic, this, autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime, DEATH_GENERIC.m_id, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * sys_frametime);
-                               vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * sys_frametime;
+                               Damage(trace_ent, vehic, this, autocvar_g_vehicle_bumblebee_raygun_dps * PHYS_INPUT_FRAMETIME, DEATH_GENERIC.m_id, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * PHYS_INPUT_FRAMETIME);
+                               vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * PHYS_INPUT_FRAMETIME;
                        }
                        else
                        {
@@ -568,25 +568,25 @@ bool bumblebee_pilot_frame(entity this)
                                                if(IS_VEHICLE(trace_ent))
                                                {
                                                        if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health)
-                                                               trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.max_health);
+                                                               trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * dt, trace_ent.tur_head.max_health);
 
                                                        if(autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health);
+                                                               trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health);
                                                }
                                                else if(IS_CLIENT(trace_ent))
                                                {
                                                        if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
+                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax);
 
                                                        if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps)
-                                                               trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * frametime, autocvar_g_vehicle_bumblebee_healgun_amax);
+                                                               trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * dt, autocvar_g_vehicle_bumblebee_healgun_amax);
 
-                                                       trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
+                                                       trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax);
                                                }
                                                else if(IS_TURRET(trace_ent))
                                                {
                                                        if(trace_ent.health  <= trace_ent.max_health && autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health);
+                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health);
                                                        //else ..hmmm what? ammo?
 
                                                        trace_ent.SendFlags |= TNSF_STATUS;
@@ -633,7 +633,7 @@ void bumblebee_land(entity this)
        float hgt;
 
        hgt = vehicle_altitude(this, 512);
-       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
+       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * PHYS_INPUT_FRAMETIME);
        this.angles_x *= 0.95;
        this.angles_z *= 0.95;
 
index b043038921460bb2768aca34238932a49352e566..b675185d49454681ed545b493921aceb421735c1 100644 (file)
@@ -1,9 +1,6 @@
-#ifndef BUMBLEBEE_H
-#define BUMBLEBEE_H
+#pragma once
 
 #ifdef CSQC
 
 void CSQC_BUMBLE_GUN_HUD();
 #endif
-
-#endif
index 367e8b7e41bafb870e4d4ca8eeba2c55ec72cc30..2bccec21a041af2b455231b88dac6861bbdd95f0 100644 (file)
@@ -170,7 +170,7 @@ void racer_fire_rocket_aim(entity player, string tagname, entity trg)
        racer_fire_rocket(player, v, v_forward, trg);
 }
 
-bool racer_frame(entity this)
+bool racer_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
@@ -195,22 +195,22 @@ bool racer_frame(entity this)
                return;
        }
 
-       racer_align4point(vehic, PHYS_INPUT_TIMELENGTH);
+       racer_align4point(vehic, dt);
 
        PHYS_INPUT_BUTTON_ZOOM(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false;
 
        vehic.angles_x *= -1;
 
        // Yaw
-       float ftmp = autocvar_g_vehicle_racer_turnspeed * PHYS_INPUT_TIMELENGTH;
+       float ftmp = autocvar_g_vehicle_racer_turnspeed * dt;
        ftmp = bound(-ftmp, shortangle_f(this.v_angle_y - vehic.angles_y, vehic.angles_y), ftmp);
        vehic.angles_y = anglemods(vehic.angles_y + ftmp);
 
        // Roll
-       vehic.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * PHYS_INPUT_TIMELENGTH;
+       vehic.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * dt;
 
        // Pitch
-       ftmp = autocvar_g_vehicle_racer_pitchspeed  * PHYS_INPUT_TIMELENGTH;
+       ftmp = autocvar_g_vehicle_racer_pitchspeed  * dt;
        ftmp = bound(-ftmp, shortangle_f(this.v_angle_x - vehic.angles_x, vehic.angles_x), ftmp);
        vehic.angles_x = bound(-autocvar_g_vehicle_racer_pitchlimit, anglemods(vehic.angles_x + ftmp), autocvar_g_vehicle_racer_pitchlimit);
 
@@ -256,7 +256,7 @@ bool racer_frame(entity this)
 #endif
 
        // Afterburn
-       if (PHYS_INPUT_BUTTON_JUMP(this) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH))
+       if (PHYS_INPUT_BUTTON_JUMP(this) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * dt))
        {
 #ifdef SVQC
                if(time - vehic.wait > 0.2)
@@ -267,12 +267,12 @@ bool racer_frame(entity this)
 
                if(cont & DPCONTENTS_LIQUIDSMASK)
                {
-                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * PHYS_INPUT_TIMELENGTH;
+                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * dt;
                        df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed);
                }
                else
                {
-                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH;
+                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * dt;
                        df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn);
                }
 
@@ -307,7 +307,7 @@ bool racer_frame(entity this)
                dforce = autocvar_g_vehicle_racer_water_downforce;
 
        df -= v_up * (vlen(vehic.velocity) * dforce);
-       this.movement = vehic.velocity += df * PHYS_INPUT_TIMELENGTH;
+       this.movement = vehic.velocity += df * dt;
 
 #ifdef SVQC
 
@@ -335,8 +335,8 @@ bool racer_frame(entity this)
                {
                        crosshair_trace(this);
 
-                       vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime,
-                                                        (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime,
+                       vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * dt,
+                                                        (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * dt,
                                                         autocvar_g_vehicle_racer_rocket_locked_time);
 
                        vehic.vehicle_last_trace = time + autocvar_g_vehicle_racer_thinkrate;
@@ -382,13 +382,13 @@ bool racer_frame(entity this)
        this.vehicle_reload2 = bound(0, 100 * ((time - vehic.lip) / (vehic.delay - vehic.lip)), 100);
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, dt, false);
 
        if(vehic.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(vehic, vehic.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, false);
+               vehicles_regen(vehic, vehic.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, dt, false);
 
        VEHICLE_UPDATE_PLAYER(this, vehic, health, racer);
        VEHICLE_UPDATE_PLAYER(this, vehic, energy, racer);
index 6b33f8c1a3b7837cba0f340206225ac41f811bf9..510f63ba1fdb2ff9c7020c5b7daac7e19efb2bdc 100644 (file)
@@ -84,7 +84,7 @@ void raptor_land(entity this)
        float hgt;
 
        hgt = vehicle_altitude(this, 512);
-       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
+       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * PHYS_INPUT_FRAMETIME);
        this.angles_x *= 0.95;
        this.angles_z *= 0.95;
 
@@ -153,7 +153,7 @@ void raptor_exit(entity this, int eject)
        this.owner = NULL;
 }
 
-bool raptor_frame(entity this)
+bool raptor_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
@@ -273,7 +273,7 @@ bool raptor_frame(entity this)
        else if (PHYS_INPUT_BUTTON_JUMP(this))
                df +=  v_up * autocvar_g_vehicle_raptor_speed_up;
 
-       vehic.velocity  += df * frametime;
+       vehic.velocity  += df * dt;
        this.velocity = this.movement  = vehic.velocity;
        setorigin(this, vehic.origin + '0 0 32');
 
@@ -331,8 +331,8 @@ bool raptor_frame(entity this)
        else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1)
        {
 
-               vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime,
-                                                        (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * frametime,
+               vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_raptor_cannon_locking_time) * dt,
+                                                        (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * dt,
                                                         autocvar_g_vehicle_raptor_cannon_locked_time);
 
                if(vehic.lock_target != NULL)
@@ -389,13 +389,13 @@ bool raptor_frame(entity this)
        }
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false);
 
        if(vehic.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
+               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, dt, false);
 
        Weapon wep2a = WEP_RAPTOR_BOMB;
        if(!forbidWeaponUse(this))
@@ -458,7 +458,7 @@ bool raptor_frame(entity this)
        PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false;
 }
 
-bool raptor_takeoff(entity this)
+bool raptor_takeoff(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
@@ -476,7 +476,7 @@ bool raptor_takeoff(entity this)
        // Takeoff sequense
        if(vehic.frame < 25)
        {
-               vehic.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime);
+               vehic.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / PHYS_INPUT_FRAMETIME);
                vehic.velocity_z = min(vehic.velocity_z * 1.5, 256);
                vehic.bomb1.gun1.avelocity_y = 90 + ((vehic.frame / 25) * 25000);
                vehic.bomb1.gun2.avelocity_y = -vehic.bomb1.gun1.avelocity_y;
@@ -488,13 +488,13 @@ bool raptor_takeoff(entity this)
                this.PlayerPhysplug = raptor_frame;
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false);
 
        if(vehic.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
+               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, dt, false);
 
 
        vehic.bomb1.alpha = vehic.bomb2.alpha = (time - vehic.lip) / (vehic.delay - vehic.lip);
index f24939ba9d21a2f697c41b6592e9ca970e598aad..fd9a7de790b8f49287d0092eafe1663ec0130da2 100644 (file)
@@ -1,9 +1,6 @@
-#ifndef RAPTOR_H
-#define RAPTOR_H
+#pragma once
 
 const int RSM_FIRST = 1;
 const int RSM_BOMB = 1;
 const int RSM_FLARE = 2;
 const int RSM_LAST = 2;
-
-#endif
index 80d26fc2f7f322756772e4f888330d9420406856..3365266f80fbd33ee9c49683da03e1d590d0a53a 100644 (file)
@@ -68,7 +68,7 @@ float autocvar_g_vehicle_spiderbot_shield_regen_pause = 0.35;
 vector autocvar_g_vehicle_spiderbot_bouncepain = '0 0 0';
 
 .float jump_delay;
-bool spiderbot_frame(entity this)
+bool spiderbot_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
@@ -116,7 +116,7 @@ bool spiderbot_frame(entity this)
        //UpdateAuxiliaryXhair(this, trace_endpos, ('1 0 0' * this.vehicle_reload2) + ('0 1 0' * (1 - this.vehicle_reload2)), 2);
 
        // Rotate head
-       float ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime;
+       float ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * PHYS_INPUT_FRAMETIME;
        ad_y = bound(-ftmp, ad_y, ftmp);
        vehic.tur_head.angles_y = bound(autocvar_g_vehicle_spiderbot_head_turnlimit * -1, vehic.tur_head.angles_y + ad_y, autocvar_g_vehicle_spiderbot_head_turnlimit);
 
@@ -193,9 +193,9 @@ bool spiderbot_frame(entity this)
                        {
                                // Turn Body
                                if(this.movement_x == 0 && this.movement_y != 0)
-                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * sys_frametime;
+                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * PHYS_INPUT_FRAMETIME;
                                else
-                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime;
+                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed * PHYS_INPUT_FRAMETIME;
 
                                ftmp = bound(-ftmp, vehic.tur_head.angles_y, ftmp);
                                vehic.angles_y = anglemods(vehic.angles_y + ftmp);
@@ -221,7 +221,7 @@ bool spiderbot_frame(entity this)
                                        vehic.velocity_z = oldvelz;
                                        float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
                                        if(vehic.velocity_z <= 20) // not while jumping
-                                               vehic.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
+                                               vehic.velocity_z -= g * PHYS_INPUT_FRAMETIME * autocvar_sv_gravity;
                                        if(IS_ONGROUND(vehic))
                                        if(vehic.sound_nexttime < time || vehic.delay != 1)
                                        {
@@ -251,7 +251,7 @@ bool spiderbot_frame(entity this)
                                        vehic.velocity_z = oldvelz;
                                        float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
                                        if(vehic.velocity_z <= 20) // not while jumping
-                                               vehic.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
+                                               vehic.velocity_z -= g * PHYS_INPUT_FRAMETIME * autocvar_sv_gravity;
                                        if(IS_ONGROUND(vehic))
                                        if(vehic.sound_nexttime < time || vehic.delay != 2)
                                        {
@@ -306,16 +306,16 @@ bool spiderbot_frame(entity this)
        else
                vehicles_regen(vehic, vehic.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max,
                                                                                   autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause,
-                                                                                  autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, false);
+                                                                                  autocvar_g_vehicle_spiderbot_minigun_ammo_regen, dt, false);
 
 
        spiderbot_rocket_do(vehic);
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, dt, false);
 
        PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
        //this.vehicle_ammo2 = vehic.tur_head.frame;
index 4c4d05f92653d6d76deb44fde08bc2cb48721f44..7725d2d8fd040ab506048fb91ce787e0a0cbc676 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef VIEWLOC_H
-#define VIEWLOC_H
+#pragma once
 
 .entity viewloc;
 
@@ -11,5 +10,3 @@ void viewloc_SetViewLocation();
 void viewloc_SetTags(entity this);
 
 #endif
-
-#endif
index ea949da50cf6a048ab613f1dd8677a68f58149cf..d4479dac7e2a848f99008a674542a731bfd3b8b8 100644 (file)
@@ -462,7 +462,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                        }
                        else
                        {
-                               LOG_WARNINGF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong\n",
+                               LOG_WARNF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong\n",
                                        this.model);
                                this.movedir = '0 0 0';
                        }
@@ -481,7 +481,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                        }
                        else
                        {
-                               LOG_WARNINGF("weapon model %s does not support the 'shell' tag, will display casings wrong\n",
+                               LOG_WARNF("weapon model %s does not support the 'shell' tag, will display casings wrong\n",
                                        this.model);
                                this.spawnorigin = this.movedir;
                        }
@@ -503,7 +503,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                        }
                        else
                        {
-                               LOG_WARNINGF(
+                               LOG_WARNF(
                                        "weapon model %s does not support the 'handle' tag "
                                        "and neither does the v_ model support the 'shot' tag, "
                                        "will display muzzle flashes TOTALLY wrong\n",
index fa9d64ab8d238666270e193eb967bd4230c19e0a..bff6e6db72318ab1be776870cb038d63650111f2 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef WEAPONS_ALL_H
-#define WEAPONS_ALL_H
+#pragma once
 
 #include <common/command/all.qh>
 #include <common/stats.qh>
@@ -26,15 +25,15 @@ WepSet ReadWepSet();
 
 #include <common/util.qh>
 
-#ifdef SVQC
-#include <server/bot/aim.qh>
-#endif
-
 REGISTRY(Weapons, 72) // Increase as needed. Can be up to 72.
 #define Weapons_from(i) _Weapons_from(i, WEP_Null)
 REGISTER_REGISTRY(Weapons)
 STATIC_INIT(WeaponPickup) { FOREACH(Weapons, true, it.m_pickup = NEW(WeaponPickup, it)); }
 
+#ifdef SVQC
+#include <server/bot/api.qh>
+#endif
+
 .WepSet m_wepset;
 #define WEPSET(id) (WEP_##id.m_wepset)
 #define WepSet_FromWeapon(it) ((it).m_wepset)
@@ -360,5 +359,3 @@ ENUMCLASS_END(WFRAME)
 vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn);
 void CL_WeaponEntity_SetModel(entity this, string name, bool _anim);
 #endif
-
-#endif
index e8307f39d20068151236b4e26d9155c820d61d2f..73d9b938efac9bdcc4917352b3833665a1dae105 100644 (file)
@@ -256,7 +256,7 @@ vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float
                default:
                        error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!");
        }
-               
+
        return '0 0 0';
        /*
         * how to derive falloff functions:
index 4feed9fc549e16a48d44c204435f4a83e21672d7..05eb9d9baf299ff4455f06eabed1adea87294587 100644 (file)
@@ -1,6 +1,5 @@
-#ifndef CALCULATIONS_H
-#define CALCULATIONS_H
+#pragma once
+
 vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor);
 vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle);
 int W_GetGunAlignment(entity player);
-#endif
index d88e18194bf7c37af8991483eb4448f7448e8a50..753147307b3a253e9fb86b5b056bcbdab222c096 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef WEAPONS_CONFIG_H
-#define WEAPONS_CONFIG_H
+#pragma once
 
 #ifdef SVQC
 // ==========================
@@ -37,4 +36,3 @@ string wep_config_queue[MAX_WEP_CONFIG];
                cvar(sprintf("g_balance_%s_%s", #wepname, #name)))) }
 
 #endif
-#endif
index 8b844f13e08e39d8b0585ac94122733b3597e6ee..b2ede3c503596bc19d1a8e3c173a91ac36bc4859 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef WEAPON_H
-#define WEAPON_H
+#pragma once
+
 #include <common/items/item/pickup.qh>
 #include <common/stats.qh>
 
@@ -216,5 +216,3 @@ int GetAmmoStat(.int ammotype);
 
 string W_Sound(string w_snd);
 string W_Model(string w_mdl);
-
-#endif
index bb782643a67bb6ace6a5fd5196c93e2786cf8ac8..643aca2069fbb1bdeb26af7a587902b0c7a3dd1e 100644 (file)
@@ -132,7 +132,7 @@ void W_Crylink_LinkExplode(entity e, entity e2, entity directhitentity)
 
        float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
 
-       RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius), 
+       RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius),
                                NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, directhitentity);
 
        W_Crylink_LinkExplode(e.queuenext, e2, directhitentity);
index 401e4e2059d41d553ccaf3b17fe4e5f6e2977f6a..b950a8f037bc354a964a5370a38a440b35586b31 100644 (file)
@@ -378,7 +378,7 @@ METHOD(PortoLaunch, wr_resetplayer, void(entity thiswep, entity actor))
 #endif
 #ifdef CSQC
 METHOD(PortoLaunch, wr_impacteffect, void(entity this, entity actor)) {
-    LOG_WARNING("Since when does Porto send DamageInfo?\n");
+    LOG_WARN("Since when does Porto send DamageInfo?\n");
 }
 #endif
 #endif
index f143bd1cc2e0fe7703d7d3ea1364ae36e40c4d50..f8f41f1b4a4197305feec1905f55223698e0b59f 100644 (file)
@@ -718,7 +718,6 @@ METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, .entity weaponent
     else if(fire & 2)
     {
         //if(actor.clip_load >= 0) // we are not currently reloading
-        if(!actor.crouch) // no crouchmelee please
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(shockwave, melee_refire)))
         {
             // attempt forcing playback of the anim by switching to another anim (that we never play) here...
index b03c6fb4da31ae397691e2bf66981dd6383975e7..d8de5ee0f674c7a9fa51eb4753f603f74011b354 100644 (file)
@@ -270,7 +270,6 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit
         }
     }
     if(actor.clip_load >= 0) // we are not currently reloading
-    if(!actor.crouch) // no crouchmelee please
     if(WEP_CVAR(shotgun, secondary) == 1)
     if(((fire & 1) && actor.(thiswep.ammo_field) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (fire & 2))
     if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(shotgun, refire)))
index 6d8182aab8cc7a357dcefe997ef7e22ffa233f57..1fba375eb75af36f14a57d9b6a4c9d055f8f0211 100644 (file)
@@ -643,7 +643,7 @@ PRECACHE(Tuba)
        Tuba_PitchStep = autocvar_g_balance_tuba_pitchstep;
        if (Tuba_PitchStep) {
                if (!checkextension("DP_SND_SOUND7_WIP2") && !checkextension("DP_SND_SOUND7")) {
-                       LOG_WARNING("requested pitch shifting, but not supported by this engine build");
+                       LOG_WARN("requested pitch shifting, but not supported by this engine build");
                        Tuba_PitchStep = 0;
                }
        }
index f424d1a5de4dcfec5323851cabb41a712eed6adb..29d797585efbe10117995db27925b739109abae6 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CSPROGSDEFS_H
-#define CSPROGSDEFS_H
+#pragma once
 
 #pragma noref 1
 
@@ -44,5 +43,3 @@
 #define use use1
 .void(entity this, entity actor, entity trigger) use;
 #define touch move_touch
-
-#endif
index ff9214844b3e5c181878b7ef31c40621e85a59e3..2ed0f9063889af40f2270740e069f47d29f62789 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef DPEXTENSIONS_H
-#define DPEXTENSIONS_H
+#pragma once
 
 #pragma noref 1
 
@@ -64,5 +63,3 @@ int() _buf_create = #460;
 #define buf_create _buf_create
 
 #pragma noref 0
-
-#endif
index f4c49f143b5c119cd3dbb1a7c434a53d701bf681..0267be882462d75055d6bf8bf6ed5b153bc7493c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef KEYCODES_H
-#define KEYCODES_H
+#pragma once
 
 #pragma noref 1
 
@@ -10,5 +9,3 @@
 //#undef float
 
 #pragma noref 0
-
-#endif
index 939a86adb359c502e8ddc767da306077ec6256f7..036d87ff2fa8e7a213932d69fc66e84e30700a94 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MENUDEFS_H
-#define MENUDEFS_H
+#pragma once
 
 #pragma noref 1
 
@@ -43,5 +42,3 @@ int() _buf_create = #440;
 bool(entity ent) wasfreed = #353;
 
 #pragma noref 0
-
-#endif
index e2668a54b7cc5173d2aad78b1b4681f0a54fbc30..ccdf9bc0c4293353a7e8392b059831e313474958 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef PROGSDEFS_H
-#define PROGSDEFS_H
+#pragma once
 
 #pragma noref 1
 
@@ -30,5 +29,3 @@ MACRO_END
 
 #define use use1
 .void(entity this, entity actor, entity trigger) use;
-
-#endif
index 206c80d96526f4001200bd8b6d0a56d9a05e3fd6..f90afcb529f40f7f2a75bed0e6cf7a0fb043f1e8 100644 (file)
@@ -13,13 +13,13 @@ void sys_phys_fix(entity this, float dt)
        PM_ClientMovement_UpdateStatus(this);
 }
 
-bool sys_phys_override(entity this)
+bool sys_phys_override(entity this, float dt)
 {
        // no vehicle prediction
        return hud != HUD_NORMAL;
 }
 
-void sys_phys_monitor(entity this) {}
+void sys_phys_monitor(entity this, float dt) {}
 
 void sys_phys_ai(entity this) {}
 
index f5052b376f89f4072ca33ef968b3e4bba3d66f56..c0a47e39b2a4aff558c3c53af50d49694d6c01f7 100644 (file)
@@ -15,7 +15,7 @@ void sys_phys_update(entity this, float dt)
        sys_in_update(this, dt);
 
        sys_phys_fix(this, dt);
-       if (sys_phys_override(this)) { return; } sys_phys_monitor(this);
+       if (sys_phys_override(this, dt)) { return; } sys_phys_monitor(this, dt);
 
        this.buttons_old = PHYS_INPUT_BUTTON_MASK(this);
        this.movement_old = this.movement;
@@ -41,7 +41,7 @@ void sys_phys_update(entity this, float dt)
 
 // conveyors: first fix velocity
        if (this.conveyor.state) { this.velocity -= this.conveyor.movedir; }
-       MUTATOR_CALLHOOK(PlayerPhysics, this);
+       MUTATOR_CALLHOOK(PlayerPhysics, this, dt);
 
        if (!IS_PLAYER(this)) {
                sys_phys_spectator_control(this);
@@ -84,7 +84,7 @@ void sys_phys_update(entity this, float dt)
                        PHYS_TELEPORT_TIME(this) = 0;
                        PHYS_WATERJUMP_TIME(this) = 0;
                }
-       } else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod)) {
+       } else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod, dt)) {
                // handled
        } else if (this.move_movetype == MOVETYPE_NOCLIP
            || this.move_movetype == MOVETYPE_FLY
@@ -115,7 +115,7 @@ void sys_phys_update(entity this, float dt)
                this.com_phys_ladder = false;
                this.com_phys_gravity = '0 0 0';
        } else if (ITEMS_STAT(this) & IT_USING_JETPACK) {
-               PM_jetpack(this, maxspeed_mod);
+               PM_jetpack(this, maxspeed_mod, dt);
        } else if (IS_ONGROUND(this)) {
                if (!WAS_ONGROUND(this)) {
                        emit(phys_land, this);
@@ -263,7 +263,7 @@ void sys_phys_simulate(entity this, float dt)
                                airaccel += (this.com_phys_acc_rate_air_stop - airaccel) * max(0, -(curdir * wishdir));
                        }
                        // note that for straight forward jumping:
-                       // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+                       // step = accel * dt * wishspeed0;
                        // accel  = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
                        // -->
                        // dv/dt = accel * maxspeed (when slow)
@@ -288,15 +288,15 @@ void sys_phys_simulate(entity this, float dt)
                        // !CPM
 
                        if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0) {
-                               PM_AirAccelerate(this, wishdir, wishspeed2);
+                               PM_AirAccelerate(this, dt, wishdir, wishspeed2);
                        } else {
                                float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0;
-                               PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw,
+                               PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw,
                                        PHYS_AIRACCEL_QW_STRETCHFACTOR(this), sidefric, PHYS_AIRSPEEDLIMIT_NONQW(this));
                        }
 
                        if (PHYS_AIRCONTROL(this)) {
-                               CPM_PM_Aircontrol(this, wishdir, wishspeed2);
+                               CPM_PM_Aircontrol(this, dt, wishdir, wishspeed2);
                        }
                }
        } else {
@@ -338,7 +338,7 @@ void sys_phys_simulate(entity this, float dt)
                                }
                        } else {
                                // water acceleration
-                               PM_Accelerate(this, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
+                               PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
                        }
                        return;
                }
@@ -391,7 +391,7 @@ void sys_phys_simulate(entity this, float dt)
                }
 
                if (IS_CSQC ? PHYS_WATERJUMP_TIME(this) <= 0 : time >= PHYS_TELEPORT_TIME(this)) {
-                       PM_Accelerate(this, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
+                       PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
                }
        }
 }
index 16c996adee6ccf97ae82f3400a0fd71eeb14fb7f..cef9916f3d60e3412bb761fbc746afed9085db86 100644 (file)
@@ -3,8 +3,8 @@
 SYSTEM(phys, 30, 10);
 
 void sys_phys_fix(entity this, float dt);
-bool sys_phys_override(entity this);
-void sys_phys_monitor(entity this);
+bool sys_phys_override(entity this, float dt);
+void sys_phys_monitor(entity this, float dt);
 void sys_phys_pregame_hold(entity this);
 void sys_phys_ai(entity this);
 void sys_phys_spectator_control(entity this);
index fe053a22eda17f17443e09ff2a5a34f415bcd49e..904ebba3fe5ccbea2b1f1aefaa55f502dd67450b 100644 (file)
@@ -6,15 +6,15 @@ void sys_phys_fix(entity this, float dt)
        Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
 }
 
-bool sys_phys_override(entity this)
+bool sys_phys_override(entity this, float dt)
 {
        int buttons = PHYS_INPUT_BUTTON_MASK(this);
        if (PM_check_specialcommand(this, buttons)) { return true; }
-       if (this.PlayerPhysplug && this.PlayerPhysplug(this)) { return true; }
+       if (this.PlayerPhysplug && this.PlayerPhysplug(this, dt)) { return true; }
        return false;
 }
 
-void sys_phys_monitor(entity this)
+void sys_phys_monitor(entity this, float dt)
 {
        int buttons = PHYS_INPUT_BUTTON_MASK(this);
        anticheat_physics(this);
@@ -24,7 +24,7 @@ void sys_phys_monitor(entity this)
                    || this.v_angle != this.v_angle_old) { this.parm_idlesince = time; }
        }
        PM_check_nickspam(this);
-       PM_check_punch(this);
+       PM_check_punch(this, dt);
 }
 
 void sys_phys_ai(entity this)
index f52c9a3c735437ef6195f8c1499baceba51842af..83a0ebf34a01bb06755ba9021eeb0e2508340285 100644 (file)
@@ -55,7 +55,7 @@
 #else
        #define TC(T, sym) MACRO_BEGIN \
                if (!is_##T(sym)) { \
-                       LOG_WARNINGF("Type check failed: " #sym " :: " #T); \
+                       LOG_WARNF("Type check failed: " #sym " :: " #T); \
                        isnt_##T(sym); \
                } \
        MACRO_END
@@ -132,12 +132,12 @@ void make_safe_for_remove(entity this);
 #endif
 
 #define objerror(this, msg) MACRO_BEGIN { \
-       LOG_WARNING("======OBJECT ERROR======"); \
+       LOG_WARN("======OBJECT ERROR======"); \
        entity _e = (this); \
        eprint(_e); \
        objerror_safe(_e); \
        delete(_e); \
-       LOG_WARNINGF("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information", PROGNAME, __FUNC__, msg); \
+       LOG_WARNF("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information", PROGNAME, __FUNC__, msg); \
 } MACRO_END
 
 #ifdef MENUQC
index 3b20972e25db6e1706aa4a59b944d0a65e71dfaa..b5d9f8bd361fff927dc54f70a5632300a64d4360 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_CL_MODEL_H
-#define LIB_CSQCMODEL_CL_MODEL_H
+#pragma once
 
 #include "common.qh"
 
@@ -50,4 +49,3 @@ void CSQCModel_InterpolateAnimation_1To2_Note(entity this, int sf, float set_tim
 void CSQCModel_InterpolateAnimation_2To4_Do(entity this);
 void CSQCModel_InterpolateAnimation_1To2_Do(entity this);
 // will overwrite lerpfrac, lerpfrac3, lerpfrac4, and possibly clear frame*time if they are undisplayed according to lerpfracs
-#endif
index 513875aa9889f05067373a4a13ea70ae417ba311..297e2e69d648c1826289fa21c95e389f5a61b5f2 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_CL_PLAYER_H
-#define LIB_CSQCMODEL_CL_PLAYER_H
+#pragma once
 
 bool autocvar_cl_movement = true;
 
@@ -42,4 +41,3 @@ void CSQCPlayer_SetCamera();
 float CSQCPlayer_PreUpdate(entity this);
 float CSQCPlayer_PostUpdate(entity this);
 float CSQCPlayer_IsLocalPlayer(entity this);
-#endif
index a700f3af2b9bda88665508f3456a0bd066c6f613..37b88997ef84bf7d456e34575fc0859d79f01442 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_COMMON_H
-#define LIB_CSQCMODEL_COMMON_H
+#pragma once
 
 #include <common/csqcmodel_settings.qh>
 
@@ -94,4 +93,3 @@ const int CSQCMODEL_PROPERTY_SIZE = BIT(15);
 #else
 #define ALLPROPERTIES ALLPROPERTIES_COMMON
 #endif
-#endif
index 2f4828053b0b98e73ec890398374aa3cd86618b6..3ea385cbcd8b5e4918c1f88ed8b3c6eaed9508d4 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_INTERPOLATE_H
-#define LIB_CSQCMODEL_INTERPOLATE_H
+#pragma once
 
 .int iflags;
 const int IFLAG_VELOCITY = BIT(0);
@@ -49,4 +48,3 @@ void InterpolateOrigin_Do(entity this);
 
 // in case we interpolate that:
 .vector v_angle;
-#endif
index 85b8a4fcbb80580059728230af0915e0096569b5..359cf1738f01666e5f9902a7cad17980bf14d194 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_CSQCMODEL_SETTINGS_H
-#define LIB_CSQCMODEL_SETTINGS_H
+#pragma once
 // define this if svqc code wants to use .frame2 and .lerpfrac
 //#define CSQCMODEL_HAVE_TWO_FRAMES
 
@@ -24,4 +23,3 @@
 //vector PL_CROUCH_MIN  = ...;
 //vector PL_CROUCH_MAX  = ...;
 //vector PL_CROUCH_VIEW_OFS  = ...;
-#endif
index 3e043d3532130748d44e5900ec6c253b435fd013..e963f12a1bf6ac79d9d3e45bf941e10e92927fef 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_SV_MODEL_H
-#define LIB_CSQCMODEL_SV_MODEL_H
+#pragma once
 
 #include "common.qh"
 
@@ -41,4 +40,3 @@ void CSQCModel_UnlinkEntity(entity e);
 #undef CSQCMODEL_PROPERTY
 #undef CSQCMODEL_ENDIF
 #undef CSQCMODEL_IF
-#endif
index 10de3e9630e8c55ba1ba86455ce4a530cd908e85..014f3a169ea583317532fe9b2d1e7fcec0702286 100644 (file)
@@ -204,7 +204,7 @@ void IL_INIT(IntrusiveList this)
                        return;
                }
        }
-       LOG_WARNINGF("IntrusiveList overflow");
+       LOG_WARNF("IntrusiveList overflow");
 }
 
 void IL_DTOR(IntrusiveList this)
index a8874adbd4630d69a03458346404391ac23bd2a6..a25dd0f48e6e46cf8cd4f3323698ea5801074af5 100644 (file)
@@ -53,9 +53,9 @@ string(string...) strcat0n = #115;
 #define  LOG_SEVEREF(...) _LOG_SEVERE(sprintf(__VA_ARGS__))
 #define _LOG_SEVERE(s) _LOG(backtrace, "^1SEVERE", s)
 
-#define  LOG_WARNING(...) _LOG_WARNING(strcat0n(__VA_ARGS__))
-#define  LOG_WARNINGF(...) _LOG_WARNING(sprintf(__VA_ARGS__))
-#define _LOG_WARNING(s) _LOG(print, "^3WARNING", s)
+#define  LOG_WARN(...) _LOG_WARN(strcat0n(__VA_ARGS__))
+#define  LOG_WARNF(...) _LOG_WARN(sprintf(__VA_ARGS__))
+#define _LOG_WARN(s) _LOG(print, "^3WARNING", s)
 
 #define  LOG_INFO(...) _LOG_INFO(strcat0n(__VA_ARGS__))
 #define  LOG_INFOF(...) _LOG_INFO(sprintf(__VA_ARGS__))
index 57b8f8c67939a113fee43e48d3f00b65673eac67..8a796c02dd5c5c6095a8b92bde47be3b713de241 100644 (file)
@@ -9,7 +9,7 @@ void db_save(int db, string filename)
        int fh = fopen(filename, FILE_WRITE);
        if (fh < 0)
        {
-               LOG_WARNINGF("^1Can't write DB to %s\n", filename);
+               LOG_WARNF("^1Can't write DB to %s\n", filename);
                return;
        }
        fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
index db51bbffa19e663386737cb059fe40bc4ede81e9..c399c2aa6cf86e2d9a9ba910667c8cd1c926ba43 100644 (file)
@@ -46,7 +46,7 @@ void MX_Messages_(entity fh, entity pass, int status)
 {
     switch (status) {
         default: {
-            LOG_WARNINGF("status: %d", status);
+            LOG_WARNF("status: %d", status);
             break;
         }
         case URL_READY_CLOSED: break;
@@ -90,7 +90,7 @@ void MX_Sync_(entity fh, entity pass, int status)
 {
     switch (status) {
         default: {
-            LOG_WARNINGF("status: %d", status);
+            LOG_WARNF("status: %d", status);
             break;
         }
         case URL_READY_CLOSED: break;
index 7cb9bb8967959214bde61b14b7788358b2446b07..4c9d9e6e188ae5c5c53639b045441f31d34bb84d 100644 (file)
@@ -161,8 +161,8 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
                }
                g_buf_i--;
                int expected = strlen(buf);
-               if (g_buf_i > expected) LOG_WARNINGF("Underflow: %d", g_buf_i - expected);
-               if (g_buf_i < expected) LOG_WARNINGF("Overrflow: %d", expected - g_buf_i);
+               if (g_buf_i > expected) LOG_WARNF("Underflow: %d", g_buf_i - expected);
+               if (g_buf_i < expected) LOG_WARNF("Overrflow: %d", expected - g_buf_i);
        }
 
 #endif
index d3228d47c864b9fdcce2d64c54570805f4d71a0a..927410cde115de908e5357781ba789c33378716b 100644 (file)
@@ -12,8 +12,7 @@
 /* without even the implied warranty of merchantability or fitness for a      */
 /* particular purpose.                                                        */
 /*                                                                            */
-#ifndef P99_H_
-#define P99_H_
+#pragma once
 
 #define P99_MAX_NUMBER 16
 #define P00_ARG(                            \
@@ -83,5 +82,3 @@
     P99_PASTE2(P99_PASTE3(_1, _2, _3), _4)
 #define P99_PASTE5(_1, _2, _3, _4, _5) \
     P99_PASTE2(P99_PASTE4(_1, _2, _3, _4), _5)
-
-#endif /* !P99_H_ */
index 7acf965a85b113b514298b8303bf81de4eee931f..31e20f22ff51ec0732179fe057faabbc59697147 100644 (file)
@@ -62,7 +62,7 @@ noref bool require_spawnfunc_prefix;
                                        if (fieldname == "") continue; \
                                        FIELDS_COMMON(_spawnfunc_check) \
                                        whitelist(_spawnfunc_check) \
-                                       LOG_WARNINGF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue.\n"), #id, fieldname, value); \
+                                       LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue.\n"), #id, fieldname, value); \
                                } \
                                this.spawnfunc_checked = true; \
                        } \
index d05ae28cd903b76edc4770204b142d5f8689c589..05bd63d3c2ed5950b5c3a06e93e6acd62034d485 100644 (file)
@@ -15,7 +15,7 @@
 #define SUCCEED() (TEST_ok = true)
 
 /** Add a failure, but continue */
-#define ADD_FAILURE(msg) MACRO_BEGIN { ++TEST_failed; LOG_WARNINGF(msg); } MACRO_END
+#define ADD_FAILURE(msg) MACRO_BEGIN { ++TEST_failed; LOG_WARNF(msg); } MACRO_END
 
 /** Add a failure and return */
 #define FAIL(msg) _TEST_ASSERT(ADD_FAILURE(msg))
@@ -87,7 +87,7 @@ int TEST_failed;
 
 #define EXPECT_NO_FATAL_FAILURE_(statement, then) \
        EXPECT_NO_FATAL_FAILURE__(statement, { \
-               LOG_WARNINGF( \
+               LOG_WARNF( \
                        "  Actual: %d fatal failures\n" \
                        "Expected: no fatal failures\n", \
                        TEST_fatal - TEST_prevfatal \
index 0a10843c0087872de9ed0a0046747331d438543f..b287651a10a4fb868acdfeca18d2c0dfaf77b4d0 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_ANGLETRANSFORM_H
-#define LIB_WARPZONE_ANGLETRANSFORM_H
+#pragma once
 
 #ifndef POSITIVE_PITCH_IS_DOWN
 #define POSITIVE_PITCH_IS_DOWN 1
@@ -42,4 +41,3 @@ vector AnglesTransform_ToVAngles(vector v);
 // transformed = original * transform + postshift
 vector AnglesTransform_Multiply_GetPostShift(vector sf0, vector st0, vector t1, vector st1);
 vector AnglesTransform_PrePostShift_GetPostShift(vector sf, vector t, vector st);
-#endif
index 8f3e643b5c0eabae6e8d30817a40ccebc071a7df..47ff24136839d23144caa37af2c9f2265e367a57 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_CLIENT_H
-#define LIB_WARPZONE_CLIENT_H
+#pragma once
 
 void WarpZone_FixPMove();
 void WarpZone_FixView();
@@ -8,4 +7,3 @@ void WarpZone_Shutdown();
 
 vector warpzone_save_view_origin;
 vector warpzone_save_view_angles;
-#endif
index 6669ae1aff4d300c7cde3fc0f9bfc34ce3feb4a3..4bbbb853b00bad0266f2482cd5ef7efcccd738d3 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_COMMON_H
-#define LIB_WARPZONE_COMMON_H
+#pragma once
 
 // uncomment this if your mod uses the roll angle in fixangle
 // #define KEEP_ROLL
@@ -113,4 +112,3 @@ void WarpZoneLib_ExactTrigger_Init(entity this);
 // WARNING: this kills the trace globals
 #define EXACTTRIGGER_TOUCH(e,t) if(WarpZoneLib_ExactTrigger_Touch((e), (t))) return
 #define EXACTTRIGGER_INIT  WarpZoneLib_ExactTrigger_Init(this)
-#endif
index c3de3838dde28cb70ed0d9e915751554436ccd49..2b35c92f8457e6fbb00ba0f05610598ea10faa5e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_MATHLIB_H
-#define LIB_WARPZONE_MATHLIB_H
+#pragma once
 
 // <math.h>
 
@@ -114,5 +113,3 @@ const float M_2_PI     = 0.63661977236758134308;  /* 2/pi */
 const float M_2_SQRTPI = 1.12837916709551257390;  /* 2/sqrt(pi) */
 const float M_SQRT2    = 1.41421356237309504880;  /* sqrt(2) */
 const float M_SQRT1_2  = 0.70710678118654752440;  /* 1/sqrt(2) */
-
-#endif
index 0ec67e4d38125b4eefe5f147a2c10ed4f5ee65e8..b0c583d2dc7bf31728f28e0d53de23219f689c22 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_SERVER_H
-#define LIB_WARPZONE_SERVER_H
+#pragma once
 
 #ifdef SVQC
 void WarpZone_StartFrame();
@@ -17,5 +16,3 @@ void WarpZone_PlayerPhysics_FixVAngle(entity this);
 
 void WarpZone_PostInitialize_Callback();
 #endif
-
-#endif
index 39d1d24699caab93e8d209c4489844f7cbd6fb5c..b73289927b474878d93081425a75876f7b2ae62d 100644 (file)
@@ -1,9 +1,7 @@
-#ifndef LIB_WARPZONE_UTIL_SERVER_H
-#define LIB_WARPZONE_UTIL_SERVER_H
+#pragma once
 
 float WarpZoneLib_MoveOutOfSolid(entity e);
 float WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher);
 #ifdef SVQC
 void WarpZoneLib_ExactTrigger_Init(entity this);
 #endif
-#endif
index 283cca0c1b98b1d0c5dfc61917c00c946175c43f..2f8df96b6bfb57d773d6e237a6ad9f542c63f3a4 100644 (file)
@@ -1 +1,3 @@
+#include "all.qh"
+
 #include <common/command/all.qc>
diff --git a/qcsrc/menu/command/all.qh b/qcsrc/menu/command/all.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index b0c4ec880c1b2da9c3bb014270f4e6fc5c2b8e80..3039b4a7d4222bda22044f2dc60c8aee5a89c7ad 100644 (file)
@@ -1,3 +1,5 @@
+#include "matrix.qh"
+
 var void MX_Handle(int buf, string ancestor)
 {
     string type = json_get(buf, strcat(ancestor, ".type"));
diff --git a/qcsrc/menu/matrix.qh b/qcsrc/menu/matrix.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 9bc82ebc28754bb8bf5ebbb87c697a3d50b7f813..8fcb975eab6764c5b43d44061102d4ff0132b7b3 100644 (file)
@@ -39,7 +39,7 @@ void XonoticMapInfoDialog_loadMapInfo(entity me, int i, entity mlb)
        {
                entity e;
                e = me.(typeLabels[i]);
-               e.disabled = !(MapInfo_Map_supportedGametypes & GameType_GetID(i));
+               e.disabled = !(MapInfo_Map_supportedGametypes & GameType_GetID(i).m_flags);
        }
 
        MapInfo_ClearTemps();
index d5532bcd92319757be8e3eea5b9f6ce7a13f39e8..2f37eb7e2b665c260d6e5174797eaaec3d58cda4 100644 (file)
@@ -10,7 +10,7 @@
 void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
 {
        bool pure_available;
-       float m, pure_violations, freeslots, j, numh, maxp, numb, sflags;
+       float m, pure_violations, freeslots, numh, maxp, numb, sflags;
        string s, typestr, versionstr, k, v, modname;
 
        // ====================================
@@ -96,7 +96,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
        freeslots = -1;
        sflags = -1;
        modname = "";
-       for(j = 2; j < m; ++j)
+       for(int j = 2; j < m; ++j)
        {
                if(argv(j) == "")
                        break;
@@ -124,7 +124,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
        if(s != "data")
                modname = sprintf("%s (%s)", modname, s);
 
-       j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type
+       Gametype j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type
        if(j) { typestr = MapInfo_Type_ToText(j); } // only set it if we actually found it
 
        me.currentServerType = strzone(typestr);
index 193f8336838acb793ed261da5566bd9cb2bddf8c..254f1c0012563edf052503b74a6a7cc819c75dc9 100644 (file)
@@ -30,8 +30,7 @@ void XonoticGametypeList_setSelected(entity me, float i)
 }
 void XonoticGametypeList_loadCvars(entity me)
 {
-       float t;
-       t = MapInfo_CurrentGametype();
+       Gametype t = MapInfo_CurrentGametype();
        float i;
        for(i = 0; i < GameType_GetCount(); ++i)
                if(t == GameType_GetID(i))
@@ -49,7 +48,7 @@ void XonoticGametypeList_loadCvars(entity me)
 }
 void XonoticGametypeList_saveCvars(entity me)
 {
-       int t = GameType_GetID(me.selectedItem);
+       Gametype t = GameType_GetID(me.selectedItem);
        if (t == MapInfo_CurrentGametype()) {
                return;
        }
index 4b41f5bd3be6eb514600614e290c834df62f3a49..8feee96ed7a05a7ac74ef8a97a8eae709a503c2e 100644 (file)
@@ -172,9 +172,8 @@ void XonoticMapList_refilter(entity me)
 {
        float i, j, n;
        string s;
-       float gt, f;
-       gt = MapInfo_CurrentGametype();
-       f = MapInfo_CurrentFeatures();
+       Gametype gt = MapInfo_CurrentGametype();
+       int f = MapInfo_CurrentFeatures();
        MapInfo_FilterGametype(gt, f, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
        if (me.stringFilter)
                MapInfo_FilterString(me.stringFilter);
@@ -256,7 +255,7 @@ void MapList_Add_All(entity btn, entity me)
 {
        float i;
        string s;
-       MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all
+       _MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all
        s = "";
        for(i = 0; i < MapInfo_count; ++i)
                s = strcat(s, " ", MapInfo_BSPName_ByID(i));
index de9d7e28a314d5fb13072cf7dd8b2aa48a95fdbe..452e17cd882fdc7ee6b7c35bc0e5d08d5dc172db 100644 (file)
@@ -23,7 +23,7 @@ CLASS(XonoticMapList, XonoticListBox)
        ATTRIB(XonoticMapList, realUpperMargin1, float, 0)
        ATTRIB(XonoticMapList, realUpperMargin2, float, 0)
 
-       ATTRIB(XonoticMapList, lastGametype, float, 0)
+       ATTRIB(XonoticMapList, lastGametype, entity, NULL)
        ATTRIB(XonoticMapList, lastFeatures, float, 0)
 
        ATTRIB(XonoticMapList, origin, vector, '0 0 0')
index 46506dcfa0f71a8627a99b6cecd36037ce2fb8fa..9c0623cfdfc32c8c1c1bc1f5c069de8635cbf016 100644 (file)
@@ -630,10 +630,8 @@ void ServerList_PlayerSort_Click(entity btn, entity me)
 }
 void ServerList_TypeSort_Click(entity btn, entity me)
 {
-       string s, t;
-       float i, m;
-       s = me.filterString;
-       m = strstrofs(s, ":", 0);
+       string s = me.filterString;
+       int m = strstrofs(s, ":", 0);
        if(m >= 0)
        {
                s = substring(s, 0, m);
@@ -643,30 +641,23 @@ void ServerList_TypeSort_Click(entity btn, entity me)
        else
                s = "";
 
-       for(i = 1; ; i *= 2) // 20 modes ought to be enough for anyone
-       {
-               t = MapInfo_Type_ToString(i);
-               if(i > 1)
-                       if(t == "") // it repeats (default case)
-                       {
-                               // no type was found
-                               // choose the first one
-                               s = MapInfo_Type_ToString(1);
-                               break;
-                       }
-               if(s == t)
-               {
-                       // the type was found
-                       // choose the next one
-                       s = MapInfo_Type_ToString(i * 2);
-                       if(s == "")
-                               s = MapInfo_Type_ToString(1);
-                       break;
-               }
+       Gametype first = NULL; FOREACH(Gametypes, !first, first = it; break);
+       bool flag = false;
+       FOREACH(Gametypes, s == MapInfo_Type_ToString(it), {
+               // the type was found
+               // choose the next one
+               flag = true;
+               s = MapInfo_Type_ToString(Gametypes_from(it.m_id + 1));
+               if (s == "") s = MapInfo_Type_ToString(first);
+               break;
+       });
+       if (!flag) {
+               // no type was found
+               // choose the first one
+               s = MapInfo_Type_ToString(first);
        }
 
-       if(s != "")
-               s = strcat(s, ":");
+       if(s != "") s = strcat(s, ":");
        s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
 
        me.controlledTextbox.setText(me.controlledTextbox, s);
index a0018b99a905f7af574d043c37f8de75711c3021..70d8071f7abff5e54aba78a8008cea33ae7e4795 100644 (file)
@@ -515,7 +515,7 @@ float preMenuInit()
 
        MapInfo_Cache_Create();
        MapInfo_Enumerate();
-       if(!MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
+       if(!_MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
        {
                draw_reset_cropped();
 
@@ -690,17 +690,17 @@ float updateCompression()
        /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
        /**/
 
-int GameType_GetID(int cnt)
+Gametype GameType_GetID(int cnt)
 {
        int i = 0;
 
-       #define GAMETYPE(id) { if (i++ == cnt) return id; }
+       #define GAMETYPE(it) { if (i++ == cnt) return it; }
        GAMETYPES
        #undef GAMETYPE
 
        unused_float = i;
 
-       return 0;
+       return NULL;
 }
 
 int GameType_GetCount()
@@ -716,22 +716,14 @@ int GameType_GetCount()
 
 string GameType_GetName(int cnt)
 {
-       int i = GameType_GetID(cnt);
-
-       if(i)
-               return MapInfo_Type_ToText(i);
-
-       return "";
+       Gametype i = GameType_GetID(cnt);
+       return i ? MapInfo_Type_ToText(i) : "";
 }
 
 string GameType_GetIcon(int cnt)
 {
-       int i = GameType_GetID(cnt);
-
-       if(i)
-               return strcat("gametype_", MapInfo_Type_ToString(i));
-
-       return "";
+       Gametype i = GameType_GetID(cnt);
+       return i ? strcat("gametype_", MapInfo_Type_ToString(i)) : "";
 }
 
 .void(entity) TR;
index d433352cf5a1734e70429d8ed33e0faf0d11fc9f..c7e7c0cd70b96d138e8f7c09fdf70a7d61693343 100644 (file)
@@ -29,7 +29,7 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data);
 
 // game type list box stuff (does not NEED to contain all game types, other
 // types stay available via console)
-int GameType_GetID(int cnt);
+entity GameType_GetID(int cnt);
 string GameType_GetName(int cnt);
 string GameType_GetIcon(int cnt);
 //string GameType_GetTeams(float cnt);
index 188cc713137adff0ff81112ecc795719b4abe612..a8d45bd5221ce1cde56fa0e9c8d3dd98fc639898 100644 (file)
@@ -5,61 +5,9 @@ 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")
-#define autocvar_bot_suffix cvar_string("bot_suffix")
-bool autocvar_bot_usemodelnames;
 int autocvar_bot_vs_human;
-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")
 float autocvar_ekg;
@@ -172,7 +120,6 @@ float autocvar_g_chat_flood_spl_tell;
 int autocvar_g_chat_nospectators;
 bool autocvar_g_chat_teamcolors;
 bool autocvar_g_chat_tellprivacy;
-bool autocvar_g_debug_bot_commands;
 bool autocvar_g_forced_respawn;
 string autocvar_g_forced_team_blue;
 string autocvar_g_forced_team_otherwise;
@@ -296,7 +243,6 @@ 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;
 #define autocvar_g_weapon_stay cvar("g_weapon_stay")
 bool autocvar_g_weapon_throwable;
@@ -321,7 +267,6 @@ 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;
@@ -429,7 +374,6 @@ 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;
index 787939110d9951f662439e926cd936a38b57dfb8..8f0672e2623074a3db074eb081ff4bb1c8617ca3 100644 (file)
@@ -1,6 +1,2 @@
 // generated file; do not modify
-#include <server/bot/aim.qc>
-#include <server/bot/bot.qc>
-#include <server/bot/navigation.qc>
-#include <server/bot/scripting.qc>
-#include <server/bot/waypoints.qc>
+#include <server/bot/api.qc>
index 802d39187818db60fd811789a83c585b1bcaae23..33f0b022911f943822c9b9a9c1ebc27f6e126c89 100644 (file)
@@ -1,6 +1,2 @@
 // generated file; do not modify
-#include <server/bot/aim.qh>
-#include <server/bot/bot.qh>
-#include <server/bot/navigation.qh>
-#include <server/bot/scripting.qh>
-#include <server/bot/waypoints.qh>
+#include <server/bot/api.qh>
diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc
deleted file mode 100644 (file)
index b867b5f..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-#include "aim.qh"
-
-#include "bot.qh"
-
-#include <common/physics/player.qh>
-#include <common/state.qh>
-
-#include "../weapons/weaponsystem.qh"
-
-#include "../mutators/all.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 = new(tracetossent);
-       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 = new(tracetossfaketarget);
-       tracetossfaketarget.solid = savesolid;
-       set_movetype(tracetossfaketarget, targ.move_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;
-                       set_movetype(tracetossfaketarget, 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;
-       set_movetype(tracetossfaketarget, 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(entity this)
-{
-       if (this.lag1_time) if (time > this.lag1_time) {this.lag_func(this, this.lag1_time, this.lag1_float1, this.lag1_float2, this.lag1_entity1, this.lag1_vec1, this.lag1_vec2, this.lag1_vec3, this.lag1_vec4);this.lag1_time = 0;}
-       if (this.lag2_time) if (time > this.lag2_time) {this.lag_func(this, this.lag2_time, this.lag2_float1, this.lag2_float2, this.lag2_entity1, this.lag2_vec1, this.lag2_vec2, this.lag2_vec3, this.lag2_vec4);this.lag2_time = 0;}
-       if (this.lag3_time) if (time > this.lag3_time) {this.lag_func(this, this.lag3_time, this.lag3_float1, this.lag3_float2, this.lag3_entity1, this.lag3_vec1, this.lag3_vec2, this.lag3_vec3, this.lag3_vec4);this.lag3_time = 0;}
-       if (this.lag4_time) if (time > this.lag4_time) {this.lag_func(this, this.lag4_time, this.lag4_float1, this.lag4_float2, this.lag4_entity1, this.lag4_vec1, this.lag4_vec2, this.lag4_vec3, this.lag4_vec4);this.lag4_time = 0;}
-       if (this.lag5_time) if (time > this.lag5_time) {this.lag_func(this, this.lag5_time, this.lag5_float1, this.lag5_float2, this.lag5_entity1, this.lag5_vec1, this.lag5_vec2, this.lag5_vec3, this.lag5_vec4);this.lag5_time = 0;}
-}
-
-float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
-{
-       if (this.lag1_time == 0) {this.lag1_time = t;this.lag1_float1 = f1;this.lag1_float2 = f2;this.lag1_entity1 = e1;this.lag1_vec1 = v1;this.lag1_vec2 = v2;this.lag1_vec3 = v3;this.lag1_vec4 = v4;return true;}
-       if (this.lag2_time == 0) {this.lag2_time = t;this.lag2_float1 = f1;this.lag2_float2 = f2;this.lag2_entity1 = e1;this.lag2_vec1 = v1;this.lag2_vec2 = v2;this.lag2_vec3 = v3;this.lag2_vec4 = v4;return true;}
-       if (this.lag3_time == 0) {this.lag3_time = t;this.lag3_float1 = f1;this.lag3_float2 = f2;this.lag3_entity1 = e1;this.lag3_vec1 = v1;this.lag3_vec2 = v2;this.lag3_vec3 = v3;this.lag3_vec4 = v4;return true;}
-       if (this.lag4_time == 0) {this.lag4_time = t;this.lag4_float1 = f1;this.lag4_float2 = f2;this.lag4_entity1 = e1;this.lag4_vec1 = v1;this.lag4_vec2 = v2;this.lag4_vec3 = v3;this.lag4_vec4 = v4;return true;}
-       if (this.lag5_time == 0) {this.lag5_time = t;this.lag5_float1 = f1;this.lag5_float2 = f2;this.lag5_entity1 = e1;this.lag5_vec1 = v1;this.lag5_vec2 = v2;this.lag5_vec3 = v3;this.lag5_vec4 = v4;return true;}
-       // no room for it (what is the best thing to do here??)
-       return false;
-}
-
-bool bot_shouldattack(entity this, entity targ)
-{
-       if (targ.team == this.team)
-       {
-               if (targ == this)
-                       return false;
-               if (teamplay)
-               if (targ.team != 0)
-                       return false;
-       }
-
-       if(STAT(FROZEN, targ))
-               return false;
-
-       if(teamplay)
-       {
-               if(targ.team==0)
-                       return false;
-       }
-       else if(bot_ignore_bots)
-               if(IS_BOT_CLIENT(targ))
-                       return false;
-
-       if (!targ.takedamage)
-               return false;
-       if (IS_DEAD(targ))
-               return false;
-       if (PHYS_INPUT_BUTTON_CHAT(targ))
-               return false;
-       if(targ.flags & FL_NOTARGET)
-               return false;
-
-       if(MUTATOR_CALLHOOK(BotShouldAttack, this, targ))
-               return false;
-
-       return true;
-}
-
-void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
-{
-       if(this.flags & FL_INWATER)
-       {
-               this.bot_aimtarg = NULL;
-               return;
-       }
-       this.bot_aimtarg = e1;
-       this.bot_aimlatency = this.ping; // FIXME?  Shouldn't this be in the lag item?
-       //this.bot_aimorigin = v1;
-       //this.bot_aimvelocity = v2;
-       this.bot_aimtargorigin = v3;
-       this.bot_aimtargvelocity = v4;
-       if(skill <= 0)
-               this.bot_canfire = (random() < 0.8);
-       else if(skill <= 1)
-               this.bot_canfire = (random() < 0.9);
-       else if(skill <= 2)
-               this.bot_canfire = (random() < 0.95);
-       else
-               this.bot_canfire = 1;
-}
-
-float bot_aimdir(entity this, vector v, float maxfiredeviation)
-{
-       float dist, delta_t, blend;
-       vector desiredang, diffang;
-
-       //dprint("aim ", this.netname, ": old:", vtos(this.v_angle));
-       // make sure v_angle is sane first
-       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
-       this.v_angle_z = 0;
-
-       // get the desired angles to aim at
-       //dprint(" at:", vtos(v));
-       v = normalize(v);
-       //te_lightning2(NULL, this.origin + this.view_ofs, this.origin + this.view_ofs + v * 200);
-       if (time >= this.bot_badaimtime)
-       {
-               this.bot_badaimtime = max(this.bot_badaimtime + 0.3, time);
-               this.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+this.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
-       }
-       desiredang = vectoangles(v) + this.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 = this.v_angle.z;
-       //dprint(" / ", vtos(desiredang));
-
-       //// pain throws off aim
-       //if (this.bot_painintensity)
-       //{
-       //      // shake from pain
-       //      desiredang = desiredang + randomvec() * this.bot_painintensity * 0.2;
-       //}
-
-       // calculate turn angles
-       diffang = (desiredang - this.bot_olddesiredang);
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       this.bot_olddesiredang = desiredang;
-       //dprint(" diff:", vtos(diffang));
-
-       delta_t = time-this.bot_prevaimtime;
-       this.bot_prevaimtime = time;
-       // Here we will try to anticipate the comming aiming direction
-       this.bot_1st_order_aimfilter= this.bot_1st_order_aimfilter
-               + (diffang * (1 / delta_t)    - this.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1);
-       this.bot_2nd_order_aimfilter= this.bot_2nd_order_aimfilter
-               + (this.bot_1st_order_aimfilter - this.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1);
-       this.bot_3th_order_aimfilter= this.bot_3th_order_aimfilter
-               + (this.bot_2nd_order_aimfilter - this.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1);
-       this.bot_4th_order_aimfilter= this.bot_4th_order_aimfilter
-               + (this.bot_3th_order_aimfilter - this.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1);
-       this.bot_5th_order_aimfilter= this.bot_5th_order_aimfilter
-               + (this.bot_4th_order_aimfilter - this.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+this.bot_aimskill,10)*0.1;
-       desiredang = desiredang + blend *
-       (
-                 this.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st
-               + this.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd
-               + this.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th
-               + this.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th
-               + this.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th
-       );
-
-       // calculate turn angles
-       diffang = desiredang - this.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 >= this.bot_aimthinktime)
-       {
-               this.bot_aimthinktime = max(this.bot_aimthinktime + 0.5 - 0.05*(skill+this.bot_thinkskill), time);
-               this.bot_mouseaim = this.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+this.bot_thinkskill),10));
-       }
-
-       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
-
-       diffang = this.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 - this.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);
-       //this.v_angle = this.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1);
-       this.v_angle = this.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+this.bot_mouseskill,3)*0.005-random()), 1);
-       this.v_angle = this.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1);
-       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
-       //this.v_angle = this.v_angle + diffang * (1/ blendrate);
-       this.v_angle_z = 0;
-       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
-       //dprint(" turn:", vtos(this.v_angle));
-
-       makevectors(this.v_angle);
-       shotorg = this.origin + this.view_ofs;
-       shotdir = v_forward;
-
-       //dprint(" dir:", vtos(v_forward));
-       //te_lightning2(NULL, shotorg, shotorg + shotdir * 100);
-
-       // calculate turn angles again
-       //diffang = desiredang - this.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(vdist(trace_endpos-shotorg, <, 500 + 500 * bound(0, skill + this.bot_aggresskill, 10)) || random()*random()>bound(0,(skill+this.bot_aggresskill)*0.05,1))
-               this.bot_firetimer = time + bound(0.1, 0.5-(skill+this.bot_aggresskill)*0.05, 0.5);
-       //traceline(shotorg,shotorg+shotdir*1000,false,NULL);
-       //dprint(ftos(maxfiredeviation),"\n");
-       //dprint(" diff:", vtos(diffang), "\n");
-
-       return this.bot_canfire && (time < this.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);
-}
-
-bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity)
-{
-       float f, r, hf, distanceratio;
-       vector v;
-       /*
-       eprint(this);
-       dprint("bot_aim(", ftos(shotspeed));
-       dprint(", ", ftos(shotspeedupward));
-       dprint(", ", ftos(maxshottime));
-       dprint(", ", ftos(applygravity));
-       dprint(");\n");
-       */
-
-       hf = this.dphitcontentsmask;
-       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-
-       shotspeed *= W_WeaponSpeedFactor(this);
-       shotspeedupward *= W_WeaponSpeedFactor(this);
-       if (!shotspeed)
-       {
-               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " shotspeed is zero!\n");
-               shotspeed = 1000000;
-       }
-       if (!maxshottime)
-       {
-               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " maxshottime is zero!\n");
-               maxshottime = 1;
-       }
-       makevectors(this.v_angle);
-       shotorg = this.origin + this.view_ofs;
-       shotdir = v_forward;
-       v = bot_shotlead(this.bot_aimtargorigin, this.bot_aimtargvelocity, shotspeed, this.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 && this.bot_aimtarg)
-       {
-               if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', this.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, this))
-               {
-                       this.dphitcontentsmask = hf;
-                       return false;
-               }
-
-               f = bot_aimdir(this, findtrajectory_velocity - shotspeedupward * '0 0 1', r);
-       }
-       else
-       {
-               f = bot_aimdir(this, v - shotorg, r);
-               //dprint("AIM: ");dprint(vtos(this.bot_aimtargorigin));dprint(" + ");dprint(vtos(this.bot_aimtargvelocity));dprint(" * ");dprint(ftos(this.bot_aimlatency + vlen(this.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, this);
-               //if (trace_ent.takedamage)
-               //if (trace_fraction < 1)
-               //if (!bot_shouldattack(this, trace_ent))
-               //      return false;
-               traceline(shotorg, this.bot_aimtargorigin, false, this);
-               if (trace_fraction < 1)
-               if (trace_ent != this.enemy)
-               if (!bot_shouldattack(this, trace_ent))
-               {
-                       this.dphitcontentsmask = hf;
-                       return false;
-               }
-       }
-
-       //if (r > maxshottime * shotspeed)
-       //      return false;
-       this.dphitcontentsmask = hf;
-       return true;
-}
diff --git a/qcsrc/server/bot/aim.qh b/qcsrc/server/bot/aim.qh
deleted file mode 100644 (file)
index dfe10e2..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#pragma once
-/*
- * 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_aimorigin;
-//.vector bot_aimvelocity;
-.vector bot_aimtargorigin;
-.vector bot_aimtargvelocity;
-
-.entity bot_aimtarg;
-
-/*
- * Functions
- */
-
-float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
-void lag_update(entity this);
-void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
-
-float bot_shouldattack(entity this, entity targ);
-float bot_aimdir(entity this, vector v, float maxfiredeviation);
-bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool 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(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func;
diff --git a/qcsrc/server/bot/api.qc b/qcsrc/server/bot/api.qc
new file mode 100644 (file)
index 0000000..85b0e46
--- /dev/null
@@ -0,0 +1,49 @@
+#include "api.qh"
+
+#if 1
+
+#include "default/_mod.inc"
+#include "default/havocbot/_mod.inc"
+
+#else
+
+bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity) { return false; }
+void bot_clientconnect(entity this) { }
+void bot_clientdisconnect(entity this) { }
+void bot_cmdhelp(string scmd) { }
+void bot_endgame() { }
+bool bot_fixcount() { return true; }
+void bot_list_commands() { }
+void bot_queuecommand(entity bot, string cmdstring) { }
+void bot_relinkplayerlist() { }
+void bot_resetqueues() { }
+void bot_serverframe() { }
+bool bot_shouldattack(entity this, entity e) { return false; }
+void bot_think(entity this) { }
+
+entity find_bot_by_name(string name) { return NULL; }
+entity find_bot_by_number(float number) { return NULL; }
+
+void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius) { }
+void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius) { }
+void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) { }
+
+entity navigation_findnearestwaypoint(entity ent, float walkfromwp) { return NULL; }
+void navigation_goalrating_end(entity this) { }
+void navigation_goalrating_start(entity this) { }
+void navigation_markroutes(entity this, entity fixed_source_waypoint) { }
+void navigation_markroutes_inverted(entity fixed_source_waypoint) { }
+void navigation_routerating(entity this, entity e, float f, float rangebias) { }
+
+bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode) { return false; }
+
+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) { return NULL; }
+#endif
diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh
new file mode 100644 (file)
index 0000000..9c52589
--- /dev/null
@@ -0,0 +1,87 @@
+#pragma once
+
+#include <common/weapons/all.qh>
+
+const int WAYPOINTFLAG_GENERATED = BIT(23);
+const int WAYPOINTFLAG_ITEM = BIT(22);
+const int WAYPOINTFLAG_TELEPORT = BIT(21);
+const int WAYPOINTFLAG_NORELINK = BIT(20);
+const int WAYPOINTFLAG_PERSONAL = BIT(19);
+const int WAYPOINTFLAG_PROTECTED = BIT(18);  // Useless WP detection never kills these.
+const int WAYPOINTFLAG_USEFUL = BIT(17);  // Useless WP detection temporary flag.
+const int WAYPOINTFLAG_DEAD_END = BIT(16);  // Useless WP detection temporary flag.
+
+entity kh_worldkeylist;
+.entity kh_worldkeynext;
+
+float bot_custom_weapon;
+float bot_weapons_close[Weapons_MAX];
+float bot_weapons_far[Weapons_MAX];
+float bot_weapons_mid[Weapons_MAX];
+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(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity);
+void bot_clientconnect(entity this);
+void bot_clientdisconnect(entity this);
+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 this, entity e);
+void bot_think(entity this);
+
+entity find_bot_by_name(string name);
+entity find_bot_by_number(float number);
+
+void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius);
+void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius);
+void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius);
+
+entity navigation_findnearestwaypoint(entity ent, float walkfromwp);
+void navigation_goalrating_end(entity this);
+void navigation_goalrating_start(entity this);
+void navigation_markroutes(entity this, entity fixed_source_waypoint);
+void navigation_markroutes_inverted(entity fixed_source_waypoint);
+void navigation_routerating(entity this, 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);
diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc
deleted file mode 100644 (file)
index 58feef6..0000000
+++ /dev/null
@@ -1,725 +0,0 @@
-#include "bot.qh"
-
-#include "aim.qh"
-#include "navigation.qh"
-#include "scripting.qh"
-#include "waypoints.qh"
-
-#include "havocbot/havocbot.qh"
-#include "havocbot/scripting.qh"
-
-#include "../teamplay.qh"
-
-#include "../antilag.qh"
-#include "../autocvars.qh"
-#include "../campaign.qh"
-#include "../cl_client.qh"
-#include "../constants.qh"
-#include "../defs.qh"
-#include "../race.qh"
-#include <common/t_items.qh>
-
-#include "../mutators/all.qh"
-
-#include "../weapons/accuracy.qh"
-
-#include <common/physics/player.qh>
-#include <common/constants.qh>
-#include <common/mapinfo.qh>
-#include <common/teams.qh>
-#include <common/util.qh>
-
-#include <common/weapons/all.qh>
-
-#include <lib/csqcmodel/sv_model.qh>
-
-#include <lib/warpzone/common.qh>
-#include <lib/warpzone/util_server.qh>
-
-entity bot_spawn()
-{
-       entity bot = spawnclient();
-       if (bot)
-       {
-               currentbots = currentbots + 1;
-               bot_setnameandstuff(bot);
-               ClientConnect(bot);
-               PutClientInServer(bot);
-       }
-       return bot;
-}
-
-void bot_think(entity this)
-{
-       if (this.bot_nextthink > time)
-               return;
-
-       this.flags &= ~FL_GODMODE;
-       if(autocvar_bot_god)
-               this.flags |= FL_GODMODE;
-
-       this.bot_nextthink = this.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill);
-       //if (this.bot_painintensity > 0)
-       //      this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime;
-
-       //this.bot_painintensity = this.bot_painintensity + this.bot_oldhealth - this.health;
-       //this.bot_painintensity = bound(0, this.bot_painintensity, 100);
-
-       if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
-       {
-               this.bot_nextthink = time + 0.5;
-               return;
-       }
-
-       if (this.fixangle)
-       {
-               this.v_angle = this.angles;
-               this.v_angle_z = 0;
-               this.fixangle = false;
-       }
-
-       this.dmg_take = 0;
-       this.dmg_save = 0;
-       this.dmg_inflictor = NULL;
-
-       // calculate an aiming latency based on the skill setting
-       // (simulated network latency + naturally delayed reflexes)
-       //this.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
-       this.ping = bound(0,0.07 - bound(0, (skill + this.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
-       PHYS_INPUT_BUTTON_ATCK(this) = false;
-       PHYS_INPUT_BUTTON_JUMP(this) = false;
-       PHYS_INPUT_BUTTON_ATCK2(this) = false;
-       PHYS_INPUT_BUTTON_ZOOM(this) = false;
-       PHYS_INPUT_BUTTON_CROUCH(this) = false;
-       PHYS_INPUT_BUTTON_HOOK(this) = false;
-       PHYS_INPUT_BUTTON_INFO(this) = false;
-       PHYS_INPUT_BUTTON_DRAG(this) = false;
-       PHYS_INPUT_BUTTON_CHAT(this) = false;
-       PHYS_INPUT_BUTTON_USE(this) = false;
-
-       if (time < game_starttime)
-       {
-               // block the bot during the countdown to game start
-               this.movement = '0 0 0';
-               this.bot_nextthink = game_starttime;
-               return;
-       }
-
-       // if dead, just wait until we can respawn
-       if (IS_DEAD(this))
-       {
-               if (this.deadflag == DEAD_DEAD)
-               {
-                       PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
-                       this.bot_strategytime = 0;
-               }
-       }
-       else if(this.aistatus & AI_STATUS_STUCK)
-               navigation_unstuck(this);
-
-       // now call the current bot AI (havocbot for example)
-       this.bot_ai(this);
-}
-
-void bot_setnameandstuff(entity this)
-{
-       string readfile, s;
-       float file, tokens, prio;
-
-       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;
-                       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
-                               if(s == it.cleanname)
-                               {
-                                       prio = 0;
-                                       break;
-                               }
-                       ));
-                       RandomSelection_Add(NULL, 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));
-
-       this.bot_forced_team = stof(argv(5));
-
-       prio = 6;
-
-       #define READSKILL(f,w,r) if(argv(prio) != "") this.f = stof(argv(prio)) * (w); else this.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
-
-       this.bot_config_loaded = true;
-
-       // this is really only a default, JoinBestTeam is called later
-       setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants));
-       this.bot_preferredcolors = this.clientcolors;
-
-       // pick the name
-       if (autocvar_bot_usemodelnames)
-               name = bot_model;
-       else
-               name = bot_name;
-
-       // number bots with identical names
-       int j = 0;
-       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
-               if(it.cleanname == name)
-                       ++j;
-       ));
-       if (j)
-               this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
-       else
-               this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
-
-       this.cleanname = strzone(name);
-
-       // pick the model and skin
-       if(substring(bot_model, -4, 1) != ".")
-               bot_model = strcat(bot_model, ".iqm");
-       this.playermodel = this.playermodel_freeme = strzone(strcat("models/player/", bot_model));
-       this.playerskin = this.playerskin_freeme = strzone(bot_skin);
-
-       this.cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the NULL
-       this.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()
-{
-       player_count = 0;
-       currentbots = 0;
-       bot_list = NULL;
-
-       entity prevbot = NULL;
-       FOREACH_CLIENT(true,
-       {
-               ++player_count;
-
-               if(IS_BOT_CLIENT(it))
-               {
-                       if(prevbot)
-                               prevbot.nextbot = it;
-                       else
-                       {
-                               bot_list = it;
-                               bot_list.nextbot = NULL;
-                       }
-                       prevbot = it;
-                       ++currentbots;
-               }
-       });
-       LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
-       bot_strategytoken = bot_list;
-       bot_strategytoken_taken = true;
-}
-
-void bot_clientdisconnect(entity this)
-{
-       if (!IS_BOT_CLIENT(this))
-               return;
-       bot_clearqueue(this);
-       if(this.cleanname)
-               strunzone(this.cleanname);
-       if(this.netname_freeme)
-               strunzone(this.netname_freeme);
-       if(this.playermodel_freeme)
-               strunzone(this.playermodel_freeme);
-       if(this.playerskin_freeme)
-               strunzone(this.playerskin_freeme);
-       this.cleanname = string_null;
-       this.netname_freeme = string_null;
-       this.playermodel_freeme = string_null;
-       this.playerskin_freeme = string_null;
-       if(this.bot_cmd_current)
-               delete(this.bot_cmd_current);
-       if(bot_waypoint_queue_owner==this)
-               bot_waypoint_queue_owner = NULL;
-}
-
-void bot_clientconnect(entity this)
-{
-       if (!IS_BOT_CLIENT(this)) return;
-       this.bot_preferredcolors = this.clientcolors;
-       this.bot_nextthink = time - random();
-       this.lag_func = bot_lagfunc;
-       this.isbot = true;
-       this.createdtime = this.bot_nextthink;
-
-       if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches
-               bot_setnameandstuff(this);
-
-       if(this.bot_forced_team==1)
-               this.team = NUM_TEAM_1;
-       else if(this.bot_forced_team==2)
-               this.team = NUM_TEAM_2;
-       else if(this.bot_forced_team==3)
-               this.team = NUM_TEAM_3;
-       else if(this.bot_forced_team==4)
-               this.team = NUM_TEAM_4;
-       else
-               JoinBestTeam(this, false, true);
-
-       havocbot_setupbot(this);
-}
-
-void bot_removefromlargestteam()
-{
-       CheckAllowedTeams(NULL);
-       GetTeamCounts(NULL);
-
-       entity best = NULL;
-       float besttime = 0;
-       int bestcount = 0;
-
-       int bcount = 0;
-       FOREACH_ENTITY_FLOAT(isbot, true,
-       {
-               ++bcount;
-
-               if(!best)
-               {
-                       best = it;
-                       besttime = it.createdtime;
-               }
-
-               int thiscount = 0;
-
-               switch(it.team)
-               {
-                       case NUM_TEAM_1: thiscount = c1; break;
-                       case NUM_TEAM_2: thiscount = c2; break;
-                       case NUM_TEAM_3: thiscount = c3; break;
-                       case NUM_TEAM_4: thiscount = c4; break;
-               }
-
-               if(thiscount > bestcount)
-               {
-                       bestcount = thiscount;
-                       besttime = it.createdtime;
-                       best = it;
-               }
-               else if(thiscount == bestcount && besttime < it.createdtime)
-               {
-                       besttime = it.createdtime;
-                       best = it;
-               }
-       });
-       if(!bcount)
-               return; // no bots to remove
-       currentbots = currentbots - 1;
-       dropclient(best);
-}
-
-void bot_removenewest()
-{
-       if(teamplay)
-       {
-               bot_removefromlargestteam();
-               return;
-       }
-
-       float besttime = 0;
-       entity best = NULL;
-       int bcount = 0;
-
-       FOREACH_ENTITY_FLOAT(isbot, true,
-       {
-               ++bcount;
-
-               if(!best)
-               {
-                       best = it;
-                       besttime = it.createdtime;
-               }
-
-               if(besttime < it.createdtime)
-               {
-                       besttime = it.createdtime;
-                       best = it;
-               }
-       });
-
-       if(!bcount)
-               return; // no bots to remove
-
-       currentbots = currentbots - 1;
-       dropclient(best);
-}
-
-void autoskill(float factor)
-{
-       float bestbot;
-       float bestplayer;
-
-       bestbot = -1;
-       bestplayer = -1;
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
-               if(IS_REAL_CLIENT(it))
-                       bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck);
-               else
-                       bestbot = max(bestbot, it.totalfrags - it.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
-       }
-
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.totalfrags_lastcheck = it.totalfrags));
-}
-
-void bot_calculate_stepheightvec()
-{
-       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()
-{
-       int activerealplayers = 0;
-       int realplayers = 0;
-       if (MUTATOR_CALLHOOK(Bot_FixCount, activerealplayers, realplayers)) {
-               activerealplayers = M_ARGV(0, int);
-               realplayers = M_ARGV(1, int);
-       } else {
-               FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
-                       if(IS_PLAYER(it))
-                               ++activerealplayers;
-                       ++realplayers;
-               ));
-       }
-
-       int bots;
-       // 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 && AvailableTeams() == 2)
-               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() == NULL)
-                       {
-                               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
-                       IL_EACH(g_waypoints, time - it.nextthink > 10,
-                       {
-                               waypoint_save_links();
-                               break;
-                       });
-               }
-       }
-       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
deleted file mode 100644 (file)
index e9dd926..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-#pragma once
-/*
- * Globals and Fields
- */
-
-const int AI_STATUS_ROAMING                                            = BIT(0);       // Bot is just crawling the map. No enemies at sight
-const int AI_STATUS_ATTACKING                                  = BIT(1);       // There are enemies at sight
-const int AI_STATUS_RUNNING                                            = BIT(2);       // Bot is bunny hopping
-const int AI_STATUS_DANGER_AHEAD                               = BIT(3);       // There is lava/slime/trigger_hurt ahead
-const int AI_STATUS_OUT_JUMPPAD                                        = BIT(4);       // Trying to get out of a "vertical" jump pad
-const int AI_STATUS_OUT_WATER                                  = BIT(5);       // Trying to get out of water
-const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = BIT(6);       // Waiting for the personal waypoint to be linked
-const int AI_STATUS_WAYPOINT_PERSONAL_GOING            = BIT(7);       // Going to a personal waypoint
-const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = BIT(8);       // Personal waypoint reached
-const int AI_STATUS_JETPACK_FLYING                             = BIT(9);
-const int AI_STATUS_JETPACK_LANDING                            = BIT(10);
-const int AI_STATUS_STUCK                                              = BIT(11);  // 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 nextbot;
-.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(entity this);
-void bot_setnameandstuff(entity this);
-void bot_custom_weapon_priority_setup();
-void bot_endgame();
-void bot_relinkplayerlist();
-void bot_clientdisconnect(entity this);
-void bot_clientconnect(entity this);
-void bot_removefromlargestteam();
-void bot_removenewest();
-void autoskill(float factor);
-void bot_serverframe();
-
-.void(entity this) bot_ai;
-.float(entity player, entity item) bot_pickupevalfunc;
-
-/*
- * Imports
- */
-
-void(entity this) havocbot_setupbot;
-
-void bot_calculate_stepheightvec();
diff --git a/qcsrc/server/bot/default/_mod.inc b/qcsrc/server/bot/default/_mod.inc
new file mode 100644 (file)
index 0000000..ec73502
--- /dev/null
@@ -0,0 +1,7 @@
+// generated file; do not modify
+#include <server/bot/default/aim.qc>
+#include <server/bot/default/bot.qc>
+#include <server/bot/default/cvars.qc>
+#include <server/bot/default/navigation.qc>
+#include <server/bot/default/scripting.qc>
+#include <server/bot/default/waypoints.qc>
diff --git a/qcsrc/server/bot/default/_mod.qh b/qcsrc/server/bot/default/_mod.qh
new file mode 100644 (file)
index 0000000..9252f19
--- /dev/null
@@ -0,0 +1,7 @@
+// generated file; do not modify
+#include <server/bot/default/aim.qh>
+#include <server/bot/default/bot.qh>
+#include <server/bot/default/cvars.qh>
+#include <server/bot/default/navigation.qh>
+#include <server/bot/default/scripting.qh>
+#include <server/bot/default/waypoints.qh>
diff --git a/qcsrc/server/bot/default/aim.qc b/qcsrc/server/bot/default/aim.qc
new file mode 100644 (file)
index 0000000..7c87db0
--- /dev/null
@@ -0,0 +1,395 @@
+#include "aim.qh"
+
+#include "cvars.qh"
+
+#include "bot.qh"
+
+#include <common/physics/player.qh>
+#include <common/state.qh>
+
+#include "../../weapons/weaponsystem.qh"
+
+#include "../../mutators/all.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 = new(tracetossent);
+       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 = new(tracetossfaketarget);
+       tracetossfaketarget.solid = savesolid;
+       set_movetype(tracetossfaketarget, targ.move_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;
+                       set_movetype(tracetossfaketarget, 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;
+       set_movetype(tracetossfaketarget, 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(entity this)
+{
+       if (this.lag1_time) if (time > this.lag1_time) {this.lag_func(this, this.lag1_time, this.lag1_float1, this.lag1_float2, this.lag1_entity1, this.lag1_vec1, this.lag1_vec2, this.lag1_vec3, this.lag1_vec4);this.lag1_time = 0;}
+       if (this.lag2_time) if (time > this.lag2_time) {this.lag_func(this, this.lag2_time, this.lag2_float1, this.lag2_float2, this.lag2_entity1, this.lag2_vec1, this.lag2_vec2, this.lag2_vec3, this.lag2_vec4);this.lag2_time = 0;}
+       if (this.lag3_time) if (time > this.lag3_time) {this.lag_func(this, this.lag3_time, this.lag3_float1, this.lag3_float2, this.lag3_entity1, this.lag3_vec1, this.lag3_vec2, this.lag3_vec3, this.lag3_vec4);this.lag3_time = 0;}
+       if (this.lag4_time) if (time > this.lag4_time) {this.lag_func(this, this.lag4_time, this.lag4_float1, this.lag4_float2, this.lag4_entity1, this.lag4_vec1, this.lag4_vec2, this.lag4_vec3, this.lag4_vec4);this.lag4_time = 0;}
+       if (this.lag5_time) if (time > this.lag5_time) {this.lag_func(this, this.lag5_time, this.lag5_float1, this.lag5_float2, this.lag5_entity1, this.lag5_vec1, this.lag5_vec2, this.lag5_vec3, this.lag5_vec4);this.lag5_time = 0;}
+}
+
+float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
+{
+       if (this.lag1_time == 0) {this.lag1_time = t;this.lag1_float1 = f1;this.lag1_float2 = f2;this.lag1_entity1 = e1;this.lag1_vec1 = v1;this.lag1_vec2 = v2;this.lag1_vec3 = v3;this.lag1_vec4 = v4;return true;}
+       if (this.lag2_time == 0) {this.lag2_time = t;this.lag2_float1 = f1;this.lag2_float2 = f2;this.lag2_entity1 = e1;this.lag2_vec1 = v1;this.lag2_vec2 = v2;this.lag2_vec3 = v3;this.lag2_vec4 = v4;return true;}
+       if (this.lag3_time == 0) {this.lag3_time = t;this.lag3_float1 = f1;this.lag3_float2 = f2;this.lag3_entity1 = e1;this.lag3_vec1 = v1;this.lag3_vec2 = v2;this.lag3_vec3 = v3;this.lag3_vec4 = v4;return true;}
+       if (this.lag4_time == 0) {this.lag4_time = t;this.lag4_float1 = f1;this.lag4_float2 = f2;this.lag4_entity1 = e1;this.lag4_vec1 = v1;this.lag4_vec2 = v2;this.lag4_vec3 = v3;this.lag4_vec4 = v4;return true;}
+       if (this.lag5_time == 0) {this.lag5_time = t;this.lag5_float1 = f1;this.lag5_float2 = f2;this.lag5_entity1 = e1;this.lag5_vec1 = v1;this.lag5_vec2 = v2;this.lag5_vec3 = v3;this.lag5_vec4 = v4;return true;}
+       // no room for it (what is the best thing to do here??)
+       return false;
+}
+
+bool bot_shouldattack(entity this, entity targ)
+{
+       if (targ.team == this.team)
+       {
+               if (targ == this)
+                       return false;
+               if (teamplay)
+               if (targ.team != 0)
+                       return false;
+       }
+
+       if(STAT(FROZEN, targ))
+               return false;
+
+       if(teamplay)
+       {
+               if(targ.team==0)
+                       return false;
+       }
+       else if(bot_ignore_bots)
+               if(IS_BOT_CLIENT(targ))
+                       return false;
+
+       if (!targ.takedamage)
+               return false;
+       if (IS_DEAD(targ))
+               return false;
+       if (PHYS_INPUT_BUTTON_CHAT(targ))
+               return false;
+       if(targ.flags & FL_NOTARGET)
+               return false;
+
+       if(MUTATOR_CALLHOOK(BotShouldAttack, this, targ))
+               return false;
+
+       return true;
+}
+
+void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
+{
+       if(this.flags & FL_INWATER)
+       {
+               this.bot_aimtarg = NULL;
+               return;
+       }
+       this.bot_aimtarg = e1;
+       this.bot_aimlatency = this.ping; // FIXME?  Shouldn't this be in the lag item?
+       //this.bot_aimorigin = v1;
+       //this.bot_aimvelocity = v2;
+       this.bot_aimtargorigin = v3;
+       this.bot_aimtargvelocity = v4;
+       if(skill <= 0)
+               this.bot_canfire = (random() < 0.8);
+       else if(skill <= 1)
+               this.bot_canfire = (random() < 0.9);
+       else if(skill <= 2)
+               this.bot_canfire = (random() < 0.95);
+       else
+               this.bot_canfire = 1;
+}
+
+float bot_aimdir(entity this, vector v, float maxfiredeviation)
+{
+       float dist, delta_t, blend;
+       vector desiredang, diffang;
+
+       //dprint("aim ", this.netname, ": old:", vtos(this.v_angle));
+       // make sure v_angle is sane first
+       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
+       this.v_angle_z = 0;
+
+       // get the desired angles to aim at
+       //dprint(" at:", vtos(v));
+       v = normalize(v);
+       //te_lightning2(NULL, this.origin + this.view_ofs, this.origin + this.view_ofs + v * 200);
+       if (time >= this.bot_badaimtime)
+       {
+               this.bot_badaimtime = max(this.bot_badaimtime + 0.3, time);
+               this.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+this.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
+       }
+       desiredang = vectoangles(v) + this.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 = this.v_angle.z;
+       //dprint(" / ", vtos(desiredang));
+
+       //// pain throws off aim
+       //if (this.bot_painintensity)
+       //{
+       //      // shake from pain
+       //      desiredang = desiredang + randomvec() * this.bot_painintensity * 0.2;
+       //}
+
+       // calculate turn angles
+       diffang = (desiredang - this.bot_olddesiredang);
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       this.bot_olddesiredang = desiredang;
+       //dprint(" diff:", vtos(diffang));
+
+       delta_t = time-this.bot_prevaimtime;
+       this.bot_prevaimtime = time;
+       // Here we will try to anticipate the comming aiming direction
+       this.bot_1st_order_aimfilter= this.bot_1st_order_aimfilter
+               + (diffang * (1 / delta_t)    - this.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1);
+       this.bot_2nd_order_aimfilter= this.bot_2nd_order_aimfilter
+               + (this.bot_1st_order_aimfilter - this.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1);
+       this.bot_3th_order_aimfilter= this.bot_3th_order_aimfilter
+               + (this.bot_2nd_order_aimfilter - this.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1);
+       this.bot_4th_order_aimfilter= this.bot_4th_order_aimfilter
+               + (this.bot_3th_order_aimfilter - this.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1);
+       this.bot_5th_order_aimfilter= this.bot_5th_order_aimfilter
+               + (this.bot_4th_order_aimfilter - this.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+this.bot_aimskill,10)*0.1;
+       desiredang = desiredang + blend *
+       (
+                 this.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st
+               + this.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd
+               + this.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th
+               + this.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th
+               + this.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th
+       );
+
+       // calculate turn angles
+       diffang = desiredang - this.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 >= this.bot_aimthinktime)
+       {
+               this.bot_aimthinktime = max(this.bot_aimthinktime + 0.5 - 0.05*(skill+this.bot_thinkskill), time);
+               this.bot_mouseaim = this.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+this.bot_thinkskill),10));
+       }
+
+       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+
+       diffang = this.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 - this.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);
+       //this.v_angle = this.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1);
+       this.v_angle = this.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+this.bot_mouseskill,3)*0.005-random()), 1);
+       this.v_angle = this.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1);
+       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+       //this.v_angle = this.v_angle + diffang * (1/ blendrate);
+       this.v_angle_z = 0;
+       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
+       //dprint(" turn:", vtos(this.v_angle));
+
+       makevectors(this.v_angle);
+       shotorg = this.origin + this.view_ofs;
+       shotdir = v_forward;
+
+       //dprint(" dir:", vtos(v_forward));
+       //te_lightning2(NULL, shotorg, shotorg + shotdir * 100);
+
+       // calculate turn angles again
+       //diffang = desiredang - this.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(vdist(trace_endpos-shotorg, <, 500 + 500 * bound(0, skill + this.bot_aggresskill, 10)) || random()*random()>bound(0,(skill+this.bot_aggresskill)*0.05,1))
+               this.bot_firetimer = time + bound(0.1, 0.5-(skill+this.bot_aggresskill)*0.05, 0.5);
+       //traceline(shotorg,shotorg+shotdir*1000,false,NULL);
+       //dprint(ftos(maxfiredeviation),"\n");
+       //dprint(" diff:", vtos(diffang), "\n");
+
+       return this.bot_canfire && (time < this.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);
+}
+
+bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity)
+{
+       float f, r, hf, distanceratio;
+       vector v;
+       /*
+       eprint(this);
+       dprint("bot_aim(", ftos(shotspeed));
+       dprint(", ", ftos(shotspeedupward));
+       dprint(", ", ftos(maxshottime));
+       dprint(", ", ftos(applygravity));
+       dprint(");\n");
+       */
+
+       hf = this.dphitcontentsmask;
+       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+
+       shotspeed *= W_WeaponSpeedFactor(this);
+       shotspeedupward *= W_WeaponSpeedFactor(this);
+       if (!shotspeed)
+       {
+               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " shotspeed is zero!\n");
+               shotspeed = 1000000;
+       }
+       if (!maxshottime)
+       {
+               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " maxshottime is zero!\n");
+               maxshottime = 1;
+       }
+       makevectors(this.v_angle);
+       shotorg = this.origin + this.view_ofs;
+       shotdir = v_forward;
+       v = bot_shotlead(this.bot_aimtargorigin, this.bot_aimtargvelocity, shotspeed, this.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 && this.bot_aimtarg)
+       {
+               if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', this.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, this))
+               {
+                       this.dphitcontentsmask = hf;
+                       return false;
+               }
+
+               f = bot_aimdir(this, findtrajectory_velocity - shotspeedupward * '0 0 1', r);
+       }
+       else
+       {
+               f = bot_aimdir(this, v - shotorg, r);
+               //dprint("AIM: ");dprint(vtos(this.bot_aimtargorigin));dprint(" + ");dprint(vtos(this.bot_aimtargvelocity));dprint(" * ");dprint(ftos(this.bot_aimlatency + vlen(this.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, this);
+               //if (trace_ent.takedamage)
+               //if (trace_fraction < 1)
+               //if (!bot_shouldattack(this, trace_ent))
+               //      return false;
+               traceline(shotorg, this.bot_aimtargorigin, false, this);
+               if (trace_fraction < 1)
+               if (trace_ent != this.enemy)
+               if (!bot_shouldattack(this, trace_ent))
+               {
+                       this.dphitcontentsmask = hf;
+                       return false;
+               }
+       }
+
+       //if (r > maxshottime * shotspeed)
+       //      return false;
+       this.dphitcontentsmask = hf;
+       return true;
+}
diff --git a/qcsrc/server/bot/default/aim.qh b/qcsrc/server/bot/default/aim.qh
new file mode 100644 (file)
index 0000000..dfe10e2
--- /dev/null
@@ -0,0 +1,99 @@
+#pragma once
+/*
+ * 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_aimorigin;
+//.vector bot_aimvelocity;
+.vector bot_aimtargorigin;
+.vector bot_aimtargvelocity;
+
+.entity bot_aimtarg;
+
+/*
+ * Functions
+ */
+
+float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
+void lag_update(entity this);
+void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
+
+float bot_shouldattack(entity this, entity targ);
+float bot_aimdir(entity this, vector v, float maxfiredeviation);
+bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool 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(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func;
diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc
new file mode 100644 (file)
index 0000000..753c186
--- /dev/null
@@ -0,0 +1,727 @@
+#include "bot.qh"
+
+#include "cvars.qh"
+
+#include "aim.qh"
+#include "navigation.qh"
+#include "scripting.qh"
+#include "waypoints.qh"
+
+#include "havocbot/havocbot.qh"
+#include "havocbot/scripting.qh"
+
+#include "../../teamplay.qh"
+
+#include "../../antilag.qh"
+#include "../../autocvars.qh"
+#include "../../campaign.qh"
+#include "../../cl_client.qh"
+#include "../../constants.qh"
+#include "../../defs.qh"
+#include "../../race.qh"
+#include <common/t_items.qh>
+
+#include "../../mutators/all.qh"
+
+#include "../../weapons/accuracy.qh"
+
+#include <common/physics/player.qh>
+#include <common/constants.qh>
+#include <common/mapinfo.qh>
+#include <common/teams.qh>
+#include <common/util.qh>
+
+#include <common/weapons/all.qh>
+
+#include <lib/csqcmodel/sv_model.qh>
+
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/util_server.qh>
+
+entity bot_spawn()
+{
+       entity bot = spawnclient();
+       if (bot)
+       {
+               currentbots = currentbots + 1;
+               bot_setnameandstuff(bot);
+               ClientConnect(bot);
+               PutClientInServer(bot);
+       }
+       return bot;
+}
+
+void bot_think(entity this)
+{
+       if (this.bot_nextthink > time)
+               return;
+
+       this.flags &= ~FL_GODMODE;
+       if(autocvar_bot_god)
+               this.flags |= FL_GODMODE;
+
+       this.bot_nextthink = this.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill);
+       //if (this.bot_painintensity > 0)
+       //      this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime;
+
+       //this.bot_painintensity = this.bot_painintensity + this.bot_oldhealth - this.health;
+       //this.bot_painintensity = bound(0, this.bot_painintensity, 100);
+
+       if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
+       {
+               this.bot_nextthink = time + 0.5;
+               return;
+       }
+
+       if (this.fixangle)
+       {
+               this.v_angle = this.angles;
+               this.v_angle_z = 0;
+               this.fixangle = false;
+       }
+
+       this.dmg_take = 0;
+       this.dmg_save = 0;
+       this.dmg_inflictor = NULL;
+
+       // calculate an aiming latency based on the skill setting
+       // (simulated network latency + naturally delayed reflexes)
+       //this.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
+       this.ping = bound(0,0.07 - bound(0, (skill + this.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
+       PHYS_INPUT_BUTTON_ATCK(this) = false;
+       PHYS_INPUT_BUTTON_JUMP(this) = false;
+       PHYS_INPUT_BUTTON_ATCK2(this) = false;
+       PHYS_INPUT_BUTTON_ZOOM(this) = false;
+       PHYS_INPUT_BUTTON_CROUCH(this) = false;
+       PHYS_INPUT_BUTTON_HOOK(this) = false;
+       PHYS_INPUT_BUTTON_INFO(this) = false;
+       PHYS_INPUT_BUTTON_DRAG(this) = false;
+       PHYS_INPUT_BUTTON_CHAT(this) = false;
+       PHYS_INPUT_BUTTON_USE(this) = false;
+
+       if (time < game_starttime)
+       {
+               // block the bot during the countdown to game start
+               this.movement = '0 0 0';
+               this.bot_nextthink = game_starttime;
+               return;
+       }
+
+       // if dead, just wait until we can respawn
+       if (IS_DEAD(this))
+       {
+               if (this.deadflag == DEAD_DEAD)
+               {
+                       PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
+                       this.bot_strategytime = 0;
+               }
+       }
+       else if(this.aistatus & AI_STATUS_STUCK)
+               navigation_unstuck(this);
+
+       // now call the current bot AI (havocbot for example)
+       this.bot_ai(this);
+}
+
+void bot_setnameandstuff(entity this)
+{
+       string readfile, s;
+       float file, tokens, prio;
+
+       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;
+                       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
+                               if(s == it.cleanname)
+                               {
+                                       prio = 0;
+                                       break;
+                               }
+                       ));
+                       RandomSelection_Add(NULL, 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));
+
+       this.bot_forced_team = stof(argv(5));
+
+       prio = 6;
+
+       #define READSKILL(f,w,r) if(argv(prio) != "") this.f = stof(argv(prio)) * (w); else this.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
+
+       this.bot_config_loaded = true;
+
+       // this is really only a default, JoinBestTeam is called later
+       setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants));
+       this.bot_preferredcolors = this.clientcolors;
+
+       // pick the name
+       if (autocvar_bot_usemodelnames)
+               name = bot_model;
+       else
+               name = bot_name;
+
+       // number bots with identical names
+       int j = 0;
+       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
+               if(it.cleanname == name)
+                       ++j;
+       ));
+       if (j)
+               this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
+       else
+               this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
+
+       this.cleanname = strzone(name);
+
+       // pick the model and skin
+       if(substring(bot_model, -4, 1) != ".")
+               bot_model = strcat(bot_model, ".iqm");
+       this.playermodel = this.playermodel_freeme = strzone(strcat("models/player/", bot_model));
+       this.playerskin = this.playerskin_freeme = strzone(bot_skin);
+
+       this.cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the NULL
+       this.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()
+{
+       player_count = 0;
+       currentbots = 0;
+       bot_list = NULL;
+
+       entity prevbot = NULL;
+       FOREACH_CLIENT(true,
+       {
+               ++player_count;
+
+               if(IS_BOT_CLIENT(it))
+               {
+                       if(prevbot)
+                               prevbot.nextbot = it;
+                       else
+                       {
+                               bot_list = it;
+                               bot_list.nextbot = NULL;
+                       }
+                       prevbot = it;
+                       ++currentbots;
+               }
+       });
+       LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
+       bot_strategytoken = bot_list;
+       bot_strategytoken_taken = true;
+}
+
+void bot_clientdisconnect(entity this)
+{
+       if (!IS_BOT_CLIENT(this))
+               return;
+       bot_clearqueue(this);
+       if(this.cleanname)
+               strunzone(this.cleanname);
+       if(this.netname_freeme)
+               strunzone(this.netname_freeme);
+       if(this.playermodel_freeme)
+               strunzone(this.playermodel_freeme);
+       if(this.playerskin_freeme)
+               strunzone(this.playerskin_freeme);
+       this.cleanname = string_null;
+       this.netname_freeme = string_null;
+       this.playermodel_freeme = string_null;
+       this.playerskin_freeme = string_null;
+       if(this.bot_cmd_current)
+               delete(this.bot_cmd_current);
+       if(bot_waypoint_queue_owner==this)
+               bot_waypoint_queue_owner = NULL;
+}
+
+void bot_clientconnect(entity this)
+{
+       if (!IS_BOT_CLIENT(this)) return;
+       this.bot_preferredcolors = this.clientcolors;
+       this.bot_nextthink = time - random();
+       this.lag_func = bot_lagfunc;
+       this.isbot = true;
+       this.createdtime = this.bot_nextthink;
+
+       if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches
+               bot_setnameandstuff(this);
+
+       if(this.bot_forced_team==1)
+               this.team = NUM_TEAM_1;
+       else if(this.bot_forced_team==2)
+               this.team = NUM_TEAM_2;
+       else if(this.bot_forced_team==3)
+               this.team = NUM_TEAM_3;
+       else if(this.bot_forced_team==4)
+               this.team = NUM_TEAM_4;
+       else
+               JoinBestTeam(this, false, true);
+
+       havocbot_setupbot(this);
+}
+
+void bot_removefromlargestteam()
+{
+       CheckAllowedTeams(NULL);
+       GetTeamCounts(NULL);
+
+       entity best = NULL;
+       float besttime = 0;
+       int bestcount = 0;
+
+       int bcount = 0;
+       FOREACH_ENTITY_FLOAT(isbot, true,
+       {
+               ++bcount;
+
+               if(!best)
+               {
+                       best = it;
+                       besttime = it.createdtime;
+               }
+
+               int thiscount = 0;
+
+               switch(it.team)
+               {
+                       case NUM_TEAM_1: thiscount = c1; break;
+                       case NUM_TEAM_2: thiscount = c2; break;
+                       case NUM_TEAM_3: thiscount = c3; break;
+                       case NUM_TEAM_4: thiscount = c4; break;
+               }
+
+               if(thiscount > bestcount)
+               {
+                       bestcount = thiscount;
+                       besttime = it.createdtime;
+                       best = it;
+               }
+               else if(thiscount == bestcount && besttime < it.createdtime)
+               {
+                       besttime = it.createdtime;
+                       best = it;
+               }
+       });
+       if(!bcount)
+               return; // no bots to remove
+       currentbots = currentbots - 1;
+       dropclient(best);
+}
+
+void bot_removenewest()
+{
+       if(teamplay)
+       {
+               bot_removefromlargestteam();
+               return;
+       }
+
+       float besttime = 0;
+       entity best = NULL;
+       int bcount = 0;
+
+       FOREACH_ENTITY_FLOAT(isbot, true,
+       {
+               ++bcount;
+
+               if(!best)
+               {
+                       best = it;
+                       besttime = it.createdtime;
+               }
+
+               if(besttime < it.createdtime)
+               {
+                       besttime = it.createdtime;
+                       best = it;
+               }
+       });
+
+       if(!bcount)
+               return; // no bots to remove
+
+       currentbots = currentbots - 1;
+       dropclient(best);
+}
+
+void autoskill(float factor)
+{
+       float bestbot;
+       float bestplayer;
+
+       bestbot = -1;
+       bestplayer = -1;
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+               if(IS_REAL_CLIENT(it))
+                       bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck);
+               else
+                       bestbot = max(bestbot, it.totalfrags - it.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
+       }
+
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.totalfrags_lastcheck = it.totalfrags));
+}
+
+void bot_calculate_stepheightvec()
+{
+       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()
+{
+       int activerealplayers = 0;
+       int realplayers = 0;
+       if (MUTATOR_CALLHOOK(Bot_FixCount, activerealplayers, realplayers)) {
+               activerealplayers = M_ARGV(0, int);
+               realplayers = M_ARGV(1, int);
+       } else {
+               FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
+                       if(IS_PLAYER(it))
+                               ++activerealplayers;
+                       ++realplayers;
+               ));
+       }
+
+       int bots;
+       // 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 && AvailableTeams() == 2)
+               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() == NULL)
+                       {
+                               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
+                       IL_EACH(g_waypoints, time - it.nextthink > 10,
+                       {
+                               waypoint_save_links();
+                               break;
+                       });
+               }
+       }
+       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
new file mode 100644 (file)
index 0000000..0959307
--- /dev/null
@@ -0,0 +1,108 @@
+#pragma once
+/*
+ * Globals and Fields
+ */
+
+const int AI_STATUS_ROAMING                                            = BIT(0);       // Bot is just crawling the map. No enemies at sight
+const int AI_STATUS_ATTACKING                                  = BIT(1);       // There are enemies at sight
+const int AI_STATUS_RUNNING                                            = BIT(2);       // Bot is bunny hopping
+const int AI_STATUS_DANGER_AHEAD                               = BIT(3);       // There is lava/slime/trigger_hurt ahead
+const int AI_STATUS_OUT_JUMPPAD                                        = BIT(4);       // Trying to get out of a "vertical" jump pad
+const int AI_STATUS_OUT_WATER                                  = BIT(5);       // Trying to get out of water
+const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = BIT(6);       // Waiting for the personal waypoint to be linked
+const int AI_STATUS_WAYPOINT_PERSONAL_GOING            = BIT(7);       // Going to a personal waypoint
+const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = BIT(8);       // Personal waypoint reached
+const int AI_STATUS_JETPACK_FLYING                             = BIT(9);
+const int AI_STATUS_JETPACK_LANDING                            = BIT(10);
+const int AI_STATUS_STUCK                                              = BIT(11);  // 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;
+
+// Custom weapon priorities
+float bot_distance_far;
+float bot_distance_close;
+
+entity bot_list;
+.entity nextbot;
+.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(entity this);
+void bot_setnameandstuff(entity this);
+void bot_custom_weapon_priority_setup();
+void bot_endgame();
+void bot_relinkplayerlist();
+void bot_clientdisconnect(entity this);
+void bot_clientconnect(entity this);
+void bot_removefromlargestteam();
+void bot_removenewest();
+void autoskill(float factor);
+void bot_serverframe();
+
+.void(entity this) bot_ai;
+.float(entity player, entity item) bot_pickupevalfunc;
+
+/*
+ * Imports
+ */
+
+void(entity this) havocbot_setupbot;
+
+void bot_calculate_stepheightvec();
diff --git a/qcsrc/server/bot/default/cvars.qc b/qcsrc/server/bot/default/cvars.qc
new file mode 100644 (file)
index 0000000..779e7f0
--- /dev/null
@@ -0,0 +1 @@
+#include "cvars.qh"
diff --git a/qcsrc/server/bot/default/cvars.qh b/qcsrc/server/bot/default/cvars.qh
new file mode 100644 (file)
index 0000000..d612c7a
--- /dev/null
@@ -0,0 +1,58 @@
+#pragma once
+
+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_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_prefix cvar_string("bot_prefix")
+#define autocvar_bot_suffix cvar_string("bot_suffix")
+bool autocvar_bot_usemodelnames;
+bool autocvar_bot_debug_tracewalk;
+bool autocvar_bot_debug_goalstack;
+bool autocvar_bot_wander_enable;
+bool autocvar_g_debug_bot_commands;
+int autocvar_g_waypointeditor_auto;
+float autocvar_skill_auto;
+bool autocvar_waypoint_benchmark;
diff --git a/qcsrc/server/bot/default/havocbot/_mod.inc b/qcsrc/server/bot/default/havocbot/_mod.inc
new file mode 100644 (file)
index 0000000..3ecfda1
--- /dev/null
@@ -0,0 +1,3 @@
+// generated file; do not modify
+#include <server/bot/default/havocbot/havocbot.qc>
+#include <server/bot/default/havocbot/roles.qc>
diff --git a/qcsrc/server/bot/default/havocbot/_mod.qh b/qcsrc/server/bot/default/havocbot/_mod.qh
new file mode 100644 (file)
index 0000000..01fcd46
--- /dev/null
@@ -0,0 +1,3 @@
+// generated file; do not modify
+#include <server/bot/default/havocbot/havocbot.qh>
+#include <server/bot/default/havocbot/roles.qh>
diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc
new file mode 100644 (file)
index 0000000..67b558e
--- /dev/null
@@ -0,0 +1,1298 @@
+#include "havocbot.qh"
+
+#include "../cvars.qh"
+
+#include "../aim.qh"
+#include "../bot.qh"
+#include "../navigation.qh"
+#include "../scripting.qh"
+#include "../waypoints.qh"
+
+#include <common/constants.qh>
+#include <common/physics/player.qh>
+#include <common/state.qh>
+#include <common/items/all.qh>
+
+#include <common/triggers/trigger/jumppads.qh>
+
+#include <lib/warpzone/common.qh>
+
+.float speed;
+
+void havocbot_ai(entity this)
+{
+       if(this.draggedby)
+               return;
+
+       if(bot_execute_commands(this))
+               return;
+
+       if (bot_strategytoken == this)
+       if (!bot_strategytoken_taken)
+       {
+               if(this.havocbot_blockhead)
+               {
+                       this.havocbot_blockhead = false;
+               }
+               else
+               {
+                       if (!this.jumppadcount)
+                               this.havocbot_role(this); // little too far down the rabbit hole
+               }
+
+               // 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(IS_DEAD(this))
+               if(!this.goalcurrent)
+               if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
+               {
+                       // Look for the closest waypoint out of water
+                       entity newgoal = NULL;
+                       IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 10000),
+                       {
+                               if(it.origin.z < this.origin.z)
+                                       continue;
+
+                               if(it.origin.z - this.origin.z - this.view_ofs.z > 100)
+                                       continue;
+
+                               if (pointcontents(it.origin + it.maxs + '0 0 1') != CONTENT_EMPTY)
+                                       continue;
+
+                               traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
+
+                               if(trace_fraction < 1)
+                                       continue;
+
+                               if(!newgoal || vlen2(it.origin - this.origin) < vlen2(newgoal.origin - this.origin))
+                                       newgoal = it;
+                       });
+
+                       if(newgoal)
+                       {
+                       //      te_wizspike(newgoal.origin);
+                               navigation_pushroute(this, newgoal);
+                       }
+               }
+
+               // token has been used this frame
+               bot_strategytoken_taken = true;
+       }
+
+       if(IS_DEAD(this))
+               return;
+
+       havocbot_chooseenemy(this);
+       if (this.bot_chooseweapontime < time )
+       {
+               this.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
+               havocbot_chooseweapon(this);
+       }
+       havocbot_aim(this);
+       lag_update(this);
+       if (this.bot_aimtarg)
+       {
+               this.aistatus |= AI_STATUS_ATTACKING;
+               this.aistatus &= ~AI_STATUS_ROAMING;
+
+               if(this.weapons)
+               {
+                       Weapon w = PS(this).m_weapon;
+                       w.wr_aim(w, this);
+                       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
+                       {
+                               PHYS_INPUT_BUTTON_ATCK(this) = false;
+                               PHYS_INPUT_BUTTON_ATCK2(this) = false;
+                       }
+                       else
+                       {
+                               if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))
+                                       this.lastfiredweapon = PS(this).m_weapon.m_id;
+                       }
+               }
+               else
+               {
+                       if(IS_PLAYER(this.bot_aimtarg))
+                               bot_aimdir(this, this.bot_aimtarg.origin + this.bot_aimtarg.view_ofs - this.origin - this.view_ofs , -1);
+               }
+       }
+       else if (this.goalcurrent)
+       {
+               this.aistatus |= AI_STATUS_ROAMING;
+               this.aistatus &= ~AI_STATUS_ATTACKING;
+
+               vector now,v,next;//,heading;
+               float aimdistance,skillblend,distanceblend,blend;
+               next = now = ( (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5) - (this.origin + this.view_ofs);
+               aimdistance = vlen(now);
+               //heading = this.velocity;
+               //dprint(this.goalstack01.classname,etos(this.goalstack01),"\n");
+               if(
+                       this.goalstack01 != this && this.goalstack01 != NULL && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
+                       !(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+               )
+                       next = ((this.goalstack01.absmin + this.goalstack01.absmax) * 0.5) - (this.origin + this.view_ofs);
+
+               skillblend=bound(0,(skill+this.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(this), " ");
+               //dprint(vtos(now), ":", vtos(next), "=", vtos(v), " (blend ", ftos(blend), ")\n");
+               //v = now * (distanceblend) + next * (1-distanceblend);
+               if (this.waterlevel < WATERLEVEL_SWIMMING)
+                       v.z = 0;
+               //dprint("walk at:", vtos(v), "\n");
+               //te_lightning2(NULL, this.origin, this.goalcurrent.origin);
+               bot_aimdir(this, v, -1);
+       }
+       havocbot_movetogoal(this);
+
+       // if the bot is not attacking, consider reloading weapons
+       if (!(this.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(this.clip_load < this.clip_size)
+                       this.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(this.clip_load >= 0) // only if we're not reloading a weapon already
+               {
+                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                               if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.weapon_load[it.m_id] < it.reloading_ammo))
+                                       PS(this).m_switchweapon = it;
+                       ));
+               }
+       }
+}
+
+void havocbot_keyboard_movement(entity this, vector destorg)
+{
+       vector keyboard;
+       float blend, maxspeed;
+       float sk;
+
+       sk = skill + this.bot_moveskill;
+
+       maxspeed = autocvar_sv_maxspeed;
+
+       if (time < this.havocbot_keyboardtime)
+               return;
+
+       this.havocbot_keyboardtime =
+               max(
+                       this.havocbot_keyboardtime
+                               + 0.05/max(1, sk+this.havocbot_keyboardskill)
+                               + random()*0.025/max(0.00025, skill+this.havocbot_keyboardskill)
+               , time);
+       keyboard = this.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;
+
+       this.havocbot_keyboard = keyboard * maxspeed;
+       if (this.havocbot_ducktime>time) PHYS_INPUT_BUTTON_CROUCH(this) = true;
+
+       keyboard = this.havocbot_keyboard;
+       blend = bound(0,vlen(destorg-this.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree
+       //dprint("movement ", vtos(this.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
+       this.movement = this.movement + (keyboard - this.movement) * blend;
+}
+
+void havocbot_bunnyhop(entity this, vector dir)
+{
+       float bunnyhopdistance;
+       vector deviation;
+       float maxspeed;
+       vector gco, gno;
+
+       // Don't jump when attacking
+       if(this.aistatus & AI_STATUS_ATTACKING)
+               return;
+
+       if(IS_PLAYER(this.goalcurrent))
+               return;
+
+       maxspeed = autocvar_sv_maxspeed;
+
+       if(this.aistatus & AI_STATUS_DANGER_AHEAD)
+       {
+               this.aistatus &= ~AI_STATUS_RUNNING;
+               PHYS_INPUT_BUTTON_JUMP(this) = false;
+               this.bot_canruntogoal = 0;
+               this.bot_timelastseengoal = 0;
+               return;
+       }
+
+       if(this.waterlevel > WATERLEVEL_WETFEET)
+       {
+               this.aistatus &= ~AI_STATUS_RUNNING;
+               return;
+       }
+
+       if(this.bot_lastseengoal != this.goalcurrent && !(this.aistatus & AI_STATUS_RUNNING))
+       {
+               this.bot_canruntogoal = 0;
+               this.bot_timelastseengoal = 0;
+       }
+
+       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
+       bunnyhopdistance = vlen(this.origin - gco);
+
+       // Run only to visible goals
+       if(IS_ONGROUND(this))
+       if(this.speed==maxspeed)
+       if(checkpvs(this.origin + this.view_ofs, this.goalcurrent))
+       {
+                       this.bot_lastseengoal = this.goalcurrent;
+
+                       // seen it before
+                       if(this.bot_timelastseengoal)
+                       {
+                               // for a period of time
+                               if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
+                               {
+                                       float checkdistance;
+                                       checkdistance = true;
+
+                                       // don't run if it is too close
+                                       if(this.bot_canruntogoal==0)
+                                       {
+                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance)
+                                                       this.bot_canruntogoal = 1;
+                                               else
+                                                       this.bot_canruntogoal = -1;
+                                       }
+
+                                       if(this.bot_canruntogoal != 1)
+                                               return;
+
+                                       if(this.aistatus & AI_STATUS_ROAMING)
+                                       if(this.goalcurrent.classname=="waypoint")
+                                       if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
+                                       if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z)
+                                       if(this.goalstack01!=NULL)
+                                       {
+                                               gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
+                                               deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.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(this.origin - gno))
+                                               if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z)
+                                               {
+                                                       if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance))
+                                                       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
+                                                       {
+                                                               checkdistance = false;
+                                                       }
+                                               }
+                                       }
+
+                                       if(checkdistance)
+                                       {
+                                               this.aistatus &= ~AI_STATUS_RUNNING;
+                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
+                                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
+                                       }
+                                       else
+                                       {
+                                               this.aistatus |= AI_STATUS_RUNNING;
+                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               this.bot_timelastseengoal = time;
+                       }
+       }
+       else
+       {
+               this.bot_timelastseengoal = 0;
+       }
+
+#if 0
+       // Release jump button
+       if(!cvar("sv_pogostick"))
+       if((IS_ONGROUND(this)) == 0)
+       {
+               if(this.velocity.z < 0 || vlen(this.velocity)<maxspeed)
+                       PHYS_INPUT_BUTTON_JUMP(this) = false;
+
+               // Strafe
+               if(this.aistatus & AI_STATUS_RUNNING)
+               if(vlen(this.velocity)>maxspeed)
+               {
+                       deviation = vectoangles(dir) - vectoangles(this.velocity);
+                       while (deviation.y < -180) deviation.y = deviation.y + 360;
+                       while (deviation.y > 180) deviation.y = deviation.y - 360;
+
+                       if(fabs(deviation.y)>10)
+                               this.movement_x = 0;
+
+                       if(deviation.y>10)
+                               this.movement_y = maxspeed * -1;
+                       else if(deviation.y<10)
+                               this.movement_y = maxspeed;
+
+               }
+       }
+#endif
+}
+
+void havocbot_movetogoal(entity this)
+{
+       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 (this.goalentity)
+       //      te_lightning2(this, this.origin, (this.goalentity.absmin + this.goalentity.absmax) * 0.5);
+       this.movement = '0 0 0';
+       maxspeed = autocvar_sv_maxspeed;
+
+       // Jetpack navigation
+       if(this.goalcurrent)
+       if(this.navigation_jetpack_goal)
+       if(this.goalcurrent==this.navigation_jetpack_goal)
+       if(this.ammo_fuel)
+       {
+               if(autocvar_bot_debug_goalstack)
+               {
+                       debuggoalstack(this);
+                       te_wizspike(this.navigation_jetpack_point);
+               }
+
+               // Take off
+               if (!(this.aistatus & AI_STATUS_JETPACK_FLYING))
+               {
+                       // Brake almost completely so it can get a good direction
+                       if(vdist(this.velocity, >, 10))
+                               return;
+                       this.aistatus |= AI_STATUS_JETPACK_FLYING;
+               }
+
+               makevectors(this.v_angle.y * '0 1 0');
+               dir = normalize(this.navigation_jetpack_point - this.origin);
+
+               // Landing
+               if(this.aistatus & AI_STATUS_JETPACK_LANDING)
+               {
+                       // Calculate brake distance in xy
+                       float db, v, d;
+                       vector dxy;
+
+                       dxy = this.origin - ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ); dxy.z = 0;
+                       d = vlen(dxy);
+                       v = vlen(this.velocity -  this.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(this.velocity.x)>maxspeed*0.3)
+                               {
+                                       this.movement_x = dir * v_forward * -maxspeed;
+                                       return;
+                               }
+                               // Switch to normal mode
+                               this.navigation_jetpack_goal = NULL;
+                               this.aistatus &= ~AI_STATUS_JETPACK_LANDING;
+                               this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
+                               return;
+                       }
+               }
+               else if(checkpvs(this.origin,this.goalcurrent))
+               {
+                       // If I can see the goal switch to landing code
+                       this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
+                       this.aistatus |= AI_STATUS_JETPACK_LANDING;
+                       return;
+               }
+
+               // Flying
+               PHYS_INPUT_BUTTON_HOOK(this) = true;
+               if(this.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < this.origin.z)
+               {
+                       this.movement_x = dir * v_forward * maxspeed;
+                       this.movement_y = dir * v_right * maxspeed;
+               }
+               return;
+       }
+
+       // Handling of jump pads
+       if(this.jumppadcount)
+       {
+               // If got stuck on the jump pad try to reach the farthest visible waypoint
+               if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
+               {
+                       if(fabs(this.velocity.z)<50)
+                       {
+                               entity newgoal = NULL;
+                               IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 1000),
+                               {
+                                       traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
+
+                                       if(trace_fraction < 1)
+                                               continue;
+
+                                       if(!newgoal || vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))
+                                               newgoal = it;
+                               });
+
+                               if(newgoal)
+                               {
+                                       this.ignoregoal = this.goalcurrent;
+                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
+                                       navigation_clearroute(this);
+                                       navigation_routetogoal(this, newgoal, this.origin);
+                                       this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
+                               }
+                       }
+                       else
+                               return;
+               }
+               else
+               {
+                       if(this.velocity.z>0)
+                       {
+                               float threshold;
+                               vector velxy = this.velocity; velxy_z = 0;
+                               threshold = maxspeed * 0.2;
+                               if(vdist(velxy, <, threshold))
+                               {
+                                       LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now\n");
+                                       this.aistatus |= AI_STATUS_OUT_JUMPPAD;
+                               }
+                               return;
+                       }
+
+                       // Don't chase players while using a jump pad
+                       if(IS_PLAYER(this.goalcurrent) || IS_PLAYER(this.goalstack01))
+                               return;
+               }
+       }
+       else if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
+               this.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 (!(IS_ONGROUND(this)))
+       {
+               tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this);
+               if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos ))
+               if(this.items & IT_JETPACK)
+               {
+                       tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 65536', MOVE_NOMONSTERS, this);
+                       if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos + '0 0 1' ))
+                       {
+                               if(this.velocity.z<0)
+                               {
+                                       PHYS_INPUT_BUTTON_HOOK(this) = true;
+                               }
+                       }
+                       else
+                               PHYS_INPUT_BUTTON_HOOK(this) = true;
+
+                       // If there is no goal try to move forward
+
+                       if(this.goalcurrent==NULL)
+                               dir = v_forward;
+                       else
+                               dir = normalize(( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - this.origin);
+
+                       vector xyvelocity = this.velocity; xyvelocity_z = 0;
+                       float xyspeed = xyvelocity * dir;
+
+                       if(xyspeed < (maxspeed / 2))
+                       {
+                               makevectors(this.v_angle.y * '0 1 0');
+                               tracebox(this.origin, this.mins, this.maxs, this.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, this);
+                               if(trace_fraction==1)
+                               {
+                                       this.movement_x = dir * v_forward * maxspeed;
+                                       this.movement_y = dir * v_right * maxspeed;
+                                       if (skill < 10)
+                                               havocbot_keyboard_movement(this, this.origin + dir * 100);
+                               }
+                       }
+
+                       this.havocbot_blockhead = true;
+
+                       return;
+               }
+               else if(this.health>WEP_CVAR(devastator, damage)*0.5)
+               {
+                       if(this.velocity.z < 0)
+                       if(client_hasweapon(this, WEP_DEVASTATOR, true, false))
+                       {
+                               this.movement_x = maxspeed;
+
+                               if(this.rocketjumptime)
+                               {
+                                       if(time > this.rocketjumptime)
+                                       {
+                                               PHYS_INPUT_BUTTON_ATCK2(this) = true;
+                                               this.rocketjumptime = 0;
+                                       }
+                                       return;
+                               }
+
+                               PS(this).m_switchweapon = WEP_DEVASTATOR;
+                               this.v_angle_x = 90;
+                               PHYS_INPUT_BUTTON_ATCK(this) = true;
+                               this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
+                               return;
+                       }
+               }
+               else
+               {
+                       // If there is no goal try to move forward
+                       if(this.goalcurrent==NULL)
+                               this.movement_x = maxspeed;
+               }
+       }
+
+       // If we are under water with no goals, swim up
+       if(this.waterlevel)
+       if(this.goalcurrent==NULL)
+       {
+               dir = '0 0 0';
+               if(this.waterlevel>WATERLEVEL_SWIMMING)
+                       dir.z = 1;
+               else if(this.velocity.z >= 0 && !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER))
+                       PHYS_INPUT_BUTTON_JUMP(this) = true;
+               else
+                       PHYS_INPUT_BUTTON_JUMP(this) = false;
+               makevectors(this.v_angle.y * '0 1 0');
+               this.movement_x = dir * v_forward * maxspeed;
+               this.movement_y = dir * v_right * maxspeed;
+               this.movement_z = dir * v_up * maxspeed;
+       }
+
+       // if there is nowhere to go, exit
+       if (this.goalcurrent == NULL)
+               return;
+
+       if (this.goalcurrent)
+               navigation_poptouchedgoals(this);
+
+       // if ran out of goals try to use an alternative goal or get a new strategy asap
+       if(this.goalcurrent == NULL)
+       {
+               this.bot_strategytime = 0;
+               return;
+       }
+
+
+       if(autocvar_bot_debug_goalstack)
+               debuggoalstack(this);
+
+       m1 = this.goalcurrent.origin + this.goalcurrent.mins;
+       m2 = this.goalcurrent.origin + this.goalcurrent.maxs;
+       destorg = this.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 - this.origin;
+       //dist = vlen(diff);
+       dir = normalize(diff);
+       flatdir = diff;flatdir.z = 0;
+       flatdir = normalize(flatdir);
+       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
+
+       //if (this.bot_dodgevector_time < time)
+       {
+       //      this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval");
+       //      this.bot_dodgevector_jumpbutton = 1;
+               evadeobstacle = '0 0 0';
+               evadelava = '0 0 0';
+
+               if (this.waterlevel)
+               {
+                       if(this.waterlevel>WATERLEVEL_SWIMMING)
+                       {
+                       //      flatdir_z = 1;
+                               this.aistatus |= AI_STATUS_OUT_WATER;
+                       }
+                       else
+                       {
+                               if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && gco.z < this.origin.z) &&
+                                       ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER))
+                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
+                               else
+                                       PHYS_INPUT_BUTTON_JUMP(this) = false;
+                       }
+                       dir = normalize(flatdir);
+                       makevectors(this.v_angle.y * '0 1 0');
+               }
+               else
+               {
+                       if(this.aistatus & AI_STATUS_OUT_WATER)
+                               this.aistatus &= ~AI_STATUS_OUT_WATER;
+
+                       // jump if going toward an obstacle that doesn't look like stairs we
+                       // can walk up directly
+                       tracebox(this.origin, this.mins, this.maxs, this.origin + this.velocity * 0.2, false, this);
+                       if (trace_fraction < 1)
+                       if (trace_plane_normal.z < 0.7)
+                       {
+                               s = trace_fraction;
+                               tracebox(this.origin + stepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + stepheightvec, false, this);
+                               if (trace_fraction < s + 0.01)
+                               if (trace_plane_normal.z < 0.7)
+                               {
+                                       s = trace_fraction;
+                                       tracebox(this.origin + jumpstepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + jumpstepheightvec, false, this);
+                                       if (trace_fraction > s)
+                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
+                               }
+                       }
+
+                       // avoiding dangers and obstacles
+                       vector dst_ahead, dst_down;
+                       makevectors(this.v_angle.y * '0 1 0');
+                       dst_ahead = this.origin + this.view_ofs + (this.velocity * 0.4) + (v_forward * 32 * 3);
+                       dst_down = dst_ahead - '0 0 1500';
+
+                       // Look ahead
+                       traceline(this.origin + this.view_ofs, dst_ahead, true, NULL);
+
+                       // Check head-banging against walls
+                       if(vdist(this.origin + this.view_ofs - trace_endpos, <, 25) && !(this.aistatus & AI_STATUS_OUT_WATER))
+                       {
+                               PHYS_INPUT_BUTTON_JUMP(this) = true;
+                               if(this.facingwalltime && time > this.facingwalltime)
+                               {
+                                       this.ignoregoal = this.goalcurrent;
+                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
+                                       this.bot_strategytime = 0;
+                                       return;
+                               }
+                               else
+                               {
+                                       this.facingwalltime = time + 0.05;
+                               }
+                       }
+                       else
+                       {
+                               this.facingwalltime = 0;
+
+                               if(this.ignoregoal != NULL && time > this.ignoregoaltime)
+                               {
+                                       this.ignoregoal = NULL;
+                                       this.ignoregoaltime = 0;
+                               }
+                       }
+
+                       // Check for water/slime/lava and dangerous edges
+                       // (only when the bot is on the ground or jumping intentionally)
+                       this.aistatus &= ~AI_STATUS_DANGER_AHEAD;
+
+                       if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired )
+                       if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || PHYS_INPUT_BUTTON_JUMP(this))
+                       {
+                               // Look downwards
+                               traceline(dst_ahead , dst_down, true, NULL);
+                       //      te_lightning2(NULL, this.origin, dst_ahead);    // Draw "ahead" look
+                       //      te_lightning2(NULL, dst_ahead, dst_down);               // Draw "downwards" look
+                               if(trace_endpos.z < this.origin.z + this.mins.z)
+                               {
+                                       s = pointcontents(trace_endpos + '0 0 1');
+                                       if (s != CONTENT_SOLID)
+                                       if (s == CONTENT_LAVA || s == CONTENT_SLIME)
+                                               evadelava = normalize(this.velocity) * -1;
+                                       else if (s == CONTENT_SKY)
+                                               evadeobstacle = normalize(this.velocity) * -1;
+                                       else if (!boxesoverlap(dst_ahead - this.view_ofs + this.mins, dst_ahead - this.view_ofs + this.maxs,
+                                                               this.goalcurrent.absmin, this.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, this.mins, this.maxs, trace_endpos))
+                                               {
+                                                       // Remove dangerous dynamic goals from stack
+                                                       LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
+                                                       navigation_clearroute(this);
+                                                       return;
+                                               }
+                                       }
+                               }
+                       }
+
+                       dir = flatdir;
+                       evadeobstacle.z = 0;
+                       evadelava.z = 0;
+                       makevectors(this.v_angle.y * '0 1 0');
+
+                       if(evadeobstacle!='0 0 0'||evadelava!='0 0 0')
+                               this.aistatus |= AI_STATUS_DANGER_AHEAD;
+               }
+
+               dodge = havocbot_dodge(this);
+               dodge = dodge * bound(0,0.5+(skill+this.bot_dodgeskill)*0.1,1);
+               evadelava = evadelava * bound(1,3-(skill+this.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it
+               traceline(this.origin, ( ( this.enemy.absmin + this.enemy.absmax ) * 0.5 ), true, NULL);
+               if(IS_PLAYER(trace_ent))
+                       dir = dir * bound(0,(skill+this.bot_dodgeskill)/7,1);
+
+               dir = normalize(dir + dodge + evadeobstacle + evadelava);
+       //      this.bot_dodgevector = dir;
+       //      this.bot_dodgevector_jumpbutton = PHYS_INPUT_BUTTON_JUMP(this);
+       }
+
+       if(time < this.ladder_time)
+       {
+               if(this.goalcurrent.origin.z + this.goalcurrent.mins.z > this.origin.z + this.mins.z)
+               {
+                       if(this.origin.z + this.mins.z  < this.ladder_entity.origin.z + this.ladder_entity.maxs.z)
+                               dir.z = 1;
+               }
+               else
+               {
+                       if(this.origin.z + this.mins.z  > this.ladder_entity.origin.z + this.ladder_entity.mins.z)
+                               dir.z = -1;
+               }
+       }
+
+       //dir = this.bot_dodgevector;
+       //if (this.bot_dodgevector_jumpbutton)
+       //      PHYS_INPUT_BUTTON_JUMP(this) = true;
+       this.movement_x = dir * v_forward * maxspeed;
+       this.movement_y = dir * v_right * maxspeed;
+       this.movement_z = dir * v_up * maxspeed;
+
+       // Emulate keyboard interface
+       if (skill < 10)
+               havocbot_keyboard_movement(this, destorg);
+
+       // Bunnyhop!
+//     if(this.aistatus & AI_STATUS_ROAMING)
+       if(this.goalcurrent)
+       if(skill+this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
+               havocbot_bunnyhop(this, dir);
+
+       if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (IS_ONGROUND(this))) PHYS_INPUT_BUTTON_JUMP(this) = true;
+       if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) PHYS_INPUT_BUTTON_JUMP(this) = true;
+       if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) this.havocbot_ducktime=time+0.3/bound(0.1,skill+this.bot_dodgeskill,10);
+}
+
+void havocbot_chooseenemy(entity this)
+{
+       entity head, best, head2;
+       float rating, bestrating, hf;
+       vector eye, v;
+       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
+       {
+               this.enemy = NULL;
+               return;
+       }
+       if (this.enemy)
+       {
+               if (!bot_shouldattack(this, this.enemy))
+               {
+                       // enemy died or something, find a new target
+                       this.enemy = NULL;
+                       this.havocbot_chooseenemy_finished = time;
+               }
+               else if (this.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(this.origin+this.view_ofs, ( this.enemy.absmin + this.enemy.absmax ) * 0.5,false,NULL);
+                       if (trace_ent == this.enemy || trace_fraction == 1)
+                       if (vdist(((this.enemy.absmin + this.enemy.absmax) * 0.5) - this.origin, <, 1000))
+                       if (this.health > 30)
+                       {
+                               // remain tracking him for a shot while (case he went after a small corner or pilar
+                               this.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)
+                       this.havocbot_stickenemy = 0;
+               }
+       }
+       if (time < this.havocbot_chooseenemy_finished)
+               return;
+       this.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval;
+       eye = this.origin + this.view_ofs;
+       best = NULL;
+       bestrating = 100000000;
+       head = head2 = findchainfloat(bot_attack, true);
+
+       // Backup hit flags
+       hf = this.dphitcontentsmask;
+
+       // Search for enemies, if no enemy can be seen directly try to look through transparent objects
+
+       this.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;
+LABEL(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(this, head))
+                       {
+                               traceline(eye, v, true, this);
+                               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 || this.weapons) // || this.weapon == WEP_RIFLE.m_id
+                       break;
+               if(scan_transparent)
+                       break;
+
+               // Set flags to see through transparent objects
+               this.dphitcontentsmask |= DPCONTENTS_OPAQUE;
+
+               head = head2;
+               scan_transparent = true;
+       }
+
+       // Restore hit flags
+       this.dphitcontentsmask = hf;
+
+       this.enemy = best;
+       this.havocbot_stickenemy = true;
+       if(best && best.classname == "misc_breakablemodel")
+               this.havocbot_stickenemy = false;
+}
+
+float havocbot_chooseweapon_checkreload(entity this, int new_weapon)
+{
+       // 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 (this.weapon_load[new_weapon] < 0)
+       {
+               bool other_weapon_available = false;
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(it.wr_checkammo1(it, this) + it.wr_checkammo2(it, this))
+                               other_weapon_available = true;
+               ));
+               if(other_weapon_available)
+                       return true;
+       }
+
+       return false;
+}
+
+void havocbot_chooseweapon(entity this)
+{
+       int i;
+
+       // ;)
+       if(g_weaponarena_weapons == WEPSET(TUBA))
+       {
+               PS(this).m_switchweapon = WEP_TUBA;
+               return;
+       }
+
+       // TODO: clean this up by moving it to weapon code
+       if(this.enemy==NULL)
+       {
+               // If no weapon was chosen get the first available weapon
+               if(PS(this).m_weapon==WEP_Null)
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(client_hasweapon(this, it, true, false))
+                       {
+                               PS(this).m_switchweapon = it;
+                               return;
+                       }
+               ));
+               return;
+       }
+
+       // Do not change weapon during the next second after a combo
+       float f = time - this.lastcombotime;
+       if(f < 1)
+               return;
+
+       float w;
+       float distance; distance=bound(10,vlen(this.origin-this.enemy.origin)-200,10000);
+
+       // Should it do a weapon combo?
+       float af, ct, combo_time, combo;
+
+       af = ATTACK_FINISHED(this, 0);
+       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+this.bot_weaponskill))+3));
+
+       combo = false;
+
+       if(autocvar_bot_ai_weapon_combo)
+       if(PS(this).m_weapon.m_id == this.lastfiredweapon)
+       if(af > combo_time)
+       {
+               combo = true;
+               this.lastcombotime = time;
+       }
+
+       distance *= pow(2, this.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(this, Weapons_from(w), true, false) )
+                               {
+                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+                                               continue;
+                                       PS(this).m_switchweapon = Weapons_from(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(this, Weapons_from(w), true, false) )
+                               {
+                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+                                               continue;
+                                       PS(this).m_switchweapon = Weapons_from(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(this, Weapons_from(w), true, false) )
+                       {
+                               if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+                                       continue;
+                               PS(this).m_switchweapon = Weapons_from(w);
+                               return;
+                       }
+               }
+       }
+}
+
+void havocbot_aim(entity this)
+{
+       vector myvel, enemyvel;
+//     if(this.flags & FL_INWATER)
+//             return;
+       if (time < this.nextaim)
+               return;
+       this.nextaim = time + 0.1;
+       myvel = this.velocity;
+       if (!this.waterlevel)
+               myvel.z = 0;
+       if (this.enemy)
+       {
+               enemyvel = this.enemy.velocity;
+               if (!this.enemy.waterlevel)
+                       enemyvel.z = 0;
+               lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel);
+       }
+       else
+               lag_additem(this, time + this.ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0');
+}
+
+bool havocbot_moveto_refresh_route(entity this)
+{
+       // Refresh path to goal if necessary
+       entity wp;
+       wp = this.havocbot_personal_waypoint;
+       navigation_goalrating_start(this);
+       navigation_routerating(this, wp, 10000, 10000);
+       navigation_goalrating_end(this);
+       return this.navigation_hasgoals;
+}
+
+float havocbot_moveto(entity this, vector pos)
+{
+       entity wp;
+
+       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+       {
+               // Step 4: Move to waypoint
+               if(this.havocbot_personal_waypoint==NULL)
+               {
+                       LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint\n");
+                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                       return CMD_STATUS_ERROR;
+               }
+
+               if (!bot_strategytoken_taken)
+               if(this.havocbot_personal_waypoint_searchtime<time)
+               {
+                       bot_strategytoken_taken = true;
+                       if(havocbot_moveto_refresh_route(this))
+                       {
+                               LOG_TRACE(this.netname, " walking to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts)\n");
+                               this.havocbot_personal_waypoint_searchtime = time + 10;
+                               this.havocbot_personal_waypoint_failcounter = 0;
+                       }
+                       else
+                       {
+                               this.havocbot_personal_waypoint_failcounter += 1;
+                               this.havocbot_personal_waypoint_searchtime = time + 2;
+                               if(this.havocbot_personal_waypoint_failcounter >= 30)
+                               {
+                                       LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin),"\n");
+                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+                                       delete(this.havocbot_personal_waypoint);
+                                       return CMD_STATUS_ERROR;
+                               }
+                               else
+                                       LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n");
+                       }
+               }
+
+               if(autocvar_bot_debug_goalstack)
+                       debuggoalstack(this);
+
+               // Heading
+               vector dir = ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - (this.origin + this.view_ofs);
+               dir.z = 0;
+               bot_aimdir(this, dir, -1);
+
+               // Go!
+               havocbot_movetogoal(this);
+
+               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED)
+               {
+                       // Step 5: Waypoint reached
+                       LOG_TRACE(this.netname, "'s personal waypoint reached\n");
+                       delete(this.havocbot_personal_waypoint);
+                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+                       return CMD_STATUS_FINISHED;
+               }
+
+               return CMD_STATUS_EXECUTING;
+       }
+
+       // Step 2: Linking waypoint
+       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_LINKING)
+       {
+               // Wait until it is linked
+               if(!this.havocbot_personal_waypoint.wplinked)
+               {
+                       LOG_TRACE(this.netname, " waiting for personal waypoint to be linked\n");
+                       return CMD_STATUS_EXECUTING;
+               }
+
+               this.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame
+               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
+
+               // Step 3: Route to waypoint
+               LOG_TRACE(this.netname, " walking to its personal waypoint\n");
+
+               return CMD_STATUS_EXECUTING;
+       }
+
+       // Step 1: Spawning waypoint
+       wp = waypoint_spawnpersonal(this, pos);
+       if(wp==NULL)
+       {
+               LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos),"\n");
+               return CMD_STATUS_ERROR;
+       }
+
+       this.havocbot_personal_waypoint = wp;
+       this.havocbot_personal_waypoint_failcounter = 0;
+       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+
+       // if pos is inside a teleport, then let's mark it as teleport waypoint
+       FOREACH_ENTITY_CLASS("trigger_teleport", WarpZoneLib_BoxTouchesBrush(pos, pos, it, NULL),
+       {
+               wp.wpflags |= WAYPOINTFLAG_TELEPORT;
+               this.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(entity this)
+{
+       navigation_clearroute(this);
+       return CMD_STATUS_FINISHED;
+}
+
+void havocbot_setupbot(entity this)
+{
+       this.bot_ai = havocbot_ai;
+       this.cmd_moveto = havocbot_moveto;
+       this.cmd_resetgoal = havocbot_resetgoal;
+
+       havocbot_chooserole(this);
+}
+
+vector havocbot_dodge(entity this)
+{
+       // 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 != this)
+               {
+                       vl = vlen(head.velocity);
+                       if (vl > autocvar_sv_maxspeed * 0.3)
+                       {
+                               n = normalize(head.velocity);
+                               v = this.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 - this.origin);
+                               if (bestdanger < danger)
+                               {
+                                       bestdanger = danger;
+                                       dodge = normalize(this.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
new file mode 100644 (file)
index 0000000..4a391b6
--- /dev/null
@@ -0,0 +1,64 @@
+#pragma once
+
+/*
+ * 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(entity this);
+void havocbot_aim(entity this);
+void havocbot_setupbot(entity this);
+void havocbot_movetogoal(entity this);
+void havocbot_chooserole(entity this);
+void havocbot_chooseenemy(entity this);
+void havocbot_chooseweapon(entity this);
+void havocbot_bunnyhop(entity this, vector dir);
+void havocbot_keyboard_movement(entity this, vector destorg);
+
+float havocbot_resetgoal(entity this);
+float havocbot_moveto(entity this, vector pos);
+float havocbot_moveto_refresh_route(entity this);
+
+vector havocbot_dodge(entity this);
+
+.void(entity this) havocbot_role;
+.void(entity this) havocbot_previous_role;
+
+void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items;
+void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
+
+/*
+ * Imports
+ */
+
+.entity draggedby;
+.float ladder_time;
+.entity ladder_entity;
diff --git a/qcsrc/server/bot/default/havocbot/roles.qc b/qcsrc/server/bot/default/havocbot/roles.qc
new file mode 100644 (file)
index 0000000..fffe6e4
--- /dev/null
@@ -0,0 +1,210 @@
+#include "roles.qh"
+
+#include "havocbot.qh"
+
+#include "../cvars.qh"
+
+#include "../bot.qh"
+#include "../navigation.qh"
+
+.float max_armorvalue;
+.float havocbot_role_timeout;
+
+.void(entity this) havocbot_previous_role;
+.void(entity this) havocbot_role;
+
+void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius)
+{
+       float rating, d, discard, friend_distance, enemy_distance;
+       vector o;
+       ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
+
+       FOREACH_ENTITY_FLOAT(bot_pickup, true,
+       {
+               o = (it.absmin + it.absmax) * 0.5;
+               friend_distance = 10000; enemy_distance = 10000;
+               rating = 0;
+
+               if(!it.solid || vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) )
+                       continue;
+
+               // Check if the item can be picked up safely
+               if(it.classname == "droppedweapon")
+               {
+                       traceline(o, o + '0 0 -1500', true, NULL);
+
+                       d = pointcontents(trace_endpos + '0 0 1');
+                       if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
+                               continue;
+                       if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
+                               continue;
+               }
+               else
+               {
+                       // Ignore items under water
+                       traceline(it.origin + it.maxs, it.origin + it.maxs, MOVE_NORMAL, it);
+                       if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
+                               continue;
+               }
+
+               if(teamplay)
+               {
+                       discard = false;
+
+                       entity picker = it;
+                       FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it),
+                       {
+                               d = vlen(it.origin - o); // distance between player and item
+
+                               if ( it.team == this.team )
+                               {
+                                       if ( !IS_REAL_CLIENT(it) || discard )
+                                               continue;
+
+                                       if( d > friend_distance)
+                                               continue;
+
+                                       friend_distance = d;
+
+                                       discard = true;
+
+                                       if( picker.health && it.health > this.health )
+                                               continue;
+
+                                       if( picker.armorvalue && it.armorvalue > this.armorvalue)
+                                               continue;
+
+                                       if( picker.weapons )
+                                       if( picker.weapons & ~it.weapons )
+                                               continue;
+
+                                       if (picker.ammo_shells && it.ammo_shells > this.ammo_shells)
+                                               continue;
+
+                                       if (picker.ammo_nails && it.ammo_nails > this.ammo_nails)
+                                               continue;
+
+                                       if (picker.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
+                                               continue;
+
+                                       if (picker.ammo_cells && it.ammo_cells > this.ammo_cells)
+                                               continue;
+
+                                       if (picker.ammo_plasma && it.ammo_plasma > this.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 && vdist(o - org, <, enemy_distance)) ||
+                               (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
+                               rating = it.bot_pickupevalfunc(this, it);
+
+               }
+               else
+                       rating = it.bot_pickupevalfunc(this, it);
+
+               if(rating > 0)
+                       navigation_routerating(this, it, rating * ratingscale, 2000);
+       });
+}
+
+void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius)
+{
+       FOREACH_ENTITY_CLASS("dom_controlpoint", vdist((((it.absmin + it.absmax) * 0.5) - org), <, sradius),
+       {
+               if(it.cnt > -1) // this is just being fought
+                       navigation_routerating(this, it, ratingscale, 5000);
+               else if(it.goalentity.cnt == 0) // unclaimed
+                       navigation_routerating(this, it, ratingscale * 0.5, 5000);
+               else if(it.goalentity.team != this.team) // other team's point
+                       navigation_routerating(this, it, ratingscale * 0.2, 5000);
+       });
+}
+
+void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius)
+{
+       if (autocvar_bot_nofire)
+               return;
+
+       // don't chase players if we're under water
+       if(this.waterlevel>WATERLEVEL_WETFEET)
+               return;
+
+       int t;
+
+       FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), LAMBDA(
+               // TODO: Merge this logic with the bot_shouldattack function
+               if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius))
+                       continue;
+
+               // rate only visible enemies
+               /*
+               traceline(this.origin + this.view_ofs, it.origin, MOVE_NOMONSTERS, this);
+               if (trace_fraction < 1 || trace_ent != it)
+                       continue;
+               */
+
+               if((it.flags & FL_INWATER) || (it.flags & FL_PARTIALGROUND))
+                       continue;
+
+               // not falling
+               if((IS_ONGROUND(it)) == 0)
+               {
+                       traceline(it.origin, it.origin + '0 0 -1500', true, NULL);
+                       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(it.origin, it.mins, it.maxs, trace_endpos))
+                               continue;
+               }
+
+               // TODO: rate waypoints near the targetted player at that moment, instead of the player itthis
+               //               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 = (this.health + this.armorvalue ) / (it.health + it.armorvalue );
+               navigation_routerating(this, it, t * ratingscale, 2000);
+       ));
+}
+
+// legacy bot role for standard gamemodes
+// go to best items
+void havocbot_role_generic(entity this)
+{
+       if(IS_DEAD(this))
+               return;
+
+       if (this.bot_strategytime < time)
+       {
+               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start(this);
+               havocbot_goalrating_items(this, 10000, this.origin, 10000);
+               havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000);
+               //havocbot_goalrating_waypoints(1, this.origin, 1000);
+               navigation_goalrating_end(this);
+       }
+}
+
+void havocbot_chooserole_generic(entity this)
+{
+       this.havocbot_role = havocbot_role_generic;
+}
+
+void havocbot_chooserole(entity this)
+{
+       LOG_TRACE("choosing a role...\n");
+       this.bot_strategytime = 0;
+       if(!MUTATOR_CALLHOOK(HavocBot_ChooseRole, this))
+               havocbot_chooserole_generic(this);
+}
diff --git a/qcsrc/server/bot/default/havocbot/roles.qh b/qcsrc/server/bot/default/havocbot/roles.qh
new file mode 100644 (file)
index 0000000..5b1f2b5
--- /dev/null
@@ -0,0 +1,2 @@
+#pragma once
+void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius);
diff --git a/qcsrc/server/bot/default/havocbot/scripting.qh b/qcsrc/server/bot/default/havocbot/scripting.qh
new file mode 100644 (file)
index 0000000..07cb4d6
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+void bot_clearqueue(entity bot);
diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc
new file mode 100644 (file)
index 0000000..5ab07b1
--- /dev/null
@@ -0,0 +1,1213 @@
+#include "navigation.qh"
+
+#include "cvars.qh"
+
+#include "bot.qh"
+#include "waypoints.qh"
+
+#include <common/t_items.qh>
+
+#include <common/items/all.qh>
+
+#include <common/constants.qh>
+#include <common/triggers/trigger/jumppads.qh>
+
+.float speed;
+
+// rough simulation of walking from one point to another to test if a path
+// can be traveled, used for waypoint linking and havocbot
+
+bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode)
+{
+       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(e, 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(e, 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(e, trace_endpos);
+
+                       if (trace_fraction < 1)
+                       {
+                               swimming = true;
+                               org = trace_endpos - normalize(org - trace_endpos) * stepdist;
+                               for (; org.z < end.z + e.maxs.z; org.z += stepdist)
+                               {
+                                               if(autocvar_bot_debug_tracewalk)
+                                                       debugnode(e, 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(e, 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(entity this)
+{
+       //print("bot ", etos(this), " clear\n");
+       this.navigation_hasgoals = false;
+       this.goalcurrent = NULL;
+       this.goalstack01 = NULL;
+       this.goalstack02 = NULL;
+       this.goalstack03 = NULL;
+       this.goalstack04 = NULL;
+       this.goalstack05 = NULL;
+       this.goalstack06 = NULL;
+       this.goalstack07 = NULL;
+       this.goalstack08 = NULL;
+       this.goalstack09 = NULL;
+       this.goalstack10 = NULL;
+       this.goalstack11 = NULL;
+       this.goalstack12 = NULL;
+       this.goalstack13 = NULL;
+       this.goalstack14 = NULL;
+       this.goalstack15 = NULL;
+       this.goalstack16 = NULL;
+       this.goalstack17 = NULL;
+       this.goalstack18 = NULL;
+       this.goalstack19 = NULL;
+       this.goalstack20 = NULL;
+       this.goalstack21 = NULL;
+       this.goalstack22 = NULL;
+       this.goalstack23 = NULL;
+       this.goalstack24 = NULL;
+       this.goalstack25 = NULL;
+       this.goalstack26 = NULL;
+       this.goalstack27 = NULL;
+       this.goalstack28 = NULL;
+       this.goalstack29 = NULL;
+       this.goalstack30 = NULL;
+       this.goalstack31 = NULL;
+}
+
+// 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 this, entity e)
+{
+       //print("bot ", etos(this), " push ", etos(e), "\n");
+       this.goalstack31 = this.goalstack30;
+       this.goalstack30 = this.goalstack29;
+       this.goalstack29 = this.goalstack28;
+       this.goalstack28 = this.goalstack27;
+       this.goalstack27 = this.goalstack26;
+       this.goalstack26 = this.goalstack25;
+       this.goalstack25 = this.goalstack24;
+       this.goalstack24 = this.goalstack23;
+       this.goalstack23 = this.goalstack22;
+       this.goalstack22 = this.goalstack21;
+       this.goalstack21 = this.goalstack20;
+       this.goalstack20 = this.goalstack19;
+       this.goalstack19 = this.goalstack18;
+       this.goalstack18 = this.goalstack17;
+       this.goalstack17 = this.goalstack16;
+       this.goalstack16 = this.goalstack15;
+       this.goalstack15 = this.goalstack14;
+       this.goalstack14 = this.goalstack13;
+       this.goalstack13 = this.goalstack12;
+       this.goalstack12 = this.goalstack11;
+       this.goalstack11 = this.goalstack10;
+       this.goalstack10 = this.goalstack09;
+       this.goalstack09 = this.goalstack08;
+       this.goalstack08 = this.goalstack07;
+       this.goalstack07 = this.goalstack06;
+       this.goalstack06 = this.goalstack05;
+       this.goalstack05 = this.goalstack04;
+       this.goalstack04 = this.goalstack03;
+       this.goalstack03 = this.goalstack02;
+       this.goalstack02 = this.goalstack01;
+       this.goalstack01 = this.goalcurrent;
+       this.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(entity this)
+{
+       //print("bot ", etos(this), " pop\n");
+       this.goalcurrent = this.goalstack01;
+       this.goalstack01 = this.goalstack02;
+       this.goalstack02 = this.goalstack03;
+       this.goalstack03 = this.goalstack04;
+       this.goalstack04 = this.goalstack05;
+       this.goalstack05 = this.goalstack06;
+       this.goalstack06 = this.goalstack07;
+       this.goalstack07 = this.goalstack08;
+       this.goalstack08 = this.goalstack09;
+       this.goalstack09 = this.goalstack10;
+       this.goalstack10 = this.goalstack11;
+       this.goalstack11 = this.goalstack12;
+       this.goalstack12 = this.goalstack13;
+       this.goalstack13 = this.goalstack14;
+       this.goalstack14 = this.goalstack15;
+       this.goalstack15 = this.goalstack16;
+       this.goalstack16 = this.goalstack17;
+       this.goalstack17 = this.goalstack18;
+       this.goalstack18 = this.goalstack19;
+       this.goalstack19 = this.goalstack20;
+       this.goalstack20 = this.goalstack21;
+       this.goalstack21 = this.goalstack22;
+       this.goalstack22 = this.goalstack23;
+       this.goalstack23 = this.goalstack24;
+       this.goalstack24 = this.goalstack25;
+       this.goalstack25 = this.goalstack26;
+       this.goalstack26 = this.goalstack27;
+       this.goalstack27 = this.goalstack28;
+       this.goalstack28 = this.goalstack29;
+       this.goalstack29 = this.goalstack30;
+       this.goalstack30 = this.goalstack31;
+       this.goalstack31 = NULL;
+}
+
+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, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), org, bot_navigation_movemode))
+                                       return true;
+                       }
+                       else
+                       {
+                               if (tracewalk(ent, org, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), 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)
+{
+       vector pm1 = ent.origin + ent.mins;
+       vector pm2 = ent.origin + ent.maxs;
+
+       // do two scans, because box test is cheaper
+       IL_EACH(g_waypoints, it != ent && it != except,
+       {
+               if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
+                       return it;
+       });
+
+       vector org = ent.origin + 0.5 * (ent.mins + ent.maxs);
+       org.z = ent.origin.z + ent.mins.z - STAT(PL_MIN, NULL).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);
+
+       entity best = NULL;
+       vector v;
+
+       // box check failed, try walk
+       IL_EACH(g_waypoints, it != ent,
+       {
+               if(it.wpisbox)
+               {
+                       vector wm1 = it.origin + it.mins;
+                       vector wm2 = it.origin + it.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 = it.origin;
+               if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist))
+               {
+                       bestdist = vlen(v - org);
+                       best = it;
+               }
+       });
+       return best;
+}
+entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
+{
+       entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, NULL);
+       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 this, float maxdist)
+{
+       vector v, m1, m2;
+//     navigation_testtracewalk = true;
+       int c = 0;
+       IL_EACH(g_waypoints, !it.wpconsidered,
+       {
+               if (it.wpisbox)
+               {
+                       m1 = it.origin + it.mins;
+                       m2 = it.origin + it.maxs;
+                       v = this.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 = it.origin;
+               vector diff = v - this.origin;
+               diff.z = max(0, diff.z);
+               if(vdist(diff, <, maxdist))
+               {
+                       it.wpconsidered = true;
+                       if (tracewalk(this, this.origin, this.mins, this.maxs, v, bot_navigation_movemode))
+                       {
+                               it.wpnearestpoint = v;
+                               it.wpcost = vlen(v - this.origin) + it.dmg;
+                               it.wpfire = 1;
+                               it.enemy = NULL;
+                               c = c + 1;
+                       }
+               }
+       });
+       //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 this, entity fixed_source_waypoint)
+{
+       float cost, cost2;
+       vector p;
+
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpconsidered = false;
+               it.wpnearestpoint = '0 0 0';
+               it.wpcost = 10000000;
+               it.wpfire = 0;
+               it.enemy = NULL;
+       });
+
+       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 = NULL;
+       }
+       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 increment, maxdistance;
+               if(IS_ONGROUND(this))
+               {
+                       increment = 750;
+                       maxdistance = 50000;
+               }
+               else
+               {
+                       increment = 500;
+                       maxdistance = 1500;
+               }
+
+               for(int j = increment; !navigation_markroutes_nearestwaypoints(this, j) && j < maxdistance; j += increment);
+       }
+
+       bool searching = true;
+       while (searching)
+       {
+               searching = false;
+               IL_EACH(g_waypoints, it.wpfire,
+               {
+                       searching = true;
+                       it.wpfire = 0;
+                       cost = it.wpcost;
+                       p = it.wpnearestpoint;
+                       entity wp;
+                       wp = it.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp00mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp01mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp02mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp03mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp04mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp05mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp06mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp07mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp08mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp09mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp10mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp11mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp12mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp13mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp14mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp15mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp16mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp17mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp18mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp19mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp20mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp21mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp22mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp23mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp24mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp25mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp26mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp27mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp28mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp29mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp30mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp31mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+               });
+       }
+}
+
+// queries the entire spawnfunc_waypoint network for pathes leading to the bot
+void navigation_markroutes_inverted(entity fixed_source_waypoint)
+{
+       float cost, cost2;
+       vector p;
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpconsidered = false;
+               it.wpnearestpoint = '0 0 0';
+               it.wpcost = 10000000;
+               it.wpfire = 0;
+               it.enemy = NULL;
+       });
+
+       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 = NULL;
+       }
+       else
+       {
+               error("need to start with a waypoint\n");
+       }
+
+       bool searching = true;
+       while (searching)
+       {
+               searching = false;
+               IL_EACH(g_waypoints, it.wpfire,
+               {
+                       searching = true;
+                       it.wpfire = 0;
+                       cost = it.wpcost; // cost to walk from it to home
+                       p = it.wpnearestpoint;
+                       entity wp = it;
+                       IL_EACH(g_waypoints, true,
+                       {
+                               if(wp != it.wp00) if(wp != it.wp01) if(wp != it.wp02) if(wp != it.wp03)
+                               if(wp != it.wp04) if(wp != it.wp05) if(wp != it.wp06) if(wp != it.wp07)
+                               if(wp != it.wp08) if(wp != it.wp09) if(wp != it.wp10) if(wp != it.wp11)
+                               if(wp != it.wp12) if(wp != it.wp13) if(wp != it.wp14) if(wp != it.wp15)
+                               if(wp != it.wp16) if(wp != it.wp17) if(wp != it.wp18) if(wp != it.wp19)
+                               if(wp != it.wp20) if(wp != it.wp21) if(wp != it.wp22) if(wp != it.wp23)
+                               if(wp != it.wp24) if(wp != it.wp25) if(wp != it.wp26) if(wp != it.wp27)
+                               if(wp != it.wp28) if(wp != it.wp29) if(wp != it.wp30) if(wp != it.wp31)
+                                       continue;
+                               cost2 = cost + it.dmg;
+                               navigation_markroutes_checkwaypoint(wp, it, cost2, p);
+                       });
+               });
+       }
+}
+
+// updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item
+void navigation_routerating(entity this, entity e, float f, float rangebias)
+{
+       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(this.items & IT_JETPACK)
+       if(autocvar_bot_ai_navigation_jetpack)
+       if(vdist(this.origin - o, >, autocvar_bot_ai_navigation_jetpack_mindistance))
+       {
+               vector pointa, pointb;
+
+               LOG_DEBUG("jetpack ai: evaluating path for ", e.classname, "\n");
+
+               // Point A
+               traceline(this.origin, this.origin + '0 0 65535', MOVE_NORMAL, this);
+               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, this);
+
+               if(trace_fraction==1)
+               {
+                       LOG_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' * (STAT(PL_MAX, NULL).z - STAT(PL_MIN, NULL).z) * 10;
+
+                       do{
+                               npa = pointa + down;
+                               npb = pointb + down;
+
+                               if(npa.z<=this.absmax.z)
+                                       break;
+
+                               if(npb.z<=e.absmax.z)
+                                       break;
+
+                               traceline(npa, npb, MOVE_NORMAL, this);
+                               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 - this.origin.z);
+
+                       t = zdistance / autocvar_g_jetpack_maxspeed_up;
+                       t += xydistance / autocvar_g_jetpack_maxspeed_side;
+                       fuel = t * autocvar_g_jetpack_fuel * 0.8;
+
+                       LOG_DEBUG(strcat("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel), "\n"));
+
+                       // enough fuel ?
+                       if(this.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)
+                               {
+                                       LOG_DEBUG(strcat("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")\n"));
+                                       navigation_bestrating = f;
+                                       navigation_bestgoal = e;
+                                       this.navigation_jetpack_goal = e;
+                                       this.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
+                       {
+                               LOG_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)
+                               {
+                                       LOG_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;
+       }
+
+       LOG_DEBUG(strcat("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")\n"));
+       if (nwp)
+       if (nwp.wpcost < 10000000)
+       {
+               //te_wizspike(nwp.wpnearestpoint);
+               LOG_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)));
+               LOG_DEBUG(strcat("considering ", e.classname, " (with rating ", ftos(f), ")\n"));
+               if (navigation_bestrating < f)
+               {
+                       LOG_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
+bool navigation_routetogoal(entity this, entity e, vector startposition)
+{
+       this.goalentity = e;
+
+       // if there is no goal, just exit
+       if (!e)
+               return false;
+
+       this.navigation_hasgoals = true;
+
+       // put the entity on the goal stack
+       //print("routetogoal ", etos(e), "\n");
+       navigation_pushroute(this, e);
+
+       if(g_jetpack)
+       if(e==this.navigation_jetpack_goal)
+               return true;
+
+       // if it can reach the goal there is nothing more to do
+       if (tracewalk(this, startposition, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), (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 == NULL)
+               return false;
+
+       for (;;)
+       {
+               // add the spawnfunc_waypoint to the path
+               navigation_pushroute(this, e);
+               e = e.enemy;
+
+               if(e==NULL)
+                       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(entity this)
+{
+       vector org, m1, m2;
+       org = this.origin;
+       m1 = org + this.mins;
+       m2 = org + this.maxs;
+
+       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+       {
+               if(this.lastteleporttime>0)
+               if(time-this.lastteleporttime<(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15)
+               {
+                       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+                       if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+                       {
+                               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+                       }
+                       navigation_poproute(this);
+                       return;
+               }
+       }
+
+       // If for some reason the bot is closer to the next goal, pop the current one
+       if(this.goalstack01)
+       if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin))
+       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
+       if(tracewalk(this, this.origin, this.mins, this.maxs, (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5, bot_navigation_movemode))
+       {
+               LOG_DEBUG(strcat("path optimized for ", this.netname, ", removed a goal from the queue\n"));
+               navigation_poproute(this);
+               // 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 itthis
+       if(IS_PLAYER(this.goalcurrent))
+               navigation_poproute(this);
+
+       // aid for detecting jump pads better (distance based check fails sometimes)
+       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT && this.jumppadcount > 0 )
+               navigation_poproute(this);
+
+       // Loose goal touching check when running
+       if(this.aistatus & AI_STATUS_RUNNING)
+       if(this.speed >= autocvar_sv_maxspeed) // if -really- running
+       if(this.goalcurrent.classname=="waypoint")
+       {
+               if(vdist(this.origin - this.goalcurrent.origin, <, 150))
+               {
+                       traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL);
+                       if(trace_fraction==1)
+                       {
+                               // Detect personal waypoints
+                               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+                               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+                               {
+                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+                               }
+
+                               navigation_poproute(this);
+                       }
+               }
+       }
+
+       while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax))
+       {
+               // Detect personal waypoints
+               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+               {
+                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+               }
+
+               navigation_poproute(this);
+       }
+}
+
+// begin a goal selection session (queries spawnfunc_waypoint network)
+void navigation_goalrating_start(entity this)
+{
+       if(this.aistatus & AI_STATUS_STUCK)
+               return;
+
+       this.navigation_jetpack_goal = NULL;
+       navigation_bestrating = -1;
+       this.navigation_hasgoals = false;
+       navigation_clearroute(this);
+       navigation_bestgoal = NULL;
+       navigation_markroutes(this, NULL);
+}
+
+// ends a goal selection session (updates goal stack to the best goal)
+void navigation_goalrating_end(entity this)
+{
+       if(this.aistatus & AI_STATUS_STUCK)
+               return;
+
+       navigation_routetogoal(this, navigation_bestgoal, this.origin);
+       LOG_DEBUG(strcat("best goal ", this.goalcurrent.classname , "\n"));
+
+       // If the bot got stuck then try to reach the farthest waypoint
+       if (!this.navigation_hasgoals)
+       if (autocvar_bot_wander_enable)
+       {
+               if (!(this.aistatus & AI_STATUS_STUCK))
+               {
+                       LOG_DEBUG(strcat(this.netname, " cannot walk to any goal\n"));
+                       this.aistatus |= AI_STATUS_STUCK;
+               }
+
+               this.navigation_hasgoals = false; // Reset this value
+       }
+}
+
+void botframe_updatedangerousobjects(float maxupdate)
+{
+       vector m1, m2, v, o;
+       float c, d, danger;
+       c = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               danger = 0;
+               m1 = it.mins;
+               m2 = it.maxs;
+               FOREACH_ENTITY_FLOAT(bot_dodge, true,
+               {
+                       v = it.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 = (it.absmin + it.absmax) * 0.5;
+                       d = it.bot_dodgerating - vlen(o - v);
+                       if (d > 0)
+                       {
+                               traceline(o, v, true, NULL);
+                               if (trace_fraction == 1)
+                                       danger = danger + d;
+                       }
+               });
+               it.dmg = danger;
+               c = c + 1;
+               if (c >= maxupdate)
+                       break;
+       });
+}
+
+void navigation_unstuck(entity this)
+{
+       float search_radius = 1000;
+
+       if (!autocvar_bot_wander_enable)
+               return;
+
+       if (!bot_waypoint_queue_owner)
+       {
+               LOG_DEBUG(strcat(this.netname, " sutck, taking over the waypoints queue\n"));
+               bot_waypoint_queue_owner = this;
+               bot_waypoint_queue_bestgoal = NULL;
+               bot_waypoint_queue_bestgoalrating = 0;
+       }
+
+       if(bot_waypoint_queue_owner!=this)
+               return;
+
+       if (bot_waypoint_queue_goal)
+       {
+               // evaluate the next goal on the queue
+               float d = vlen(this.origin - bot_waypoint_queue_goal.origin);
+               LOG_DEBUG(strcat(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d), "\n"));
+               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), 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)
+                       {
+                               LOG_DEBUG(strcat(this.netname, " stuck, reachable waypoint found, heading to it\n"));
+                               navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin);
+                               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+                               this.aistatus &= ~AI_STATUS_STUCK;
+                       }
+                       else
+                       {
+                               LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n"));
+                       }
+
+                       bot_waypoint_queue_owner = NULL;
+               }
+       }
+       else
+       {
+               if(bot_strategytoken!=this)
+                       return;
+
+               // build a new queue
+               LOG_DEBUG(strcat(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu\n"));
+
+               entity first = NULL;
+
+               FOREACH_ENTITY_RADIUS(this.origin, search_radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
+               {
+                       if(bot_waypoint_queue_goal)
+                               bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = it;
+                       else
+                               first = it;
+
+                       bot_waypoint_queue_goal = it;
+                       bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
+               });
+
+               if (first)
+                       bot_waypoint_queue_goal = first;
+               else
+               {
+                       LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n"));
+                       bot_waypoint_queue_owner = NULL;
+               }
+       }
+}
+
+// Support for debugging tracewalk visually
+
+void debugresetnodes()
+{
+       debuglastnode = '0 0 0';
+}
+
+void debugnode(entity this, vector node)
+{
+       if (!IS_PLAYER(this))
+               return;
+
+       if(debuglastnode=='0 0 0')
+       {
+               debuglastnode = node;
+               return;
+       }
+
+       te_lightning2(NULL, 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(entity this)
+{
+       entity goal;
+       vector org, go;
+
+       if(this.goalcounter==0)goal=this.goalcurrent;
+       else if(this.goalcounter==1)goal=this.goalstack01;
+       else if(this.goalcounter==2)goal=this.goalstack02;
+       else if(this.goalcounter==3)goal=this.goalstack03;
+       else if(this.goalcounter==4)goal=this.goalstack04;
+       else if(this.goalcounter==5)goal=this.goalstack05;
+       else if(this.goalcounter==6)goal=this.goalstack06;
+       else if(this.goalcounter==7)goal=this.goalstack07;
+       else if(this.goalcounter==8)goal=this.goalstack08;
+       else if(this.goalcounter==9)goal=this.goalstack09;
+       else if(this.goalcounter==10)goal=this.goalstack10;
+       else if(this.goalcounter==11)goal=this.goalstack11;
+       else if(this.goalcounter==12)goal=this.goalstack12;
+       else if(this.goalcounter==13)goal=this.goalstack13;
+       else if(this.goalcounter==14)goal=this.goalstack14;
+       else if(this.goalcounter==15)goal=this.goalstack15;
+       else if(this.goalcounter==16)goal=this.goalstack16;
+       else if(this.goalcounter==17)goal=this.goalstack17;
+       else if(this.goalcounter==18)goal=this.goalstack18;
+       else if(this.goalcounter==19)goal=this.goalstack19;
+       else if(this.goalcounter==20)goal=this.goalstack20;
+       else if(this.goalcounter==21)goal=this.goalstack21;
+       else if(this.goalcounter==22)goal=this.goalstack22;
+       else if(this.goalcounter==23)goal=this.goalstack23;
+       else if(this.goalcounter==24)goal=this.goalstack24;
+       else if(this.goalcounter==25)goal=this.goalstack25;
+       else if(this.goalcounter==26)goal=this.goalstack26;
+       else if(this.goalcounter==27)goal=this.goalstack27;
+       else if(this.goalcounter==28)goal=this.goalstack28;
+       else if(this.goalcounter==29)goal=this.goalstack29;
+       else if(this.goalcounter==30)goal=this.goalstack30;
+       else if(this.goalcounter==31)goal=this.goalstack31;
+       else goal=NULL;
+
+       if(goal==NULL)
+       {
+               this.goalcounter = 0;
+               this.lastposition='0 0 0';
+               return;
+       }
+
+       if(this.lastposition=='0 0 0')
+               org = this.origin;
+       else
+               org = this.lastposition;
+
+
+       go = ( goal.absmin + goal.absmax ) * 0.5;
+       te_lightning2(NULL, org, go);
+       this.lastposition = go;
+
+       this.goalcounter++;
+}
diff --git a/qcsrc/server/bot/default/navigation.qh b/qcsrc/server/bot/default/navigation.qh
new file mode 100644 (file)
index 0000000..ad01776
--- /dev/null
@@ -0,0 +1,77 @@
+#pragma once
+/*
+ * Globals and Fields
+ */
+
+float navigation_bestrating;
+float bot_navigation_movemode;
+float navigation_testtracewalk;
+
+vector jumpstepheightvec;
+vector stepheightvec;
+
+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(entity this, vector node);
+void debugnodestatus(vector position, float status);
+
+void debuggoalstack(entity this);
+
+float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
+
+float navigation_markroutes_nearestwaypoints(entity this, float maxdist);
+float navigation_routetogoal(entity this, entity e, vector startposition);
+
+void navigation_clearroute(entity this);
+void navigation_pushroute(entity this, entity e);
+void navigation_poproute(entity this);
+void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p);
+void navigation_markroutes(entity this, entity fixed_source_waypoint);
+void navigation_markroutes_inverted(entity fixed_source_waypoint);
+void navigation_routerating(entity this, entity e, float f, float rangebias);
+void navigation_poptouchedgoals(entity this);
+void navigation_goalrating_start(entity this);
+void navigation_goalrating_end(entity this);
+void navigation_unstuck(entity this);
+
+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);
diff --git a/qcsrc/server/bot/default/scripting.qc b/qcsrc/server/bot/default/scripting.qc
new file mode 100644 (file)
index 0000000..8b2f9e6
--- /dev/null
@@ -0,0 +1,1342 @@
+#include "scripting.qh"
+
+#include "cvars.qh"
+
+#include <common/state.qh>
+#include <common/physics/player.qh>
+
+#include "bot.qh"
+
+.int state;
+
+.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);
+}
+
+bool bot_havecommand(entity this, int idx)
+{
+       if(!this.bot_cmdqueuebuf_allocated)
+               return false;
+       if(idx < this.bot_cmdqueuebuf_start)
+               return false;
+       if(idx >= this.bot_cmdqueuebuf_end)
+               return false;
+       return true;
+}
+
+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(entity this, string placename)
+{
+       entity e;
+       if(substring(placename, 0, 1) == "@")
+       {
+               int i, p;
+               placename = substring(placename, 1, -1);
+               string s, s2;
+               for(i = 0; i < this.bot_places_count; ++i)
+                       if(this.(bot_placenames[i]) == placename)
+                               return this.(bot_places[i]);
+               // now: i == this.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(NULL, targetname, s);
+               if(!e)
+                       LOG_INFO("invalid place ", s, "\n");
+               if(i < MAX_BOT_PLACES)
+               {
+                       this.(bot_placenames[i]) = strzone(placename);
+                       this.(bot_places[i]) = e;
+                       this.bot_places_count += 1;
+               }
+               return e;
+       }
+       else
+       {
+               e = find(NULL, 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 NULL;
+}
+
+// Returns a bot by number on list
+entity find_bot_by_number(float number)
+{
+       entity bot;
+       float c = 0;
+
+       if(!number)
+               return NULL;
+
+       bot = findchainflags(flags, FL_CLIENT); // TODO: doesn't findchainflags loop backwards through entities?
+       while (bot)
+       {
+               if(IS_BOT_CLIENT(bot))
+               {
+                       if(++c==number)
+                               return bot;
+               }
+               bot = bot.chain;
+       }
+
+       return NULL;
+}
+
+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;
+
+float bot_cmd_cc(entity this)
+{
+       SV_ParseClientCommand(this, bot_cmd.bot_cmd_parm_string);
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_impulse(entity this)
+{
+       this.impulse = bot_cmd.bot_cmd_parm_float;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_continue(entity this)
+{
+       this.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
+       return CMD_STATUS_FINISHED;
+}
+
+.float bot_cmd_wait_time;
+float bot_cmd_wait(entity this)
+{
+       if(this.bot_exec_status & BOT_EXEC_STATUS_WAITING)
+       {
+               if(time>=this.bot_cmd_wait_time)
+               {
+                       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
+                       return CMD_STATUS_FINISHED;
+               }
+               else
+                       return CMD_STATUS_EXECUTING;
+       }
+
+       this.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
+       this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
+       return CMD_STATUS_EXECUTING;
+}
+
+float bot_cmd_wait_until(entity this)
+{
+       if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
+       {
+               this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
+               return CMD_STATUS_EXECUTING;
+       }
+       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_barrier(entity this)
+{
+       // 0 = no barrier, 1 = waiting, 2 = waiting finished
+
+       if(this.bot_barrier == 0) // initialization
+       {
+               this.bot_barrier = 1;
+
+               //this.colormod = '4 4 0';
+       }
+
+       if(this.bot_barrier == 1) // find other bots
+       {
+               FOREACH_CLIENT(it.isbot, LAMBDA(
+                       if(it.bot_cmdqueuebuf_allocated)
+                       if(it.bot_barrier != 1)
+                               return CMD_STATUS_EXECUTING; // not all are at the barrier yet
+               ));
+
+               // all bots hit the barrier!
+
+               // acknowledge barrier
+               FOREACH_CLIENT(it.isbot, LAMBDA(it.bot_barrier = 2));
+
+               bot_barriertime = time;
+       }
+
+       // if we get here, the barrier is finished
+       // so end it...
+       this.bot_barrier = 0;
+       //this.colormod = '0 0 0';
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_turn(entity this)
+{
+       this.v_angle_y = this.v_angle.y + bot_cmd.bot_cmd_parm_float;
+       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_select_weapon(entity this)
+{
+       float id = bot_cmd.bot_cmd_parm_float;
+
+       if(id < WEP_FIRST || id > WEP_LAST)
+               return CMD_STATUS_ERROR;
+
+       if(client_hasweapon(this, Weapons_from(id), true, false))
+               PS(this).m_switchweapon = Weapons_from(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(entity this, string expr)
+{
+       // 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 this.health;
+               case "speed":
+                       return vlen(this.velocity);
+               case "flagcarrier":
+                       return ((this.flagcarried!=NULL));
+       }
+
+       LOG_INFO(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
+       return 0;
+}
+
+float bot_cmd_if(entity this)
+{
+       string expr, val_a, val_b;
+       float cmpofs;
+
+       if(this.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(this);
+               return CMD_STATUS_ERROR;
+       }
+
+       this.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(this, val_a)==bot_cmd_eval(this, val_b))
+                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
+               else
+                       this.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(this, val_a)>bot_cmd_eval(this, val_b))
+                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
+               else
+                       this.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(this, val_a)<bot_cmd_eval(this, val_b))
+                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
+               else
+                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
+
+               return CMD_STATUS_FINISHED;
+       }
+
+       if(bot_cmd_eval(this, expr))
+               this.bot_cmd_condition_status |= CMD_CONDITION_true;
+       else
+               this.bot_cmd_condition_status |= CMD_CONDITION_false;
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_else(entity this)
+{
+       this.bot_cmd_condition_status &= ~CMD_CONDITION_true_BLOCK;
+       this.bot_cmd_condition_status |= CMD_CONDITION_false_BLOCK;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_fi(entity this)
+{
+       this.bot_cmd_condition_status = CMD_CONDITION_NONE;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_resetaim(entity this)
+{
+       this.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(entity this)
+{
+       // Current direction
+       if(this.bot_cmd_aim_endtime)
+       {
+               float progress;
+
+               progress = min(1 - (this.bot_cmd_aim_endtime - time) / (this.bot_cmd_aim_endtime - this.bot_cmd_aim_begintime),1);
+               this.v_angle = this.bot_cmd_aim_begin + ((this.bot_cmd_aim_end - this.bot_cmd_aim_begin) * progress);
+
+               if(time>=this.bot_cmd_aim_endtime)
+               {
+                       this.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)
+       {
+               this.v_angle_x -= stof(argv(1));
+               this.v_angle_y += stof(argv(0));
+               return CMD_STATUS_FINISHED;
+       }
+
+       this.bot_cmd_aim_begin = this.v_angle;
+
+       this.bot_cmd_aim_end_x = this.v_angle.x - stof(argv(1));
+       this.bot_cmd_aim_end_y = this.v_angle.y + stof(argv(0));
+       this.bot_cmd_aim_end_z = 0;
+
+       this.bot_cmd_aim_begintime = time;
+       this.bot_cmd_aim_endtime = time + step;
+
+       return CMD_STATUS_EXECUTING;
+}
+
+float bot_cmd_aimtarget(entity this)
+{
+       if(this.bot_cmd_aim_endtime)
+       {
+               return bot_cmd_aim(this);
+       }
+
+       entity e;
+       string parms;
+       vector v;
+       float tokens, step;
+
+       parms = bot_cmd.bot_cmd_parm_string;
+
+       tokens = tokenizebyseparator(parms, " ");
+
+       e = bot_getplace(this, argv(0));
+       if(!e)
+               return CMD_STATUS_ERROR;
+
+       v = e.origin + (e.mins + e.maxs) * 0.5;
+
+       if(tokens==1)
+       {
+               this.v_angle = vectoangles(v - (this.origin + this.view_ofs));
+               this.v_angle_x = -this.v_angle.x;
+               return CMD_STATUS_FINISHED;
+       }
+
+       if(tokens<1||tokens>2)
+               return CMD_STATUS_ERROR;
+
+       step = stof(argv(1));
+
+       this.bot_cmd_aim_begin = this.v_angle;
+       this.bot_cmd_aim_end = vectoangles(v - (this.origin + this.view_ofs));
+       this.bot_cmd_aim_end_x = -this.bot_cmd_aim_end.x;
+
+       this.bot_cmd_aim_begintime = time;
+       this.bot_cmd_aim_endtime = time + step;
+
+       return CMD_STATUS_EXECUTING;
+}
+
+.int bot_cmd_keys;
+
+const int BOT_CMD_KEY_NONE             = 0;
+const int BOT_CMD_KEY_FORWARD  = BIT(0);
+const int BOT_CMD_KEY_BACKWARD         = BIT(1);
+const int BOT_CMD_KEY_RIGHT    = BIT(2);
+const int BOT_CMD_KEY_LEFT             = BIT(3);
+const int BOT_CMD_KEY_JUMP             = BIT(4);
+const int BOT_CMD_KEY_ATTACK1  = BIT(5);
+const int BOT_CMD_KEY_ATTACK2  = BIT(6);
+const int BOT_CMD_KEY_USE              = BIT(7);
+const int BOT_CMD_KEY_HOOK             = BIT(8);
+const int BOT_CMD_KEY_CROUCH   = BIT(9);
+const int BOT_CMD_KEY_CHAT             = BIT(10);
+
+bool bot_presskeys(entity this)
+{
+       this.movement = '0 0 0';
+       PHYS_INPUT_BUTTON_JUMP(this) = false;
+       PHYS_INPUT_BUTTON_CROUCH(this) = false;
+       PHYS_INPUT_BUTTON_ATCK(this) = false;
+       PHYS_INPUT_BUTTON_ATCK2(this) = false;
+       PHYS_INPUT_BUTTON_USE(this) = false;
+       PHYS_INPUT_BUTTON_HOOK(this) = false;
+       PHYS_INPUT_BUTTON_CHAT(this) = false;
+
+       if(this.bot_cmd_keys == BOT_CMD_KEY_NONE)
+               return false;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
+               this.movement_x = autocvar_sv_maxspeed;
+       else if(this.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
+               this.movement_x = -autocvar_sv_maxspeed;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
+               this.movement_y = autocvar_sv_maxspeed;
+       else if(this.bot_cmd_keys & BOT_CMD_KEY_LEFT)
+               this.movement_y = -autocvar_sv_maxspeed;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_JUMP)
+               PHYS_INPUT_BUTTON_JUMP(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
+               PHYS_INPUT_BUTTON_CROUCH(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
+               PHYS_INPUT_BUTTON_ATCK(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
+               PHYS_INPUT_BUTTON_ATCK2(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_USE)
+               PHYS_INPUT_BUTTON_USE(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_HOOK)
+               PHYS_INPUT_BUTTON_HOOK(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_CHAT)
+               PHYS_INPUT_BUTTON_CHAT(this) = true;
+
+       return true;
+}
+
+
+float bot_cmd_keypress_handler(entity this, string key, float enabled)
+{
+       switch(key)
+       {
+               case "all":
+                       if(enabled)
+                               this.bot_cmd_keys = power2of(20) - 1; // >:)
+                       else
+                               this.bot_cmd_keys = BOT_CMD_KEY_NONE;
+               case "forward":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
+                       break;
+               case "backward":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
+                       break;
+               case "left":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
+                       break;
+               case "right":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
+                       break;
+               case "jump":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
+                       break;
+               case "crouch":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
+                       break;
+               case "attack1":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
+                       break;
+               case "attack2":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
+                       break;
+               case "use":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_USE;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
+                       break;
+               case "hook":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
+                       break;
+               case "chat":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
+                       break;
+               default:
+                       break;
+       }
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_presskey(entity this)
+{
+       string key;
+
+       key = bot_cmd.bot_cmd_parm_string;
+
+       bot_cmd_keypress_handler(this, key,true);
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_releasekey(entity this)
+{
+       string key;
+
+       key = bot_cmd.bot_cmd_parm_string;
+
+       return bot_cmd_keypress_handler(this, key,false);
+}
+
+float bot_cmd_pause(entity this)
+{
+       PHYS_INPUT_BUTTON_DRAG(this) = false;
+       PHYS_INPUT_BUTTON_USE(this) = false;
+       PHYS_INPUT_BUTTON_ATCK(this) = false;
+       PHYS_INPUT_BUTTON_JUMP(this) = false;
+       PHYS_INPUT_BUTTON_HOOK(this) = false;
+       PHYS_INPUT_BUTTON_CHAT(this) = false;
+       PHYS_INPUT_BUTTON_ATCK2(this) = false;
+       PHYS_INPUT_BUTTON_CROUCH(this) = false;
+
+       this.movement = '0 0 0';
+       this.bot_cmd_keys = BOT_CMD_KEY_NONE;
+
+       this.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_moveto(entity this)
+{
+       return this.cmd_moveto(this, bot_cmd.bot_cmd_parm_vector);
+}
+
+float bot_cmd_movetotarget(entity this)
+{
+       entity e;
+       e = bot_getplace(this, bot_cmd.bot_cmd_parm_string);
+       if(!e)
+               return CMD_STATUS_ERROR;
+       return this.cmd_moveto(this, e.origin + (e.mins + e.maxs) * 0.5);
+}
+
+float bot_cmd_resetgoal(entity this)
+{
+       return this.cmd_resetgoal(this);
+}
+
+
+float bot_cmd_sound(entity this)
+{
+       string f;
+       f = bot_cmd.bot_cmd_parm_string;
+
+       float n = tokenizebyseparator(f, " ");
+
+       string sample = f;
+       float chan = CH_WEAPON_B;
+       float vol = VOL_BASE;
+       float atten = ATTEN_MIN;
+
+       if(n >= 1)
+               sample = argv(n - 1);
+       if(n >= 2)
+               chan = stof(argv(0));
+       if(n >= 3)
+               vol = stof(argv(1));
+       if(n >= 4)
+               atten = stof(argv(2));
+
+       precache_sound(f);
+       _sound(this, chan, sample, vol, atten);
+
+       return CMD_STATUS_FINISHED;
+}
+
+.entity tuba_note;
+float bot_cmd_debug_assert_canfire(entity this)
+{
+       float f = bot_cmd.bot_cmd_parm_float;
+
+       int slot = 0;
+       .entity weaponentity = weaponentities[slot];
+       if(this.(weaponentity).state != WS_READY)
+       {
+               if(f)
+               {
+                       this.colormod = '0 8 8';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by weaponentity state\n");
+               }
+       }
+       else if(ATTACK_FINISHED(this, slot) > time)
+       {
+               if(f)
+               {
+                       this.colormod = '8 0 8';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)\n");
+               }
+       }
+       else if(this.tuba_note)
+       {
+               if(f)
+               {
+                       this.colormod = '8 0 0';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, bot still has an active tuba note\n");
+               }
+       }
+       else
+       {
+               if(!f)
+               {
+                       this.colormod = '8 8 0';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left\n");
+               }
+       }
+
+       return CMD_STATUS_FINISHED;
+}
+
+//
+
+void bot_command_executed(entity this, bool rm)
+{
+       entity cmd;
+
+       cmd = bot_cmd;
+
+       if(rm)
+               bot_dequeuecommand(this, this.bot_cmd_execution_index);
+
+       this.bot_cmd_execution_index++;
+}
+
+void bot_setcurrentcommand(entity this)
+{
+       bot_cmd = NULL;
+
+       if(!this.bot_cmd_current)
+       {
+               this.bot_cmd_current = new_pure(bot_cmd);
+               this.bot_cmd_current.is_bot_cmd = true;
+       }
+
+       bot_cmd = this.bot_cmd_current;
+       if(bot_cmd.bot_cmd_index != this.bot_cmd_execution_index || this.bot_cmd_execution_index == 0)
+       {
+               if(bot_havecommand(this, this.bot_cmd_execution_index))
+               {
+                       string cmdstring;
+                       cmdstring = bot_readcommand(this, this.bot_cmd_execution_index);
+                       if(bot_decodecommand(cmdstring))
+                       {
+                               bot_cmd.owner = this;
+                               bot_cmd.bot_cmd_index = this.bot_cmd_execution_index;
+                       }
+                       else
+                       {
+                               // Invalid command, remove from queue
+                               bot_cmd = NULL;
+                               bot_dequeuecommand(this, this.bot_cmd_execution_index);
+                               this.bot_cmd_execution_index++;
+                       }
+               }
+               else
+                       bot_cmd = NULL;
+       }
+}
+
+void bot_resetqueues()
+{
+       FOREACH_CLIENT(it.isbot, LAMBDA(
+               it.bot_cmd_execution_index = 0;
+               bot_clearqueue(it);
+               // also, cancel all barriers
+               it.bot_barrier = 0;
+               for(int i = 0; i < it.bot_places_count; ++i)
+               {
+                       strunzone(it.(bot_placenames[i]));
+                       it.(bot_placenames[i]) = string_null;
+               }
+               it.bot_places_count = 0;
+       ));
+
+       bot_barriertime = time;
+}
+
+// Here we map commands to functions and deal with complex interactions between commands and execution states
+// NOTE: Of course you need to include your commands here too :)
+float bot_execute_commands_once(entity this)
+{
+       float status, ispressingkey;
+
+       // Find command
+       bot_setcurrentcommand(this);
+
+       // if we have no bot command, better return
+       // old logic kept pressing previously pressed keys, but that has problems
+       // (namely, it means you cannot make a bot "normal" ever again)
+       // to keep a bot walking for a while, use the "wait" bot command
+       if(bot_cmd == NULL)
+               return false;
+
+       // Ignore all commands except continue when the bot is paused
+       if(this.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
+       if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
+       {
+               if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
+               {
+                       bot_command_executed(this, true);
+                       LOG_INFO( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
+               }
+               return 1;
+       }
+
+       // Keep pressing keys raised by the "presskey" command
+       ispressingkey = boolean(bot_presskeys(this));
+
+       // Handle conditions
+       if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE))
+       if(this.bot_cmd_condition_status & CMD_CONDITION_true && this.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK)
+       {
+               bot_command_executed(this, true);
+               return -1;
+       }
+       else if(this.bot_cmd_condition_status & CMD_CONDITION_false && this.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK)
+       {
+               bot_command_executed(this, true);
+               return -1;
+       }
+
+       // Map commands to functions
+       switch(bot_cmd.bot_cmd_type)
+       {
+               case BOT_CMD_NULL:
+                       return ispressingkey;
+                       //break;
+               case BOT_CMD_PAUSE:
+                       status = bot_cmd_pause(this);
+                       break;
+               case BOT_CMD_CONTINUE:
+                       status = bot_cmd_continue(this);
+                       break;
+               case BOT_CMD_WAIT:
+                       status = bot_cmd_wait(this);
+                       break;
+               case BOT_CMD_WAIT_UNTIL:
+                       status = bot_cmd_wait_until(this);
+                       break;
+               case BOT_CMD_TURN:
+                       status = bot_cmd_turn(this);
+                       break;
+               case BOT_CMD_MOVETO:
+                       status = bot_cmd_moveto(this);
+                       break;
+               case BOT_CMD_MOVETOTARGET:
+                       status = bot_cmd_movetotarget(this);
+                       break;
+               case BOT_CMD_RESETGOAL:
+                       status = bot_cmd_resetgoal(this);
+                       break;
+               case BOT_CMD_CC:
+                       status = bot_cmd_cc(this);
+                       break;
+               case BOT_CMD_IF:
+                       status = bot_cmd_if(this);
+                       break;
+               case BOT_CMD_ELSE:
+                       status = bot_cmd_else(this);
+                       break;
+               case BOT_CMD_FI:
+                       status = bot_cmd_fi(this);
+                       break;
+               case BOT_CMD_RESETAIM:
+                       status = bot_cmd_resetaim(this);
+                       break;
+               case BOT_CMD_AIM:
+                       status = bot_cmd_aim(this);
+                       break;
+               case BOT_CMD_AIMTARGET:
+                       status = bot_cmd_aimtarget(this);
+                       break;
+               case BOT_CMD_PRESSKEY:
+                       status = bot_cmd_presskey(this);
+                       break;
+               case BOT_CMD_RELEASEKEY:
+                       status = bot_cmd_releasekey(this);
+                       break;
+               case BOT_CMD_SELECTWEAPON:
+                       status = bot_cmd_select_weapon(this);
+                       break;
+               case BOT_CMD_IMPULSE:
+                       status = bot_cmd_impulse(this);
+                       break;
+               case BOT_CMD_BARRIER:
+                       status = bot_cmd_barrier(this);
+                       break;
+               case BOT_CMD_CONSOLE:
+                       localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
+                       status = CMD_STATUS_FINISHED;
+                       break;
+               case BOT_CMD_SOUND:
+                       status = bot_cmd_sound(this);
+                       break;
+               case BOT_CMD_DEBUG_ASSERT_CANFIRE:
+                       status = bot_cmd_debug_assert_canfire(this);
+                       break;
+               default:
+                       LOG_INFO(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
+                       return 0;
+       }
+
+       if (status==CMD_STATUS_ERROR)
+               LOG_INFO(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
+
+       // Move execution pointer
+       if(status==CMD_STATUS_EXECUTING)
+       {
+               return 1;
+       }
+       else
+       {
+               if(autocvar_g_debug_bot_commands)
+               {
+                       string parms;
+
+                       switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
+                       {
+                               case BOT_CMD_PARAMETER_FLOAT:
+                                       parms = ftos(bot_cmd.bot_cmd_parm_float);
+                                       break;
+                               case BOT_CMD_PARAMETER_STRING:
+                                       parms = bot_cmd.bot_cmd_parm_string;
+                                       break;
+                               case BOT_CMD_PARAMETER_VECTOR:
+                                       parms = vtos(bot_cmd.bot_cmd_parm_vector);
+                                       break;
+                               default:
+                                       parms = "";
+                                       break;
+                       }
+                       clientcommand(this,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
+               }
+
+               bot_command_executed(this, true);
+       }
+
+       if(status == CMD_STATUS_FINISHED)
+               return -1;
+
+       return CMD_STATUS_ERROR;
+}
+
+// This function should be (the only) called directly from the bot ai loop
+int bot_execute_commands(entity this)
+{
+       int f;
+       do
+       {
+               f = bot_execute_commands_once(this);
+       }
+       while(f < 0);
+       return f;
+}
diff --git a/qcsrc/server/bot/default/scripting.qh b/qcsrc/server/bot/default/scripting.qh
new file mode 100644 (file)
index 0000000..cb6cd87
--- /dev/null
@@ -0,0 +1,81 @@
+#pragma once
+
+#define BOT_EXEC_STATUS_IDLE 0
+#define BOT_EXEC_STATUS_PAUSED 1
+#define BOT_EXEC_STATUS_WAITING 2
+
+#define CMD_STATUS_EXECUTING 0
+#define CMD_STATUS_FINISHED 1
+#define CMD_STATUS_ERROR 2
+
+
+// NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
+const int BOT_CMD_NULL                         = 0;
+const int BOT_CMD_PAUSE                = 1;
+const int BOT_CMD_CONTINUE             = 2;
+const int BOT_CMD_WAIT                         = 3;
+const int BOT_CMD_TURN                         = 4;
+const int BOT_CMD_MOVETO               = 5;
+const int BOT_CMD_RESETGOAL    = 6;    // Not implemented yet
+const int BOT_CMD_CC                   = 7;
+const int BOT_CMD_IF                   = 8;
+const int BOT_CMD_ELSE                         = 9;
+const int BOT_CMD_FI                   = 10;
+const int BOT_CMD_RESETAIM             = 11;
+const int BOT_CMD_AIM                  = 12;
+const int BOT_CMD_PRESSKEY             = 13;
+const int BOT_CMD_RELEASEKEY   = 14;
+const int BOT_CMD_SELECTWEAPON         = 15;
+const int BOT_CMD_IMPULSE              = 16;
+const int BOT_CMD_WAIT_UNTIL   = 17;
+const int BOT_CMD_MOVETOTARGET         = 18;
+const int BOT_CMD_AIMTARGET    = 19;
+const int BOT_CMD_BARRIER              = 20;
+const int BOT_CMD_CONSOLE              = 21;
+const int BOT_CMD_SOUND                = 22;
+const int BOT_CMD_DEBUG_ASSERT_CANFIRE = 23;
+const int BOT_CMD_WHILE                = 24;   // TODO: Not implemented yet
+const int BOT_CMD_WEND                         = 25;   // TODO: Not implemented yet
+const int BOT_CMD_CHASE                = 26;   // TODO: Not implemented yet
+
+const int BOT_CMD_COUNTER              = 24;   // Update this value if you add/remove a command
+
+// NOTE: Following commands should be implemented on the bot ai
+//              If a new command should be handled by the target ai(s) please declare it here
+.float(entity, vector) cmd_moveto;
+.float(entity) cmd_resetgoal;
+
+//
+const int BOT_CMD_PARAMETER_NONE = 0;
+const int BOT_CMD_PARAMETER_FLOAT = 1;
+const int BOT_CMD_PARAMETER_STRING = 2;
+const int BOT_CMD_PARAMETER_VECTOR = 3;
+
+float bot_cmds_initialized;
+int bot_cmd_parm_type[BOT_CMD_COUNTER];
+string bot_cmd_string[BOT_CMD_COUNTER];
+
+// Bots command queue
+entity bot_cmd;        // global current command
+.entity bot_cmd_current; // current command of this bot
+
+.float is_bot_cmd;                     // Tells if the entity is a bot command
+.float bot_cmd_index;                  // Position of the command in the queue
+.int bot_cmd_type;                     // If of command (see the BOT_CMD_* defines)
+.float bot_cmd_parm_float;             // Field to store a float parameter
+.string bot_cmd_parm_string;           // Field to store a string parameter
+.vector bot_cmd_parm_vector;           // Field to store a vector parameter
+
+float bot_barriertime;
+.float bot_barrier;
+
+.float bot_cmd_execution_index;                // Position in the queue of the command to be executed
+
+
+void bot_resetqueues();
+void bot_queuecommand(entity bot, string cmdstring);
+void bot_cmdhelp(string scmd);
+void bot_list_commands();
+float bot_execute_commands(entity this);
+entity find_bot_by_name(string name);
+entity find_bot_by_number(float number);
diff --git a/qcsrc/server/bot/default/waypoints.qc b/qcsrc/server/bot/default/waypoints.qc
new file mode 100644 (file)
index 0000000..84995d1
--- /dev/null
@@ -0,0 +1,1104 @@
+#include "waypoints.qh"
+
+#include "cvars.qh"
+
+#include "bot.qh"
+#include "navigation.qh"
+
+#include <common/state.qh>
+
+#include "../../antilag.qh"
+
+#include <common/constants.qh>
+
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/util_server.qh>
+
+// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link
+// them back to it as well
+// (suitable for spawnfunc_waypoint editor)
+entity waypoint_spawn(vector m1, vector m2, float f)
+{
+       if(!(f & WAYPOINTFLAG_PERSONAL))
+       {
+               IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax),
+               {
+                       return it;
+               });
+       }
+
+       entity w = new(waypoint);
+       IL_PUSH(g_waypoints, w);
+       w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+       w.wpflags = f;
+       w.solid = SOLID_TRIGGER;
+       setorigin(w, (m1 + m2) * 0.5);
+       setsize(w, m1 - w.origin, m2 - w.origin);
+       if (vlen(w.size) > 0)
+               w.wpisbox = true;
+
+       if(!w.wpisbox)
+       {
+               setsize(w, STAT(PL_MIN, NULL) - '1 1 0', STAT(PL_MAX, NULL) + '1 1 0');
+               if(!move_out_of_solid(w))
+               {
+                       if(!(f & WAYPOINTFLAG_GENERATED))
+                       {
+                               LOG_TRACE("Killed a waypoint that was stuck in solid at ", vtos(w.origin), "\n");
+                               delete(w);
+                               return NULL;
+                       }
+                       else
+                       {
+                               if(autocvar_developer)
+                               {
+                                       LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin), "\n");
+                                       backtrace("Waypoint stuck");
+                               }
+                       }
+               }
+               setsize(w, '0 0 0', '0 0 0');
+       }
+
+       waypoint_clearlinks(w);
+       //waypoint_schedulerelink(w);
+
+       if (autocvar_g_waypointeditor)
+       {
+               m1 = w.mins;
+               m2 = w.maxs;
+               setmodel(w, MDL_WAYPOINT); w.effects = EF_LOWPRECISION;
+               setsize(w, m1, m2);
+               if (w.wpflags & WAYPOINTFLAG_ITEM)
+                       w.colormod = '1 0 0';
+               else if (w.wpflags & WAYPOINTFLAG_GENERATED)
+                       w.colormod = '1 1 0';
+               else
+                       w.colormod = '1 1 1';
+       }
+       else
+               w.model = "";
+
+       return w;
+}
+
+// add a new link to the spawnfunc_waypoint, replacing the furthest link it already has
+void waypoint_addlink(entity from, entity to)
+{
+       float c;
+
+       if (from == to)
+               return;
+       if (from.wpflags & WAYPOINTFLAG_NORELINK)
+               return;
+
+       if (from.wp00 == to) return;if (from.wp01 == to) return;if (from.wp02 == to) return;if (from.wp03 == to) return;
+       if (from.wp04 == to) return;if (from.wp05 == to) return;if (from.wp06 == to) return;if (from.wp07 == to) return;
+       if (from.wp08 == to) return;if (from.wp09 == to) return;if (from.wp10 == to) return;if (from.wp11 == to) return;
+       if (from.wp12 == to) return;if (from.wp13 == to) return;if (from.wp14 == to) return;if (from.wp15 == to) return;
+       if (from.wp16 == to) return;if (from.wp17 == to) return;if (from.wp18 == to) return;if (from.wp19 == to) return;
+       if (from.wp20 == to) return;if (from.wp21 == to) return;if (from.wp22 == to) return;if (from.wp23 == to) return;
+       if (from.wp24 == to) return;if (from.wp25 == to) return;if (from.wp26 == to) return;if (from.wp27 == to) return;
+       if (from.wp28 == to) return;if (from.wp29 == to) return;if (from.wp30 == to) return;if (from.wp31 == to) return;
+
+       if (to.wpisbox || from.wpisbox)
+       {
+               // if either is a box we have to find the nearest points on them to
+               // calculate the distance properly
+               vector v1, v2, m1, m2;
+               v1 = from.origin;
+               m1 = to.absmin;
+               m2 = to.absmax;
+               v1_x = bound(m1_x, v1_x, m2_x);
+               v1_y = bound(m1_y, v1_y, m2_y);
+               v1_z = bound(m1_z, v1_z, m2_z);
+               v2 = to.origin;
+               m1 = from.absmin;
+               m2 = from.absmax;
+               v2_x = bound(m1_x, v2_x, m2_x);
+               v2_y = bound(m1_y, v2_y, m2_y);
+               v2_z = bound(m1_z, v2_z, m2_z);
+               v2 = to.origin;
+               c = vlen(v2 - v1);
+       }
+       else
+               c = vlen(to.origin - from.origin);
+
+       if (from.wp31mincost < c) return;
+       if (from.wp30mincost < c) {from.wp31 = to;from.wp31mincost = c;return;} from.wp31 = from.wp30;from.wp31mincost = from.wp30mincost;
+       if (from.wp29mincost < c) {from.wp30 = to;from.wp30mincost = c;return;} from.wp30 = from.wp29;from.wp30mincost = from.wp29mincost;
+       if (from.wp28mincost < c) {from.wp29 = to;from.wp29mincost = c;return;} from.wp29 = from.wp28;from.wp29mincost = from.wp28mincost;
+       if (from.wp27mincost < c) {from.wp28 = to;from.wp28mincost = c;return;} from.wp28 = from.wp27;from.wp28mincost = from.wp27mincost;
+       if (from.wp26mincost < c) {from.wp27 = to;from.wp27mincost = c;return;} from.wp27 = from.wp26;from.wp27mincost = from.wp26mincost;
+       if (from.wp25mincost < c) {from.wp26 = to;from.wp26mincost = c;return;} from.wp26 = from.wp25;from.wp26mincost = from.wp25mincost;
+       if (from.wp24mincost < c) {from.wp25 = to;from.wp25mincost = c;return;} from.wp25 = from.wp24;from.wp25mincost = from.wp24mincost;
+       if (from.wp23mincost < c) {from.wp24 = to;from.wp24mincost = c;return;} from.wp24 = from.wp23;from.wp24mincost = from.wp23mincost;
+       if (from.wp22mincost < c) {from.wp23 = to;from.wp23mincost = c;return;} from.wp23 = from.wp22;from.wp23mincost = from.wp22mincost;
+       if (from.wp21mincost < c) {from.wp22 = to;from.wp22mincost = c;return;} from.wp22 = from.wp21;from.wp22mincost = from.wp21mincost;
+       if (from.wp20mincost < c) {from.wp21 = to;from.wp21mincost = c;return;} from.wp21 = from.wp20;from.wp21mincost = from.wp20mincost;
+       if (from.wp19mincost < c) {from.wp20 = to;from.wp20mincost = c;return;} from.wp20 = from.wp19;from.wp20mincost = from.wp19mincost;
+       if (from.wp18mincost < c) {from.wp19 = to;from.wp19mincost = c;return;} from.wp19 = from.wp18;from.wp19mincost = from.wp18mincost;
+       if (from.wp17mincost < c) {from.wp18 = to;from.wp18mincost = c;return;} from.wp18 = from.wp17;from.wp18mincost = from.wp17mincost;
+       if (from.wp16mincost < c) {from.wp17 = to;from.wp17mincost = c;return;} from.wp17 = from.wp16;from.wp17mincost = from.wp16mincost;
+       if (from.wp15mincost < c) {from.wp16 = to;from.wp16mincost = c;return;} from.wp16 = from.wp15;from.wp16mincost = from.wp15mincost;
+       if (from.wp14mincost < c) {from.wp15 = to;from.wp15mincost = c;return;} from.wp15 = from.wp14;from.wp15mincost = from.wp14mincost;
+       if (from.wp13mincost < c) {from.wp14 = to;from.wp14mincost = c;return;} from.wp14 = from.wp13;from.wp14mincost = from.wp13mincost;
+       if (from.wp12mincost < c) {from.wp13 = to;from.wp13mincost = c;return;} from.wp13 = from.wp12;from.wp13mincost = from.wp12mincost;
+       if (from.wp11mincost < c) {from.wp12 = to;from.wp12mincost = c;return;} from.wp12 = from.wp11;from.wp12mincost = from.wp11mincost;
+       if (from.wp10mincost < c) {from.wp11 = to;from.wp11mincost = c;return;} from.wp11 = from.wp10;from.wp11mincost = from.wp10mincost;
+       if (from.wp09mincost < c) {from.wp10 = to;from.wp10mincost = c;return;} from.wp10 = from.wp09;from.wp10mincost = from.wp09mincost;
+       if (from.wp08mincost < c) {from.wp09 = to;from.wp09mincost = c;return;} from.wp09 = from.wp08;from.wp09mincost = from.wp08mincost;
+       if (from.wp07mincost < c) {from.wp08 = to;from.wp08mincost = c;return;} from.wp08 = from.wp07;from.wp08mincost = from.wp07mincost;
+       if (from.wp06mincost < c) {from.wp07 = to;from.wp07mincost = c;return;} from.wp07 = from.wp06;from.wp07mincost = from.wp06mincost;
+       if (from.wp05mincost < c) {from.wp06 = to;from.wp06mincost = c;return;} from.wp06 = from.wp05;from.wp06mincost = from.wp05mincost;
+       if (from.wp04mincost < c) {from.wp05 = to;from.wp05mincost = c;return;} from.wp05 = from.wp04;from.wp05mincost = from.wp04mincost;
+       if (from.wp03mincost < c) {from.wp04 = to;from.wp04mincost = c;return;} from.wp04 = from.wp03;from.wp04mincost = from.wp03mincost;
+       if (from.wp02mincost < c) {from.wp03 = to;from.wp03mincost = c;return;} from.wp03 = from.wp02;from.wp03mincost = from.wp02mincost;
+       if (from.wp01mincost < c) {from.wp02 = to;from.wp02mincost = c;return;} from.wp02 = from.wp01;from.wp02mincost = from.wp01mincost;
+       if (from.wp00mincost < c) {from.wp01 = to;from.wp01mincost = c;return;} from.wp01 = from.wp00;from.wp01mincost = from.wp00mincost;
+       from.wp00 = to;from.wp00mincost = c;return;
+}
+
+// relink this spawnfunc_waypoint
+// (precompile a list of all reachable waypoints from this spawnfunc_waypoint)
+// (SLOW!)
+void waypoint_think(entity this)
+{
+       vector sv, sm1, sm2, ev, em1, em2, dv;
+
+       bot_calculate_stepheightvec();
+
+       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
+
+       //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
+       sm1 = this.origin + this.mins;
+       sm2 = this.origin + this.maxs;
+       IL_EACH(g_waypoints, true,
+       {
+               if (boxesoverlap(this.absmin, this.absmax, it.absmin, it.absmax))
+               {
+                       waypoint_addlink(this, it);
+                       waypoint_addlink(it, this);
+               }
+               else
+               {
+                       ++relink_total;
+                       if(!checkpvs(this.origin, it))
+                       {
+                               ++relink_pvsculled;
+                               continue;
+                       }
+                       sv = it.origin;
+                       sv.x = bound(sm1_x, sv.x, sm2_x);
+                       sv.y = bound(sm1_y, sv.y, sm2_y);
+                       sv.z = bound(sm1_z, sv.z, sm2_z);
+                       ev = this.origin;
+                       em1 = it.origin + it.mins;
+                       em2 = it.origin + it.maxs;
+                       ev.x = bound(em1_x, ev.x, em2_x);
+                       ev.y = bound(em1_y, ev.y, em2_y);
+                       ev.z = bound(em1_z, ev.z, em2_z);
+                       dv = ev - sv;
+                       dv.z = 0;
+                       if(vdist(dv, >=, 1050)) // max search distance in XY
+                       {
+                               ++relink_lengthculled;
+                               continue;
+                       }
+                       navigation_testtracewalk = 0;
+                       if (!this.wpisbox)
+                       {
+                               tracebox(sv - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, false, this);
+                               if (!trace_startsolid)
+                               {
+                                       //dprint("sv deviation", vtos(trace_endpos - sv), "\n");
+                                       sv = trace_endpos + '0 0 1';
+                               }
+                       }
+                       if (!it.wpisbox)
+                       {
+                               tracebox(ev - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, false, it);
+                               if (!trace_startsolid)
+                               {
+                                       //dprint("ev deviation", vtos(trace_endpos - ev), "\n");
+                                       ev = trace_endpos + '0 0 1';
+                               }
+                       }
+                       //traceline(this.origin, it.origin, false, NULL);
+                       //if (trace_fraction == 1)
+                       if (!this.wpisbox && tracewalk(this, sv, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, MOVE_NOMONSTERS))
+                               waypoint_addlink(this, it);
+                       else
+                               relink_walkculled += 0.5;
+                       if (!it.wpisbox && tracewalk(it, ev, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, MOVE_NOMONSTERS))
+                               waypoint_addlink(it, this);
+                       else
+                               relink_walkculled += 0.5;
+               }
+       });
+       navigation_testtracewalk = 0;
+       this.wplinked = true;
+}
+
+void waypoint_clearlinks(entity wp)
+{
+       // clear links to other waypoints
+       float f;
+       f = 10000000;
+       wp.wp00 = wp.wp01 = wp.wp02 = wp.wp03 = wp.wp04 = wp.wp05 = wp.wp06 = wp.wp07 = NULL;
+       wp.wp08 = wp.wp09 = wp.wp10 = wp.wp11 = wp.wp12 = wp.wp13 = wp.wp14 = wp.wp15 = NULL;
+       wp.wp16 = wp.wp17 = wp.wp18 = wp.wp19 = wp.wp20 = wp.wp21 = wp.wp22 = wp.wp23 = NULL;
+       wp.wp24 = wp.wp25 = wp.wp26 = wp.wp27 = wp.wp28 = wp.wp29 = wp.wp30 = wp.wp31 = NULL;
+
+       wp.wp00mincost = wp.wp01mincost = wp.wp02mincost = wp.wp03mincost = wp.wp04mincost = wp.wp05mincost = wp.wp06mincost = wp.wp07mincost = f;
+       wp.wp08mincost = wp.wp09mincost = wp.wp10mincost = wp.wp11mincost = wp.wp12mincost = wp.wp13mincost = wp.wp14mincost = wp.wp15mincost = f;
+       wp.wp16mincost = wp.wp17mincost = wp.wp18mincost = wp.wp19mincost = wp.wp20mincost = wp.wp21mincost = wp.wp22mincost = wp.wp23mincost = f;
+       wp.wp24mincost = wp.wp25mincost = wp.wp26mincost = wp.wp27mincost = wp.wp28mincost = wp.wp29mincost = wp.wp30mincost = wp.wp31mincost = f;
+
+       wp.wplinked = false;
+}
+
+// tell a spawnfunc_waypoint to relink
+void waypoint_schedulerelink(entity wp)
+{
+       if (wp == NULL)
+               return;
+       // TODO: add some sort of visible box in edit mode for box waypoints
+       if (autocvar_g_waypointeditor)
+       {
+               vector m1, m2;
+               m1 = wp.mins;
+               m2 = wp.maxs;
+               setmodel(wp, MDL_WAYPOINT); wp.effects = EF_LOWPRECISION;
+               setsize(wp, m1, m2);
+               if (wp.wpflags & WAYPOINTFLAG_ITEM)
+                       wp.colormod = '1 0 0';
+               else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
+                       wp.colormod = '1 1 0';
+               else
+                       wp.colormod = '1 1 1';
+       }
+       else
+               wp.model = "";
+       wp.wpisbox = vlen(wp.size) > 0;
+       wp.enemy = NULL;
+       if (!(wp.wpflags & WAYPOINTFLAG_PERSONAL))
+               wp.owner = NULL;
+       if (!(wp.wpflags & WAYPOINTFLAG_NORELINK))
+               waypoint_clearlinks(wp);
+       // schedule an actual relink on next frame
+       setthink(wp, waypoint_think);
+       wp.nextthink = time;
+       wp.effects = EF_LOWPRECISION;
+}
+
+// spawnfunc_waypoint map entity
+spawnfunc(waypoint)
+{
+       IL_PUSH(g_waypoints, this);
+
+       setorigin(this, this.origin);
+       // schedule a relink after other waypoints have had a chance to spawn
+       waypoint_clearlinks(this);
+       //waypoint_schedulerelink(this);
+}
+
+// remove a spawnfunc_waypoint, and schedule all neighbors to relink
+void waypoint_remove(entity e)
+{
+       // tell all linked waypoints that they need to relink
+       waypoint_schedulerelink(e.wp00);
+       waypoint_schedulerelink(e.wp01);
+       waypoint_schedulerelink(e.wp02);
+       waypoint_schedulerelink(e.wp03);
+       waypoint_schedulerelink(e.wp04);
+       waypoint_schedulerelink(e.wp05);
+       waypoint_schedulerelink(e.wp06);
+       waypoint_schedulerelink(e.wp07);
+       waypoint_schedulerelink(e.wp08);
+       waypoint_schedulerelink(e.wp09);
+       waypoint_schedulerelink(e.wp10);
+       waypoint_schedulerelink(e.wp11);
+       waypoint_schedulerelink(e.wp12);
+       waypoint_schedulerelink(e.wp13);
+       waypoint_schedulerelink(e.wp14);
+       waypoint_schedulerelink(e.wp15);
+       waypoint_schedulerelink(e.wp16);
+       waypoint_schedulerelink(e.wp17);
+       waypoint_schedulerelink(e.wp18);
+       waypoint_schedulerelink(e.wp19);
+       waypoint_schedulerelink(e.wp20);
+       waypoint_schedulerelink(e.wp21);
+       waypoint_schedulerelink(e.wp22);
+       waypoint_schedulerelink(e.wp23);
+       waypoint_schedulerelink(e.wp24);
+       waypoint_schedulerelink(e.wp25);
+       waypoint_schedulerelink(e.wp26);
+       waypoint_schedulerelink(e.wp27);
+       waypoint_schedulerelink(e.wp28);
+       waypoint_schedulerelink(e.wp29);
+       waypoint_schedulerelink(e.wp30);
+       waypoint_schedulerelink(e.wp31);
+       // and now remove the spawnfunc_waypoint
+       delete(e);
+}
+
+// empties the map of waypoints
+void waypoint_removeall()
+{
+       IL_EACH(g_waypoints, true,
+       {
+               delete(it);
+       });
+}
+
+// tell all waypoints to relink
+// (is this useful at all?)
+void waypoint_schedulerelinkall()
+{
+       relink_total = relink_walkculled = relink_pvsculled = relink_lengthculled = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               waypoint_schedulerelink(it);
+       });
+}
+
+// Load waypoint links from file
+float waypoint_load_links()
+{
+       string filename, s;
+       float file, tokens, c = 0, found;
+       entity wp_from = NULL, wp_to;
+       vector wp_to_pos, wp_from_pos;
+       filename = strcat("maps/", mapname);
+       filename = strcat(filename, ".waypoints.cache");
+       file = fopen(filename, FILE_READ);
+
+       if (file < 0)
+       {
+               LOG_TRACE("waypoint links load from ");
+               LOG_TRACE(filename);
+               LOG_TRACE(" failed\n");
+               return false;
+       }
+
+       while ((s = fgets(file)))
+       {
+               tokens = tokenizebyseparator(s, "*");
+
+               if (tokens!=2)
+               {
+                       // bad file format
+                       fclose(file);
+                       return false;
+               }
+
+               wp_from_pos     = stov(argv(0));
+               wp_to_pos       = stov(argv(1));
+
+               // Search "from" waypoint
+               if(!wp_from || wp_from.origin!=wp_from_pos)
+               {
+                       wp_from = findradius(wp_from_pos, 1);
+                       found = false;
+                       while(wp_from)
+                       {
+                               if(vdist(wp_from.origin - wp_from_pos, <, 1))
+                               if(wp_from.classname == "waypoint")
+                               {
+                                       found = true;
+                                       break;
+                               }
+                               wp_from = wp_from.chain;
+                       }
+
+                       if(!found)
+                       {
+                               LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from.origin),"\n");
+                               continue;
+                       }
+
+               }
+
+               // Search "to" waypoint
+               wp_to = findradius(wp_to_pos, 1);
+               found = false;
+               while(wp_to)
+               {
+                       if(vdist(wp_to.origin - wp_to_pos, <, 1))
+                       if(wp_to.classname == "waypoint")
+                       {
+                               found = true;
+                               break;
+                       }
+                       wp_to = wp_to.chain;
+               }
+
+               if(!found)
+               {
+                       LOG_TRACE("waypoint_load_links: couldn't find 'to' waypoint at ", vtos(wp_to.origin),"\n");
+                       continue;
+               }
+
+               ++c;
+               waypoint_addlink(wp_from, wp_to);
+       }
+
+       fclose(file);
+
+       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.cache\n");
+
+       botframe_cachedwaypointlinks = true;
+       return true;
+}
+
+void waypoint_load_links_hardwired()
+{
+       string filename, s;
+       float file, tokens, c = 0, found;
+       entity wp_from = NULL, wp_to;
+       vector wp_to_pos, wp_from_pos;
+       filename = strcat("maps/", mapname);
+       filename = strcat(filename, ".waypoints.hardwired");
+       file = fopen(filename, FILE_READ);
+
+       botframe_loadedforcedlinks = true;
+
+       if (file < 0)
+       {
+               LOG_TRACE("waypoint links load from ", filename, " failed\n");
+               return;
+       }
+
+       while ((s = fgets(file)))
+       {
+               if(substring(s, 0, 2)=="//")
+                       continue;
+
+               if(substring(s, 0, 1)=="#")
+                       continue;
+
+               tokens = tokenizebyseparator(s, "*");
+
+               if (tokens!=2)
+                       continue;
+
+               wp_from_pos     = stov(argv(0));
+               wp_to_pos       = stov(argv(1));
+
+               // Search "from" waypoint
+               if(!wp_from || wp_from.origin!=wp_from_pos)
+               {
+                       wp_from = findradius(wp_from_pos, 5);
+                       found = false;
+                       while(wp_from)
+                       {
+                               if(vdist(wp_from.origin - wp_from_pos, <, 5))
+                               if(wp_from.classname == "waypoint")
+                               {
+                                       found = true;
+                                       break;
+                               }
+                               wp_from = wp_from.chain;
+                       }
+
+                       if(!found)
+                       {
+                               LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped\n"));
+                               continue;
+                       }
+               }
+
+               // Search "to" waypoint
+               wp_to = findradius(wp_to_pos, 5);
+               found = false;
+               while(wp_to)
+               {
+                       if(vdist(wp_to.origin - wp_to_pos, <, 5))
+                       if(wp_to.classname == "waypoint")
+                       {
+                               found = true;
+                               break;
+                       }
+                       wp_to = wp_to.chain;
+               }
+
+               if(!found)
+               {
+                       LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped\n"));
+                       continue;
+               }
+
+               ++c;
+               waypoint_addlink(wp_from, wp_to);
+               wp_from.wphardwired = true;
+               wp_to.wphardwired = true;
+       }
+
+       fclose(file);
+
+       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired\n");
+}
+
+entity waypoint_get_link(entity w, float i)
+{
+       switch(i)
+       {
+               case  0:return w.wp00;
+               case  1:return w.wp01;
+               case  2:return w.wp02;
+               case  3:return w.wp03;
+               case  4:return w.wp04;
+               case  5:return w.wp05;
+               case  6:return w.wp06;
+               case  7:return w.wp07;
+               case  8:return w.wp08;
+               case  9:return w.wp09;
+               case 10:return w.wp10;
+               case 11:return w.wp11;
+               case 12:return w.wp12;
+               case 13:return w.wp13;
+               case 14:return w.wp14;
+               case 15:return w.wp15;
+               case 16:return w.wp16;
+               case 17:return w.wp17;
+               case 18:return w.wp18;
+               case 19:return w.wp19;
+               case 20:return w.wp20;
+               case 21:return w.wp21;
+               case 22:return w.wp22;
+               case 23:return w.wp23;
+               case 24:return w.wp24;
+               case 25:return w.wp25;
+               case 26:return w.wp26;
+               case 27:return w.wp27;
+               case 28:return w.wp28;
+               case 29:return w.wp29;
+               case 30:return w.wp30;
+               case 31:return w.wp31;
+               default:return NULL;
+       }
+}
+
+// Save all waypoint links to a file
+void waypoint_save_links()
+{
+       string filename = sprintf("maps/%s.waypoints.cache", mapname);
+       int file = fopen(filename, FILE_WRITE);
+       if (file < 0)
+       {
+               LOG_INFOF("waypoint link save to %s failed\n", filename);
+               return;
+       }
+
+       int c = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               for(int j = 0; j < 32; ++j)
+               {
+                       entity link = waypoint_get_link(it, j);
+                       if(link)
+                       {
+                               string s = strcat(vtos(it.origin), "*", vtos(link.origin), "\n");
+                               fputs(file, s);
+                               ++c;
+                       }
+               }
+       });
+       fclose(file);
+       botframe_cachedwaypointlinks = true;
+
+       LOG_INFOF("saved %d waypoint links to maps/%s.waypoints.cache\n", c, mapname);
+}
+
+// save waypoints to gamedir/data/maps/mapname.waypoints
+void waypoint_saveall()
+{
+       string filename = sprintf("maps/%s.waypoints", mapname);
+       int file = fopen(filename, FILE_WRITE);
+       if (file < 0)
+       {
+               waypoint_save_links(); // save anyway?
+               botframe_loadedforcedlinks = false;
+
+               LOG_INFOF("waypoint links: save to %s failed\n", filename);
+               return;
+       }
+
+       int c = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               if(it.wpflags & WAYPOINTFLAG_GENERATED)
+                       continue;
+
+               for(int j = 0; j < 32; ++j)
+               {
+                       string s;
+                       s = strcat(vtos(it.origin + it.mins), "\n");
+                       s = strcat(s, vtos(it.origin + it.maxs));
+                       s = strcat(s, "\n");
+                       s = strcat(s, ftos(it.wpflags));
+                       s = strcat(s, "\n");
+                       fputs(file, s);
+                       ++c;
+               }
+       });
+       fclose(file);
+       waypoint_save_links();
+       botframe_loadedforcedlinks = false;
+
+       LOG_INFOF("saved %d waypoints to maps/%s.waypoints\n", c, mapname);
+}
+
+// load waypoints from file
+float waypoint_loadall()
+{
+       string filename, s;
+       float file, cwp, cwb, fl;
+       vector m1, m2;
+       cwp = 0;
+       cwb = 0;
+       filename = strcat("maps/", mapname);
+       filename = strcat(filename, ".waypoints");
+       file = fopen(filename, FILE_READ);
+       if (file >= 0)
+       {
+               while ((s = fgets(file)))
+               {
+                       m1 = stov(s);
+                       s = fgets(file);
+                       if (!s)
+                               break;
+                       m2 = stov(s);
+                       s = fgets(file);
+                       if (!s)
+                               break;
+                       fl = stof(s);
+                       waypoint_spawn(m1, m2, fl);
+                       if (m1 == m2)
+                               cwp = cwp + 1;
+                       else
+                               cwb = cwb + 1;
+               }
+               fclose(file);
+               LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints\n");
+       }
+       else
+       {
+               LOG_TRACE("waypoint load from ", filename, " failed\n");
+       }
+       return cwp + cwb;
+}
+
+vector waypoint_fixorigin(vector position)
+{
+       tracebox(position + '0 0 1' * (1 - STAT(PL_MIN, NULL).z), STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), position + '0 0 -512', MOVE_NOMONSTERS, NULL);
+       if(trace_fraction < 1)
+               position = trace_endpos;
+       //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
+       //print("position is ", ftos(trace_endpos_z - position_z), " above solid\n");
+       return position;
+}
+
+void waypoint_spawnforitem_force(entity e, vector org)
+{
+       // Fix the waypoint altitude if necessary
+       org = waypoint_fixorigin(org);
+
+       // don't spawn an item spawnfunc_waypoint if it already exists
+       IL_EACH(g_waypoints, true,
+       {
+               if(it.wpisbox)
+               {
+                       if(boxesoverlap(org, org, it.absmin, it.absmax))
+                       {
+                               e.nearestwaypoint = it;
+                               return;
+                       }
+               }
+               else
+               {
+                       if(vdist(it.origin - org, <, 16))
+                       {
+                               e.nearestwaypoint = it;
+                               return;
+                       }
+               }
+       });
+
+       e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM);
+}
+
+void waypoint_spawnforitem(entity e)
+{
+       if(!bot_waypoints_for_items)
+               return;
+
+       waypoint_spawnforitem_force(e, e.origin);
+}
+
+void waypoint_spawnforteleporter_boxes(entity e, vector org1, vector org2, vector destination1, vector destination2, float timetaken)
+{
+       entity w;
+       entity dw;
+       w = waypoint_spawn(org1, org2, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_NORELINK);
+       dw = waypoint_spawn(destination1, destination2, WAYPOINTFLAG_GENERATED);
+       // one way link to the destination
+       w.wp00 = dw;
+       w.wp00mincost = timetaken; // this is just for jump pads
+       // the teleporter's nearest spawnfunc_waypoint is this one
+       // (teleporters are not goals, so this is probably useless)
+       e.nearestwaypoint = w;
+       e.nearestwaypointtimeout = time + 1000000000;
+}
+
+void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken)
+{
+       org = waypoint_fixorigin(org);
+       destination = waypoint_fixorigin(destination);
+       waypoint_spawnforteleporter_boxes(e, org, org, destination, destination, timetaken);
+}
+
+void waypoint_spawnforteleporter(entity e, vector destination, float timetaken)
+{
+       destination = waypoint_fixorigin(destination);
+       waypoint_spawnforteleporter_boxes(e, e.absmin, e.absmax, destination, destination, timetaken);
+}
+
+entity waypoint_spawnpersonal(entity this, vector position)
+{
+       entity w;
+
+       // drop the waypoint to a proper location:
+       //   first move it up by a player height
+       //   then move it down to hit the floor with player bbox size
+       position = waypoint_fixorigin(position);
+
+       w = waypoint_spawn(position, position, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_PERSONAL);
+       w.nearestwaypoint = NULL;
+       w.nearestwaypointtimeout = 0;
+       w.owner = this;
+
+       waypoint_schedulerelink(w);
+
+       return w;
+}
+
+void botframe_showwaypointlinks()
+{
+       if (time < botframe_waypointeditorlightningtime)
+               return;
+       botframe_waypointeditorlightningtime = time + 0.5;
+       FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot,
+       {
+               if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE)
+               {
+                       //navigation_testtracewalk = true;
+                       entity head = navigation_findnearestwaypoint(it, false);
+               //      print("currently selected WP is ", etos(head), "\n");
+                       //navigation_testtracewalk = false;
+                       if (head)
+                       {
+                               entity w;
+                               w = head     ;if (w) te_lightning2(NULL, w.origin, it.origin);
+                               w = head.wp00;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp01;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp02;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp03;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp04;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp05;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp06;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp07;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp08;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp09;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp10;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp11;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp12;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp13;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp14;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp15;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp16;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp17;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp18;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp19;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp20;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp21;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp22;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp23;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp24;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp25;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp26;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp27;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp28;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp29;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp30;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp31;if (w) te_lightning2(NULL, w.origin, head.origin);
+                       }
+               }
+       });
+}
+
+float botframe_autowaypoints_fixdown(vector v)
+{
+       tracebox(v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v + '0 0 -64', MOVE_NOMONSTERS, NULL);
+       if(trace_fraction >= 1)
+               return 0;
+       return 1;
+}
+
+float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f)
+{
+       IL_EACH(g_waypoints, boxesoverlap(v - '32 32 32', v + '32 32 32', it.absmin, it.absmax),
+       {
+               // if a matching spawnfunc_waypoint already exists, don't add a duplicate
+               return 0;
+       });
+
+       waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f));
+       return 1;
+}
+
+// return value:
+//    1 = WP created
+//    0 = no action needed
+//   -1 = temp fail, try from world too
+//   -2 = permanent fail, do not retry
+float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .entity fld)
+{
+       // make it possible to go from p to wp, if we can
+       // if wp is NULL, nearest is chosen
+
+       entity w;
+       vector porg;
+       float t, tmin, tmax;
+       vector o;
+       vector save;
+
+       if(!botframe_autowaypoints_fixdown(p.origin))
+               return -2;
+       porg = trace_endpos;
+
+       if(wp)
+       {
+               // if any WP w fulfills wp -> w -> porg and w is closer than wp, then switch from wp to w
+
+               // if wp -> porg, then OK
+               float maxdist;
+               if(navigation_waypoint_will_link(wp.origin, porg, p, walkfromwp, 1050))
+               {
+                       // we may find a better one
+                       maxdist = vlen(wp.origin - porg);
+               }
+               else
+               {
+                       // accept any "good"
+                       maxdist = 2100;
+               }
+
+               float bestdist = maxdist;
+               IL_EACH(g_waypoints, it != wp && !(it.wpflags & WAYPOINTFLAG_NORELINK),
+               {
+                       float d = vlen(wp.origin - it.origin) + vlen(it.origin - porg);
+                       if(d < bestdist)
+                       if(navigation_waypoint_will_link(wp.origin, it.origin, p, walkfromwp, 1050))
+                       if(navigation_waypoint_will_link(it.origin, porg, p, walkfromwp, 1050))
+                       {
+                               bestdist = d;
+                               p.(fld) = it;
+                       }
+               });
+               if(bestdist < maxdist)
+               {
+                       LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n");
+                       return 0;
+               }
+
+               if(bestdist < 2100)
+               {
+                       // we know maxdist < 2100
+                       // so wp -> porg is still valid
+                       // all is good
+                       p.(fld) = wp;
+                       return 0;
+               }
+
+               // otherwise, no existing WP can fix our issues
+       }
+       else
+       {
+               save = p.origin;
+               setorigin(p, porg);
+               w = navigation_findnearestwaypoint(p, walkfromwp);
+               setorigin(p, save);
+               if(w)
+               {
+                       p.(fld) = w;
+                       return 0;
+               }
+       }
+
+       tmin = 0;
+       tmax = 1;
+       for (;;)
+       {
+               if(tmax - tmin < 0.001)
+               {
+                       // did not get a good candidate
+                       return -1;
+               }
+
+               t = (tmin + tmax) * 0.5;
+               o = antilag_takebackorigin(p, CS(p), time - t);
+               if(!botframe_autowaypoints_fixdown(o))
+                       return -2;
+               o = trace_endpos;
+
+               if(wp)
+               {
+                       if(!navigation_waypoint_will_link(wp.origin, o, p, walkfromwp, 1050))
+                       {
+                               // we cannot walk from wp.origin to o
+                               // get closer to tmax
+                               tmin = t;
+                               continue;
+                       }
+               }
+               else
+               {
+                       save = p.origin;
+                       setorigin(p, o);
+                       w = navigation_findnearestwaypoint(p, walkfromwp);
+                       setorigin(p, save);
+                       if(!w)
+                       {
+                               // we cannot walk from any WP to o
+                               // get closer to tmax
+                               tmin = t;
+                               continue;
+                       }
+               }
+
+               // if we get here, o is valid regarding waypoints
+               // check if o is connected right to the player
+               // we break if it succeeds, as that means o is a good waypoint location
+               if(navigation_waypoint_will_link(o, porg, p, walkfromwp, 1050))
+                       break;
+
+               // o is no good, we need to get closer to the player
+               tmax = t;
+       }
+
+       LOG_INFO("spawning a waypoint for connecting to ", etos(wp), "\n");
+       botframe_autowaypoints_createwp(o, p, fld, 0);
+       return 1;
+}
+
+// automatically create missing waypoints
+.entity botframe_autowaypoints_lastwp0, botframe_autowaypoints_lastwp1;
+void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld)
+{
+       float r = botframe_autowaypoints_fix_from(p, walkfromwp, p.(fld), fld);
+       if(r != -1)
+               return;
+       r = botframe_autowaypoints_fix_from(p, walkfromwp, NULL, fld);
+       if(r != -1)
+               return;
+
+       LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain\n");
+       if(!botframe_autowaypoints_fixdown(p.origin))
+               return; // shouldn't happen, caught above
+       botframe_autowaypoints_createwp(trace_endpos, p, fld, WAYPOINTFLAG_PROTECTED);
+}
+
+void botframe_deleteuselesswaypoints()
+{
+       FOREACH_ENTITY_FLOAT(bot_pickup, true,
+       {
+               // NOTE: this protects waypoints if they're the ONLY nearest
+               // waypoint. That's the intention.
+               navigation_findnearestwaypoint(it, false);  // Walk TO item.
+               navigation_findnearestwaypoint(it, true);  // Walk FROM item.
+       });
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpflags |= WAYPOINTFLAG_DEAD_END;
+               it.wpflags &= ~WAYPOINTFLAG_USEFUL;
+               // WP is useful if:
+               if (it.wpflags & WAYPOINTFLAG_ITEM)
+                       it.wpflags |= WAYPOINTFLAG_USEFUL;
+               if (it.wpflags & WAYPOINTFLAG_TELEPORT)
+                       it.wpflags |= WAYPOINTFLAG_USEFUL;
+               if (it.wpflags & WAYPOINTFLAG_PROTECTED)
+                       it.wpflags |= WAYPOINTFLAG_USEFUL;
+               // b) WP is closest WP for an item/spawnpoint/other entity
+               //    This has been done above by protecting these WPs.
+       });
+       // c) There are w1, w, w2 so that w1 -> w, w -> w2 and not w1 -> w2.
+       IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_PERSONAL),
+       {
+               for (int m = 0; m < 32; ++m)
+               {
+                       entity w = waypoint_get_link(it, m);
+                       if (!w)
+                               break;
+                       if (w.wpflags & WAYPOINTFLAG_PERSONAL)
+                               continue;
+                       if (w.wpflags & WAYPOINTFLAG_USEFUL)
+                               continue;
+                       for (int j = 0; j < 32; ++j)
+                       {
+                               entity w2 = waypoint_get_link(w, j);
+                               if (!w2)
+                                       break;
+                               if (it == w2)
+                                       continue;
+                               if (w2.wpflags & WAYPOINTFLAG_PERSONAL)
+                                       continue;
+                               // If we got here, it != w2 exist with it -> w
+                               // and w -> w2. That means the waypoint is not
+                               // a dead end.
+                               w.wpflags &= ~WAYPOINTFLAG_DEAD_END;
+                               for (int k = 0; k < 32; ++k)
+                               {
+                                       if (waypoint_get_link(it, k) == w2)
+                                               continue;
+                                       // IF WE GET HERE, w is proven useful
+                                       // to get from it to w2!
+                                       w.wpflags |= WAYPOINTFLAG_USEFUL;
+                                       goto next;
+                               }
+                       }
+LABEL(next)
+               }
+       });
+       // d) The waypoint is a dead end. Dead end waypoints must be kept as
+       //     they are needed to complete routes while autowaypointing.
+
+       IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)),
+       {
+               LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", it.origin);
+               te_explosion(it.origin);
+               waypoint_remove(it);
+               break;
+       });
+
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpflags &= ~(WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END); // temp flag
+       });
+}
+
+void botframe_autowaypoints()
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), LAMBDA(
+               // going back is broken, so only fix waypoints to walk TO the player
+               //botframe_autowaypoints_fix(p, false, botframe_autowaypoints_lastwp0);
+               botframe_autowaypoints_fix(it, true, botframe_autowaypoints_lastwp1);
+               //te_explosion(p.botframe_autowaypoints_lastwp0.origin);
+       ));
+
+       if (autocvar_g_waypointeditor_auto >= 2) {
+               botframe_deleteuselesswaypoints();
+       }
+}
+
diff --git a/qcsrc/server/bot/default/waypoints.qh b/qcsrc/server/bot/default/waypoints.qh
new file mode 100644 (file)
index 0000000..38d57a0
--- /dev/null
@@ -0,0 +1,58 @@
+#pragma once
+/*
+ * Globals and Fields
+ */
+
+// fields you can query using prvm_global server to get some statistics about waypoint linking culling
+float relink_total, relink_walkculled, relink_pvsculled, relink_lengthculled;
+
+float botframe_waypointeditorlightningtime;
+float botframe_loadedforcedlinks;
+float botframe_cachedwaypointlinks;
+
+.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;
+
+// itemscore = (howmuchmoreIwant / howmuchIcanwant) / itemdistance
+.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 wpfire, wpcost, wpconsidered, wpisbox, wplinked, wphardwired;
+.int wpflags;
+
+.vector wpnearestpoint;
+
+/*
+ * Functions
+ */
+
+spawnfunc(waypoint);
+void waypoint_addlink(entity from, entity to);
+void waypoint_think(entity this);
+void waypoint_clearlinks(entity wp);
+void waypoint_schedulerelink(entity wp);
+
+void waypoint_remove(entity e);
+void waypoint_removeall();
+void waypoint_schedulerelinkall();
+void waypoint_load_links_hardwired();
+void waypoint_save_links();
+void waypoint_saveall();
+
+void waypoint_spawnforitem_force(entity e, vector org);
+void waypoint_spawnforitem(entity e);
+void waypoint_spawnforteleporter(entity e, vector destination, float timetaken);
+void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken);
+void botframe_showwaypointlinks();
+
+float waypoint_loadall();
+float waypoint_load_links();
+
+entity waypoint_spawn(vector m1, vector m2, float f);
+entity waypoint_spawnpersonal(entity this, vector position);
+
+vector waypoint_fixorigin(vector position);
+
+void botframe_autowaypoints();
diff --git a/qcsrc/server/bot/havocbot/_mod.inc b/qcsrc/server/bot/havocbot/_mod.inc
deleted file mode 100644 (file)
index a6270bc..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-// generated file; do not modify
-#include <server/bot/havocbot/havocbot.qc>
-#include <server/bot/havocbot/roles.qc>
diff --git a/qcsrc/server/bot/havocbot/_mod.qh b/qcsrc/server/bot/havocbot/_mod.qh
deleted file mode 100644 (file)
index 4b62d1b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-// generated file; do not modify
-#include <server/bot/havocbot/havocbot.qh>
-#include <server/bot/havocbot/roles.qh>
diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc
deleted file mode 100644 (file)
index ceb9b29..0000000
+++ /dev/null
@@ -1,1296 +0,0 @@
-#include "havocbot.qh"
-
-#include "../aim.qh"
-#include "../bot.qh"
-#include "../navigation.qh"
-#include "../scripting.qh"
-#include "../waypoints.qh"
-
-#include <common/constants.qh>
-#include <common/physics/player.qh>
-#include <common/state.qh>
-#include <common/items/all.qh>
-
-#include <common/triggers/trigger/jumppads.qh>
-
-#include <lib/warpzone/common.qh>
-
-.float speed;
-
-void havocbot_ai(entity this)
-{
-       if(this.draggedby)
-               return;
-
-       if(bot_execute_commands(this))
-               return;
-
-       if (bot_strategytoken == this)
-       if (!bot_strategytoken_taken)
-       {
-               if(this.havocbot_blockhead)
-               {
-                       this.havocbot_blockhead = false;
-               }
-               else
-               {
-                       if (!this.jumppadcount)
-                               this.havocbot_role(this); // little too far down the rabbit hole
-               }
-
-               // 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(IS_DEAD(this))
-               if(!this.goalcurrent)
-               if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
-               {
-                       // Look for the closest waypoint out of water
-                       entity newgoal = NULL;
-                       IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 10000),
-                       {
-                               if(it.origin.z < this.origin.z)
-                                       continue;
-
-                               if(it.origin.z - this.origin.z - this.view_ofs.z > 100)
-                                       continue;
-
-                               if (pointcontents(it.origin + it.maxs + '0 0 1') != CONTENT_EMPTY)
-                                       continue;
-
-                               traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
-
-                               if(trace_fraction < 1)
-                                       continue;
-
-                               if(!newgoal || vlen2(it.origin - this.origin) < vlen2(newgoal.origin - this.origin))
-                                       newgoal = it;
-                       });
-
-                       if(newgoal)
-                       {
-                       //      te_wizspike(newgoal.origin);
-                               navigation_pushroute(this, newgoal);
-                       }
-               }
-
-               // token has been used this frame
-               bot_strategytoken_taken = true;
-       }
-
-       if(IS_DEAD(this))
-               return;
-
-       havocbot_chooseenemy(this);
-       if (this.bot_chooseweapontime < time )
-       {
-               this.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
-               havocbot_chooseweapon(this);
-       }
-       havocbot_aim(this);
-       lag_update(this);
-       if (this.bot_aimtarg)
-       {
-               this.aistatus |= AI_STATUS_ATTACKING;
-               this.aistatus &= ~AI_STATUS_ROAMING;
-
-               if(this.weapons)
-               {
-                       Weapon w = PS(this).m_weapon;
-                       w.wr_aim(w, this);
-                       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
-                       {
-                               PHYS_INPUT_BUTTON_ATCK(this) = false;
-                               PHYS_INPUT_BUTTON_ATCK2(this) = false;
-                       }
-                       else
-                       {
-                               if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))
-                                       this.lastfiredweapon = PS(this).m_weapon.m_id;
-                       }
-               }
-               else
-               {
-                       if(IS_PLAYER(this.bot_aimtarg))
-                               bot_aimdir(this, this.bot_aimtarg.origin + this.bot_aimtarg.view_ofs - this.origin - this.view_ofs , -1);
-               }
-       }
-       else if (this.goalcurrent)
-       {
-               this.aistatus |= AI_STATUS_ROAMING;
-               this.aistatus &= ~AI_STATUS_ATTACKING;
-
-               vector now,v,next;//,heading;
-               float aimdistance,skillblend,distanceblend,blend;
-               next = now = ( (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5) - (this.origin + this.view_ofs);
-               aimdistance = vlen(now);
-               //heading = this.velocity;
-               //dprint(this.goalstack01.classname,etos(this.goalstack01),"\n");
-               if(
-                       this.goalstack01 != this && this.goalstack01 != NULL && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
-                       !(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
-               )
-                       next = ((this.goalstack01.absmin + this.goalstack01.absmax) * 0.5) - (this.origin + this.view_ofs);
-
-               skillblend=bound(0,(skill+this.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(this), " ");
-               //dprint(vtos(now), ":", vtos(next), "=", vtos(v), " (blend ", ftos(blend), ")\n");
-               //v = now * (distanceblend) + next * (1-distanceblend);
-               if (this.waterlevel < WATERLEVEL_SWIMMING)
-                       v.z = 0;
-               //dprint("walk at:", vtos(v), "\n");
-               //te_lightning2(NULL, this.origin, this.goalcurrent.origin);
-               bot_aimdir(this, v, -1);
-       }
-       havocbot_movetogoal(this);
-
-       // if the bot is not attacking, consider reloading weapons
-       if (!(this.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(this.clip_load < this.clip_size)
-                       this.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(this.clip_load >= 0) // only if we're not reloading a weapon already
-               {
-                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                               if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.weapon_load[it.m_id] < it.reloading_ammo))
-                                       PS(this).m_switchweapon = it;
-                       ));
-               }
-       }
-}
-
-void havocbot_keyboard_movement(entity this, vector destorg)
-{
-       vector keyboard;
-       float blend, maxspeed;
-       float sk;
-
-       sk = skill + this.bot_moveskill;
-
-       maxspeed = autocvar_sv_maxspeed;
-
-       if (time < this.havocbot_keyboardtime)
-               return;
-
-       this.havocbot_keyboardtime =
-               max(
-                       this.havocbot_keyboardtime
-                               + 0.05/max(1, sk+this.havocbot_keyboardskill)
-                               + random()*0.025/max(0.00025, skill+this.havocbot_keyboardskill)
-               , time);
-       keyboard = this.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;
-
-       this.havocbot_keyboard = keyboard * maxspeed;
-       if (this.havocbot_ducktime>time) PHYS_INPUT_BUTTON_CROUCH(this) = true;
-
-       keyboard = this.havocbot_keyboard;
-       blend = bound(0,vlen(destorg-this.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree
-       //dprint("movement ", vtos(this.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
-       this.movement = this.movement + (keyboard - this.movement) * blend;
-}
-
-void havocbot_bunnyhop(entity this, vector dir)
-{
-       float bunnyhopdistance;
-       vector deviation;
-       float maxspeed;
-       vector gco, gno;
-
-       // Don't jump when attacking
-       if(this.aistatus & AI_STATUS_ATTACKING)
-               return;
-
-       if(IS_PLAYER(this.goalcurrent))
-               return;
-
-       maxspeed = autocvar_sv_maxspeed;
-
-       if(this.aistatus & AI_STATUS_DANGER_AHEAD)
-       {
-               this.aistatus &= ~AI_STATUS_RUNNING;
-               PHYS_INPUT_BUTTON_JUMP(this) = false;
-               this.bot_canruntogoal = 0;
-               this.bot_timelastseengoal = 0;
-               return;
-       }
-
-       if(this.waterlevel > WATERLEVEL_WETFEET)
-       {
-               this.aistatus &= ~AI_STATUS_RUNNING;
-               return;
-       }
-
-       if(this.bot_lastseengoal != this.goalcurrent && !(this.aistatus & AI_STATUS_RUNNING))
-       {
-               this.bot_canruntogoal = 0;
-               this.bot_timelastseengoal = 0;
-       }
-
-       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
-       bunnyhopdistance = vlen(this.origin - gco);
-
-       // Run only to visible goals
-       if(IS_ONGROUND(this))
-       if(this.speed==maxspeed)
-       if(checkpvs(this.origin + this.view_ofs, this.goalcurrent))
-       {
-                       this.bot_lastseengoal = this.goalcurrent;
-
-                       // seen it before
-                       if(this.bot_timelastseengoal)
-                       {
-                               // for a period of time
-                               if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
-                               {
-                                       float checkdistance;
-                                       checkdistance = true;
-
-                                       // don't run if it is too close
-                                       if(this.bot_canruntogoal==0)
-                                       {
-                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance)
-                                                       this.bot_canruntogoal = 1;
-                                               else
-                                                       this.bot_canruntogoal = -1;
-                                       }
-
-                                       if(this.bot_canruntogoal != 1)
-                                               return;
-
-                                       if(this.aistatus & AI_STATUS_ROAMING)
-                                       if(this.goalcurrent.classname=="waypoint")
-                                       if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
-                                       if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z)
-                                       if(this.goalstack01!=NULL)
-                                       {
-                                               gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
-                                               deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.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(this.origin - gno))
-                                               if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z)
-                                               {
-                                                       if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance))
-                                                       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
-                                                       {
-                                                               checkdistance = false;
-                                                       }
-                                               }
-                                       }
-
-                                       if(checkdistance)
-                                       {
-                                               this.aistatus &= ~AI_STATUS_RUNNING;
-                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
-                                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
-                                       }
-                                       else
-                                       {
-                                               this.aistatus |= AI_STATUS_RUNNING;
-                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               this.bot_timelastseengoal = time;
-                       }
-       }
-       else
-       {
-               this.bot_timelastseengoal = 0;
-       }
-
-#if 0
-       // Release jump button
-       if(!cvar("sv_pogostick"))
-       if((IS_ONGROUND(this)) == 0)
-       {
-               if(this.velocity.z < 0 || vlen(this.velocity)<maxspeed)
-                       PHYS_INPUT_BUTTON_JUMP(this) = false;
-
-               // Strafe
-               if(this.aistatus & AI_STATUS_RUNNING)
-               if(vlen(this.velocity)>maxspeed)
-               {
-                       deviation = vectoangles(dir) - vectoangles(this.velocity);
-                       while (deviation.y < -180) deviation.y = deviation.y + 360;
-                       while (deviation.y > 180) deviation.y = deviation.y - 360;
-
-                       if(fabs(deviation.y)>10)
-                               this.movement_x = 0;
-
-                       if(deviation.y>10)
-                               this.movement_y = maxspeed * -1;
-                       else if(deviation.y<10)
-                               this.movement_y = maxspeed;
-
-               }
-       }
-#endif
-}
-
-void havocbot_movetogoal(entity this)
-{
-       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 (this.goalentity)
-       //      te_lightning2(this, this.origin, (this.goalentity.absmin + this.goalentity.absmax) * 0.5);
-       this.movement = '0 0 0';
-       maxspeed = autocvar_sv_maxspeed;
-
-       // Jetpack navigation
-       if(this.goalcurrent)
-       if(this.navigation_jetpack_goal)
-       if(this.goalcurrent==this.navigation_jetpack_goal)
-       if(this.ammo_fuel)
-       {
-               if(autocvar_bot_debug_goalstack)
-               {
-                       debuggoalstack(this);
-                       te_wizspike(this.navigation_jetpack_point);
-               }
-
-               // Take off
-               if (!(this.aistatus & AI_STATUS_JETPACK_FLYING))
-               {
-                       // Brake almost completely so it can get a good direction
-                       if(vdist(this.velocity, >, 10))
-                               return;
-                       this.aistatus |= AI_STATUS_JETPACK_FLYING;
-               }
-
-               makevectors(this.v_angle.y * '0 1 0');
-               dir = normalize(this.navigation_jetpack_point - this.origin);
-
-               // Landing
-               if(this.aistatus & AI_STATUS_JETPACK_LANDING)
-               {
-                       // Calculate brake distance in xy
-                       float db, v, d;
-                       vector dxy;
-
-                       dxy = this.origin - ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ); dxy.z = 0;
-                       d = vlen(dxy);
-                       v = vlen(this.velocity -  this.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(this.velocity.x)>maxspeed*0.3)
-                               {
-                                       this.movement_x = dir * v_forward * -maxspeed;
-                                       return;
-                               }
-                               // Switch to normal mode
-                               this.navigation_jetpack_goal = NULL;
-                               this.aistatus &= ~AI_STATUS_JETPACK_LANDING;
-                               this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
-                               return;
-                       }
-               }
-               else if(checkpvs(this.origin,this.goalcurrent))
-               {
-                       // If I can see the goal switch to landing code
-                       this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
-                       this.aistatus |= AI_STATUS_JETPACK_LANDING;
-                       return;
-               }
-
-               // Flying
-               PHYS_INPUT_BUTTON_HOOK(this) = true;
-               if(this.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < this.origin.z)
-               {
-                       this.movement_x = dir * v_forward * maxspeed;
-                       this.movement_y = dir * v_right * maxspeed;
-               }
-               return;
-       }
-
-       // Handling of jump pads
-       if(this.jumppadcount)
-       {
-               // If got stuck on the jump pad try to reach the farthest visible waypoint
-               if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
-               {
-                       if(fabs(this.velocity.z)<50)
-                       {
-                               entity newgoal = NULL;
-                               IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 1000),
-                               {
-                                       traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
-
-                                       if(trace_fraction < 1)
-                                               continue;
-
-                                       if(!newgoal || vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))
-                                               newgoal = it;
-                               });
-
-                               if(newgoal)
-                               {
-                                       this.ignoregoal = this.goalcurrent;
-                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
-                                       navigation_clearroute(this);
-                                       navigation_routetogoal(this, newgoal, this.origin);
-                                       this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
-                               }
-                       }
-                       else
-                               return;
-               }
-               else
-               {
-                       if(this.velocity.z>0)
-                       {
-                               float threshold;
-                               vector velxy = this.velocity; velxy_z = 0;
-                               threshold = maxspeed * 0.2;
-                               if(vdist(velxy, <, threshold))
-                               {
-                                       LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now\n");
-                                       this.aistatus |= AI_STATUS_OUT_JUMPPAD;
-                               }
-                               return;
-                       }
-
-                       // Don't chase players while using a jump pad
-                       if(IS_PLAYER(this.goalcurrent) || IS_PLAYER(this.goalstack01))
-                               return;
-               }
-       }
-       else if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
-               this.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 (!(IS_ONGROUND(this)))
-       {
-               tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this);
-               if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos ))
-               if(this.items & IT_JETPACK)
-               {
-                       tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 65536', MOVE_NOMONSTERS, this);
-                       if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos + '0 0 1' ))
-                       {
-                               if(this.velocity.z<0)
-                               {
-                                       PHYS_INPUT_BUTTON_HOOK(this) = true;
-                               }
-                       }
-                       else
-                               PHYS_INPUT_BUTTON_HOOK(this) = true;
-
-                       // If there is no goal try to move forward
-
-                       if(this.goalcurrent==NULL)
-                               dir = v_forward;
-                       else
-                               dir = normalize(( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - this.origin);
-
-                       vector xyvelocity = this.velocity; xyvelocity_z = 0;
-                       float xyspeed = xyvelocity * dir;
-
-                       if(xyspeed < (maxspeed / 2))
-                       {
-                               makevectors(this.v_angle.y * '0 1 0');
-                               tracebox(this.origin, this.mins, this.maxs, this.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, this);
-                               if(trace_fraction==1)
-                               {
-                                       this.movement_x = dir * v_forward * maxspeed;
-                                       this.movement_y = dir * v_right * maxspeed;
-                                       if (skill < 10)
-                                               havocbot_keyboard_movement(this, this.origin + dir * 100);
-                               }
-                       }
-
-                       this.havocbot_blockhead = true;
-
-                       return;
-               }
-               else if(this.health>WEP_CVAR(devastator, damage)*0.5)
-               {
-                       if(this.velocity.z < 0)
-                       if(client_hasweapon(this, WEP_DEVASTATOR, true, false))
-                       {
-                               this.movement_x = maxspeed;
-
-                               if(this.rocketjumptime)
-                               {
-                                       if(time > this.rocketjumptime)
-                                       {
-                                               PHYS_INPUT_BUTTON_ATCK2(this) = true;
-                                               this.rocketjumptime = 0;
-                                       }
-                                       return;
-                               }
-
-                               PS(this).m_switchweapon = WEP_DEVASTATOR;
-                               this.v_angle_x = 90;
-                               PHYS_INPUT_BUTTON_ATCK(this) = true;
-                               this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
-                               return;
-                       }
-               }
-               else
-               {
-                       // If there is no goal try to move forward
-                       if(this.goalcurrent==NULL)
-                               this.movement_x = maxspeed;
-               }
-       }
-
-       // If we are under water with no goals, swim up
-       if(this.waterlevel)
-       if(this.goalcurrent==NULL)
-       {
-               dir = '0 0 0';
-               if(this.waterlevel>WATERLEVEL_SWIMMING)
-                       dir.z = 1;
-               else if(this.velocity.z >= 0 && !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER))
-                       PHYS_INPUT_BUTTON_JUMP(this) = true;
-               else
-                       PHYS_INPUT_BUTTON_JUMP(this) = false;
-               makevectors(this.v_angle.y * '0 1 0');
-               this.movement_x = dir * v_forward * maxspeed;
-               this.movement_y = dir * v_right * maxspeed;
-               this.movement_z = dir * v_up * maxspeed;
-       }
-
-       // if there is nowhere to go, exit
-       if (this.goalcurrent == NULL)
-               return;
-
-       if (this.goalcurrent)
-               navigation_poptouchedgoals(this);
-
-       // if ran out of goals try to use an alternative goal or get a new strategy asap
-       if(this.goalcurrent == NULL)
-       {
-               this.bot_strategytime = 0;
-               return;
-       }
-
-
-       if(autocvar_bot_debug_goalstack)
-               debuggoalstack(this);
-
-       m1 = this.goalcurrent.origin + this.goalcurrent.mins;
-       m2 = this.goalcurrent.origin + this.goalcurrent.maxs;
-       destorg = this.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 - this.origin;
-       //dist = vlen(diff);
-       dir = normalize(diff);
-       flatdir = diff;flatdir.z = 0;
-       flatdir = normalize(flatdir);
-       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
-
-       //if (this.bot_dodgevector_time < time)
-       {
-       //      this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval");
-       //      this.bot_dodgevector_jumpbutton = 1;
-               evadeobstacle = '0 0 0';
-               evadelava = '0 0 0';
-
-               if (this.waterlevel)
-               {
-                       if(this.waterlevel>WATERLEVEL_SWIMMING)
-                       {
-                       //      flatdir_z = 1;
-                               this.aistatus |= AI_STATUS_OUT_WATER;
-                       }
-                       else
-                       {
-                               if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && gco.z < this.origin.z) &&
-                                       ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER))
-                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
-                               else
-                                       PHYS_INPUT_BUTTON_JUMP(this) = false;
-                       }
-                       dir = normalize(flatdir);
-                       makevectors(this.v_angle.y * '0 1 0');
-               }
-               else
-               {
-                       if(this.aistatus & AI_STATUS_OUT_WATER)
-                               this.aistatus &= ~AI_STATUS_OUT_WATER;
-
-                       // jump if going toward an obstacle that doesn't look like stairs we
-                       // can walk up directly
-                       tracebox(this.origin, this.mins, this.maxs, this.origin + this.velocity * 0.2, false, this);
-                       if (trace_fraction < 1)
-                       if (trace_plane_normal.z < 0.7)
-                       {
-                               s = trace_fraction;
-                               tracebox(this.origin + stepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + stepheightvec, false, this);
-                               if (trace_fraction < s + 0.01)
-                               if (trace_plane_normal.z < 0.7)
-                               {
-                                       s = trace_fraction;
-                                       tracebox(this.origin + jumpstepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + jumpstepheightvec, false, this);
-                                       if (trace_fraction > s)
-                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
-                               }
-                       }
-
-                       // avoiding dangers and obstacles
-                       vector dst_ahead, dst_down;
-                       makevectors(this.v_angle.y * '0 1 0');
-                       dst_ahead = this.origin + this.view_ofs + (this.velocity * 0.4) + (v_forward * 32 * 3);
-                       dst_down = dst_ahead - '0 0 1500';
-
-                       // Look ahead
-                       traceline(this.origin + this.view_ofs, dst_ahead, true, NULL);
-
-                       // Check head-banging against walls
-                       if(vdist(this.origin + this.view_ofs - trace_endpos, <, 25) && !(this.aistatus & AI_STATUS_OUT_WATER))
-                       {
-                               PHYS_INPUT_BUTTON_JUMP(this) = true;
-                               if(this.facingwalltime && time > this.facingwalltime)
-                               {
-                                       this.ignoregoal = this.goalcurrent;
-                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
-                                       this.bot_strategytime = 0;
-                                       return;
-                               }
-                               else
-                               {
-                                       this.facingwalltime = time + 0.05;
-                               }
-                       }
-                       else
-                       {
-                               this.facingwalltime = 0;
-
-                               if(this.ignoregoal != NULL && time > this.ignoregoaltime)
-                               {
-                                       this.ignoregoal = NULL;
-                                       this.ignoregoaltime = 0;
-                               }
-                       }
-
-                       // Check for water/slime/lava and dangerous edges
-                       // (only when the bot is on the ground or jumping intentionally)
-                       this.aistatus &= ~AI_STATUS_DANGER_AHEAD;
-
-                       if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired )
-                       if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || PHYS_INPUT_BUTTON_JUMP(this))
-                       {
-                               // Look downwards
-                               traceline(dst_ahead , dst_down, true, NULL);
-                       //      te_lightning2(NULL, this.origin, dst_ahead);    // Draw "ahead" look
-                       //      te_lightning2(NULL, dst_ahead, dst_down);               // Draw "downwards" look
-                               if(trace_endpos.z < this.origin.z + this.mins.z)
-                               {
-                                       s = pointcontents(trace_endpos + '0 0 1');
-                                       if (s != CONTENT_SOLID)
-                                       if (s == CONTENT_LAVA || s == CONTENT_SLIME)
-                                               evadelava = normalize(this.velocity) * -1;
-                                       else if (s == CONTENT_SKY)
-                                               evadeobstacle = normalize(this.velocity) * -1;
-                                       else if (!boxesoverlap(dst_ahead - this.view_ofs + this.mins, dst_ahead - this.view_ofs + this.maxs,
-                                                               this.goalcurrent.absmin, this.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, this.mins, this.maxs, trace_endpos))
-                                               {
-                                                       // Remove dangerous dynamic goals from stack
-                                                       LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
-                                                       navigation_clearroute(this);
-                                                       return;
-                                               }
-                                       }
-                               }
-                       }
-
-                       dir = flatdir;
-                       evadeobstacle.z = 0;
-                       evadelava.z = 0;
-                       makevectors(this.v_angle.y * '0 1 0');
-
-                       if(evadeobstacle!='0 0 0'||evadelava!='0 0 0')
-                               this.aistatus |= AI_STATUS_DANGER_AHEAD;
-               }
-
-               dodge = havocbot_dodge(this);
-               dodge = dodge * bound(0,0.5+(skill+this.bot_dodgeskill)*0.1,1);
-               evadelava = evadelava * bound(1,3-(skill+this.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it
-               traceline(this.origin, ( ( this.enemy.absmin + this.enemy.absmax ) * 0.5 ), true, NULL);
-               if(IS_PLAYER(trace_ent))
-                       dir = dir * bound(0,(skill+this.bot_dodgeskill)/7,1);
-
-               dir = normalize(dir + dodge + evadeobstacle + evadelava);
-       //      this.bot_dodgevector = dir;
-       //      this.bot_dodgevector_jumpbutton = PHYS_INPUT_BUTTON_JUMP(this);
-       }
-
-       if(time < this.ladder_time)
-       {
-               if(this.goalcurrent.origin.z + this.goalcurrent.mins.z > this.origin.z + this.mins.z)
-               {
-                       if(this.origin.z + this.mins.z  < this.ladder_entity.origin.z + this.ladder_entity.maxs.z)
-                               dir.z = 1;
-               }
-               else
-               {
-                       if(this.origin.z + this.mins.z  > this.ladder_entity.origin.z + this.ladder_entity.mins.z)
-                               dir.z = -1;
-               }
-       }
-
-       //dir = this.bot_dodgevector;
-       //if (this.bot_dodgevector_jumpbutton)
-       //      PHYS_INPUT_BUTTON_JUMP(this) = true;
-       this.movement_x = dir * v_forward * maxspeed;
-       this.movement_y = dir * v_right * maxspeed;
-       this.movement_z = dir * v_up * maxspeed;
-
-       // Emulate keyboard interface
-       if (skill < 10)
-               havocbot_keyboard_movement(this, destorg);
-
-       // Bunnyhop!
-//     if(this.aistatus & AI_STATUS_ROAMING)
-       if(this.goalcurrent)
-       if(skill+this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
-               havocbot_bunnyhop(this, dir);
-
-       if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (IS_ONGROUND(this))) PHYS_INPUT_BUTTON_JUMP(this) = true;
-       if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) PHYS_INPUT_BUTTON_JUMP(this) = true;
-       if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) this.havocbot_ducktime=time+0.3/bound(0.1,skill+this.bot_dodgeskill,10);
-}
-
-void havocbot_chooseenemy(entity this)
-{
-       entity head, best, head2;
-       float rating, bestrating, hf;
-       vector eye, v;
-       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
-       {
-               this.enemy = NULL;
-               return;
-       }
-       if (this.enemy)
-       {
-               if (!bot_shouldattack(this, this.enemy))
-               {
-                       // enemy died or something, find a new target
-                       this.enemy = NULL;
-                       this.havocbot_chooseenemy_finished = time;
-               }
-               else if (this.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(this.origin+this.view_ofs, ( this.enemy.absmin + this.enemy.absmax ) * 0.5,false,NULL);
-                       if (trace_ent == this.enemy || trace_fraction == 1)
-                       if (vdist(((this.enemy.absmin + this.enemy.absmax) * 0.5) - this.origin, <, 1000))
-                       if (this.health > 30)
-                       {
-                               // remain tracking him for a shot while (case he went after a small corner or pilar
-                               this.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)
-                       this.havocbot_stickenemy = 0;
-               }
-       }
-       if (time < this.havocbot_chooseenemy_finished)
-               return;
-       this.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval;
-       eye = this.origin + this.view_ofs;
-       best = NULL;
-       bestrating = 100000000;
-       head = head2 = findchainfloat(bot_attack, true);
-
-       // Backup hit flags
-       hf = this.dphitcontentsmask;
-
-       // Search for enemies, if no enemy can be seen directly try to look through transparent objects
-
-       this.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;
-LABEL(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(this, head))
-                       {
-                               traceline(eye, v, true, this);
-                               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 || this.weapons) // || this.weapon == WEP_RIFLE.m_id
-                       break;
-               if(scan_transparent)
-                       break;
-
-               // Set flags to see through transparent objects
-               this.dphitcontentsmask |= DPCONTENTS_OPAQUE;
-
-               head = head2;
-               scan_transparent = true;
-       }
-
-       // Restore hit flags
-       this.dphitcontentsmask = hf;
-
-       this.enemy = best;
-       this.havocbot_stickenemy = true;
-       if(best && best.classname == "misc_breakablemodel")
-               this.havocbot_stickenemy = false;
-}
-
-float havocbot_chooseweapon_checkreload(entity this, int new_weapon)
-{
-       // 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 (this.weapon_load[new_weapon] < 0)
-       {
-               bool other_weapon_available = false;
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       if(it.wr_checkammo1(it, this) + it.wr_checkammo2(it, this))
-                               other_weapon_available = true;
-               ));
-               if(other_weapon_available)
-                       return true;
-       }
-
-       return false;
-}
-
-void havocbot_chooseweapon(entity this)
-{
-       int i;
-
-       // ;)
-       if(g_weaponarena_weapons == WEPSET(TUBA))
-       {
-               PS(this).m_switchweapon = WEP_TUBA;
-               return;
-       }
-
-       // TODO: clean this up by moving it to weapon code
-       if(this.enemy==NULL)
-       {
-               // If no weapon was chosen get the first available weapon
-               if(PS(this).m_weapon==WEP_Null)
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       if(client_hasweapon(this, it, true, false))
-                       {
-                               PS(this).m_switchweapon = it;
-                               return;
-                       }
-               ));
-               return;
-       }
-
-       // Do not change weapon during the next second after a combo
-       float f = time - this.lastcombotime;
-       if(f < 1)
-               return;
-
-       float w;
-       float distance; distance=bound(10,vlen(this.origin-this.enemy.origin)-200,10000);
-
-       // Should it do a weapon combo?
-       float af, ct, combo_time, combo;
-
-       af = ATTACK_FINISHED(this, 0);
-       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+this.bot_weaponskill))+3));
-
-       combo = false;
-
-       if(autocvar_bot_ai_weapon_combo)
-       if(PS(this).m_weapon.m_id == this.lastfiredweapon)
-       if(af > combo_time)
-       {
-               combo = true;
-               this.lastcombotime = time;
-       }
-
-       distance *= pow(2, this.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(this, Weapons_from(w), true, false) )
-                               {
-                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
-                                               continue;
-                                       PS(this).m_switchweapon = Weapons_from(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(this, Weapons_from(w), true, false) )
-                               {
-                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
-                                               continue;
-                                       PS(this).m_switchweapon = Weapons_from(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(this, Weapons_from(w), true, false) )
-                       {
-                               if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
-                                       continue;
-                               PS(this).m_switchweapon = Weapons_from(w);
-                               return;
-                       }
-               }
-       }
-}
-
-void havocbot_aim(entity this)
-{
-       vector myvel, enemyvel;
-//     if(this.flags & FL_INWATER)
-//             return;
-       if (time < this.nextaim)
-               return;
-       this.nextaim = time + 0.1;
-       myvel = this.velocity;
-       if (!this.waterlevel)
-               myvel.z = 0;
-       if (this.enemy)
-       {
-               enemyvel = this.enemy.velocity;
-               if (!this.enemy.waterlevel)
-                       enemyvel.z = 0;
-               lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel);
-       }
-       else
-               lag_additem(this, time + this.ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0');
-}
-
-bool havocbot_moveto_refresh_route(entity this)
-{
-       // Refresh path to goal if necessary
-       entity wp;
-       wp = this.havocbot_personal_waypoint;
-       navigation_goalrating_start(this);
-       navigation_routerating(this, wp, 10000, 10000);
-       navigation_goalrating_end(this);
-       return this.navigation_hasgoals;
-}
-
-float havocbot_moveto(entity this, vector pos)
-{
-       entity wp;
-
-       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-       {
-               // Step 4: Move to waypoint
-               if(this.havocbot_personal_waypoint==NULL)
-               {
-                       LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint\n");
-                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                       return CMD_STATUS_ERROR;
-               }
-
-               if (!bot_strategytoken_taken)
-               if(this.havocbot_personal_waypoint_searchtime<time)
-               {
-                       bot_strategytoken_taken = true;
-                       if(havocbot_moveto_refresh_route(this))
-                       {
-                               LOG_TRACE(this.netname, " walking to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts)\n");
-                               this.havocbot_personal_waypoint_searchtime = time + 10;
-                               this.havocbot_personal_waypoint_failcounter = 0;
-                       }
-                       else
-                       {
-                               this.havocbot_personal_waypoint_failcounter += 1;
-                               this.havocbot_personal_waypoint_searchtime = time + 2;
-                               if(this.havocbot_personal_waypoint_failcounter >= 30)
-                               {
-                                       LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin),"\n");
-                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-                                       delete(this.havocbot_personal_waypoint);
-                                       return CMD_STATUS_ERROR;
-                               }
-                               else
-                                       LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n");
-                       }
-               }
-
-               if(autocvar_bot_debug_goalstack)
-                       debuggoalstack(this);
-
-               // Heading
-               vector dir = ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - (this.origin + this.view_ofs);
-               dir.z = 0;
-               bot_aimdir(this, dir, -1);
-
-               // Go!
-               havocbot_movetogoal(this);
-
-               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED)
-               {
-                       // Step 5: Waypoint reached
-                       LOG_TRACE(this.netname, "'s personal waypoint reached\n");
-                       delete(this.havocbot_personal_waypoint);
-                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                       return CMD_STATUS_FINISHED;
-               }
-
-               return CMD_STATUS_EXECUTING;
-       }
-
-       // Step 2: Linking waypoint
-       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_LINKING)
-       {
-               // Wait until it is linked
-               if(!this.havocbot_personal_waypoint.wplinked)
-               {
-                       LOG_TRACE(this.netname, " waiting for personal waypoint to be linked\n");
-                       return CMD_STATUS_EXECUTING;
-               }
-
-               this.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame
-               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
-
-               // Step 3: Route to waypoint
-               LOG_TRACE(this.netname, " walking to its personal waypoint\n");
-
-               return CMD_STATUS_EXECUTING;
-       }
-
-       // Step 1: Spawning waypoint
-       wp = waypoint_spawnpersonal(this, pos);
-       if(wp==NULL)
-       {
-               LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos),"\n");
-               return CMD_STATUS_ERROR;
-       }
-
-       this.havocbot_personal_waypoint = wp;
-       this.havocbot_personal_waypoint_failcounter = 0;
-       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-
-       // if pos is inside a teleport, then let's mark it as teleport waypoint
-       FOREACH_ENTITY_CLASS("trigger_teleport", WarpZoneLib_BoxTouchesBrush(pos, pos, it, NULL),
-       {
-               wp.wpflags |= WAYPOINTFLAG_TELEPORT;
-               this.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(entity this)
-{
-       navigation_clearroute(this);
-       return CMD_STATUS_FINISHED;
-}
-
-void havocbot_setupbot(entity this)
-{
-       this.bot_ai = havocbot_ai;
-       this.cmd_moveto = havocbot_moveto;
-       this.cmd_resetgoal = havocbot_resetgoal;
-
-       havocbot_chooserole(this);
-}
-
-vector havocbot_dodge(entity this)
-{
-       // 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 != this)
-               {
-                       vl = vlen(head.velocity);
-                       if (vl > autocvar_sv_maxspeed * 0.3)
-                       {
-                               n = normalize(head.velocity);
-                               v = this.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 - this.origin);
-                               if (bestdanger < danger)
-                               {
-                                       bestdanger = danger;
-                                       dodge = normalize(this.origin - head.origin);
-                               }
-                       }
-               }
-               head = head.chain;
-       }
-       return dodge;
-#endif
-}
diff --git a/qcsrc/server/bot/havocbot/havocbot.qh b/qcsrc/server/bot/havocbot/havocbot.qh
deleted file mode 100644 (file)
index 4a391b6..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-
-/*
- * 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(entity this);
-void havocbot_aim(entity this);
-void havocbot_setupbot(entity this);
-void havocbot_movetogoal(entity this);
-void havocbot_chooserole(entity this);
-void havocbot_chooseenemy(entity this);
-void havocbot_chooseweapon(entity this);
-void havocbot_bunnyhop(entity this, vector dir);
-void havocbot_keyboard_movement(entity this, vector destorg);
-
-float havocbot_resetgoal(entity this);
-float havocbot_moveto(entity this, vector pos);
-float havocbot_moveto_refresh_route(entity this);
-
-vector havocbot_dodge(entity this);
-
-.void(entity this) havocbot_role;
-.void(entity this) havocbot_previous_role;
-
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items;
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
-
-/*
- * Imports
- */
-
-.entity draggedby;
-.float ladder_time;
-.entity ladder_entity;
diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc
deleted file mode 100644 (file)
index 9ea6a3d..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-#include "roles.qh"
-
-#include "havocbot.qh"
-
-#include "../bot.qh"
-#include "../navigation.qh"
-
-.float max_armorvalue;
-.float havocbot_role_timeout;
-
-.void(entity this) havocbot_previous_role;
-.void(entity this) havocbot_role;
-
-void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius)
-{
-       float rating, d, discard, friend_distance, enemy_distance;
-       vector o;
-       ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
-
-       FOREACH_ENTITY_FLOAT(bot_pickup, true,
-       {
-               o = (it.absmin + it.absmax) * 0.5;
-               friend_distance = 10000; enemy_distance = 10000;
-               rating = 0;
-
-               if(!it.solid || vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) )
-                       continue;
-
-               // Check if the item can be picked up safely
-               if(it.classname == "droppedweapon")
-               {
-                       traceline(o, o + '0 0 -1500', true, NULL);
-
-                       d = pointcontents(trace_endpos + '0 0 1');
-                       if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
-                               continue;
-                       if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
-                               continue;
-               }
-               else
-               {
-                       // Ignore items under water
-                       traceline(it.origin + it.maxs, it.origin + it.maxs, MOVE_NORMAL, it);
-                       if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
-                               continue;
-               }
-
-               if(teamplay)
-               {
-                       discard = false;
-
-                       entity picker = it;
-                       FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it),
-                       {
-                               d = vlen(it.origin - o); // distance between player and item
-
-                               if ( it.team == this.team )
-                               {
-                                       if ( !IS_REAL_CLIENT(it) || discard )
-                                               continue;
-
-                                       if( d > friend_distance)
-                                               continue;
-
-                                       friend_distance = d;
-
-                                       discard = true;
-
-                                       if( picker.health && it.health > this.health )
-                                               continue;
-
-                                       if( picker.armorvalue && it.armorvalue > this.armorvalue)
-                                               continue;
-
-                                       if( picker.weapons )
-                                       if( picker.weapons & ~it.weapons )
-                                               continue;
-
-                                       if (picker.ammo_shells && it.ammo_shells > this.ammo_shells)
-                                               continue;
-
-                                       if (picker.ammo_nails && it.ammo_nails > this.ammo_nails)
-                                               continue;
-
-                                       if (picker.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
-                                               continue;
-
-                                       if (picker.ammo_cells && it.ammo_cells > this.ammo_cells)
-                                               continue;
-
-                                       if (picker.ammo_plasma && it.ammo_plasma > this.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 && vdist(o - org, <, enemy_distance)) ||
-                               (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
-                               rating = it.bot_pickupevalfunc(this, it);
-
-               }
-               else
-                       rating = it.bot_pickupevalfunc(this, it);
-
-               if(rating > 0)
-                       navigation_routerating(this, it, rating * ratingscale, 2000);
-       });
-}
-
-void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius)
-{
-       FOREACH_ENTITY_CLASS("dom_controlpoint", vdist((((it.absmin + it.absmax) * 0.5) - org), <, sradius),
-       {
-               if(it.cnt > -1) // this is just being fought
-                       navigation_routerating(this, it, ratingscale, 5000);
-               else if(it.goalentity.cnt == 0) // unclaimed
-                       navigation_routerating(this, it, ratingscale * 0.5, 5000);
-               else if(it.goalentity.team != this.team) // other team's point
-                       navigation_routerating(this, it, ratingscale * 0.2, 5000);
-       });
-}
-
-void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius)
-{
-       if (autocvar_bot_nofire)
-               return;
-
-       // don't chase players if we're under water
-       if(this.waterlevel>WATERLEVEL_WETFEET)
-               return;
-
-       int t;
-
-       FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), LAMBDA(
-               // TODO: Merge this logic with the bot_shouldattack function
-               if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius))
-                       continue;
-
-               // rate only visible enemies
-               /*
-               traceline(this.origin + this.view_ofs, it.origin, MOVE_NOMONSTERS, this);
-               if (trace_fraction < 1 || trace_ent != it)
-                       continue;
-               */
-
-               if((it.flags & FL_INWATER) || (it.flags & FL_PARTIALGROUND))
-                       continue;
-
-               // not falling
-               if((IS_ONGROUND(it)) == 0)
-               {
-                       traceline(it.origin, it.origin + '0 0 -1500', true, NULL);
-                       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(it.origin, it.mins, it.maxs, trace_endpos))
-                               continue;
-               }
-
-               // TODO: rate waypoints near the targetted player at that moment, instead of the player itthis
-               //               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 = (this.health + this.armorvalue ) / (it.health + it.armorvalue );
-               navigation_routerating(this, it, t * ratingscale, 2000);
-       ));
-}
-
-// legacy bot role for standard gamemodes
-// go to best items
-void havocbot_role_generic(entity this)
-{
-       if(IS_DEAD(this))
-               return;
-
-       if (this.bot_strategytime < time)
-       {
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start(this);
-               havocbot_goalrating_items(this, 10000, this.origin, 10000);
-               havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000);
-               //havocbot_goalrating_waypoints(1, this.origin, 1000);
-               navigation_goalrating_end(this);
-       }
-}
-
-void havocbot_chooserole_generic(entity this)
-{
-       this.havocbot_role = havocbot_role_generic;
-}
-
-void havocbot_chooserole(entity this)
-{
-       LOG_TRACE("choosing a role...\n");
-       this.bot_strategytime = 0;
-       if(!MUTATOR_CALLHOOK(HavocBot_ChooseRole, this))
-               havocbot_chooserole_generic(this);
-}
diff --git a/qcsrc/server/bot/havocbot/roles.qh b/qcsrc/server/bot/havocbot/roles.qh
deleted file mode 100644 (file)
index 5b1f2b5..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#pragma once
-void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius);
diff --git a/qcsrc/server/bot/havocbot/scripting.qh b/qcsrc/server/bot/havocbot/scripting.qh
deleted file mode 100644 (file)
index 07cb4d6..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-void bot_clearqueue(entity bot);
diff --git a/qcsrc/server/bot/navigation.qc b/qcsrc/server/bot/navigation.qc
deleted file mode 100644 (file)
index c9418e2..0000000
+++ /dev/null
@@ -1,1211 +0,0 @@
-#include "navigation.qh"
-
-#include "bot.qh"
-#include "waypoints.qh"
-
-#include <common/t_items.qh>
-
-#include <common/items/all.qh>
-
-#include <common/constants.qh>
-#include <common/triggers/trigger/jumppads.qh>
-
-.float speed;
-
-// rough simulation of walking from one point to another to test if a path
-// can be traveled, used for waypoint linking and havocbot
-
-bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode)
-{
-       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(e, 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(e, 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(e, trace_endpos);
-
-                       if (trace_fraction < 1)
-                       {
-                               swimming = true;
-                               org = trace_endpos - normalize(org - trace_endpos) * stepdist;
-                               for (; org.z < end.z + e.maxs.z; org.z += stepdist)
-                               {
-                                               if(autocvar_bot_debug_tracewalk)
-                                                       debugnode(e, 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(e, 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(entity this)
-{
-       //print("bot ", etos(this), " clear\n");
-       this.navigation_hasgoals = false;
-       this.goalcurrent = NULL;
-       this.goalstack01 = NULL;
-       this.goalstack02 = NULL;
-       this.goalstack03 = NULL;
-       this.goalstack04 = NULL;
-       this.goalstack05 = NULL;
-       this.goalstack06 = NULL;
-       this.goalstack07 = NULL;
-       this.goalstack08 = NULL;
-       this.goalstack09 = NULL;
-       this.goalstack10 = NULL;
-       this.goalstack11 = NULL;
-       this.goalstack12 = NULL;
-       this.goalstack13 = NULL;
-       this.goalstack14 = NULL;
-       this.goalstack15 = NULL;
-       this.goalstack16 = NULL;
-       this.goalstack17 = NULL;
-       this.goalstack18 = NULL;
-       this.goalstack19 = NULL;
-       this.goalstack20 = NULL;
-       this.goalstack21 = NULL;
-       this.goalstack22 = NULL;
-       this.goalstack23 = NULL;
-       this.goalstack24 = NULL;
-       this.goalstack25 = NULL;
-       this.goalstack26 = NULL;
-       this.goalstack27 = NULL;
-       this.goalstack28 = NULL;
-       this.goalstack29 = NULL;
-       this.goalstack30 = NULL;
-       this.goalstack31 = NULL;
-}
-
-// 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 this, entity e)
-{
-       //print("bot ", etos(this), " push ", etos(e), "\n");
-       this.goalstack31 = this.goalstack30;
-       this.goalstack30 = this.goalstack29;
-       this.goalstack29 = this.goalstack28;
-       this.goalstack28 = this.goalstack27;
-       this.goalstack27 = this.goalstack26;
-       this.goalstack26 = this.goalstack25;
-       this.goalstack25 = this.goalstack24;
-       this.goalstack24 = this.goalstack23;
-       this.goalstack23 = this.goalstack22;
-       this.goalstack22 = this.goalstack21;
-       this.goalstack21 = this.goalstack20;
-       this.goalstack20 = this.goalstack19;
-       this.goalstack19 = this.goalstack18;
-       this.goalstack18 = this.goalstack17;
-       this.goalstack17 = this.goalstack16;
-       this.goalstack16 = this.goalstack15;
-       this.goalstack15 = this.goalstack14;
-       this.goalstack14 = this.goalstack13;
-       this.goalstack13 = this.goalstack12;
-       this.goalstack12 = this.goalstack11;
-       this.goalstack11 = this.goalstack10;
-       this.goalstack10 = this.goalstack09;
-       this.goalstack09 = this.goalstack08;
-       this.goalstack08 = this.goalstack07;
-       this.goalstack07 = this.goalstack06;
-       this.goalstack06 = this.goalstack05;
-       this.goalstack05 = this.goalstack04;
-       this.goalstack04 = this.goalstack03;
-       this.goalstack03 = this.goalstack02;
-       this.goalstack02 = this.goalstack01;
-       this.goalstack01 = this.goalcurrent;
-       this.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(entity this)
-{
-       //print("bot ", etos(this), " pop\n");
-       this.goalcurrent = this.goalstack01;
-       this.goalstack01 = this.goalstack02;
-       this.goalstack02 = this.goalstack03;
-       this.goalstack03 = this.goalstack04;
-       this.goalstack04 = this.goalstack05;
-       this.goalstack05 = this.goalstack06;
-       this.goalstack06 = this.goalstack07;
-       this.goalstack07 = this.goalstack08;
-       this.goalstack08 = this.goalstack09;
-       this.goalstack09 = this.goalstack10;
-       this.goalstack10 = this.goalstack11;
-       this.goalstack11 = this.goalstack12;
-       this.goalstack12 = this.goalstack13;
-       this.goalstack13 = this.goalstack14;
-       this.goalstack14 = this.goalstack15;
-       this.goalstack15 = this.goalstack16;
-       this.goalstack16 = this.goalstack17;
-       this.goalstack17 = this.goalstack18;
-       this.goalstack18 = this.goalstack19;
-       this.goalstack19 = this.goalstack20;
-       this.goalstack20 = this.goalstack21;
-       this.goalstack21 = this.goalstack22;
-       this.goalstack22 = this.goalstack23;
-       this.goalstack23 = this.goalstack24;
-       this.goalstack24 = this.goalstack25;
-       this.goalstack25 = this.goalstack26;
-       this.goalstack26 = this.goalstack27;
-       this.goalstack27 = this.goalstack28;
-       this.goalstack28 = this.goalstack29;
-       this.goalstack29 = this.goalstack30;
-       this.goalstack30 = this.goalstack31;
-       this.goalstack31 = NULL;
-}
-
-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, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), org, bot_navigation_movemode))
-                                       return true;
-                       }
-                       else
-                       {
-                               if (tracewalk(ent, org, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), 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)
-{
-       vector pm1 = ent.origin + ent.mins;
-       vector pm2 = ent.origin + ent.maxs;
-
-       // do two scans, because box test is cheaper
-       IL_EACH(g_waypoints, it != ent && it != except,
-       {
-               if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
-                       return it;
-       });
-
-       vector org = ent.origin + 0.5 * (ent.mins + ent.maxs);
-       org.z = ent.origin.z + ent.mins.z - STAT(PL_MIN, NULL).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);
-
-       entity best = NULL;
-       vector v;
-
-       // box check failed, try walk
-       IL_EACH(g_waypoints, it != ent,
-       {
-               if(it.wpisbox)
-               {
-                       vector wm1 = it.origin + it.mins;
-                       vector wm2 = it.origin + it.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 = it.origin;
-               if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist))
-               {
-                       bestdist = vlen(v - org);
-                       best = it;
-               }
-       });
-       return best;
-}
-entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
-{
-       entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, NULL);
-       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 this, float maxdist)
-{
-       vector v, m1, m2;
-//     navigation_testtracewalk = true;
-       int c = 0;
-       IL_EACH(g_waypoints, !it.wpconsidered,
-       {
-               if (it.wpisbox)
-               {
-                       m1 = it.origin + it.mins;
-                       m2 = it.origin + it.maxs;
-                       v = this.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 = it.origin;
-               vector diff = v - this.origin;
-               diff.z = max(0, diff.z);
-               if(vdist(diff, <, maxdist))
-               {
-                       it.wpconsidered = true;
-                       if (tracewalk(this, this.origin, this.mins, this.maxs, v, bot_navigation_movemode))
-                       {
-                               it.wpnearestpoint = v;
-                               it.wpcost = vlen(v - this.origin) + it.dmg;
-                               it.wpfire = 1;
-                               it.enemy = NULL;
-                               c = c + 1;
-                       }
-               }
-       });
-       //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 this, entity fixed_source_waypoint)
-{
-       float cost, cost2;
-       vector p;
-
-       IL_EACH(g_waypoints, true,
-       {
-               it.wpconsidered = false;
-               it.wpnearestpoint = '0 0 0';
-               it.wpcost = 10000000;
-               it.wpfire = 0;
-               it.enemy = NULL;
-       });
-
-       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 = NULL;
-       }
-       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 increment, maxdistance;
-               if(IS_ONGROUND(this))
-               {
-                       increment = 750;
-                       maxdistance = 50000;
-               }
-               else
-               {
-                       increment = 500;
-                       maxdistance = 1500;
-               }
-
-               for(int j = increment; !navigation_markroutes_nearestwaypoints(this, j) && j < maxdistance; j += increment);
-       }
-
-       bool searching = true;
-       while (searching)
-       {
-               searching = false;
-               IL_EACH(g_waypoints, it.wpfire,
-               {
-                       searching = true;
-                       it.wpfire = 0;
-                       cost = it.wpcost;
-                       p = it.wpnearestpoint;
-                       entity wp;
-                       wp = it.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp00mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp01mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp02mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp03mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp04mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp05mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp06mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp07mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp08mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp09mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp10mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp11mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp12mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp13mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp14mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp15mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp16mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp17mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp18mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp19mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp20mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp21mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp22mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp23mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp24mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp25mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp26mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp27mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp28mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp29mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp30mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       wp = it.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp31mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
-                       }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
-               });
-       }
-}
-
-// queries the entire spawnfunc_waypoint network for pathes leading to the bot
-void navigation_markroutes_inverted(entity fixed_source_waypoint)
-{
-       float cost, cost2;
-       vector p;
-       IL_EACH(g_waypoints, true,
-       {
-               it.wpconsidered = false;
-               it.wpnearestpoint = '0 0 0';
-               it.wpcost = 10000000;
-               it.wpfire = 0;
-               it.enemy = NULL;
-       });
-
-       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 = NULL;
-       }
-       else
-       {
-               error("need to start with a waypoint\n");
-       }
-
-       bool searching = true;
-       while (searching)
-       {
-               searching = false;
-               IL_EACH(g_waypoints, it.wpfire,
-               {
-                       searching = true;
-                       it.wpfire = 0;
-                       cost = it.wpcost; // cost to walk from it to home
-                       p = it.wpnearestpoint;
-                       entity wp = it;
-                       IL_EACH(g_waypoints, true,
-                       {
-                               if(wp != it.wp00) if(wp != it.wp01) if(wp != it.wp02) if(wp != it.wp03)
-                               if(wp != it.wp04) if(wp != it.wp05) if(wp != it.wp06) if(wp != it.wp07)
-                               if(wp != it.wp08) if(wp != it.wp09) if(wp != it.wp10) if(wp != it.wp11)
-                               if(wp != it.wp12) if(wp != it.wp13) if(wp != it.wp14) if(wp != it.wp15)
-                               if(wp != it.wp16) if(wp != it.wp17) if(wp != it.wp18) if(wp != it.wp19)
-                               if(wp != it.wp20) if(wp != it.wp21) if(wp != it.wp22) if(wp != it.wp23)
-                               if(wp != it.wp24) if(wp != it.wp25) if(wp != it.wp26) if(wp != it.wp27)
-                               if(wp != it.wp28) if(wp != it.wp29) if(wp != it.wp30) if(wp != it.wp31)
-                                       continue;
-                               cost2 = cost + it.dmg;
-                               navigation_markroutes_checkwaypoint(wp, it, cost2, p);
-                       });
-               });
-       }
-}
-
-// updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item
-void navigation_routerating(entity this, entity e, float f, float rangebias)
-{
-       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(this.items & IT_JETPACK)
-       if(autocvar_bot_ai_navigation_jetpack)
-       if(vdist(this.origin - o, >, autocvar_bot_ai_navigation_jetpack_mindistance))
-       {
-               vector pointa, pointb;
-
-               LOG_DEBUG("jetpack ai: evaluating path for ", e.classname, "\n");
-
-               // Point A
-               traceline(this.origin, this.origin + '0 0 65535', MOVE_NORMAL, this);
-               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, this);
-
-               if(trace_fraction==1)
-               {
-                       LOG_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' * (STAT(PL_MAX, NULL).z - STAT(PL_MIN, NULL).z) * 10;
-
-                       do{
-                               npa = pointa + down;
-                               npb = pointb + down;
-
-                               if(npa.z<=this.absmax.z)
-                                       break;
-
-                               if(npb.z<=e.absmax.z)
-                                       break;
-
-                               traceline(npa, npb, MOVE_NORMAL, this);
-                               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 - this.origin.z);
-
-                       t = zdistance / autocvar_g_jetpack_maxspeed_up;
-                       t += xydistance / autocvar_g_jetpack_maxspeed_side;
-                       fuel = t * autocvar_g_jetpack_fuel * 0.8;
-
-                       LOG_DEBUG(strcat("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel), "\n"));
-
-                       // enough fuel ?
-                       if(this.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)
-                               {
-                                       LOG_DEBUG(strcat("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")\n"));
-                                       navigation_bestrating = f;
-                                       navigation_bestgoal = e;
-                                       this.navigation_jetpack_goal = e;
-                                       this.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
-                       {
-                               LOG_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)
-                               {
-                                       LOG_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;
-       }
-
-       LOG_DEBUG(strcat("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")\n"));
-       if (nwp)
-       if (nwp.wpcost < 10000000)
-       {
-               //te_wizspike(nwp.wpnearestpoint);
-               LOG_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)));
-               LOG_DEBUG(strcat("considering ", e.classname, " (with rating ", ftos(f), ")\n"));
-               if (navigation_bestrating < f)
-               {
-                       LOG_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
-bool navigation_routetogoal(entity this, entity e, vector startposition)
-{
-       this.goalentity = e;
-
-       // if there is no goal, just exit
-       if (!e)
-               return false;
-
-       this.navigation_hasgoals = true;
-
-       // put the entity on the goal stack
-       //print("routetogoal ", etos(e), "\n");
-       navigation_pushroute(this, e);
-
-       if(g_jetpack)
-       if(e==this.navigation_jetpack_goal)
-               return true;
-
-       // if it can reach the goal there is nothing more to do
-       if (tracewalk(this, startposition, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), (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 == NULL)
-               return false;
-
-       for (;;)
-       {
-               // add the spawnfunc_waypoint to the path
-               navigation_pushroute(this, e);
-               e = e.enemy;
-
-               if(e==NULL)
-                       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(entity this)
-{
-       vector org, m1, m2;
-       org = this.origin;
-       m1 = org + this.mins;
-       m2 = org + this.maxs;
-
-       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
-       {
-               if(this.lastteleporttime>0)
-               if(time-this.lastteleporttime<(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15)
-               {
-                       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-                       if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
-                       {
-                               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                       }
-                       navigation_poproute(this);
-                       return;
-               }
-       }
-
-       // If for some reason the bot is closer to the next goal, pop the current one
-       if(this.goalstack01)
-       if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin))
-       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
-       if(tracewalk(this, this.origin, this.mins, this.maxs, (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5, bot_navigation_movemode))
-       {
-               LOG_DEBUG(strcat("path optimized for ", this.netname, ", removed a goal from the queue\n"));
-               navigation_poproute(this);
-               // 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 itthis
-       if(IS_PLAYER(this.goalcurrent))
-               navigation_poproute(this);
-
-       // aid for detecting jump pads better (distance based check fails sometimes)
-       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT && this.jumppadcount > 0 )
-               navigation_poproute(this);
-
-       // Loose goal touching check when running
-       if(this.aistatus & AI_STATUS_RUNNING)
-       if(this.speed >= autocvar_sv_maxspeed) // if -really- running
-       if(this.goalcurrent.classname=="waypoint")
-       {
-               if(vdist(this.origin - this.goalcurrent.origin, <, 150))
-               {
-                       traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL);
-                       if(trace_fraction==1)
-                       {
-                               // Detect personal waypoints
-                               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-                               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
-                               {
-                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                               }
-
-                               navigation_poproute(this);
-                       }
-               }
-       }
-
-       while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax))
-       {
-               // Detect personal waypoints
-               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
-               {
-                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-               }
-
-               navigation_poproute(this);
-       }
-}
-
-// begin a goal selection session (queries spawnfunc_waypoint network)
-void navigation_goalrating_start(entity this)
-{
-       if(this.aistatus & AI_STATUS_STUCK)
-               return;
-
-       this.navigation_jetpack_goal = NULL;
-       navigation_bestrating = -1;
-       this.navigation_hasgoals = false;
-       navigation_clearroute(this);
-       navigation_bestgoal = NULL;
-       navigation_markroutes(this, NULL);
-}
-
-// ends a goal selection session (updates goal stack to the best goal)
-void navigation_goalrating_end(entity this)
-{
-       if(this.aistatus & AI_STATUS_STUCK)
-               return;
-
-       navigation_routetogoal(this, navigation_bestgoal, this.origin);
-       LOG_DEBUG(strcat("best goal ", this.goalcurrent.classname , "\n"));
-
-       // If the bot got stuck then try to reach the farthest waypoint
-       if (!this.navigation_hasgoals)
-       if (autocvar_bot_wander_enable)
-       {
-               if (!(this.aistatus & AI_STATUS_STUCK))
-               {
-                       LOG_DEBUG(strcat(this.netname, " cannot walk to any goal\n"));
-                       this.aistatus |= AI_STATUS_STUCK;
-               }
-
-               this.navigation_hasgoals = false; // Reset this value
-       }
-}
-
-void botframe_updatedangerousobjects(float maxupdate)
-{
-       vector m1, m2, v, o;
-       float c, d, danger;
-       c = 0;
-       IL_EACH(g_waypoints, true,
-       {
-               danger = 0;
-               m1 = it.mins;
-               m2 = it.maxs;
-               FOREACH_ENTITY_FLOAT(bot_dodge, true,
-               {
-                       v = it.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 = (it.absmin + it.absmax) * 0.5;
-                       d = it.bot_dodgerating - vlen(o - v);
-                       if (d > 0)
-                       {
-                               traceline(o, v, true, NULL);
-                               if (trace_fraction == 1)
-                                       danger = danger + d;
-                       }
-               });
-               it.dmg = danger;
-               c = c + 1;
-               if (c >= maxupdate)
-                       break;
-       });
-}
-
-void navigation_unstuck(entity this)
-{
-       float search_radius = 1000;
-
-       if (!autocvar_bot_wander_enable)
-               return;
-
-       if (!bot_waypoint_queue_owner)
-       {
-               LOG_DEBUG(strcat(this.netname, " sutck, taking over the waypoints queue\n"));
-               bot_waypoint_queue_owner = this;
-               bot_waypoint_queue_bestgoal = NULL;
-               bot_waypoint_queue_bestgoalrating = 0;
-       }
-
-       if(bot_waypoint_queue_owner!=this)
-               return;
-
-       if (bot_waypoint_queue_goal)
-       {
-               // evaluate the next goal on the queue
-               float d = vlen(this.origin - bot_waypoint_queue_goal.origin);
-               LOG_DEBUG(strcat(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d), "\n"));
-               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), 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)
-                       {
-                               LOG_DEBUG(strcat(this.netname, " stuck, reachable waypoint found, heading to it\n"));
-                               navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin);
-                               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-                               this.aistatus &= ~AI_STATUS_STUCK;
-                       }
-                       else
-                       {
-                               LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n"));
-                       }
-
-                       bot_waypoint_queue_owner = NULL;
-               }
-       }
-       else
-       {
-               if(bot_strategytoken!=this)
-                       return;
-
-               // build a new queue
-               LOG_DEBUG(strcat(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu\n"));
-
-               entity first = NULL;
-
-               FOREACH_ENTITY_RADIUS(this.origin, search_radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
-               {
-                       if(bot_waypoint_queue_goal)
-                               bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = it;
-                       else
-                               first = it;
-
-                       bot_waypoint_queue_goal = it;
-                       bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
-               });
-
-               if (first)
-                       bot_waypoint_queue_goal = first;
-               else
-               {
-                       LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n"));
-                       bot_waypoint_queue_owner = NULL;
-               }
-       }
-}
-
-// Support for debugging tracewalk visually
-
-void debugresetnodes()
-{
-       debuglastnode = '0 0 0';
-}
-
-void debugnode(entity this, vector node)
-{
-       if (!IS_PLAYER(this))
-               return;
-
-       if(debuglastnode=='0 0 0')
-       {
-               debuglastnode = node;
-               return;
-       }
-
-       te_lightning2(NULL, 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(entity this)
-{
-       entity goal;
-       vector org, go;
-
-       if(this.goalcounter==0)goal=this.goalcurrent;
-       else if(this.goalcounter==1)goal=this.goalstack01;
-       else if(this.goalcounter==2)goal=this.goalstack02;
-       else if(this.goalcounter==3)goal=this.goalstack03;
-       else if(this.goalcounter==4)goal=this.goalstack04;
-       else if(this.goalcounter==5)goal=this.goalstack05;
-       else if(this.goalcounter==6)goal=this.goalstack06;
-       else if(this.goalcounter==7)goal=this.goalstack07;
-       else if(this.goalcounter==8)goal=this.goalstack08;
-       else if(this.goalcounter==9)goal=this.goalstack09;
-       else if(this.goalcounter==10)goal=this.goalstack10;
-       else if(this.goalcounter==11)goal=this.goalstack11;
-       else if(this.goalcounter==12)goal=this.goalstack12;
-       else if(this.goalcounter==13)goal=this.goalstack13;
-       else if(this.goalcounter==14)goal=this.goalstack14;
-       else if(this.goalcounter==15)goal=this.goalstack15;
-       else if(this.goalcounter==16)goal=this.goalstack16;
-       else if(this.goalcounter==17)goal=this.goalstack17;
-       else if(this.goalcounter==18)goal=this.goalstack18;
-       else if(this.goalcounter==19)goal=this.goalstack19;
-       else if(this.goalcounter==20)goal=this.goalstack20;
-       else if(this.goalcounter==21)goal=this.goalstack21;
-       else if(this.goalcounter==22)goal=this.goalstack22;
-       else if(this.goalcounter==23)goal=this.goalstack23;
-       else if(this.goalcounter==24)goal=this.goalstack24;
-       else if(this.goalcounter==25)goal=this.goalstack25;
-       else if(this.goalcounter==26)goal=this.goalstack26;
-       else if(this.goalcounter==27)goal=this.goalstack27;
-       else if(this.goalcounter==28)goal=this.goalstack28;
-       else if(this.goalcounter==29)goal=this.goalstack29;
-       else if(this.goalcounter==30)goal=this.goalstack30;
-       else if(this.goalcounter==31)goal=this.goalstack31;
-       else goal=NULL;
-
-       if(goal==NULL)
-       {
-               this.goalcounter = 0;
-               this.lastposition='0 0 0';
-               return;
-       }
-
-       if(this.lastposition=='0 0 0')
-               org = this.origin;
-       else
-               org = this.lastposition;
-
-
-       go = ( goal.absmin + goal.absmax ) * 0.5;
-       te_lightning2(NULL, org, go);
-       this.lastposition = go;
-
-       this.goalcounter++;
-}
diff --git a/qcsrc/server/bot/navigation.qh b/qcsrc/server/bot/navigation.qh
deleted file mode 100644 (file)
index ad01776..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-/*
- * Globals and Fields
- */
-
-float navigation_bestrating;
-float bot_navigation_movemode;
-float navigation_testtracewalk;
-
-vector jumpstepheightvec;
-vector stepheightvec;
-
-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(entity this, vector node);
-void debugnodestatus(vector position, float status);
-
-void debuggoalstack(entity this);
-
-float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
-
-float navigation_markroutes_nearestwaypoints(entity this, float maxdist);
-float navigation_routetogoal(entity this, entity e, vector startposition);
-
-void navigation_clearroute(entity this);
-void navigation_pushroute(entity this, entity e);
-void navigation_poproute(entity this);
-void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p);
-void navigation_markroutes(entity this, entity fixed_source_waypoint);
-void navigation_markroutes_inverted(entity fixed_source_waypoint);
-void navigation_routerating(entity this, entity e, float f, float rangebias);
-void navigation_poptouchedgoals(entity this);
-void navigation_goalrating_start(entity this);
-void navigation_goalrating_end(entity this);
-void navigation_unstuck(entity this);
-
-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);
diff --git a/qcsrc/server/bot/scripting.qc b/qcsrc/server/bot/scripting.qc
deleted file mode 100644 (file)
index 291b22f..0000000
+++ /dev/null
@@ -1,1340 +0,0 @@
-#include "scripting.qh"
-
-#include <common/state.qh>
-#include <common/physics/player.qh>
-
-#include "bot.qh"
-
-.int state;
-
-.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);
-}
-
-bool bot_havecommand(entity this, int idx)
-{
-       if(!this.bot_cmdqueuebuf_allocated)
-               return false;
-       if(idx < this.bot_cmdqueuebuf_start)
-               return false;
-       if(idx >= this.bot_cmdqueuebuf_end)
-               return false;
-       return true;
-}
-
-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(entity this, string placename)
-{
-       entity e;
-       if(substring(placename, 0, 1) == "@")
-       {
-               int i, p;
-               placename = substring(placename, 1, -1);
-               string s, s2;
-               for(i = 0; i < this.bot_places_count; ++i)
-                       if(this.(bot_placenames[i]) == placename)
-                               return this.(bot_places[i]);
-               // now: i == this.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(NULL, targetname, s);
-               if(!e)
-                       LOG_INFO("invalid place ", s, "\n");
-               if(i < MAX_BOT_PLACES)
-               {
-                       this.(bot_placenames[i]) = strzone(placename);
-                       this.(bot_places[i]) = e;
-                       this.bot_places_count += 1;
-               }
-               return e;
-       }
-       else
-       {
-               e = find(NULL, 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 NULL;
-}
-
-// Returns a bot by number on list
-entity find_bot_by_number(float number)
-{
-       entity bot;
-       float c = 0;
-
-       if(!number)
-               return NULL;
-
-       bot = findchainflags(flags, FL_CLIENT); // TODO: doesn't findchainflags loop backwards through entities?
-       while (bot)
-       {
-               if(IS_BOT_CLIENT(bot))
-               {
-                       if(++c==number)
-                               return bot;
-               }
-               bot = bot.chain;
-       }
-
-       return NULL;
-}
-
-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;
-
-float bot_cmd_cc(entity this)
-{
-       SV_ParseClientCommand(this, bot_cmd.bot_cmd_parm_string);
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_impulse(entity this)
-{
-       this.impulse = bot_cmd.bot_cmd_parm_float;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_continue(entity this)
-{
-       this.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
-       return CMD_STATUS_FINISHED;
-}
-
-.float bot_cmd_wait_time;
-float bot_cmd_wait(entity this)
-{
-       if(this.bot_exec_status & BOT_EXEC_STATUS_WAITING)
-       {
-               if(time>=this.bot_cmd_wait_time)
-               {
-                       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
-                       return CMD_STATUS_FINISHED;
-               }
-               else
-                       return CMD_STATUS_EXECUTING;
-       }
-
-       this.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
-       this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
-       return CMD_STATUS_EXECUTING;
-}
-
-float bot_cmd_wait_until(entity this)
-{
-       if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
-       {
-               this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
-               return CMD_STATUS_EXECUTING;
-       }
-       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_barrier(entity this)
-{
-       // 0 = no barrier, 1 = waiting, 2 = waiting finished
-
-       if(this.bot_barrier == 0) // initialization
-       {
-               this.bot_barrier = 1;
-
-               //this.colormod = '4 4 0';
-       }
-
-       if(this.bot_barrier == 1) // find other bots
-       {
-               FOREACH_CLIENT(it.isbot, LAMBDA(
-                       if(it.bot_cmdqueuebuf_allocated)
-                       if(it.bot_barrier != 1)
-                               return CMD_STATUS_EXECUTING; // not all are at the barrier yet
-               ));
-
-               // all bots hit the barrier!
-
-               // acknowledge barrier
-               FOREACH_CLIENT(it.isbot, LAMBDA(it.bot_barrier = 2));
-
-               bot_barriertime = time;
-       }
-
-       // if we get here, the barrier is finished
-       // so end it...
-       this.bot_barrier = 0;
-       //this.colormod = '0 0 0';
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_turn(entity this)
-{
-       this.v_angle_y = this.v_angle.y + bot_cmd.bot_cmd_parm_float;
-       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_select_weapon(entity this)
-{
-       float id = bot_cmd.bot_cmd_parm_float;
-
-       if(id < WEP_FIRST || id > WEP_LAST)
-               return CMD_STATUS_ERROR;
-
-       if(client_hasweapon(this, Weapons_from(id), true, false))
-               PS(this).m_switchweapon = Weapons_from(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(entity this, string expr)
-{
-       // 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 this.health;
-               case "speed":
-                       return vlen(this.velocity);
-               case "flagcarrier":
-                       return ((this.flagcarried!=NULL));
-       }
-
-       LOG_INFO(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
-       return 0;
-}
-
-float bot_cmd_if(entity this)
-{
-       string expr, val_a, val_b;
-       float cmpofs;
-
-       if(this.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(this);
-               return CMD_STATUS_ERROR;
-       }
-
-       this.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(this, val_a)==bot_cmd_eval(this, val_b))
-                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       this.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(this, val_a)>bot_cmd_eval(this, val_b))
-                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       this.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(this, val_a)<bot_cmd_eval(this, val_b))
-                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-               return CMD_STATUS_FINISHED;
-       }
-
-       if(bot_cmd_eval(this, expr))
-               this.bot_cmd_condition_status |= CMD_CONDITION_true;
-       else
-               this.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_else(entity this)
-{
-       this.bot_cmd_condition_status &= ~CMD_CONDITION_true_BLOCK;
-       this.bot_cmd_condition_status |= CMD_CONDITION_false_BLOCK;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_fi(entity this)
-{
-       this.bot_cmd_condition_status = CMD_CONDITION_NONE;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_resetaim(entity this)
-{
-       this.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(entity this)
-{
-       // Current direction
-       if(this.bot_cmd_aim_endtime)
-       {
-               float progress;
-
-               progress = min(1 - (this.bot_cmd_aim_endtime - time) / (this.bot_cmd_aim_endtime - this.bot_cmd_aim_begintime),1);
-               this.v_angle = this.bot_cmd_aim_begin + ((this.bot_cmd_aim_end - this.bot_cmd_aim_begin) * progress);
-
-               if(time>=this.bot_cmd_aim_endtime)
-               {
-                       this.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)
-       {
-               this.v_angle_x -= stof(argv(1));
-               this.v_angle_y += stof(argv(0));
-               return CMD_STATUS_FINISHED;
-       }
-
-       this.bot_cmd_aim_begin = this.v_angle;
-
-       this.bot_cmd_aim_end_x = this.v_angle.x - stof(argv(1));
-       this.bot_cmd_aim_end_y = this.v_angle.y + stof(argv(0));
-       this.bot_cmd_aim_end_z = 0;
-
-       this.bot_cmd_aim_begintime = time;
-       this.bot_cmd_aim_endtime = time + step;
-
-       return CMD_STATUS_EXECUTING;
-}
-
-float bot_cmd_aimtarget(entity this)
-{
-       if(this.bot_cmd_aim_endtime)
-       {
-               return bot_cmd_aim(this);
-       }
-
-       entity e;
-       string parms;
-       vector v;
-       float tokens, step;
-
-       parms = bot_cmd.bot_cmd_parm_string;
-
-       tokens = tokenizebyseparator(parms, " ");
-
-       e = bot_getplace(this, argv(0));
-       if(!e)
-               return CMD_STATUS_ERROR;
-
-       v = e.origin + (e.mins + e.maxs) * 0.5;
-
-       if(tokens==1)
-       {
-               this.v_angle = vectoangles(v - (this.origin + this.view_ofs));
-               this.v_angle_x = -this.v_angle.x;
-               return CMD_STATUS_FINISHED;
-       }
-
-       if(tokens<1||tokens>2)
-               return CMD_STATUS_ERROR;
-
-       step = stof(argv(1));
-
-       this.bot_cmd_aim_begin = this.v_angle;
-       this.bot_cmd_aim_end = vectoangles(v - (this.origin + this.view_ofs));
-       this.bot_cmd_aim_end_x = -this.bot_cmd_aim_end.x;
-
-       this.bot_cmd_aim_begintime = time;
-       this.bot_cmd_aim_endtime = time + step;
-
-       return CMD_STATUS_EXECUTING;
-}
-
-.int bot_cmd_keys;
-
-const int BOT_CMD_KEY_NONE             = 0;
-const int BOT_CMD_KEY_FORWARD  = BIT(0);
-const int BOT_CMD_KEY_BACKWARD         = BIT(1);
-const int BOT_CMD_KEY_RIGHT    = BIT(2);
-const int BOT_CMD_KEY_LEFT             = BIT(3);
-const int BOT_CMD_KEY_JUMP             = BIT(4);
-const int BOT_CMD_KEY_ATTACK1  = BIT(5);
-const int BOT_CMD_KEY_ATTACK2  = BIT(6);
-const int BOT_CMD_KEY_USE              = BIT(7);
-const int BOT_CMD_KEY_HOOK             = BIT(8);
-const int BOT_CMD_KEY_CROUCH   = BIT(9);
-const int BOT_CMD_KEY_CHAT             = BIT(10);
-
-bool bot_presskeys(entity this)
-{
-       this.movement = '0 0 0';
-       PHYS_INPUT_BUTTON_JUMP(this) = false;
-       PHYS_INPUT_BUTTON_CROUCH(this) = false;
-       PHYS_INPUT_BUTTON_ATCK(this) = false;
-       PHYS_INPUT_BUTTON_ATCK2(this) = false;
-       PHYS_INPUT_BUTTON_USE(this) = false;
-       PHYS_INPUT_BUTTON_HOOK(this) = false;
-       PHYS_INPUT_BUTTON_CHAT(this) = false;
-
-       if(this.bot_cmd_keys == BOT_CMD_KEY_NONE)
-               return false;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
-               this.movement_x = autocvar_sv_maxspeed;
-       else if(this.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
-               this.movement_x = -autocvar_sv_maxspeed;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
-               this.movement_y = autocvar_sv_maxspeed;
-       else if(this.bot_cmd_keys & BOT_CMD_KEY_LEFT)
-               this.movement_y = -autocvar_sv_maxspeed;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_JUMP)
-               PHYS_INPUT_BUTTON_JUMP(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
-               PHYS_INPUT_BUTTON_CROUCH(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
-               PHYS_INPUT_BUTTON_ATCK(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
-               PHYS_INPUT_BUTTON_ATCK2(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_USE)
-               PHYS_INPUT_BUTTON_USE(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_HOOK)
-               PHYS_INPUT_BUTTON_HOOK(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_CHAT)
-               PHYS_INPUT_BUTTON_CHAT(this) = true;
-
-       return true;
-}
-
-
-float bot_cmd_keypress_handler(entity this, string key, float enabled)
-{
-       switch(key)
-       {
-               case "all":
-                       if(enabled)
-                               this.bot_cmd_keys = power2of(20) - 1; // >:)
-                       else
-                               this.bot_cmd_keys = BOT_CMD_KEY_NONE;
-               case "forward":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
-                       break;
-               case "backward":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
-                       break;
-               case "left":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
-                       break;
-               case "right":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
-                       break;
-               case "jump":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
-                       break;
-               case "crouch":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
-                       break;
-               case "attack1":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
-                       break;
-               case "attack2":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
-                       break;
-               case "use":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_USE;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
-                       break;
-               case "hook":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
-                       break;
-               case "chat":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
-                       break;
-               default:
-                       break;
-       }
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_presskey(entity this)
-{
-       string key;
-
-       key = bot_cmd.bot_cmd_parm_string;
-
-       bot_cmd_keypress_handler(this, key,true);
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_releasekey(entity this)
-{
-       string key;
-
-       key = bot_cmd.bot_cmd_parm_string;
-
-       return bot_cmd_keypress_handler(this, key,false);
-}
-
-float bot_cmd_pause(entity this)
-{
-       PHYS_INPUT_BUTTON_DRAG(this) = false;
-       PHYS_INPUT_BUTTON_USE(this) = false;
-       PHYS_INPUT_BUTTON_ATCK(this) = false;
-       PHYS_INPUT_BUTTON_JUMP(this) = false;
-       PHYS_INPUT_BUTTON_HOOK(this) = false;
-       PHYS_INPUT_BUTTON_CHAT(this) = false;
-       PHYS_INPUT_BUTTON_ATCK2(this) = false;
-       PHYS_INPUT_BUTTON_CROUCH(this) = false;
-
-       this.movement = '0 0 0';
-       this.bot_cmd_keys = BOT_CMD_KEY_NONE;
-
-       this.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_moveto(entity this)
-{
-       return this.cmd_moveto(this, bot_cmd.bot_cmd_parm_vector);
-}
-
-float bot_cmd_movetotarget(entity this)
-{
-       entity e;
-       e = bot_getplace(this, bot_cmd.bot_cmd_parm_string);
-       if(!e)
-               return CMD_STATUS_ERROR;
-       return this.cmd_moveto(this, e.origin + (e.mins + e.maxs) * 0.5);
-}
-
-float bot_cmd_resetgoal(entity this)
-{
-       return this.cmd_resetgoal(this);
-}
-
-
-float bot_cmd_sound(entity this)
-{
-       string f;
-       f = bot_cmd.bot_cmd_parm_string;
-
-       float n = tokenizebyseparator(f, " ");
-
-       string sample = f;
-       float chan = CH_WEAPON_B;
-       float vol = VOL_BASE;
-       float atten = ATTEN_MIN;
-
-       if(n >= 1)
-               sample = argv(n - 1);
-       if(n >= 2)
-               chan = stof(argv(0));
-       if(n >= 3)
-               vol = stof(argv(1));
-       if(n >= 4)
-               atten = stof(argv(2));
-
-       precache_sound(f);
-       _sound(this, chan, sample, vol, atten);
-
-       return CMD_STATUS_FINISHED;
-}
-
-.entity tuba_note;
-float bot_cmd_debug_assert_canfire(entity this)
-{
-       float f = bot_cmd.bot_cmd_parm_float;
-
-       int slot = 0;
-       .entity weaponentity = weaponentities[slot];
-       if(this.(weaponentity).state != WS_READY)
-       {
-               if(f)
-               {
-                       this.colormod = '0 8 8';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by weaponentity state\n");
-               }
-       }
-       else if(ATTACK_FINISHED(this, slot) > time)
-       {
-               if(f)
-               {
-                       this.colormod = '8 0 8';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)\n");
-               }
-       }
-       else if(this.tuba_note)
-       {
-               if(f)
-               {
-                       this.colormod = '8 0 0';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, bot still has an active tuba note\n");
-               }
-       }
-       else
-       {
-               if(!f)
-               {
-                       this.colormod = '8 8 0';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left\n");
-               }
-       }
-
-       return CMD_STATUS_FINISHED;
-}
-
-//
-
-void bot_command_executed(entity this, bool rm)
-{
-       entity cmd;
-
-       cmd = bot_cmd;
-
-       if(rm)
-               bot_dequeuecommand(this, this.bot_cmd_execution_index);
-
-       this.bot_cmd_execution_index++;
-}
-
-void bot_setcurrentcommand(entity this)
-{
-       bot_cmd = NULL;
-
-       if(!this.bot_cmd_current)
-       {
-               this.bot_cmd_current = new_pure(bot_cmd);
-               this.bot_cmd_current.is_bot_cmd = true;
-       }
-
-       bot_cmd = this.bot_cmd_current;
-       if(bot_cmd.bot_cmd_index != this.bot_cmd_execution_index || this.bot_cmd_execution_index == 0)
-       {
-               if(bot_havecommand(this, this.bot_cmd_execution_index))
-               {
-                       string cmdstring;
-                       cmdstring = bot_readcommand(this, this.bot_cmd_execution_index);
-                       if(bot_decodecommand(cmdstring))
-                       {
-                               bot_cmd.owner = this;
-                               bot_cmd.bot_cmd_index = this.bot_cmd_execution_index;
-                       }
-                       else
-                       {
-                               // Invalid command, remove from queue
-                               bot_cmd = NULL;
-                               bot_dequeuecommand(this, this.bot_cmd_execution_index);
-                               this.bot_cmd_execution_index++;
-                       }
-               }
-               else
-                       bot_cmd = NULL;
-       }
-}
-
-void bot_resetqueues()
-{
-       FOREACH_CLIENT(it.isbot, LAMBDA(
-               it.bot_cmd_execution_index = 0;
-               bot_clearqueue(it);
-               // also, cancel all barriers
-               it.bot_barrier = 0;
-               for(int i = 0; i < it.bot_places_count; ++i)
-               {
-                       strunzone(it.(bot_placenames[i]));
-                       it.(bot_placenames[i]) = string_null;
-               }
-               it.bot_places_count = 0;
-       ));
-
-       bot_barriertime = time;
-}
-
-// Here we map commands to functions and deal with complex interactions between commands and execution states
-// NOTE: Of course you need to include your commands here too :)
-float bot_execute_commands_once(entity this)
-{
-       float status, ispressingkey;
-
-       // Find command
-       bot_setcurrentcommand(this);
-
-       // if we have no bot command, better return
-       // old logic kept pressing previously pressed keys, but that has problems
-       // (namely, it means you cannot make a bot "normal" ever again)
-       // to keep a bot walking for a while, use the "wait" bot command
-       if(bot_cmd == NULL)
-               return false;
-
-       // Ignore all commands except continue when the bot is paused
-       if(this.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
-       if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
-       {
-               if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
-               {
-                       bot_command_executed(this, true);
-                       LOG_INFO( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
-               }
-               return 1;
-       }
-
-       // Keep pressing keys raised by the "presskey" command
-       ispressingkey = boolean(bot_presskeys(this));
-
-       // Handle conditions
-       if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE))
-       if(this.bot_cmd_condition_status & CMD_CONDITION_true && this.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK)
-       {
-               bot_command_executed(this, true);
-               return -1;
-       }
-       else if(this.bot_cmd_condition_status & CMD_CONDITION_false && this.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK)
-       {
-               bot_command_executed(this, true);
-               return -1;
-       }
-
-       // Map commands to functions
-       switch(bot_cmd.bot_cmd_type)
-       {
-               case BOT_CMD_NULL:
-                       return ispressingkey;
-                       //break;
-               case BOT_CMD_PAUSE:
-                       status = bot_cmd_pause(this);
-                       break;
-               case BOT_CMD_CONTINUE:
-                       status = bot_cmd_continue(this);
-                       break;
-               case BOT_CMD_WAIT:
-                       status = bot_cmd_wait(this);
-                       break;
-               case BOT_CMD_WAIT_UNTIL:
-                       status = bot_cmd_wait_until(this);
-                       break;
-               case BOT_CMD_TURN:
-                       status = bot_cmd_turn(this);
-                       break;
-               case BOT_CMD_MOVETO:
-                       status = bot_cmd_moveto(this);
-                       break;
-               case BOT_CMD_MOVETOTARGET:
-                       status = bot_cmd_movetotarget(this);
-                       break;
-               case BOT_CMD_RESETGOAL:
-                       status = bot_cmd_resetgoal(this);
-                       break;
-               case BOT_CMD_CC:
-                       status = bot_cmd_cc(this);
-                       break;
-               case BOT_CMD_IF:
-                       status = bot_cmd_if(this);
-                       break;
-               case BOT_CMD_ELSE:
-                       status = bot_cmd_else(this);
-                       break;
-               case BOT_CMD_FI:
-                       status = bot_cmd_fi(this);
-                       break;
-               case BOT_CMD_RESETAIM:
-                       status = bot_cmd_resetaim(this);
-                       break;
-               case BOT_CMD_AIM:
-                       status = bot_cmd_aim(this);
-                       break;
-               case BOT_CMD_AIMTARGET:
-                       status = bot_cmd_aimtarget(this);
-                       break;
-               case BOT_CMD_PRESSKEY:
-                       status = bot_cmd_presskey(this);
-                       break;
-               case BOT_CMD_RELEASEKEY:
-                       status = bot_cmd_releasekey(this);
-                       break;
-               case BOT_CMD_SELECTWEAPON:
-                       status = bot_cmd_select_weapon(this);
-                       break;
-               case BOT_CMD_IMPULSE:
-                       status = bot_cmd_impulse(this);
-                       break;
-               case BOT_CMD_BARRIER:
-                       status = bot_cmd_barrier(this);
-                       break;
-               case BOT_CMD_CONSOLE:
-                       localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
-                       status = CMD_STATUS_FINISHED;
-                       break;
-               case BOT_CMD_SOUND:
-                       status = bot_cmd_sound(this);
-                       break;
-               case BOT_CMD_DEBUG_ASSERT_CANFIRE:
-                       status = bot_cmd_debug_assert_canfire(this);
-                       break;
-               default:
-                       LOG_INFO(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
-                       return 0;
-       }
-
-       if (status==CMD_STATUS_ERROR)
-               LOG_INFO(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
-
-       // Move execution pointer
-       if(status==CMD_STATUS_EXECUTING)
-       {
-               return 1;
-       }
-       else
-       {
-               if(autocvar_g_debug_bot_commands)
-               {
-                       string parms;
-
-                       switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
-                       {
-                               case BOT_CMD_PARAMETER_FLOAT:
-                                       parms = ftos(bot_cmd.bot_cmd_parm_float);
-                                       break;
-                               case BOT_CMD_PARAMETER_STRING:
-                                       parms = bot_cmd.bot_cmd_parm_string;
-                                       break;
-                               case BOT_CMD_PARAMETER_VECTOR:
-                                       parms = vtos(bot_cmd.bot_cmd_parm_vector);
-                                       break;
-                               default:
-                                       parms = "";
-                                       break;
-                       }
-                       clientcommand(this,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
-               }
-
-               bot_command_executed(this, true);
-       }
-
-       if(status == CMD_STATUS_FINISHED)
-               return -1;
-
-       return CMD_STATUS_ERROR;
-}
-
-// This function should be (the only) called directly from the bot ai loop
-int bot_execute_commands(entity this)
-{
-       int f;
-       do
-       {
-               f = bot_execute_commands_once(this);
-       }
-       while(f < 0);
-       return f;
-}
diff --git a/qcsrc/server/bot/scripting.qh b/qcsrc/server/bot/scripting.qh
deleted file mode 100644 (file)
index cb6cd87..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-#define BOT_EXEC_STATUS_IDLE 0
-#define BOT_EXEC_STATUS_PAUSED 1
-#define BOT_EXEC_STATUS_WAITING 2
-
-#define CMD_STATUS_EXECUTING 0
-#define CMD_STATUS_FINISHED 1
-#define CMD_STATUS_ERROR 2
-
-
-// NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
-const int BOT_CMD_NULL                         = 0;
-const int BOT_CMD_PAUSE                = 1;
-const int BOT_CMD_CONTINUE             = 2;
-const int BOT_CMD_WAIT                         = 3;
-const int BOT_CMD_TURN                         = 4;
-const int BOT_CMD_MOVETO               = 5;
-const int BOT_CMD_RESETGOAL    = 6;    // Not implemented yet
-const int BOT_CMD_CC                   = 7;
-const int BOT_CMD_IF                   = 8;
-const int BOT_CMD_ELSE                         = 9;
-const int BOT_CMD_FI                   = 10;
-const int BOT_CMD_RESETAIM             = 11;
-const int BOT_CMD_AIM                  = 12;
-const int BOT_CMD_PRESSKEY             = 13;
-const int BOT_CMD_RELEASEKEY   = 14;
-const int BOT_CMD_SELECTWEAPON         = 15;
-const int BOT_CMD_IMPULSE              = 16;
-const int BOT_CMD_WAIT_UNTIL   = 17;
-const int BOT_CMD_MOVETOTARGET         = 18;
-const int BOT_CMD_AIMTARGET    = 19;
-const int BOT_CMD_BARRIER              = 20;
-const int BOT_CMD_CONSOLE              = 21;
-const int BOT_CMD_SOUND                = 22;
-const int BOT_CMD_DEBUG_ASSERT_CANFIRE = 23;
-const int BOT_CMD_WHILE                = 24;   // TODO: Not implemented yet
-const int BOT_CMD_WEND                         = 25;   // TODO: Not implemented yet
-const int BOT_CMD_CHASE                = 26;   // TODO: Not implemented yet
-
-const int BOT_CMD_COUNTER              = 24;   // Update this value if you add/remove a command
-
-// NOTE: Following commands should be implemented on the bot ai
-//              If a new command should be handled by the target ai(s) please declare it here
-.float(entity, vector) cmd_moveto;
-.float(entity) cmd_resetgoal;
-
-//
-const int BOT_CMD_PARAMETER_NONE = 0;
-const int BOT_CMD_PARAMETER_FLOAT = 1;
-const int BOT_CMD_PARAMETER_STRING = 2;
-const int BOT_CMD_PARAMETER_VECTOR = 3;
-
-float bot_cmds_initialized;
-int bot_cmd_parm_type[BOT_CMD_COUNTER];
-string bot_cmd_string[BOT_CMD_COUNTER];
-
-// Bots command queue
-entity bot_cmd;        // global current command
-.entity bot_cmd_current; // current command of this bot
-
-.float is_bot_cmd;                     // Tells if the entity is a bot command
-.float bot_cmd_index;                  // Position of the command in the queue
-.int bot_cmd_type;                     // If of command (see the BOT_CMD_* defines)
-.float bot_cmd_parm_float;             // Field to store a float parameter
-.string bot_cmd_parm_string;           // Field to store a string parameter
-.vector bot_cmd_parm_vector;           // Field to store a vector parameter
-
-float bot_barriertime;
-.float bot_barrier;
-
-.float bot_cmd_execution_index;                // Position in the queue of the command to be executed
-
-
-void bot_resetqueues();
-void bot_queuecommand(entity bot, string cmdstring);
-void bot_cmdhelp(string scmd);
-void bot_list_commands();
-float bot_execute_commands(entity this);
-entity find_bot_by_name(string name);
-entity find_bot_by_number(float number);
diff --git a/qcsrc/server/bot/waypoints.qc b/qcsrc/server/bot/waypoints.qc
deleted file mode 100644 (file)
index 89078f5..0000000
+++ /dev/null
@@ -1,1102 +0,0 @@
-#include "waypoints.qh"
-
-#include "bot.qh"
-#include "navigation.qh"
-
-#include <common/state.qh>
-
-#include "../antilag.qh"
-
-#include <common/constants.qh>
-
-#include <lib/warpzone/common.qh>
-#include <lib/warpzone/util_server.qh>
-
-// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link
-// them back to it as well
-// (suitable for spawnfunc_waypoint editor)
-entity waypoint_spawn(vector m1, vector m2, float f)
-{
-       if(!(f & WAYPOINTFLAG_PERSONAL))
-       {
-               IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax),
-               {
-                       return it;
-               });
-       }
-
-       entity w = new(waypoint);
-       IL_PUSH(g_waypoints, w);
-       w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
-       w.wpflags = f;
-       w.solid = SOLID_TRIGGER;
-       setorigin(w, (m1 + m2) * 0.5);
-       setsize(w, m1 - w.origin, m2 - w.origin);
-       if (vlen(w.size) > 0)
-               w.wpisbox = true;
-
-       if(!w.wpisbox)
-       {
-               setsize(w, STAT(PL_MIN, NULL) - '1 1 0', STAT(PL_MAX, NULL) + '1 1 0');
-               if(!move_out_of_solid(w))
-               {
-                       if(!(f & WAYPOINTFLAG_GENERATED))
-                       {
-                               LOG_TRACE("Killed a waypoint that was stuck in solid at ", vtos(w.origin), "\n");
-                               delete(w);
-                               return NULL;
-                       }
-                       else
-                       {
-                               if(autocvar_developer)
-                               {
-                                       LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin), "\n");
-                                       backtrace("Waypoint stuck");
-                               }
-                       }
-               }
-               setsize(w, '0 0 0', '0 0 0');
-       }
-
-       waypoint_clearlinks(w);
-       //waypoint_schedulerelink(w);
-
-       if (autocvar_g_waypointeditor)
-       {
-               m1 = w.mins;
-               m2 = w.maxs;
-               setmodel(w, MDL_WAYPOINT); w.effects = EF_LOWPRECISION;
-               setsize(w, m1, m2);
-               if (w.wpflags & WAYPOINTFLAG_ITEM)
-                       w.colormod = '1 0 0';
-               else if (w.wpflags & WAYPOINTFLAG_GENERATED)
-                       w.colormod = '1 1 0';
-               else
-                       w.colormod = '1 1 1';
-       }
-       else
-               w.model = "";
-
-       return w;
-}
-
-// add a new link to the spawnfunc_waypoint, replacing the furthest link it already has
-void waypoint_addlink(entity from, entity to)
-{
-       float c;
-
-       if (from == to)
-               return;
-       if (from.wpflags & WAYPOINTFLAG_NORELINK)
-               return;
-
-       if (from.wp00 == to) return;if (from.wp01 == to) return;if (from.wp02 == to) return;if (from.wp03 == to) return;
-       if (from.wp04 == to) return;if (from.wp05 == to) return;if (from.wp06 == to) return;if (from.wp07 == to) return;
-       if (from.wp08 == to) return;if (from.wp09 == to) return;if (from.wp10 == to) return;if (from.wp11 == to) return;
-       if (from.wp12 == to) return;if (from.wp13 == to) return;if (from.wp14 == to) return;if (from.wp15 == to) return;
-       if (from.wp16 == to) return;if (from.wp17 == to) return;if (from.wp18 == to) return;if (from.wp19 == to) return;
-       if (from.wp20 == to) return;if (from.wp21 == to) return;if (from.wp22 == to) return;if (from.wp23 == to) return;
-       if (from.wp24 == to) return;if (from.wp25 == to) return;if (from.wp26 == to) return;if (from.wp27 == to) return;
-       if (from.wp28 == to) return;if (from.wp29 == to) return;if (from.wp30 == to) return;if (from.wp31 == to) return;
-
-       if (to.wpisbox || from.wpisbox)
-       {
-               // if either is a box we have to find the nearest points on them to
-               // calculate the distance properly
-               vector v1, v2, m1, m2;
-               v1 = from.origin;
-               m1 = to.absmin;
-               m2 = to.absmax;
-               v1_x = bound(m1_x, v1_x, m2_x);
-               v1_y = bound(m1_y, v1_y, m2_y);
-               v1_z = bound(m1_z, v1_z, m2_z);
-               v2 = to.origin;
-               m1 = from.absmin;
-               m2 = from.absmax;
-               v2_x = bound(m1_x, v2_x, m2_x);
-               v2_y = bound(m1_y, v2_y, m2_y);
-               v2_z = bound(m1_z, v2_z, m2_z);
-               v2 = to.origin;
-               c = vlen(v2 - v1);
-       }
-       else
-               c = vlen(to.origin - from.origin);
-
-       if (from.wp31mincost < c) return;
-       if (from.wp30mincost < c) {from.wp31 = to;from.wp31mincost = c;return;} from.wp31 = from.wp30;from.wp31mincost = from.wp30mincost;
-       if (from.wp29mincost < c) {from.wp30 = to;from.wp30mincost = c;return;} from.wp30 = from.wp29;from.wp30mincost = from.wp29mincost;
-       if (from.wp28mincost < c) {from.wp29 = to;from.wp29mincost = c;return;} from.wp29 = from.wp28;from.wp29mincost = from.wp28mincost;
-       if (from.wp27mincost < c) {from.wp28 = to;from.wp28mincost = c;return;} from.wp28 = from.wp27;from.wp28mincost = from.wp27mincost;
-       if (from.wp26mincost < c) {from.wp27 = to;from.wp27mincost = c;return;} from.wp27 = from.wp26;from.wp27mincost = from.wp26mincost;
-       if (from.wp25mincost < c) {from.wp26 = to;from.wp26mincost = c;return;} from.wp26 = from.wp25;from.wp26mincost = from.wp25mincost;
-       if (from.wp24mincost < c) {from.wp25 = to;from.wp25mincost = c;return;} from.wp25 = from.wp24;from.wp25mincost = from.wp24mincost;
-       if (from.wp23mincost < c) {from.wp24 = to;from.wp24mincost = c;return;} from.wp24 = from.wp23;from.wp24mincost = from.wp23mincost;
-       if (from.wp22mincost < c) {from.wp23 = to;from.wp23mincost = c;return;} from.wp23 = from.wp22;from.wp23mincost = from.wp22mincost;
-       if (from.wp21mincost < c) {from.wp22 = to;from.wp22mincost = c;return;} from.wp22 = from.wp21;from.wp22mincost = from.wp21mincost;
-       if (from.wp20mincost < c) {from.wp21 = to;from.wp21mincost = c;return;} from.wp21 = from.wp20;from.wp21mincost = from.wp20mincost;
-       if (from.wp19mincost < c) {from.wp20 = to;from.wp20mincost = c;return;} from.wp20 = from.wp19;from.wp20mincost = from.wp19mincost;
-       if (from.wp18mincost < c) {from.wp19 = to;from.wp19mincost = c;return;} from.wp19 = from.wp18;from.wp19mincost = from.wp18mincost;
-       if (from.wp17mincost < c) {from.wp18 = to;from.wp18mincost = c;return;} from.wp18 = from.wp17;from.wp18mincost = from.wp17mincost;
-       if (from.wp16mincost < c) {from.wp17 = to;from.wp17mincost = c;return;} from.wp17 = from.wp16;from.wp17mincost = from.wp16mincost;
-       if (from.wp15mincost < c) {from.wp16 = to;from.wp16mincost = c;return;} from.wp16 = from.wp15;from.wp16mincost = from.wp15mincost;
-       if (from.wp14mincost < c) {from.wp15 = to;from.wp15mincost = c;return;} from.wp15 = from.wp14;from.wp15mincost = from.wp14mincost;
-       if (from.wp13mincost < c) {from.wp14 = to;from.wp14mincost = c;return;} from.wp14 = from.wp13;from.wp14mincost = from.wp13mincost;
-       if (from.wp12mincost < c) {from.wp13 = to;from.wp13mincost = c;return;} from.wp13 = from.wp12;from.wp13mincost = from.wp12mincost;
-       if (from.wp11mincost < c) {from.wp12 = to;from.wp12mincost = c;return;} from.wp12 = from.wp11;from.wp12mincost = from.wp11mincost;
-       if (from.wp10mincost < c) {from.wp11 = to;from.wp11mincost = c;return;} from.wp11 = from.wp10;from.wp11mincost = from.wp10mincost;
-       if (from.wp09mincost < c) {from.wp10 = to;from.wp10mincost = c;return;} from.wp10 = from.wp09;from.wp10mincost = from.wp09mincost;
-       if (from.wp08mincost < c) {from.wp09 = to;from.wp09mincost = c;return;} from.wp09 = from.wp08;from.wp09mincost = from.wp08mincost;
-       if (from.wp07mincost < c) {from.wp08 = to;from.wp08mincost = c;return;} from.wp08 = from.wp07;from.wp08mincost = from.wp07mincost;
-       if (from.wp06mincost < c) {from.wp07 = to;from.wp07mincost = c;return;} from.wp07 = from.wp06;from.wp07mincost = from.wp06mincost;
-       if (from.wp05mincost < c) {from.wp06 = to;from.wp06mincost = c;return;} from.wp06 = from.wp05;from.wp06mincost = from.wp05mincost;
-       if (from.wp04mincost < c) {from.wp05 = to;from.wp05mincost = c;return;} from.wp05 = from.wp04;from.wp05mincost = from.wp04mincost;
-       if (from.wp03mincost < c) {from.wp04 = to;from.wp04mincost = c;return;} from.wp04 = from.wp03;from.wp04mincost = from.wp03mincost;
-       if (from.wp02mincost < c) {from.wp03 = to;from.wp03mincost = c;return;} from.wp03 = from.wp02;from.wp03mincost = from.wp02mincost;
-       if (from.wp01mincost < c) {from.wp02 = to;from.wp02mincost = c;return;} from.wp02 = from.wp01;from.wp02mincost = from.wp01mincost;
-       if (from.wp00mincost < c) {from.wp01 = to;from.wp01mincost = c;return;} from.wp01 = from.wp00;from.wp01mincost = from.wp00mincost;
-       from.wp00 = to;from.wp00mincost = c;return;
-}
-
-// relink this spawnfunc_waypoint
-// (precompile a list of all reachable waypoints from this spawnfunc_waypoint)
-// (SLOW!)
-void waypoint_think(entity this)
-{
-       vector sv, sm1, sm2, ev, em1, em2, dv;
-
-       bot_calculate_stepheightvec();
-
-       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
-
-       //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
-       sm1 = this.origin + this.mins;
-       sm2 = this.origin + this.maxs;
-       IL_EACH(g_waypoints, true,
-       {
-               if (boxesoverlap(this.absmin, this.absmax, it.absmin, it.absmax))
-               {
-                       waypoint_addlink(this, it);
-                       waypoint_addlink(it, this);
-               }
-               else
-               {
-                       ++relink_total;
-                       if(!checkpvs(this.origin, it))
-                       {
-                               ++relink_pvsculled;
-                               continue;
-                       }
-                       sv = it.origin;
-                       sv.x = bound(sm1_x, sv.x, sm2_x);
-                       sv.y = bound(sm1_y, sv.y, sm2_y);
-                       sv.z = bound(sm1_z, sv.z, sm2_z);
-                       ev = this.origin;
-                       em1 = it.origin + it.mins;
-                       em2 = it.origin + it.maxs;
-                       ev.x = bound(em1_x, ev.x, em2_x);
-                       ev.y = bound(em1_y, ev.y, em2_y);
-                       ev.z = bound(em1_z, ev.z, em2_z);
-                       dv = ev - sv;
-                       dv.z = 0;
-                       if(vdist(dv, >=, 1050)) // max search distance in XY
-                       {
-                               ++relink_lengthculled;
-                               continue;
-                       }
-                       navigation_testtracewalk = 0;
-                       if (!this.wpisbox)
-                       {
-                               tracebox(sv - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, false, this);
-                               if (!trace_startsolid)
-                               {
-                                       //dprint("sv deviation", vtos(trace_endpos - sv), "\n");
-                                       sv = trace_endpos + '0 0 1';
-                               }
-                       }
-                       if (!it.wpisbox)
-                       {
-                               tracebox(ev - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, false, it);
-                               if (!trace_startsolid)
-                               {
-                                       //dprint("ev deviation", vtos(trace_endpos - ev), "\n");
-                                       ev = trace_endpos + '0 0 1';
-                               }
-                       }
-                       //traceline(this.origin, it.origin, false, NULL);
-                       //if (trace_fraction == 1)
-                       if (!this.wpisbox && tracewalk(this, sv, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, MOVE_NOMONSTERS))
-                               waypoint_addlink(this, it);
-                       else
-                               relink_walkculled += 0.5;
-                       if (!it.wpisbox && tracewalk(it, ev, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, MOVE_NOMONSTERS))
-                               waypoint_addlink(it, this);
-                       else
-                               relink_walkculled += 0.5;
-               }
-       });
-       navigation_testtracewalk = 0;
-       this.wplinked = true;
-}
-
-void waypoint_clearlinks(entity wp)
-{
-       // clear links to other waypoints
-       float f;
-       f = 10000000;
-       wp.wp00 = wp.wp01 = wp.wp02 = wp.wp03 = wp.wp04 = wp.wp05 = wp.wp06 = wp.wp07 = NULL;
-       wp.wp08 = wp.wp09 = wp.wp10 = wp.wp11 = wp.wp12 = wp.wp13 = wp.wp14 = wp.wp15 = NULL;
-       wp.wp16 = wp.wp17 = wp.wp18 = wp.wp19 = wp.wp20 = wp.wp21 = wp.wp22 = wp.wp23 = NULL;
-       wp.wp24 = wp.wp25 = wp.wp26 = wp.wp27 = wp.wp28 = wp.wp29 = wp.wp30 = wp.wp31 = NULL;
-
-       wp.wp00mincost = wp.wp01mincost = wp.wp02mincost = wp.wp03mincost = wp.wp04mincost = wp.wp05mincost = wp.wp06mincost = wp.wp07mincost = f;
-       wp.wp08mincost = wp.wp09mincost = wp.wp10mincost = wp.wp11mincost = wp.wp12mincost = wp.wp13mincost = wp.wp14mincost = wp.wp15mincost = f;
-       wp.wp16mincost = wp.wp17mincost = wp.wp18mincost = wp.wp19mincost = wp.wp20mincost = wp.wp21mincost = wp.wp22mincost = wp.wp23mincost = f;
-       wp.wp24mincost = wp.wp25mincost = wp.wp26mincost = wp.wp27mincost = wp.wp28mincost = wp.wp29mincost = wp.wp30mincost = wp.wp31mincost = f;
-
-       wp.wplinked = false;
-}
-
-// tell a spawnfunc_waypoint to relink
-void waypoint_schedulerelink(entity wp)
-{
-       if (wp == NULL)
-               return;
-       // TODO: add some sort of visible box in edit mode for box waypoints
-       if (autocvar_g_waypointeditor)
-       {
-               vector m1, m2;
-               m1 = wp.mins;
-               m2 = wp.maxs;
-               setmodel(wp, MDL_WAYPOINT); wp.effects = EF_LOWPRECISION;
-               setsize(wp, m1, m2);
-               if (wp.wpflags & WAYPOINTFLAG_ITEM)
-                       wp.colormod = '1 0 0';
-               else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
-                       wp.colormod = '1 1 0';
-               else
-                       wp.colormod = '1 1 1';
-       }
-       else
-               wp.model = "";
-       wp.wpisbox = vlen(wp.size) > 0;
-       wp.enemy = NULL;
-       if (!(wp.wpflags & WAYPOINTFLAG_PERSONAL))
-               wp.owner = NULL;
-       if (!(wp.wpflags & WAYPOINTFLAG_NORELINK))
-               waypoint_clearlinks(wp);
-       // schedule an actual relink on next frame
-       setthink(wp, waypoint_think);
-       wp.nextthink = time;
-       wp.effects = EF_LOWPRECISION;
-}
-
-// spawnfunc_waypoint map entity
-spawnfunc(waypoint)
-{
-       IL_PUSH(g_waypoints, this);
-
-       setorigin(this, this.origin);
-       // schedule a relink after other waypoints have had a chance to spawn
-       waypoint_clearlinks(this);
-       //waypoint_schedulerelink(this);
-}
-
-// remove a spawnfunc_waypoint, and schedule all neighbors to relink
-void waypoint_remove(entity e)
-{
-       // tell all linked waypoints that they need to relink
-       waypoint_schedulerelink(e.wp00);
-       waypoint_schedulerelink(e.wp01);
-       waypoint_schedulerelink(e.wp02);
-       waypoint_schedulerelink(e.wp03);
-       waypoint_schedulerelink(e.wp04);
-       waypoint_schedulerelink(e.wp05);
-       waypoint_schedulerelink(e.wp06);
-       waypoint_schedulerelink(e.wp07);
-       waypoint_schedulerelink(e.wp08);
-       waypoint_schedulerelink(e.wp09);
-       waypoint_schedulerelink(e.wp10);
-       waypoint_schedulerelink(e.wp11);
-       waypoint_schedulerelink(e.wp12);
-       waypoint_schedulerelink(e.wp13);
-       waypoint_schedulerelink(e.wp14);
-       waypoint_schedulerelink(e.wp15);
-       waypoint_schedulerelink(e.wp16);
-       waypoint_schedulerelink(e.wp17);
-       waypoint_schedulerelink(e.wp18);
-       waypoint_schedulerelink(e.wp19);
-       waypoint_schedulerelink(e.wp20);
-       waypoint_schedulerelink(e.wp21);
-       waypoint_schedulerelink(e.wp22);
-       waypoint_schedulerelink(e.wp23);
-       waypoint_schedulerelink(e.wp24);
-       waypoint_schedulerelink(e.wp25);
-       waypoint_schedulerelink(e.wp26);
-       waypoint_schedulerelink(e.wp27);
-       waypoint_schedulerelink(e.wp28);
-       waypoint_schedulerelink(e.wp29);
-       waypoint_schedulerelink(e.wp30);
-       waypoint_schedulerelink(e.wp31);
-       // and now remove the spawnfunc_waypoint
-       delete(e);
-}
-
-// empties the map of waypoints
-void waypoint_removeall()
-{
-       IL_EACH(g_waypoints, true,
-       {
-               delete(it);
-       });
-}
-
-// tell all waypoints to relink
-// (is this useful at all?)
-void waypoint_schedulerelinkall()
-{
-       relink_total = relink_walkculled = relink_pvsculled = relink_lengthculled = 0;
-       IL_EACH(g_waypoints, true,
-       {
-               waypoint_schedulerelink(it);
-       });
-}
-
-// Load waypoint links from file
-float waypoint_load_links()
-{
-       string filename, s;
-       float file, tokens, c = 0, found;
-       entity wp_from = NULL, wp_to;
-       vector wp_to_pos, wp_from_pos;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints.cache");
-       file = fopen(filename, FILE_READ);
-
-       if (file < 0)
-       {
-               LOG_TRACE("waypoint links load from ");
-               LOG_TRACE(filename);
-               LOG_TRACE(" failed\n");
-               return false;
-       }
-
-       while ((s = fgets(file)))
-       {
-               tokens = tokenizebyseparator(s, "*");
-
-               if (tokens!=2)
-               {
-                       // bad file format
-                       fclose(file);
-                       return false;
-               }
-
-               wp_from_pos     = stov(argv(0));
-               wp_to_pos       = stov(argv(1));
-
-               // Search "from" waypoint
-               if(!wp_from || wp_from.origin!=wp_from_pos)
-               {
-                       wp_from = findradius(wp_from_pos, 1);
-                       found = false;
-                       while(wp_from)
-                       {
-                               if(vdist(wp_from.origin - wp_from_pos, <, 1))
-                               if(wp_from.classname == "waypoint")
-                               {
-                                       found = true;
-                                       break;
-                               }
-                               wp_from = wp_from.chain;
-                       }
-
-                       if(!found)
-                       {
-                               LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from.origin),"\n");
-                               continue;
-                       }
-
-               }
-
-               // Search "to" waypoint
-               wp_to = findradius(wp_to_pos, 1);
-               found = false;
-               while(wp_to)
-               {
-                       if(vdist(wp_to.origin - wp_to_pos, <, 1))
-                       if(wp_to.classname == "waypoint")
-                       {
-                               found = true;
-                               break;
-                       }
-                       wp_to = wp_to.chain;
-               }
-
-               if(!found)
-               {
-                       LOG_TRACE("waypoint_load_links: couldn't find 'to' waypoint at ", vtos(wp_to.origin),"\n");
-                       continue;
-               }
-
-               ++c;
-               waypoint_addlink(wp_from, wp_to);
-       }
-
-       fclose(file);
-
-       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.cache\n");
-
-       botframe_cachedwaypointlinks = true;
-       return true;
-}
-
-void waypoint_load_links_hardwired()
-{
-       string filename, s;
-       float file, tokens, c = 0, found;
-       entity wp_from = NULL, wp_to;
-       vector wp_to_pos, wp_from_pos;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints.hardwired");
-       file = fopen(filename, FILE_READ);
-
-       botframe_loadedforcedlinks = true;
-
-       if (file < 0)
-       {
-               LOG_TRACE("waypoint links load from ", filename, " failed\n");
-               return;
-       }
-
-       while ((s = fgets(file)))
-       {
-               if(substring(s, 0, 2)=="//")
-                       continue;
-
-               if(substring(s, 0, 1)=="#")
-                       continue;
-
-               tokens = tokenizebyseparator(s, "*");
-
-               if (tokens!=2)
-                       continue;
-
-               wp_from_pos     = stov(argv(0));
-               wp_to_pos       = stov(argv(1));
-
-               // Search "from" waypoint
-               if(!wp_from || wp_from.origin!=wp_from_pos)
-               {
-                       wp_from = findradius(wp_from_pos, 5);
-                       found = false;
-                       while(wp_from)
-                       {
-                               if(vdist(wp_from.origin - wp_from_pos, <, 5))
-                               if(wp_from.classname == "waypoint")
-                               {
-                                       found = true;
-                                       break;
-                               }
-                               wp_from = wp_from.chain;
-                       }
-
-                       if(!found)
-                       {
-                               LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped\n"));
-                               continue;
-                       }
-               }
-
-               // Search "to" waypoint
-               wp_to = findradius(wp_to_pos, 5);
-               found = false;
-               while(wp_to)
-               {
-                       if(vdist(wp_to.origin - wp_to_pos, <, 5))
-                       if(wp_to.classname == "waypoint")
-                       {
-                               found = true;
-                               break;
-                       }
-                       wp_to = wp_to.chain;
-               }
-
-               if(!found)
-               {
-                       LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped\n"));
-                       continue;
-               }
-
-               ++c;
-               waypoint_addlink(wp_from, wp_to);
-               wp_from.wphardwired = true;
-               wp_to.wphardwired = true;
-       }
-
-       fclose(file);
-
-       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired\n");
-}
-
-entity waypoint_get_link(entity w, float i)
-{
-       switch(i)
-       {
-               case  0:return w.wp00;
-               case  1:return w.wp01;
-               case  2:return w.wp02;
-               case  3:return w.wp03;
-               case  4:return w.wp04;
-               case  5:return w.wp05;
-               case  6:return w.wp06;
-               case  7:return w.wp07;
-               case  8:return w.wp08;
-               case  9:return w.wp09;
-               case 10:return w.wp10;
-               case 11:return w.wp11;
-               case 12:return w.wp12;
-               case 13:return w.wp13;
-               case 14:return w.wp14;
-               case 15:return w.wp15;
-               case 16:return w.wp16;
-               case 17:return w.wp17;
-               case 18:return w.wp18;
-               case 19:return w.wp19;
-               case 20:return w.wp20;
-               case 21:return w.wp21;
-               case 22:return w.wp22;
-               case 23:return w.wp23;
-               case 24:return w.wp24;
-               case 25:return w.wp25;
-               case 26:return w.wp26;
-               case 27:return w.wp27;
-               case 28:return w.wp28;
-               case 29:return w.wp29;
-               case 30:return w.wp30;
-               case 31:return w.wp31;
-               default:return NULL;
-       }
-}
-
-// Save all waypoint links to a file
-void waypoint_save_links()
-{
-       string filename = sprintf("maps/%s.waypoints.cache", mapname);
-       int file = fopen(filename, FILE_WRITE);
-       if (file < 0)
-       {
-               LOG_INFOF("waypoint link save to %s failed\n", filename);
-               return;
-       }
-
-       int c = 0;
-       IL_EACH(g_waypoints, true,
-       {
-               for(int j = 0; j < 32; ++j)
-               {
-                       entity link = waypoint_get_link(it, j);
-                       if(link)
-                       {
-                               string s = strcat(vtos(it.origin), "*", vtos(link.origin), "\n");
-                               fputs(file, s);
-                               ++c;
-                       }
-               }
-       });
-       fclose(file);
-       botframe_cachedwaypointlinks = true;
-
-       LOG_INFOF("saved %d waypoint links to maps/%s.waypoints.cache\n", c, mapname);
-}
-
-// save waypoints to gamedir/data/maps/mapname.waypoints
-void waypoint_saveall()
-{
-       string filename = sprintf("maps/%s.waypoints", mapname);
-       int file = fopen(filename, FILE_WRITE);
-       if (file < 0)
-       {
-               waypoint_save_links(); // save anyway?
-               botframe_loadedforcedlinks = false;
-
-               LOG_INFOF("waypoint links: save to %s failed\n", filename);
-               return;
-       }
-
-       int c = 0;
-       IL_EACH(g_waypoints, true,
-       {
-               if(it.wpflags & WAYPOINTFLAG_GENERATED)
-                       continue;
-
-               for(int j = 0; j < 32; ++j)
-               {
-                       string s;
-                       s = strcat(vtos(it.origin + it.mins), "\n");
-                       s = strcat(s, vtos(it.origin + it.maxs));
-                       s = strcat(s, "\n");
-                       s = strcat(s, ftos(it.wpflags));
-                       s = strcat(s, "\n");
-                       fputs(file, s);
-                       ++c;
-               }
-       });
-       fclose(file);
-       waypoint_save_links();
-       botframe_loadedforcedlinks = false;
-
-       LOG_INFOF("saved %d waypoints to maps/%s.waypoints\n", c, mapname);
-}
-
-// load waypoints from file
-float waypoint_loadall()
-{
-       string filename, s;
-       float file, cwp, cwb, fl;
-       vector m1, m2;
-       cwp = 0;
-       cwb = 0;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints");
-       file = fopen(filename, FILE_READ);
-       if (file >= 0)
-       {
-               while ((s = fgets(file)))
-               {
-                       m1 = stov(s);
-                       s = fgets(file);
-                       if (!s)
-                               break;
-                       m2 = stov(s);
-                       s = fgets(file);
-                       if (!s)
-                               break;
-                       fl = stof(s);
-                       waypoint_spawn(m1, m2, fl);
-                       if (m1 == m2)
-                               cwp = cwp + 1;
-                       else
-                               cwb = cwb + 1;
-               }
-               fclose(file);
-               LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints\n");
-       }
-       else
-       {
-               LOG_TRACE("waypoint load from ", filename, " failed\n");
-       }
-       return cwp + cwb;
-}
-
-vector waypoint_fixorigin(vector position)
-{
-       tracebox(position + '0 0 1' * (1 - STAT(PL_MIN, NULL).z), STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), position + '0 0 -512', MOVE_NOMONSTERS, NULL);
-       if(trace_fraction < 1)
-               position = trace_endpos;
-       //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
-       //print("position is ", ftos(trace_endpos_z - position_z), " above solid\n");
-       return position;
-}
-
-void waypoint_spawnforitem_force(entity e, vector org)
-{
-       // Fix the waypoint altitude if necessary
-       org = waypoint_fixorigin(org);
-
-       // don't spawn an item spawnfunc_waypoint if it already exists
-       IL_EACH(g_waypoints, true,
-       {
-               if(it.wpisbox)
-               {
-                       if(boxesoverlap(org, org, it.absmin, it.absmax))
-                       {
-                               e.nearestwaypoint = it;
-                               return;
-                       }
-               }
-               else
-               {
-                       if(vdist(it.origin - org, <, 16))
-                       {
-                               e.nearestwaypoint = it;
-                               return;
-                       }
-               }
-       });
-
-       e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM);
-}
-
-void waypoint_spawnforitem(entity e)
-{
-       if(!bot_waypoints_for_items)
-               return;
-
-       waypoint_spawnforitem_force(e, e.origin);
-}
-
-void waypoint_spawnforteleporter_boxes(entity e, vector org1, vector org2, vector destination1, vector destination2, float timetaken)
-{
-       entity w;
-       entity dw;
-       w = waypoint_spawn(org1, org2, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_NORELINK);
-       dw = waypoint_spawn(destination1, destination2, WAYPOINTFLAG_GENERATED);
-       // one way link to the destination
-       w.wp00 = dw;
-       w.wp00mincost = timetaken; // this is just for jump pads
-       // the teleporter's nearest spawnfunc_waypoint is this one
-       // (teleporters are not goals, so this is probably useless)
-       e.nearestwaypoint = w;
-       e.nearestwaypointtimeout = time + 1000000000;
-}
-
-void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken)
-{
-       org = waypoint_fixorigin(org);
-       destination = waypoint_fixorigin(destination);
-       waypoint_spawnforteleporter_boxes(e, org, org, destination, destination, timetaken);
-}
-
-void waypoint_spawnforteleporter(entity e, vector destination, float timetaken)
-{
-       destination = waypoint_fixorigin(destination);
-       waypoint_spawnforteleporter_boxes(e, e.absmin, e.absmax, destination, destination, timetaken);
-}
-
-entity waypoint_spawnpersonal(entity this, vector position)
-{
-       entity w;
-
-       // drop the waypoint to a proper location:
-       //   first move it up by a player height
-       //   then move it down to hit the floor with player bbox size
-       position = waypoint_fixorigin(position);
-
-       w = waypoint_spawn(position, position, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_PERSONAL);
-       w.nearestwaypoint = NULL;
-       w.nearestwaypointtimeout = 0;
-       w.owner = this;
-
-       waypoint_schedulerelink(w);
-
-       return w;
-}
-
-void botframe_showwaypointlinks()
-{
-       if (time < botframe_waypointeditorlightningtime)
-               return;
-       botframe_waypointeditorlightningtime = time + 0.5;
-       FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot,
-       {
-               if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE)
-               {
-                       //navigation_testtracewalk = true;
-                       entity head = navigation_findnearestwaypoint(it, false);
-               //      print("currently selected WP is ", etos(head), "\n");
-                       //navigation_testtracewalk = false;
-                       if (head)
-                       {
-                               entity w;
-                               w = head     ;if (w) te_lightning2(NULL, w.origin, it.origin);
-                               w = head.wp00;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp01;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp02;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp03;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp04;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp05;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp06;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp07;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp08;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp09;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp10;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp11;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp12;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp13;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp14;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp15;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp16;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp17;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp18;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp19;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp20;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp21;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp22;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp23;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp24;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp25;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp26;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp27;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp28;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp29;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp30;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp31;if (w) te_lightning2(NULL, w.origin, head.origin);
-                       }
-               }
-       });
-}
-
-float botframe_autowaypoints_fixdown(vector v)
-{
-       tracebox(v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v + '0 0 -64', MOVE_NOMONSTERS, NULL);
-       if(trace_fraction >= 1)
-               return 0;
-       return 1;
-}
-
-float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f)
-{
-       IL_EACH(g_waypoints, boxesoverlap(v - '32 32 32', v + '32 32 32', it.absmin, it.absmax),
-       {
-               // if a matching spawnfunc_waypoint already exists, don't add a duplicate
-               return 0;
-       });
-
-       waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f));
-       return 1;
-}
-
-// return value:
-//    1 = WP created
-//    0 = no action needed
-//   -1 = temp fail, try from world too
-//   -2 = permanent fail, do not retry
-float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .entity fld)
-{
-       // make it possible to go from p to wp, if we can
-       // if wp is NULL, nearest is chosen
-
-       entity w;
-       vector porg;
-       float t, tmin, tmax;
-       vector o;
-       vector save;
-
-       if(!botframe_autowaypoints_fixdown(p.origin))
-               return -2;
-       porg = trace_endpos;
-
-       if(wp)
-       {
-               // if any WP w fulfills wp -> w -> porg and w is closer than wp, then switch from wp to w
-
-               // if wp -> porg, then OK
-               float maxdist;
-               if(navigation_waypoint_will_link(wp.origin, porg, p, walkfromwp, 1050))
-               {
-                       // we may find a better one
-                       maxdist = vlen(wp.origin - porg);
-               }
-               else
-               {
-                       // accept any "good"
-                       maxdist = 2100;
-               }
-
-               float bestdist = maxdist;
-               IL_EACH(g_waypoints, it != wp && !(it.wpflags & WAYPOINTFLAG_NORELINK),
-               {
-                       float d = vlen(wp.origin - it.origin) + vlen(it.origin - porg);
-                       if(d < bestdist)
-                       if(navigation_waypoint_will_link(wp.origin, it.origin, p, walkfromwp, 1050))
-                       if(navigation_waypoint_will_link(it.origin, porg, p, walkfromwp, 1050))
-                       {
-                               bestdist = d;
-                               p.(fld) = it;
-                       }
-               });
-               if(bestdist < maxdist)
-               {
-                       LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n");
-                       return 0;
-               }
-
-               if(bestdist < 2100)
-               {
-                       // we know maxdist < 2100
-                       // so wp -> porg is still valid
-                       // all is good
-                       p.(fld) = wp;
-                       return 0;
-               }
-
-               // otherwise, no existing WP can fix our issues
-       }
-       else
-       {
-               save = p.origin;
-               setorigin(p, porg);
-               w = navigation_findnearestwaypoint(p, walkfromwp);
-               setorigin(p, save);
-               if(w)
-               {
-                       p.(fld) = w;
-                       return 0;
-               }
-       }
-
-       tmin = 0;
-       tmax = 1;
-       for (;;)
-       {
-               if(tmax - tmin < 0.001)
-               {
-                       // did not get a good candidate
-                       return -1;
-               }
-
-               t = (tmin + tmax) * 0.5;
-               o = antilag_takebackorigin(p, CS(p), time - t);
-               if(!botframe_autowaypoints_fixdown(o))
-                       return -2;
-               o = trace_endpos;
-
-               if(wp)
-               {
-                       if(!navigation_waypoint_will_link(wp.origin, o, p, walkfromwp, 1050))
-                       {
-                               // we cannot walk from wp.origin to o
-                               // get closer to tmax
-                               tmin = t;
-                               continue;
-                       }
-               }
-               else
-               {
-                       save = p.origin;
-                       setorigin(p, o);
-                       w = navigation_findnearestwaypoint(p, walkfromwp);
-                       setorigin(p, save);
-                       if(!w)
-                       {
-                               // we cannot walk from any WP to o
-                               // get closer to tmax
-                               tmin = t;
-                               continue;
-                       }
-               }
-
-               // if we get here, o is valid regarding waypoints
-               // check if o is connected right to the player
-               // we break if it succeeds, as that means o is a good waypoint location
-               if(navigation_waypoint_will_link(o, porg, p, walkfromwp, 1050))
-                       break;
-
-               // o is no good, we need to get closer to the player
-               tmax = t;
-       }
-
-       LOG_INFO("spawning a waypoint for connecting to ", etos(wp), "\n");
-       botframe_autowaypoints_createwp(o, p, fld, 0);
-       return 1;
-}
-
-// automatically create missing waypoints
-.entity botframe_autowaypoints_lastwp0, botframe_autowaypoints_lastwp1;
-void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld)
-{
-       float r = botframe_autowaypoints_fix_from(p, walkfromwp, p.(fld), fld);
-       if(r != -1)
-               return;
-       r = botframe_autowaypoints_fix_from(p, walkfromwp, NULL, fld);
-       if(r != -1)
-               return;
-
-       LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain\n");
-       if(!botframe_autowaypoints_fixdown(p.origin))
-               return; // shouldn't happen, caught above
-       botframe_autowaypoints_createwp(trace_endpos, p, fld, WAYPOINTFLAG_PROTECTED);
-}
-
-void botframe_deleteuselesswaypoints()
-{
-       FOREACH_ENTITY_FLOAT(bot_pickup, true,
-       {
-               // NOTE: this protects waypoints if they're the ONLY nearest
-               // waypoint. That's the intention.
-               navigation_findnearestwaypoint(it, false);  // Walk TO item.
-               navigation_findnearestwaypoint(it, true);  // Walk FROM item.
-       });
-       IL_EACH(g_waypoints, true,
-       {
-               it.wpflags |= WAYPOINTFLAG_DEAD_END;
-               it.wpflags &= ~WAYPOINTFLAG_USEFUL;
-               // WP is useful if:
-               if (it.wpflags & WAYPOINTFLAG_ITEM)
-                       it.wpflags |= WAYPOINTFLAG_USEFUL;
-               if (it.wpflags & WAYPOINTFLAG_TELEPORT)
-                       it.wpflags |= WAYPOINTFLAG_USEFUL;
-               if (it.wpflags & WAYPOINTFLAG_PROTECTED)
-                       it.wpflags |= WAYPOINTFLAG_USEFUL;
-               // b) WP is closest WP for an item/spawnpoint/other entity
-               //    This has been done above by protecting these WPs.
-       });
-       // c) There are w1, w, w2 so that w1 -> w, w -> w2 and not w1 -> w2.
-       IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_PERSONAL),
-       {
-               for (int m = 0; m < 32; ++m)
-               {
-                       entity w = waypoint_get_link(it, m);
-                       if (!w)
-                               break;
-                       if (w.wpflags & WAYPOINTFLAG_PERSONAL)
-                               continue;
-                       if (w.wpflags & WAYPOINTFLAG_USEFUL)
-                               continue;
-                       for (int j = 0; j < 32; ++j)
-                       {
-                               entity w2 = waypoint_get_link(w, j);
-                               if (!w2)
-                                       break;
-                               if (it == w2)
-                                       continue;
-                               if (w2.wpflags & WAYPOINTFLAG_PERSONAL)
-                                       continue;
-                               // If we got here, it != w2 exist with it -> w
-                               // and w -> w2. That means the waypoint is not
-                               // a dead end.
-                               w.wpflags &= ~WAYPOINTFLAG_DEAD_END;
-                               for (int k = 0; k < 32; ++k)
-                               {
-                                       if (waypoint_get_link(it, k) == w2)
-                                               continue;
-                                       // IF WE GET HERE, w is proven useful
-                                       // to get from it to w2!
-                                       w.wpflags |= WAYPOINTFLAG_USEFUL;
-                                       goto next;
-                               }
-                       }
-LABEL(next)
-               }
-       });
-       // d) The waypoint is a dead end. Dead end waypoints must be kept as
-       //     they are needed to complete routes while autowaypointing.
-
-       IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)),
-       {
-               LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", it.origin);
-               te_explosion(it.origin);
-               waypoint_remove(it);
-               break;
-       });
-
-       IL_EACH(g_waypoints, true,
-       {
-               it.wpflags &= ~(WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END); // temp flag
-       });
-}
-
-void botframe_autowaypoints()
-{
-       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), LAMBDA(
-               // going back is broken, so only fix waypoints to walk TO the player
-               //botframe_autowaypoints_fix(p, false, botframe_autowaypoints_lastwp0);
-               botframe_autowaypoints_fix(it, true, botframe_autowaypoints_lastwp1);
-               //te_explosion(p.botframe_autowaypoints_lastwp0.origin);
-       ));
-
-       if (autocvar_g_waypointeditor_auto >= 2) {
-               botframe_deleteuselesswaypoints();
-       }
-}
-
diff --git a/qcsrc/server/bot/waypoints.qh b/qcsrc/server/bot/waypoints.qh
deleted file mode 100644 (file)
index 23c0fa6..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-/*
- * Globals and Fields
- */
-
-const int WAYPOINTFLAG_GENERATED = BIT(23);
-const int WAYPOINTFLAG_ITEM = BIT(22);
-const int WAYPOINTFLAG_TELEPORT = BIT(21);
-const int WAYPOINTFLAG_NORELINK = BIT(20);
-const int WAYPOINTFLAG_PERSONAL = BIT(19);
-const int WAYPOINTFLAG_PROTECTED = BIT(18);  // Useless WP detection never kills these.
-const int WAYPOINTFLAG_USEFUL = BIT(17);  // Useless WP detection temporary flag.
-const int WAYPOINTFLAG_DEAD_END = BIT(16);  // Useless WP detection temporary flag.
-
-// fields you can query using prvm_global server to get some statistics about waypoint linking culling
-float relink_total, relink_walkculled, relink_pvsculled, relink_lengthculled;
-
-float botframe_waypointeditorlightningtime;
-float botframe_loadedforcedlinks;
-float botframe_cachedwaypointlinks;
-
-.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;
-
-// itemscore = (howmuchmoreIwant / howmuchIcanwant) / itemdistance
-.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 wpfire, wpcost, wpconsidered, wpisbox, wplinked, wphardwired;
-.int wpflags;
-
-.vector wpnearestpoint;
-
-/*
- * Functions
- */
-
-spawnfunc(waypoint);
-void waypoint_addlink(entity from, entity to);
-void waypoint_think(entity this);
-void waypoint_clearlinks(entity wp);
-void waypoint_schedulerelink(entity wp);
-
-void waypoint_remove(entity e);
-void waypoint_removeall();
-void waypoint_schedulerelinkall();
-void waypoint_load_links_hardwired();
-void waypoint_save_links();
-void waypoint_saveall();
-
-void waypoint_spawnforitem_force(entity e, vector org);
-void waypoint_spawnforitem(entity e);
-void waypoint_spawnforteleporter(entity e, vector destination, float timetaken);
-void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken);
-void botframe_showwaypointlinks();
-
-float waypoint_loadall();
-float waypoint_load_links();
-
-entity waypoint_spawn(vector m1, vector m2, float f);
-entity waypoint_spawnpersonal(entity this, vector position);
-
-vector waypoint_fixorigin(vector position);
-
-void botframe_autowaypoints();
index 8de814507c39e53e16436826f921b30db1bea6b3..a1e6c7cca1fe10996b384621c47c8e784bbb716d 100644 (file)
@@ -19,8 +19,7 @@
 #include "campaign.qh"
 #include "command/common.qh"
 
-#include "bot/bot.qh"
-#include "bot/navigation.qh"
+#include "bot/api.qh"
 
 #include "../common/ent_cs.qh"
 #include <common/state.qh>
@@ -248,7 +247,7 @@ void PutObserverInServer(entity this)
                // needed for player sounds
                this.model = "";
                FixPlayermodel(this);
-        } 
+        }
         setmodel(this, MDL_Null);
         setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL));
         this.view_ofs = '0 0 0';
@@ -352,6 +351,10 @@ void PutObserverInServer(entity this)
        this.oldvelocity = this.velocity;
        this.fire_endtime = -1;
        this.event_damage = func_null;
+
+       STAT(ACTIVEWEAPON, this) = WEP_Null.m_id;
+       STAT(SWITCHINGWEAPON, this) = WEP_Null.m_id;
+       STAT(SWITCHWEAPON, this) = WEP_Null.m_id;
 }
 
 int player_getspecies(entity this)
@@ -497,7 +500,7 @@ void PutClientInServer(entity this)
                PutObserverInServer(this);
        } else if (IS_PLAYER(this)) {
                if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
-               
+
                PlayerState_attach(this);
                accuracy_resend(this);
 
@@ -2326,7 +2329,6 @@ void PlayerPreThink (entity this)
                this.prevorigin = this.origin;
 
                bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
-        .entity weaponentity = weaponentities[0]; // TODO: unhardcode
                if (this.hook.state) {
                        do_crouch = false;
                } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
@@ -2335,10 +2337,6 @@ void PlayerPreThink (entity this)
                        do_crouch = false;
                } else if (STAT(FROZEN, this)) {
                        do_crouch = false;
-        } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI) && this.(weaponentity).wframe == WFRAME_FIRE1 && time < this.(weaponentity).weapon_nextthink) {
-                       do_crouch = false;
-        } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) {
-                       do_crouch = false;
         }
 
                if (do_crouch) {
index a2aeb88a9b20dc8239ea1bbb1d7533f4be7e64be..51cb60a727136da39a2fd9f62666d46f44e7dd3f 100644 (file)
@@ -1,12 +1,11 @@
 #include "cl_impulse.qh"
 #include "round_handler.qh"
 
-#include "bot/waypoints.qh"
+#include "bot/api.qh"
 
 #include "weapons/throwing.qh"
 #include "command/common.qh"
 #include "cheats.qh"
-#include "bot/navigation.qh"
 #include "weapons/selection.qh"
 #include "weapons/tracing.qh"
 #include "weapons/weaponsystem.qh"
index 29a55cbd85dd796dbf615d39fe19b64d5eb2538d..a542e6431345095828ccd90dc87dd9ecc081406e 100644 (file)
@@ -1,6 +1,6 @@
 #include "cl_player.qh"
 
-#include "bot/bot.qh"
+#include "bot/api.qh"
 #include "cheats.qh"
 #include "g_damage.qh"
 #include "g_subs.qh"
index f263f5e61c4223837af1dd7f5a675c9a20c14534..9440a3673ec59b850965166e984b4bff28861681 100644 (file)
@@ -16,9 +16,7 @@
 #include "../playerdemo.qh"
 #include "../teamplay.qh"
 
-#include "../bot/bot.qh"
-#include "../bot/navigation.qh"
-#include "../bot/scripting.qh"
+#include "../bot/api.qh"
 
 #include "../mutators/all.qh"
 
@@ -41,7 +39,7 @@ void PutObserverInServer(entity this);
 //  used by GameCommand_make_mapinfo()
 void make_mapinfo_Think(entity this)
 {
-       if (MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
+       if (_MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
        {
                LOG_INFO("Done rebuiling mapinfos.\n");
                MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
@@ -742,7 +740,7 @@ void GameCommand_gametype(float request, float argc)
                        if (argv(1) != "")
                        {
                                string s = argv(1);
-                               float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
+                               Gametype t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
 
                                if (t)
                                {
index debc54743275b0e1bfc64e9f6c5a2a4e21ef05f9..d7e9b07c05279a5c26a7f67655d9039ca4339c1a 100644 (file)
@@ -1,6 +1,6 @@
 #include "g_damage.qh"
 
-#include "bot/bot.qh"
+#include "bot/api.qh"
 #include "g_hook.qh"
 #include "mutators/all.qh"
 #include "scores.qh"
index ed35a5b0d8bb8ab0871ec1a0c1e59d798c1a5f9c..c0f592992fc839ce24e79b73f3a00b0f1f32368f 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "anticheat.qh"
 #include "antilag.qh"
-#include "bot/bot.qh"
+#include "bot/api.qh"
 #include "campaign.qh"
 #include "cheats.qh"
 #include "cl_client.qh"
@@ -646,9 +646,6 @@ spawnfunc(worldspawn)
                }
        }
 
-       float fd, l;
-       string s;
-
        cvar = cvar_normal;
        cvar_string = cvar_string_normal;
        cvar_set = cvar_set_normal;
@@ -661,8 +658,6 @@ spawnfunc(worldspawn)
 
        cvar_changes_init(); // do this very early now so it REALLY matches the server config
 
-       compressShortVector_init();
-
        maxclients = 0;
        for (entity head = nextent(NULL); head; head = nextent(head))
        {
@@ -753,7 +748,7 @@ spawnfunc(worldspawn)
        // character set: ASCII 33-126 without the following characters: : ; ' " \ $
        if(autocvar_sv_eventlog)
        {
-               s = sprintf("%d.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000));
+               string s = sprintf("%d.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000));
                matchid = strzone(s);
 
                GameLogEcho(strcat(":gamestart:", GetGametype(), "_", GetMapname(), ":", s));
@@ -806,12 +801,13 @@ spawnfunc(worldspawn)
 
        if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
        {
-               fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
+               int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
                if(fd != -1)
                {
+                       string s;
                        while((s = fgets(fd)))
                        {
-                               l = tokenize_console(s);
+                               int l = tokenize_console(s);
                                if(l < 2)
                                        continue;
                                if(argv(0) == "cd")
@@ -853,7 +849,7 @@ spawnfunc(worldspawn)
        monsterlist_reply = strzone(getmonsterlist());
        for(int i = 0; i < 10; ++i)
        {
-               s = getrecords(i);
+               string s = getrecords(i);
                if (s)
                        records_reply[i] = strzone(s);
        }
@@ -872,7 +868,7 @@ spawnfunc(worldspawn)
        // fill sv_curl_serverpackages from .serverpackage files
        if (autocvar_sv_curl_serverpackages_auto)
        {
-               s = "csprogs-" WATERMARK ".txt";
+               string s = "csprogs-" WATERMARK ".txt";
                // remove automatically managed files from the list to prevent duplicates
                for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
                {
@@ -884,7 +880,7 @@ spawnfunc(worldspawn)
                }
                // add automatically managed files to the list
                #define X(match) MACRO_BEGIN { \
-                       fd = search_begin(match, true, false); \
+                       int fd = search_begin(match, true, false); \
                        if (fd >= 0) \
                        { \
                                for (int i = 0, j = search_getsize(fd); i < j; ++i) \
index b3a496f41e988c1ef19dce7d448e12772daecd0c..7ffe4b934fbfa389bcac17b021855f97411d772c 100644 (file)
@@ -41,10 +41,10 @@ entity mapvote_ent;
  * Returns the gamtype ID from its name, if type_name isn't a real gametype it
  * checks for sv_vote_gametype_(type_name)_type
  */
-float GameTypeVote_Type_FromString(string type_name)
+Gametype GameTypeVote_Type_FromString(string type_name)
 {
-       float type = MapInfo_Type_FromString(type_name);
-       if ( type == 0 )
+       Gametype type = MapInfo_Type_FromString(type_name);
+       if (type == NULL)
                type = MapInfo_Type_FromString(cvar_string(
                        strcat("sv_vote_gametype_",type_name,"_type")));
        return type;
@@ -54,22 +54,22 @@ int GameTypeVote_AvailabilityStatus(string type_name)
 {
        int flag = GTV_FORBIDDEN;
 
-       float type = MapInfo_Type_FromString(type_name);
-       if ( type == 0 )
+       Gametype type = MapInfo_Type_FromString(type_name);
+       if ( type == NULL )
        {
                type = MapInfo_Type_FromString(cvar_string(
                        strcat("sv_vote_gametype_",type_name,"_type")));
                flag |= GTV_CUSTOM;
        }
 
-       if( type == 0 )
+       if( type == NULL )
                return flag;
 
        if ( autocvar_nextmap != "" )
        {
-               if ( !MapInfo_Get_ByName(autocvar_nextmap, false, 0) )
+               if ( !MapInfo_Get_ByName(autocvar_nextmap, false, NULL) )
                        return flag;
-               if (!(MapInfo_Map_supportedGametypes & type))
+               if (!(MapInfo_Map_supportedGametypes & type.m_flags))
                        return flag;
        }
 
@@ -83,7 +83,7 @@ float GameTypeVote_GetMask()
        n = min(MAPVOTE_COUNT, n);
        gametype_mask = 0;
        for(j = 0; j < n; ++j)
-               gametype_mask |= GameTypeVote_Type_FromString(argv(j));
+               gametype_mask |= GameTypeVote_Type_FromString(argv(j)).m_flags;
        return gametype_mask;
 }
 
@@ -92,7 +92,7 @@ string GameTypeVote_MapInfo_FixName(string m)
        if ( autocvar_sv_vote_gametype )
        {
                MapInfo_Enumerate();
-               MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+               _MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
        }
        return MapInfo_FixName(m);
 }
@@ -695,12 +695,12 @@ void MapVote_Think()
        MapVote_Tick();
 }
 
-float GameTypeVote_SetGametype(float type)
+float GameTypeVote_SetGametype(Gametype type)
 {
        if (MapInfo_CurrentGametype() == type)
                return true;
 
-       float tsave = MapInfo_CurrentGametype();
+       Gametype tsave = MapInfo_CurrentGametype();
 
        MapInfo_SwitchGameType(type);
 
@@ -752,7 +752,7 @@ float GameTypeVote_Finished(float pos)
 float GameTypeVote_AddVotable(string nextMode)
 {
        float j;
-       if ( nextMode == "" || GameTypeVote_Type_FromString(nextMode) == 0 )
+       if ( nextMode == "" || GameTypeVote_Type_FromString(nextMode) == NULL )
                return false;
        for(j = 0; j < mapvote_count; ++j)
                if(mapvote_maps[j] == nextMode)
index d742c77af105f6d33e916fcd1704acc23f3010ce..78ba560265e0b17ec95b2d36d578cef9ab6e43fe 100644 (file)
@@ -74,7 +74,7 @@
 #include "all.qh"
 
 STATIC_INIT_LATE(Gametype) {
-    Gametype g = MapInfo_Type(MapInfo_CurrentGametype());
+    Gametype g = MapInfo_CurrentGametype();
     if (g) {
         for (string _s = g.m_mutators; _s != ""; _s = cdr(_s)) {
             string s = car(_s);
index ee80b44b06df6213c745d39e0c2b3dc70bb9c116..1e60b203e3caa7226883860198bc9f9e130977df 100644 (file)
 #include <server/scores_rules.qh>
 #include <server/teamplay.qh>
 
-#include <server/bot/bot.qh>
-#include <server/bot/navigation.qh>
-#include <server/bot/waypoints.qh>
-#include <server/bot/havocbot/roles.qh>
-
-#include <server/bot/havocbot/havocbot.qh>
+#include <server/bot/api.qh>
 
 #include <server/command/vote.qh>
 
index 40fad29b34426e03fae679d508eab6968ca327ba..a63321a36a4731d9253021233886c1e4d9c02e84 100644 (file)
 #include <server/scores.qh>
 #include <server/scores_rules.qh>
 
-#include <server/bot/bot.qh>
-#include <server/bot/navigation.qh>
-#include <server/bot/waypoints.qh>
-
-#include <server/bot/havocbot/havocbot.qh>
-#include <server/bot/havocbot/roles.qh>
+#include <server/bot/api.qh>
 
 #include <server/command/vote.qh>
 #include <server/command/common.qh>
index 7f5b0ccbd1e4c61c5cd2ca9e415b380e7d4c7082..c1f798af60adad6491ed5aa4a14350a0ad522488 100644 (file)
@@ -2444,7 +2444,7 @@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand)
 MUTATOR_HOOKFUNCTION(ctf, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        if(frag_target.flagcarried)
                ctf_Handle_Throw(frag_target, NULL, DROP_THROW);
 }
index 8efa3a35dacf17aa9380de07a23976134946fac6..a712e033df7414ef7d7a929c3ef0c84bdaaf01ce 100644 (file)
@@ -108,8 +108,9 @@ void CTS_ClientKill(entity e) // silent version of ClientKill, used when player
 MUTATOR_HOOKFUNCTION(cts, PlayerPhysics)
 {
        entity player = M_ARGV(0, entity);
+       float dt = M_ARGV(1, float);
 
-       player.race_movetime_frac += PHYS_INPUT_TIMELENGTH;
+       player.race_movetime_frac += dt;
        float f = floor(player.race_movetime_frac);
        player.race_movetime_frac -= f;
        player.race_movetime_count += f;
@@ -282,7 +283,7 @@ MUTATOR_HOOKFUNCTION(cts, PutClientInServer)
 MUTATOR_HOOKFUNCTION(cts, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        frag_target.respawn_flags |= RESPAWN_FORCE;
        race_AbandonRaceCheck(frag_target);
 }
index 407ac6c98bb7ed05a083c49b4db0801aea3aca5d..da43b84deb3bb3f1909493c18057cb2c5e6e9d91 100644 (file)
@@ -466,7 +466,7 @@ MUTATOR_HOOKFUNCTION(ka, HavocBot_ChooseRole)
 MUTATOR_HOOKFUNCTION(ka, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        if(frag_target.ballcarried)
                ka_DropEvent(frag_target);
 }
index 236db8aa86fad3dd93cafc3faf907a6901d5dd24..c3acf86e628b453a43a938ff346ef95618787b37 100644 (file)
@@ -50,8 +50,6 @@ USING(kh_Think_t, void());
 void kh_StartRound();
 void kh_Controller_SetThink(float t, kh_Think_t func);
 
-entity kh_worldkeylist;
-.entity kh_worldkeynext;
 #endif
 
 #ifdef IMPLEMENTATION
@@ -1386,7 +1384,7 @@ MUTATOR_HOOKFUNCTION(kh, HavocBot_ChooseRole)
 MUTATOR_HOOKFUNCTION(kh, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        kh_Key_DropAll(frag_target, false);
 }
 
index a869827937573d7d852e9b92680ad33f4e1b5831..e96e7f2f920db0d69acfd3766e2c266abde8a4dc 100644 (file)
@@ -176,7 +176,7 @@ MUTATOR_HOOKFUNCTION(lms, PutClientInServer)
 MUTATOR_HOOKFUNCTION(lms, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        frag_target.respawn_flags |= RESPAWN_FORCE;
 }
 
index a496ef2e221801637f7727c010eb2a90e96bb2a1..04561db369e806d5d6d227d49f56614680bb7f8c 100644 (file)
@@ -143,8 +143,9 @@ float WinningCondition_QualifyingThenRace(float limit)
 MUTATOR_HOOKFUNCTION(rc, PlayerPhysics)
 {
        entity player = M_ARGV(0, entity);
+       float dt = M_ARGV(1, float);
 
-       player.race_movetime_frac += PHYS_INPUT_TIMELENGTH;
+       player.race_movetime_frac += dt;
        float f = floor(player.race_movetime_frac);
        player.race_movetime_frac -= f;
        player.race_movetime_count += f;
@@ -318,7 +319,7 @@ MUTATOR_HOOKFUNCTION(rc, PutClientInServer)
 MUTATOR_HOOKFUNCTION(rc, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        frag_target.respawn_flags |= RESPAWN_FORCE;
        race_AbandonRaceCheck(frag_target);
 }
index a2aaf55635d119e08959f93ae9f3a6292834a989..f60932c6688b4899238161abbea45db03dfb396a 100644 (file)
@@ -1,5 +1,5 @@
 #include "path_waypoint.qh"
-#include "../bot/waypoints.qh"
+#include "../bot/api.qh"
 
 #include "pathlib.qh"
 #include "main.qh"
index 163b1667449eb239bf20e392d828b8a263589eef..c49b604e69c0da8660bd9968a6c5680ce9cac6ab 100644 (file)
@@ -10,7 +10,6 @@
 
 #include "../server/_mod.inc"
 #include "bot/_mod.inc"
-#include "bot/havocbot/_mod.inc"
 #include "command/_mod.inc"
 #include "mutators/_mod.inc"
 #include "pathlib/_all.inc"
index a46c8b3d935268f87c4069248fe82398b45c1550..b315d6cad4ac45b8513a842e8b8081a7a8dac81d 100644 (file)
@@ -4,8 +4,7 @@
 #include "portals.qh"
 #include "scores.qh"
 #include "spawnpoints.qh"
-#include "bot/waypoints.qh"
-#include "bot/navigation.qh"
+#include "bot/api.qh"
 #include "command/getreplies.qh"
 #include "../common/deathtypes/all.qh"
 #include "../common/notifications/all.qh"
index 17c56fb543dd167e3f8302bf8155efb79e2323ab..e3d5fd9d72e5e0da5a904528c8ded99b87dd7a7e 100644 (file)
@@ -185,7 +185,7 @@ bool ScoreInfo_SendEntity(entity this, entity to, int sf)
 {
        float i;
        WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
-       WriteInt24_t(MSG_ENTITY, MapInfo_LoadedGametype);
+       WriteRegistered(Gametypes, MSG_ENTITY, MapInfo_LoadedGametype);
        FOREACH(Scores, true, {
                WriteString(MSG_ENTITY, scores_label(it));
                WriteByte(MSG_ENTITY, scores_flags(it));
@@ -333,7 +333,7 @@ float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
        {
                if(gameover)
                        return 0;
-               LOG_WARNING("Adding score to unknown player!");
+               LOG_WARN("Adding score to unknown player!");
                return 0;
        }
        if(score)
index 52887cc1550777367194d918df936c70f8b89b6c..4a72bb1aee81d2444355b7d305d37ed83b32c13c 100644 (file)
@@ -4,8 +4,7 @@
 #include "g_hook.qh"
 #include "g_world.qh"
 
-#include "bot/bot.qh"
-#include "bot/waypoints.qh"
+#include "bot/api.qh"
 
 #include "command/common.qh"
 
index e2156705a03b0cd1de67161f0b61ed6ba7d8eb42..56f85bd4689fd47a7f189dec8d57949435213adf 100644 (file)
@@ -5,7 +5,7 @@
 #include "scores.qh"
 #include "scores_rules.qh"
 
-#include "bot/bot.qh"
+#include "bot/api.qh"
 
 #include "command/vote.qh"
 
index 8e3e1075899707fdcf6248ce297f5e0394ec061d..8a787241a095695376a8879484d6c8c7390c7b07 100644 (file)
@@ -101,7 +101,15 @@ bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain)
                if (weaponsInMap & WepSet_FromWeapon(wpn))
                {
                        Send_WeaponComplain(this, wpn.m_id, 1);
-                       Weapon_whereis(wpn, this);
+                       if(autocvar_g_showweaponspawns < 3)
+                               Weapon_whereis(wpn, this);
+                       else
+                       {
+                               FOREACH(Weapons, it.impulse == wpn.impulse,
+                               {
+                                       Weapon_whereis(it, this);
+                               });
+                       }
                }
                else
                {
index c69b8e9a9ce1c2ebb70e2c8799f84535a3a2b606..ed9f2be4e0ef51fda59183c60b5a320005b24b81 100644 (file)
@@ -45,6 +45,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto
        wep.owner = wep.enemy = own;
        wep.flags |= FL_TOSSED;
        wep.colormap = own.colormap;
+       wep.glowmod = weaponentity_glowmod(info, own.clientcolors);
 
        W_DropEvent(wr_drop,own,wpn,wep);
 
@@ -79,7 +80,6 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto
        weapon_defaultspawnfunc(wep, info);
        if(startitem_failed)
                return string_null;
-       wep.glowmod = weaponentity_glowmod(info, own.clientcolors);
        setthink(wep, thrown_wep_think);
        wep.savenextthink = wep.nextthink;
        wep.nextthink = min(wep.nextthink, time + 0.5);
index 3f372371bd3d1af12fd3e1518e434ffb9c6e5871..22fd70a2ec8c7ebe50c01aad9595c2a9bd8682e5 100644 (file)
@@ -459,7 +459,7 @@ void W_WeaponFrame(Player actor)
                switch (this.state)
                {
                        default:
-                               LOG_WARNINGF("unhandled weaponentity (%i) state for player (%i): %d\n", this, actor, this.state);
+                               LOG_WARNF("unhandled weaponentity (%i) state for player (%i): %d\n", this, actor, this.state);
                                break;
                        case WS_INUSE:
                        case WS_RAISE:
diff --git a/qcsrc/tools/cloc.txt b/qcsrc/tools/cloc.txt
new file mode 100644 (file)
index 0000000..9b1b3af
--- /dev/null
@@ -0,0 +1,13 @@
+QC
+    filter call_regexp_common C++
+    filter remove_inline //.*$
+    extension qc
+    extension inc
+    3rd_gen_scale 0.5
+    end_of_line_continuation \\$
+QC Header
+    filter call_regexp_common C++
+    filter remove_inline //.*$
+    extension qh
+    3rd_gen_scale 0.5
+    end_of_line_continuation \\$
index b638927bf4a50441d0adef391b4f48101337cef6..daf20ebf3ba748a68be9a1712be73c532f605fa9 100755 (executable)
@@ -1,6 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -eu
-cd "$(dirname "$0")"
+cd ${0%/*}
 cd ..
 
 function startswith() {
index 5662337411f3767e852ec0a8b65226cc64826ca6..594c60ea29d8ae509dfd3d748987e74f0fe21b60 100755 (executable)
@@ -1,6 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -eu
-cd "$(dirname "$0")"
+cd ${0%/*}
 cd ..
 
 function check() {
@@ -14,6 +14,7 @@ function check() {
 
 check lib
 check common
+check ecs
 check client
 check server
 check menu