X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fbot%2Fdefault%2Fbot.qc;h=59ff81df94678b5f744c0da45e617019e56416a7;hb=68049a125cf7c4680ef38d80eb1fb744349a6f34;hp=40afed17990b7e93c40e41e194b5893054c54911;hpb=629f6f3b55e891281bb265f8022d129d313c110f;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index 40afed179..59ff81df9 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -1,53 +1,42 @@ #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 "../../client.qh" -#include "../../constants.qh" -#include "../../defs.qh" -#include "../../race.qh" -#include - -#include - -#include "../../weapons/accuracy.qh" - -#include #include -#include #include +#include +#include +#include #include #include - -#include - #include - #include - #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include STATIC_INIT(bot) { bot_calculate_stepheightvec(); } // TODO: remove this function! its only purpose is to update these fields since bot_setnameandstuff is called before ClientState void bot_setclientfields(entity this) { - CS(this).cvar_cl_accuracy_data_share = 1; // share the bots weapon accuracy data with the world - CS(this).cvar_cl_accuracy_data_receive = 0; // don't receive any weapon accuracy data + CS_CVAR(this).cvar_cl_accuracy_data_share = 1; // share the bots weapon accuracy data with the world + CS_CVAR(this).cvar_cl_accuracy_data_receive = 0; // don't receive any weapon accuracy data } entity bot_spawn() @@ -104,7 +93,8 @@ void bot_think(entity this) // clear buttons PHYS_INPUT_BUTTON_ATCK(this) = false; - PHYS_INPUT_BUTTON_JUMP(this) = false; + // keep jump button pressed for a short while, useful with ramp jumps + PHYS_INPUT_BUTTON_JUMP(this) = (!IS_DEAD(this) && time < this.bot_jump_time + 0.2); PHYS_INPUT_BUTTON_ATCK2(this) = false; PHYS_INPUT_BUTTON_ZOOM(this) = false; PHYS_INPUT_BUTTON_CROUCH(this) = false; @@ -123,16 +113,24 @@ void bot_think(entity this) } // if dead, just wait until we can respawn - if (IS_DEAD(this)) + if (IS_DEAD(this) || IS_OBSERVER(this)) { if (bot_waypoint_queue_owner == this) bot_waypoint_queue_owner = NULL; this.aistatus = 0; CS(this).movement = '0 0 0'; - if (this.deadflag == DEAD_DEAD) + if (IS_OBSERVER(this)) + return; + if (IS_DEAD(this)) { - PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn - navigation_goalrating_timeout_force(this); + if (!navigation_goalrating_timeout(this)) + navigation_goalrating_timeout_force(this); + // jump must not be pressed for at least one frame in order for + // PlayerThink to detect the key down event + if (this.deadflag == DEAD_DYING) + PHYS_INPUT_BUTTON_JUMP(this) = false; + else if (this.deadflag == DEAD_DEAD) + PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn } } else if(this.aistatus & AI_STATUS_STUCK) @@ -145,21 +143,7 @@ void bot_think(entity 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; - } + int file, tokens, prio; file = fopen(autocvar_bot_config_file, FILE_READ); @@ -170,6 +154,26 @@ void bot_setnameandstuff(entity this) } else { + entity balance = TeamBalance_CheckAllowedTeams(NULL); + TeamBalance_GetTeamCounts(balance, NULL); + int smallest_team = -1; + int smallest_count = -1; + if (teamplay) + { + for (int i = 1; i <= AvailableTeams(); ++i) + { + // NOTE if (autocvar_g_campaign && autocvar_g_campaign_forceteam == i) + // TeamBalance_GetNumberOfPlayers(balance, i); returns the number of players + 1 + // since it keeps a spot for the real player in the desired team + int count = TeamBalance_GetNumberOfPlayers(balance, i); + if (smallest_count < 0 || count < smallest_count) + { + smallest_team = i; + smallest_count = count; + } + } + } + TeamBalance_Destroy(balance); RandomSelection_Init(); while((readfile = fgets(file))) { @@ -177,24 +181,41 @@ void bot_setnameandstuff(entity this) continue; if(substring(readfile, 0, 1) == "#") continue; + // NOTE if the line is empty tokenizebyseparator(readfile, "\t") + // will create 1 empty token because there's no separator (bug?) + if (readfile == "") + continue; tokens = tokenizebyseparator(readfile, "\t"); if(tokens == 0) continue; s = argv(0); - prio = 1; + prio = 0; + bool conflict = false; FOREACH_CLIENT(IS_BOT_CLIENT(it), { - if(s == it.cleanname) + if (s == it.cleanname) { - prio = 0; + conflict = true; break; } }); + if (!conflict) + prio += 1; + if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2)) + { + int forced_team = stof(argv(5)); + if (!Team_IsValidIndex(forced_team)) + forced_team = 0; + if (!forced_team || forced_team == smallest_team) + prio += 2; + } RandomSelection_AddString(readfile, 1, prio); } readfile = RandomSelection_chosen_string; fclose(file); } + string bot_name, bot_model, bot_skin, bot_shirt, bot_pants; + tokens = tokenizebyseparator(readfile, "\t"); if(argv(0) != "") bot_name = argv(0); else bot_name = "Bot"; @@ -211,7 +232,10 @@ void bot_setnameandstuff(entity this) 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)); + if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2)) + this.bot_forced_team = stof(argv(5)); + else + this.bot_forced_team = 0; prio = 6; @@ -250,13 +274,10 @@ void bot_setnameandstuff(entity this) 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; + string prefix = (autocvar_g_campaign ? "" : autocvar_bot_prefix); + string suffix = (autocvar_g_campaign ? "" : autocvar_bot_suffix); + string name = (autocvar_bot_usemodelnames ? bot_model : bot_name); - // number bots with identical names if (name == "") { name = ftos(etof(this)); @@ -264,6 +285,7 @@ void bot_setnameandstuff(entity this) } else { + // number bots with identical names int j = 0; FOREACH_CLIENT(IS_BOT_CLIENT(it), { if(it.cleanname == name) @@ -285,6 +307,10 @@ void bot_setnameandstuff(entity this) void bot_custom_weapon_priority_setup() { + static string bot_priority_far_prev; + static string bot_priority_mid_prev; + static string bot_priority_close_prev; + static string bot_priority_distances_prev; float tokens, i, w; bot_custom_weapon = false; @@ -292,70 +318,50 @@ void bot_custom_weapon_priority_setup() 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 == "" + 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; + if (bot_priority_distances_prev != autocvar_bot_ai_custom_weapon_priority_distances) + { + strcpy(bot_priority_distances_prev, autocvar_bot_ai_custom_weapon_priority_distances); + tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," "); - // Parse far distance weapon priorities - tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," "); + if (tokens!=2) + return; - 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; + bot_distance_far = stof(argv(0)); + bot_distance_close = stof(argv(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(bot_distance_far < bot_distance_close){ + bot_distance_far = stof(argv(1)); + bot_distance_close = stof(argv(0)); } } - 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)," "); + int c; + + #define PARSE_WEAPON_PRIORITIES(dist) MACRO_BEGIN \ + if (bot_priority_##dist##_prev != autocvar_bot_ai_custom_weapon_priority_##dist) { \ + strcpy(bot_priority_##dist##_prev, autocvar_bot_ai_custom_weapon_priority_##dist); \ + tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_##dist)," "); \ + bot_weapons_##dist[0] = -1; \ + c = 0; \ + for(i = 0; i < tokens && c < REGISTRY_COUNT(Weapons); ++i) { \ + w = stof(argv(i)); \ + if (w >= WEP_FIRST && w <= WEP_LAST) { \ + bot_weapons_##dist[c] = w; \ + ++c; \ + } \ + } \ + if (c < REGISTRY_COUNT(Weapons)) \ + bot_weapons_##dist[c] = -1; \ + } \ + MACRO_END - 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; + PARSE_WEAPON_PRIORITIES(far); + PARSE_WEAPON_PRIORITIES(mid); + PARSE_WEAPON_PRIORITIES(close); bot_custom_weapon = true; } @@ -385,17 +391,19 @@ void bot_relinkplayerlist() if(IS_BOT_CLIENT(it)) { - if(prevbot) - prevbot.nextbot = it; - else - bot_list = it; - prevbot = it; + if (!IS_OBSERVER(it) && !bot_ispaused(it)) + { + if(prevbot) + prevbot.nextbot = it; + else + bot_list = it; + prevbot = it; + } ++currentbots; } }); if(prevbot) prevbot.nextbot = NULL; - LOG_TRACE("relink: ", ftos(currentbots), " bots seen."); bot_strategytoken = bot_list; bot_strategytoken_taken = true; } @@ -646,39 +654,6 @@ bool bot_fixcount() return true; } -void bot_remove_from_bot_list(entity this) -{ - entity e = bot_list; - entity prev_bot = NULL; - while (e) - { - if(e == this) - { - if(!prev_bot) - bot_list = this.nextbot; - else - prev_bot.nextbot = this.nextbot; - if(bot_strategytoken == this) - { - bot_strategytoken = this.nextbot; - bot_strategytoken_taken = true; - } - this.nextbot = NULL; - break; - } - prev_bot = e; - e = e.nextbot; - } -} - -void bot_clear(entity this) -{ - bot_remove_from_bot_list(this); - if(bot_waypoint_queue_owner == this) - bot_waypoint_queue_owner = NULL; - this.aistatus &= ~AI_STATUS_STUCK; // otherwise bot_waypoint_queue_owner will be set again to this by navigation_unstuck -} - void bot_serverframe() { if (intermission_running && currentbots > 0) @@ -751,7 +726,7 @@ void bot_serverframe() localcmd("quit\n"); } - if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto) + if (currentbots > 0 || waypointeditor_enabled || autocvar_g_waypointeditor_auto) if (botframe_spawnedwaypoints) { if(botframe_cachedwaypointlinks) @@ -816,16 +791,12 @@ void bot_serverframe() } } - if (autocvar_g_waypointeditor) + if (waypointeditor_enabled) 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; - } + if (currentbots > 0) + bot_custom_weapon_priority_setup(); }