- wget -O data/maps/g-23.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.cache
- wget -O data/maps/g-23.waypoints.hardwired https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.hardwired
- make
- - EXPECT=b58f9c7587f1a14e5c52176d4e62a9fb
+ - EXPECT=37be9dc5489925451eb497390fdf58b9
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
set bot_ai_ignoregoal_timeout 3 "Ignore goals making bots to get stuck in front of a wall for N seconds"
set bot_ai_bunnyhop_skilloffset 7 "Bots with skill equal or greater than this value will perform the \"bunnyhop\" technique"
set bot_ai_bunnyhop_startdistance 200 "Run to goals located further than this distance"
-set bot_ai_bunnyhop_stopdistance 200 "Stop jumping after reaching this distance to the goal"
+set bot_ai_bunnyhop_stopdistance 300 "Stop jumping after reaching this distance to the goal"
set bot_ai_bunnyhop_firstjumpdelay 0.2 "Start running to the goal only if it was seen for more than N seconds"
set bot_god 0 "god mode for bots"
set bot_ai_navigation_jetpack 0 "Enable bots to navigate maps using the jetpack"
// buffs
// =======
set cl_buffs_autoreplace 1 "automatically drop current buff when picking up another"
-set g_buffs 0 "enable buffs (requires buff items or powerups)"
+set g_buffs -1 "enable buffs (requires buff items or powerups)"
set g_buffs_effects 1 "show particle effects from carried buffs"
set g_buffs_waypoint_distance 1024 "maximum distance at which buff waypoint can be seen from item"
set g_buffs_randomize 1 "randomize buff type when player drops buff"
set g_buffs_random_location 0 "randomize buff location on start and when reset"
set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up"
set g_buffs_spawn_count 0 "how many buffs to spawn on the map if none exist already"
-set g_buffs_replace_powerups 1 "replace powerups on the map with random buffs"
+set g_buffs_replace_powerups 0 "replace powerups on the map with random buffs"
set g_buffs_cooldown_activate 5 "cooldown period when buff is first activated"
set g_buffs_cooldown_respawn 3 "cooldown period when buff is reloading"
set g_buffs_ammo 1 "ammo buff: infinite ammunition"
set g_buffs_luck_time 60 "luck buff carry time"
set g_buffs_luck_chance 0.15 "chance for 'critical' hit (multiplied damage) with luck buff"
set g_buffs_luck_damagemultiplier 3 "luck damage multiplier"
+set g_buffs_flight 0 "flight buff: crouch jump to reverse your gravity!"
+set g_buffs_flight_time 60 "flight buff carry time"
// ==============
this.m_color = '0 1 0';
this.m_waypoint = _("Big armor");
#ifdef SVQC
- this.m_botvalue = 20000; // FIXME: higher than BOT_PICKUP_RATING_HIGH?
+ this.m_botvalue = BOT_PICKUP_RATING_HIGH;
this.m_itemid = IT_ARMOR;
this.m_respawntime = GET(g_pickup_respawntime_long);
this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long);
this.m_color = '1 0.23 0.44';
}
BUFF_SPAWNFUNCS(luck, BUFF_LUCK)
+
+REGISTER_BUFF(FLIGHT) {
+ this.m_prettyName = _("Flight");
+ this.m_name = "flight";
+ this.m_skin = 11;
+ this.m_color = '0.23 0.44 1';
+}
+BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT)
.float buff_time = _STAT(BUFF_TIME);
void buffs_DelayedInit(entity this);
-REGISTER_MUTATOR(buffs, cvar("g_buffs"))
+AUTOCVAR(g_buffs, int, -1, _("Enable buffs, -1: enabled but no auto location or replacing powerups, 1: enabled and can replace them"));
+
+REGISTER_MUTATOR(buffs, autocvar_g_buffs)
{
MUTATOR_ONADD
{
- InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
+ if(autocvar_g_buffs > 0)
+ InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
}
}
if(player.buffs & BUFF_JUMP.m_itemid)
M_ARGV(1, float) = autocvar_g_buffs_jump_height;
+
+ if(player.buffs & BUFF_FLIGHT.m_itemid)
+ if(!IS_JUMP_HELD(player) && PHYS_INPUT_BUTTON_CROUCH(player))
+ player.gravity *= -1;
}
MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
{
+ if(autocvar_g_buffs < 0)
+ return; // no auto replacing of entities in this mode
+
entity ent = M_ARGV(0, entity);
if(autocvar_g_buffs_replace_powerups)
BUFF_ONREM(BUFF_INVISIBLE)
player.alpha = player.buff_invisible_prev_alpha;
+ BUFF_ONADD(BUFF_FLIGHT)
+ {
+ player.buff_flight_oldgravity = player.gravity;
+ if(!player.gravity)
+ player.gravity = 1;
+ }
+
+ BUFF_ONREM(BUFF_FLIGHT)
+ player.gravity = ((player.trigger_gravity_check) ? player.trigger_gravity_check.enemy.gravity : player.buff_flight_oldgravity);
+
player.oldbuffs = player.buffs;
if(player.buffs)
{
MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
{
- M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
+ if(autocvar_g_buffs > 0) // only report as a mutator if they're enabled
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
}
MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
{
- M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
+ if(autocvar_g_buffs > 0)
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
}
void buffs_DelayedInit(entity this)
// disability
.float buff_disability_time;
.float buff_disability_effect_time;
+// flight
+.float buff_flight_oldgravity;
// common buff variables
.float buff_effect_delay;
InterpolateOrigin_Do(this);
+ float t = entcs_GetTeam(player_localnum) + 1;
string spriteimage = "";
// choose the sprite
switch (this.rule)
{
case SPRITERULE_SPECTATOR:
- float t = entcs_GetTeam(player_localnum) + 1;
if (!(
(autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
|| (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage || STAT(ITEMSTIME) == 2))
case SPRITERULE_DEFAULT:
if (this.team)
{
- if (this.team == myteam + 1)
+ if (this.team == t)
spriteimage = this.netname;
else
spriteimage = "";
spriteimage = this.netname;
break;
case SPRITERULE_TEAMPLAY:
- if (myteam == NUM_SPECTATOR)
+ if (t == NUM_SPECTATOR + 1)
spriteimage = this.netname3;
- else if (this.team == myteam + 1)
+ else if (this.team == t)
spriteimage = this.netname2;
else
spriteimage = this.netname;
float crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
- float t = waypointsprite_scale;
+ t = waypointsprite_scale;
a *= waypointsprite_alpha;
{
float weapon_pickupevalfunc(entity player, entity item)
{
float c;
+ int rating = item.bot_pickupbasevalue;
// See if I have it already
- if(item.weapons & ~player.weapons)
+ if(player.weapons & item.weapons)
{
// If I can pick it up
if(!item.spawnshieldtime)
c = 0;
else if(player.ammo_cells || player.ammo_shells || player.ammo_plasma || player.ammo_nails || player.ammo_rockets)
{
+ if (rating > 0)
+ rating = BOT_PICKUP_RATING_LOW * 0.5 * (1 + rating / BOT_PICKUP_RATING_HIGH);
// Skilled bots will grab more
- c = bound(0, skill / 10, 1) * 0.5;
+ c = 1 + bound(0, skill / 10, 1) * 0.5;
}
else
c = 0;
else
c = 1;
+ if (c <= 0)
+ return 0;
+
// If custom weapon priorities for bots is enabled rate most wanted weapons higher
- if( bot_custom_weapon && c )
- {
- // Find the highest position on any range
- int position = -1;
- for (int j = 0; j < WEP_LAST ; ++j){
- if(
- bot_weapons_far[j] == item.weapon ||
- bot_weapons_mid[j] == item.weapon ||
- bot_weapons_close[j] == item.weapon
- )
+ if(bot_custom_weapon)
+ {
+ int best_ratio = 0;
+ int missing = 0;
+
+ // evaluate weapon usefulness in all ranges
+ for(int list = 0; list < 3; list++)
+ {
+ int position = -1;
+ int wep_count = 0;
+ int wpn = item.weapon;
+ for (int j = 0; j < WEP_LAST; ++j)
{
- position = j;
- break;
+ int list_wpn = 0;
+ if (list == 0) list_wpn = bot_weapons_far[j];
+ else if (list == 1) list_wpn = bot_weapons_mid[j];
+ else list_wpn = bot_weapons_close[j];
+
+ if (weaponsInMap & Weapons_from(list_wpn).m_wepset) // only if available
+ {
+ if (list_wpn > 0)
+ wep_count++;
+ if (position == -1 && list_wpn == wpn)
+ position = wep_count;
+ }
+ }
+ if (position == -1)
+ {
+ missing++;
+ position = wep_count; // if missing assume last
+ }
+ if (wep_count)
+ {
+ if (!best_ratio || position / wep_count < best_ratio)
+ best_ratio = position / wep_count;
}
}
- // Rate it
- if (position >= 0 )
- {
- position = WEP_LAST - position;
- // item.bot_pickupbasevalue is overwritten here
- return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c;
- }
+ if (missing < 3 && best_ratio)
+ c = c - best_ratio * 0.3;
}
- return item.bot_pickupbasevalue * c;
+ return rating * c;
}
float commodity_pickupevalfunc(entity player, entity item)
{
- float c;
- float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
- c = 0;
+ bool need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
+ float c = 0;
// Detect needed ammo
FOREACH(Weapons, it != WEP_Null, {
if(!(player.weapons & (it.m_wepset)))
continue;
- if(it.items & ITEM_Shells.m_itemid)
- need_shells = true;
- else if(it.items & ITEM_Bullets.m_itemid)
- need_nails = true;
- else if(it.items & ITEM_Rockets.m_itemid)
- need_rockets = true;
- else if(it.items & ITEM_Cells.m_itemid)
- need_cells = true;
- else if(it.items & ITEM_Plasma.m_itemid)
- need_plasma = true;
- else if(it.items & ITEM_JetpackFuel.m_itemid)
- need_fuel = true;
+ switch(it.ammo_field)
+ {
+ case ammo_shells: need_shells = true; break;
+ case ammo_nails: need_nails = true; break;
+ case ammo_rockets: need_rockets = true; break;
+ case ammo_cells: need_cells = true; break;
+ case ammo_plasma: need_plasma = true; break;
+ case ammo_fuel: need_fuel = true; break;
+ }
});
// TODO: figure out if the player even has the weapon this ammo is for?
return;
float dist = vlen(this.origin - view_origin);
+ float t = (entcs_GetTeam(player_localnum) + 1);
vector o;
string txt;
if(autocvar_cl_vehicles_hud_tactical)
- if(dist < 10240 && (myteam + 1 != this.team))
+ if(dist < 10240 && t != this.team)
{
// TODO: Vehicle tactical hud
o = project_3d_to_2d(this.origin + '0 0 32');
else
txt = spritelookuptext(this, spriteimage);
- if(time - floor(time) > 0.5 && (myteam + 1 == this.team))
+ if(time - floor(time) > 0.5 && t == this.team)
{
if(this.helpme && time < this.helpme)
{
float crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
- float t = waypointsprite_scale;
+ t = waypointsprite_scale;
a *= waypointsprite_alpha;
{
s = strcat(s, ", ", _("Invincible Projectiles"));
if(cvar_string("g_weaponarena") != "0")
s = strcat(s, ", ", WeaponArenaString());
- else if(cvar("g_balance_blaster_weaponstart") == 0)
+ else if(cvar("g_balance_blaster_weaponstartoverride") == 0)
s = strcat(s, ", ", _("No start weapons"));
if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
s = strcat(s, ", ", _("Low gravity"));
return 0;
if(cvar_string("g_weaponarena") == "0")
return 0;
- if(cvar_string("g_balance_blaster_weaponstart") == "0")
+ if(cvar_string("g_balance_blaster_weaponstartoverride") == "0")
return 0;
return 1;
}
setDependent(e, "g_nix", 1, 1);
me.TR(me);
me.TDempty(me, 0.2);
- me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstart", "0", _("No start weapons"), "-"));
+ me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstartoverride", "0", _("No start weapons"), "-"));
e.cvarOffValue = "-1";
- makeMulti(e, "g_balance_shotgun_weaponstart g_balance_machinegun_weaponstart g_balance_devastator_weaponstart g_balance_minelayer_weaponstart g_balance_electro_weaponstart g_balance_crylink_weaponstart g_balance_hagar_weaponstart g_balance_porto_weaponstart g_balance_vaporizer_weaponstart g_balance_hook_weaponstart g_balance_rifle_weaponstart g_balance_fireball_weaponstart g_balance_seeker_weaponstart g_balance_tuba_weaponstart g_balance_arc_weaponstart g_balance_vortex_weaponstart g_balance_mortar_weaponstart");
+ makeMulti(e, "g_balance_shotgun_weaponstartoverride g_balance_machinegun_weaponstartoverride g_balance_devastator_weaponstartoverride g_balance_minelayer_weaponstartoverride g_balance_electro_weaponstartoverride g_balance_crylink_weaponstartoverride g_balance_hagar_weaponstartoverride g_balance_porto_weaponstartoverride g_balance_vaporizer_weaponstartoverride g_balance_hook_weaponstartoverride g_balance_rifle_weaponstartoverride g_balance_fireball_weaponstartoverride g_balance_seeker_weaponstartoverride g_balance_tuba_weaponstartoverride g_balance_arc_weaponstartoverride g_balance_vortex_weaponstartoverride g_balance_mortar_weaponstartoverride");
me.gotoRC(me, me.rows - 1, 0);
me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0'));
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);
//if (this.bot_painintensity > 0)
// this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime;
* 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
+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;
if(bot_execute_commands(this))
return;
- while(this.goalcurrent && wasfreed(this.goalcurrent))
- {
- navigation_poproute(this);
- if(!this.goalcurrent)
- this.bot_strategytime = 0;
- }
-
if (bot_strategytoken == this)
if (!bot_strategytoken_taken)
{
if(checkdistance)
{
this.aistatus &= ~AI_STATUS_RUNNING;
- if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
+ // increase stop distance in case the goal is on a slope or a lower platform
+ if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z))
PHYS_INPUT_BUTTON_JUMP(this) = true;
}
else
if(this.jumppadcount)
{
// If got stuck on the jump pad try to reach the farthest visible waypoint
+ // but with some randomness so it can try out different paths
if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
{
if(fabs(this.velocity.z)<50)
if(trace_fraction < 1)
continue;
- if(!newgoal || vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))
+ if(!newgoal || ((random() < 0.8) && vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin)))
newgoal = it;
});
this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
navigation_clearroute(this);
navigation_routetogoal(this, newgoal, this.origin);
+ if(autocvar_bot_debug_goalstack)
+ debuggoalstack(this);
this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
}
}
if (this.goalcurrent == NULL)
return;
- if (this.goalcurrent)
- navigation_poptouchedgoals(this);
+ navigation_poptouchedgoals(this);
// if ran out of goals try to use an alternative goal or get a new strategy asap
if(this.goalcurrent == NULL)
}
// 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';
+ vector dst_ahead = this.origin + this.view_ofs + this.velocity * 0.5;
+ vector dst_down = dst_ahead - '0 0 3000';
// Look ahead
traceline(this.origin + this.view_ofs, dst_ahead, true, NULL);
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))
+ if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || (this.aistatus & AI_STATUS_ROAMING) || 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
+ //te_lightning2(NULL, this.origin + this.view_ofs, 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');
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))
+ else if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
{
- // 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))
+ // the traceline check isn't enough but is good as optimization,
+ // when not true (most of the time) this tracebox call is avoided
+ tracebox(dst_ahead, this.mins, this.maxs, dst_down, true, this);
+ 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");
- navigation_clearroute(this);
- return;
+ if (gco.z > this.origin.z + jumpstepheightvec.z)
+ {
+ // the goal is probably on an upper platform, assume bot can't get there
+ LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared");
+ navigation_clearroute(this);
+ this.bot_strategytime = 0;
+ }
+ else
+ evadelava = normalize(this.velocity) * -1;
}
}
}
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)
+ 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;
continue;
// not falling
- if((IS_ONGROUND(it)) == 0)
+ if(!IS_ONGROUND(it))
{
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)
+ 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;
m1 = org + this.mins;
m2 = org + this.maxs;
+ while(this.goalcurrent && wasfreed(this.goalcurrent))
+ navigation_poproute(this);
+
if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
{
+ // make sure jumppad is really hit, don't rely on distance based checks
+ // as they may report a touch even if it didn't really happen
if(this.lastteleporttime>0)
- if(time-this.lastteleporttime<(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15)
+ 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)
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(!(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
{
if(vdist(this.origin - this.goalcurrent.origin, <, 150))
{
while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax))
{
+ if((this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
+ break;
+
// Detect personal waypoints
if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
else
modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
}
- else if(cvar("g_balance_blaster_weaponstart") == 0)
+ else if(cvar("g_balance_blaster_weaponstartoverride") == 0)
modifications = strcat(modifications, ", No start weapons");
if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
modifications = strcat(modifications, ", Low gravity");
{
this.SendFlags |= ISF_LOCATION;
this.oldorigin = this.origin;
+ this.bot_pickup = false;
}
+ else
+ this.bot_pickup = true;
this.owner = NULL;
float timeleft = this.savenextthink - time;
if(timeleft > 1)