X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fserver%2Fbot%2Fdefault%2Fbot.qc;h=20af7e71984dfdac24a5895ec91b7bf128f03b8b;hp=2fb0a43c2b150cc9d726336a7ed095996b865902;hb=99f5aae787d0e46852e2340594c5cb4805c0f3f3;hpb=07f92afac006497cfe761652ff4ee8aa18925c5a diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index 2fb0a43c2b..20af7e7198 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -41,6 +41,13 @@ #include #include +// 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 +} + entity bot_spawn() { entity bot = spawnclient(); @@ -50,6 +57,7 @@ entity bot_spawn() currentbots = currentbots + 1; bot_setnameandstuff(bot); ClientConnect(bot); + bot_setclientfields(bot); PutClientInServer(bot); } return bot; @@ -64,9 +72,8 @@ void bot_think(entity this) 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_nextthink < time) - this.bot_nextthink = time + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill); + this.bot_nextthink = max(time, this.bot_nextthink) + max(0.01, autocvar_bot_ai_thinkinterval * (0.5 ** this.bot_aiskill) * min(14 / (skill + 14), 1)); + //if (this.bot_painintensity > 0) // this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime; @@ -75,7 +82,7 @@ void bot_think(entity this) if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start)) { - this.movement = '0 0 0'; + CS(this).movement = '0 0 0'; this.bot_nextthink = time + 0.5; return; } @@ -95,7 +102,7 @@ void bot_think(entity this) // (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 + CS(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) @@ -114,7 +121,7 @@ void bot_think(entity this) if (time < game_starttime) { // block the bot during the countdown to game start - this.movement = '0 0 0'; + CS(this).movement = '0 0 0'; this.bot_nextthink = game_starttime; return; } @@ -122,7 +129,7 @@ void bot_think(entity this) // if dead, just wait until we can respawn if (IS_DEAD(this)) { - this.movement = '0 0 0'; + CS(this).movement = '0 0 0'; if (this.deadflag == DEAD_DEAD) { PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn @@ -159,7 +166,7 @@ void bot_setnameandstuff(entity this) if(file < 0) { - LOG_INFO(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n")); + LOG_INFOF("Error: Can not open the bot configuration file '%s'", autocvar_bot_config_file); readfile = ""; } else @@ -176,13 +183,13 @@ void bot_setnameandstuff(entity this) continue; s = argv(0); prio = 1; - FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA( + FOREACH_CLIENT(IS_BOT_CLIENT(it), { if(s == it.cleanname) { prio = 0; break; } - )); + }); RandomSelection_AddString(readfile, 1, prio); } readfile = RandomSelection_chosen_string; @@ -209,7 +216,13 @@ void bot_setnameandstuff(entity this) 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 + #define READSKILL(f, w, r) MACRO_BEGIN { \ + if(argv(prio) != "") \ + this.f = stof(argv(prio)) * w; \ + else \ + this.f = (!autocvar_g_campaign) * (2 * random() - 1) * r * w; \ + prio++; \ + } MACRO_END //print(bot_name, ": ping=", argv(9), "\n"); READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill @@ -243,10 +256,10 @@ void bot_setnameandstuff(entity this) // number bots with identical names int j = 0; - FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA( + FOREACH_CLIENT(IS_BOT_CLIENT(it), { if(it.cleanname == name) ++j; - )); + }); if (j) this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix)); else @@ -259,9 +272,6 @@ void bot_setnameandstuff(entity this) 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() @@ -369,14 +379,13 @@ void bot_relinkplayerlist() if(prevbot) prevbot.nextbot = it; else - { bot_list = it; - bot_list.nextbot = NULL; - } prevbot = it; ++currentbots; } }); + if(prevbot) + prevbot.nextbot = NULL; LOG_TRACE("relink: ", ftos(currentbots), " bots seen."); bot_strategytoken = bot_list; bot_strategytoken_taken = true; @@ -415,7 +424,10 @@ void bot_clientconnect(entity this) this.createdtime = this.bot_nextthink; if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches + { bot_setnameandstuff(this); + bot_setclientfields(this); + } if(this.bot_forced_team==1) this.team = NUM_TEAM_1; @@ -426,7 +438,7 @@ void bot_clientconnect(entity this) else if(this.bot_forced_team==4) this.team = NUM_TEAM_4; else - JoinBestTeam(this, false, true); + JoinBestTeam(this, true); havocbot_setupbot(this); } @@ -522,25 +534,25 @@ void autoskill(float factor) bestbot = -1; bestplayer = -1; - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + FOREACH_CLIENT(IS_PLAYER(it), { 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), "; "); + LOG_DEBUG("autoskill: best player got ", ftos(bestplayer), ", "); + LOG_DEBUG("best bot got ", ftos(bestbot), "; "); if(bestbot < 0 || bestplayer < 0) { - LOG_TRACE("not doing anything"); + LOG_DEBUG("not doing anything"); // 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"); + LOG_DEBUG("2 frags difference, increasing skill"); cvar_set("skill", ftos(autocvar_skill + 1)); bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n"); } @@ -549,27 +561,26 @@ void autoskill(float factor) { if(autocvar_skill > 0) { - LOG_TRACE("2 frags difference, decreasing skill"); + LOG_DEBUG("2 frags difference, decreasing skill"); cvar_set("skill", ftos(autocvar_skill - 1)); bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n"); } } else { - LOG_TRACE("not doing anything"); + LOG_DEBUG("not doing anything"); return; // don't reset counters, wait for them to accumulate } - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.totalfrags_lastcheck = it.totalfrags)); + FOREACH_CLIENT(IS_PLAYER(it), { 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 + jumpheight_vec = (autocvar_sv_jumpvelocity ** 2) / (2 * autocvar_sv_gravity) * '0 0 1'; + jumpstepheightvec = stepheightvec + jumpheight_vec * 0.85; // reduce it a bit to make the jumps easy } float bot_fixcount() @@ -580,11 +591,21 @@ float bot_fixcount() activerealplayers = M_ARGV(0, int); realplayers = M_ARGV(1, int); } else { - FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA( + FOREACH_CLIENT(IS_REAL_CLIENT(it), { if(IS_PLAYER(it)) ++activerealplayers; ++realplayers; - )); + }); + } + if(currentbots == -1) + { + currentbots = 0; + // human players joining early may cause weird issues (bots appearing on + // the scoreboard as spectators) when switching map with the gotomap + // command, as it doesn't remove bots of the previous match, and with + // minplayers > 1, so ignore human players in the first bot frame + // TODO maybe find a cleaner solution + activerealplayers = 0; } int bots; @@ -618,8 +639,7 @@ float bot_fixcount() // only add one bot per frame to avoid utter chaos if(time > botframe_nextthink) { - //dprint(ftos(bots), " ? ", ftos(currentbots), "\n"); - while (currentbots < bots) + if (currentbots < bots) { if (bot_spawn() == NULL) { @@ -673,7 +693,23 @@ void bot_serverframe() return; if (time < 2) + { + currentbots = -1; return; + } + + if(autocvar_skill != skill) + { + float wpcost_update = false; + if(skill >= autocvar_bot_ai_bunnyhop_skilloffset && autocvar_skill < autocvar_bot_ai_bunnyhop_skilloffset) + wpcost_update = true; + if(skill < autocvar_bot_ai_bunnyhop_skilloffset && autocvar_skill >= autocvar_bot_ai_bunnyhop_skilloffset) + wpcost_update = true; + + skill = autocvar_skill; + if (wpcost_update) + waypoint_updatecost_foralllinks(); + } bot_calculate_stepheightvec(); bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL); @@ -724,7 +760,7 @@ void bot_serverframe() botframe_spawnedwaypoints = true; waypoint_loadall(); if(!waypoint_load_links()) - waypoint_schedulerelinkall(); + waypoint_schedulerelinkall(); // link all the autogenerated waypoints (teleporters) } if (bot_list)