seta cl_reticle 1 "enable zoom reticles"
seta cl_reticle_stretch 0 "stretch reticles so they fit the screen (breaks image proportions)"
-seta cl_reticle_normal 1 "draw an aiminig reticle when zooming with the zoom button"
+seta cl_reticle_normal 1 "draw an aiming reticle when zooming with the zoom button"
seta cl_reticle_normal_alpha 1 "alpha of the normal reticle"
seta cl_reticle_weapon 1 "draw custom aiming reticle when zooming with certain weapons"
seta cl_reticle_weapon_alpha 1 "alpha of the custom reticle"
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"
set g_overkill_filter_armorbig 0
set g_overkill_filter_armormega 0
-set g_overkill_ammo_charge 0
-set g_overkill_ammo_charge_notice 1
-set g_overkill_ammo_charge_limit 1
-set g_overkill_ammo_charge_rate 0.5
-set g_overkill_ammo_charge_rate_vortex 0.5
-set g_overkill_ammo_charge_rate_machinegun 0.5
-set g_overkill_ammo_charge_rate_shotgun 0.5
-set g_overkill_ammo_charge_rate_hmg 0.25
-set g_overkill_ammo_charge_rate_rpc 1.5
-set g_overkill_ammo_decharge 0.1
-set g_overkill_ammo_decharge_machinegun 0.025
-set g_overkill_ammo_decharge_shotgun 0.15
-set g_overkill_ammo_decharge_vortex 0.2
-set g_overkill_ammo_decharge_rpc 1
-set g_overkill_ammo_decharge_hmg 0.01
-
// =========
// vampire
// 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"
// ==============
.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)
}
if(player.buffs & BUFF_AMMO.m_itemid)
- if(player.clip_size)
- player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
+ {
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(player.(weaponentity).clip_size)
+ player.(weaponentity).clip_load = player.(weaponentity).(weapon_load[player.(weaponentity).m_switchweapon.m_id]) = player.(weaponentity).clip_size;
+ }
+ }
if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid))
if(player.alpha != autocvar_g_buffs_invisible_alpha)
player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO);
player.items |= IT_UNLIMITED_WEAPON_AMMO;
- if(player.clip_load)
- player.buff_ammo_prev_clipload = player.clip_load;
- player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
+ if(player.buffs & BUFF_AMMO.m_itemid)
+ {
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(player.(weaponentity).clip_load)
+ player.(weaponentity).buff_ammo_prev_clipload = player.(weaponentity).clip_load;
+ if(player.(weaponentity).clip_size)
+ player.(weaponentity).clip_load = player.(weaponentity).(weapon_load[player.(weaponentity).m_switchweapon.m_id]) = player.(weaponentity).clip_size;
+ }
+ }
}
BUFF_ONREM(BUFF_AMMO)
else
player.items &= ~IT_UNLIMITED_WEAPON_AMMO;
- if(player.buff_ammo_prev_clipload)
- player.clip_load = player.buff_ammo_prev_clipload;
+ if(player.buffs & BUFF_AMMO.m_itemid)
+ {
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(player.(weaponentity).buff_ammo_prev_clipload)
+ player.(weaponentity).clip_load = player.(weaponentity).buff_ammo_prev_clipload;
+ }
+ }
}
BUFF_ONADD(BUFF_INVISIBLE)
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)
float Item_GiveTo(entity item, entity player)
{
- float _switchweapon;
float pickedup;
// if nothing happens to player, just return without taking the item
pickedup = false;
- _switchweapon = false;
+ int _switchweapon = 0;
// in case the player has autoswitch enabled do the following:
// if the player is using their best weapon before items are given, they
// probably want to switch to an even better weapon after items are given
- if (player.autoswitch)
- if (PS(player).m_switchweapon == w_getbestweapon(player))
- _switchweapon = true;
- if (!(player.weapons & WepSet_FromWeapon(PS(player).m_switchweapon)))
- _switchweapon = true;
+ if(player.autoswitch)
+ {
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(player.(weaponentity).m_weapon != WEP_Null || slot == 0)
+ {
+ if(player.(weaponentity).m_switchweapon == w_getbestweapon(player, weaponentity))
+ _switchweapon |= BIT(slot);
+
+ if(!(player.weapons & WepSet_FromWeapon(player.(weaponentity).m_switchweapon)))
+ _switchweapon |= BIT(slot);
+ }
+ }
+ }
pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL);
pickedup |= Item_GiveAmmoTo(item, player, ammo_shells, g_pickup_shells_max, ITEM_MODE_NONE);
FOREACH(Weapons, it != WEP_Null, {
if(w & (it.m_wepset))
{
- W_DropEvent(wr_pickup, player, it.m_id, item);
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(player.(weaponentity).m_weapon != WEP_Null || slot == 0)
+ W_DropEvent(wr_pickup, player, it.m_id, item, weaponentity);
+ }
W_GiveWeapon(player, it.m_id);
}
});
// crude hack to enforce switching weapons
if(g_cts && item.itemdef.instanceOfWeaponPickup)
{
- W_SwitchWeapon_Force(player, Weapons_from(item.weapon));
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(player.(weaponentity).m_weapon != WEP_Null || slot == 0)
+ W_SwitchWeapon_Force(player, Weapons_from(item.weapon), weaponentity);
+ }
return 1;
}
- if (_switchweapon)
- if (PS(player).m_switchweapon != w_getbestweapon(player))
- W_SwitchWeapon_Force(player, w_getbestweapon(player));
+ if(_switchweapon)
+ {
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(_switchweapon & BIT(slot))
+ if(player.(weaponentity).m_switchweapon != w_getbestweapon(player, weaponentity))
+ W_SwitchWeapon_Force(player, w_getbestweapon(player, weaponentity), weaponentity);
+ }
+ }
return 1;
}
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 )
+ if(bot_custom_weapon)
{
- // 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
- )
+ 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)
+ {
+ 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)
{
- position = j;
- break;
+ 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?
float GiveItems(entity e, float beginarg, float endarg)
{
float got, i, val, op;
- float _switchweapon;
string cmd;
val = 999;
got = 0;
- _switchweapon = false;
- if (e.autoswitch)
- if (PS(e).m_switchweapon == w_getbestweapon(e))
- _switchweapon = true;
+ int _switchweapon = 0;
+
+ if(e.autoswitch)
+ {
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(e.(weaponentity).m_weapon != WEP_Null || slot == 0)
+ if(e.(weaponentity).m_switchweapon == w_getbestweapon(e, weaponentity))
+ _switchweapon |= BIT(slot);
+ }
+ }
e.strength_finished = max(0, e.strength_finished - time);
e.invincible_finished = max(0, e.invincible_finished - time);
else
e.superweapons_finished += time;
- if (!(e.weapons & WepSet_FromWeapon(PS(e).m_switchweapon)))
- _switchweapon = true;
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(e.(weaponentity).m_weapon != WEP_Null || slot == 0)
+ if(!(e.weapons & WepSet_FromWeapon(e.(weaponentity).m_switchweapon)))
+ _switchweapon |= BIT(slot);
+ }
+
if(_switchweapon)
- W_SwitchWeapon_Force(e, w_getbestweapon(e));
+ {
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(_switchweapon & BIT(slot))
+ W_SwitchWeapon_Force(e, w_getbestweapon(e, weaponentity), weaponentity);
+ }
+ }
return got;
}
#include <common/physics/player.qh>
#include <common/state.qh>
#include <common/items/_mod.qh>
+#include <common/wepent.qh>
#include <common/triggers/teleporters.qh>
#include <common/triggers/trigger/jumppads.qh>
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)
{
return;
havocbot_chooseenemy(this);
- if (this.bot_chooseweapontime < time )
+
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
- this.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
- havocbot_chooseweapon(this);
+ .entity weaponentity = weaponentities[slot];
+ if(this.(weaponentity).m_weapon != WEP_Null || slot == 0)
+ if(this.(weaponentity).bot_chooseweapontime < time)
+ {
+ this.(weaponentity).bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
+ havocbot_chooseweapon(this, weaponentity);
+ }
}
havocbot_aim(this);
lag_update(this);
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;
}
else
{
- if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))
- this.lastfiredweapon = PS(this).m_weapon.m_id;
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ Weapon w = this.(weaponentity).m_weapon;
+ if(w == WEP_Null && slot != 0)
+ continue;
+ w.wr_aim(w, this, weaponentity);
+ if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) // TODO: what if we didn't fire this weapon, but the previous?
+ this.(weaponentity).lastfiredweapon = this.(weaponentity).m_weapon.m_id;
+ }
}
}
else
// 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
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
- 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;
- ));
+ .entity weaponentity = weaponentities[slot];
+
+ if(this.(weaponentity).m_weapon == WEP_Null && slot != 0)
+ continue;
+
+ // 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.(weaponentity).clip_load < this.(weaponentity).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.(weaponentity).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.(weaponentity).weapon_load[it.m_id] < it.reloading_ammo))
+ this.(weaponentity).m_switchweapon = it;
+ ));
+ }
}
}
}
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;
}
}
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)
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
- if(time > this.rocketjumptime)
+ .entity weaponentity = weaponentities[slot];
+
+ if(this.(weaponentity).m_weapon == WEP_Null && slot != 0)
+ continue;
+
+ if(client_hasweapon(this, WEP_DEVASTATOR, weaponentity, true, false))
{
- PHYS_INPUT_BUTTON_ATCK2(this) = true;
- this.rocketjumptime = 0;
+ this.movement_x = maxspeed;
+
+ if(this.rocketjumptime)
+ {
+ if(time > this.rocketjumptime)
+ {
+ PHYS_INPUT_BUTTON_ATCK2(this) = true;
+ this.rocketjumptime = 0;
+ }
+ return;
+ }
+
+ this.(weaponentity).m_switchweapon = WEP_DEVASTATOR;
+ this.v_angle_x = 90;
+ PHYS_INPUT_BUTTON_ATCK(this) = true;
+ this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
+ return;
}
- 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 (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;
}
}
}
this.havocbot_stickenemy = false;
}
-float havocbot_chooseweapon_checkreload(entity this, int new_weapon)
+float havocbot_chooseweapon_checkreload(entity this, .entity weaponentity, 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.
return false;
// if this weapon is scheduled for reloading, don't switch to it during combat
- if (this.weapon_load[new_weapon] < 0)
+ if (this.(weaponentity).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))
+ if(it.wr_checkammo1(it, this, weaponentity) + it.wr_checkammo2(it, this, weaponentity))
other_weapon_available = true;
));
if(other_weapon_available)
return false;
}
-void havocbot_chooseweapon(entity this)
+void havocbot_chooseweapon(entity this, .entity weaponentity)
{
int i;
// ;)
if(g_weaponarena_weapons == WEPSET(TUBA))
{
- PS(this).m_switchweapon = WEP_TUBA;
+ this.(weaponentity).m_switchweapon = WEP_TUBA;
return;
}
if(this.enemy==NULL)
{
// If no weapon was chosen get the first available weapon
- if(PS(this).m_weapon==WEP_Null)
+ if(this.(weaponentity).m_weapon==WEP_Null)
FOREACH(Weapons, it != WEP_Null, LAMBDA(
- if(client_hasweapon(this, it, true, false))
+ if(client_hasweapon(this, it, weaponentity, true, false))
{
- PS(this).m_switchweapon = it;
+ this.(weaponentity).m_switchweapon = it;
return;
}
));
combo = false;
if(autocvar_bot_ai_weapon_combo)
- if(PS(this).m_weapon.m_id == this.lastfiredweapon)
+ if(this.(weaponentity).m_weapon.m_id == this.(weaponentity).lastfiredweapon)
if(af > combo_time)
{
combo = true;
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 ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
{
- if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+ if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
continue;
- PS(this).m_switchweapon = Weapons_from(w);
+ this.(weaponentity).m_switchweapon = Weapons_from(w);
return;
}
}
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 ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
{
- if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+ if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
continue;
- PS(this).m_switchweapon = Weapons_from(w);
+ this.(weaponentity).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 ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
{
- if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+ if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
continue;
- PS(this).m_switchweapon = Weapons_from(w);
+ this.(weaponentity).m_switchweapon = Weapons_from(w);
return;
}
}
#include <common/util.qh>
#include <common/weapons/_all.qh>
#include <common/state.qh>
+#include <common/wepent.qh>
void thrown_wep_think(entity this)
{
{
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)
}
// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count
-string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo)
+string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity)
{
float thisammo;
string s;
wep.owner = wep.enemy = own;
wep.flags |= FL_TOSSED;
wep.colormap = own.colormap;
- wep.glowmod = weaponentity_glowmod(info, own, own.clientcolors);
+ wep.glowmod = weaponentity_glowmod(info, own, own.clientcolors, own.(weaponentity));
- W_DropEvent(wr_drop,own,wpn,wep);
+ W_DropEvent(wr_drop,own,wpn,wep,weaponentity);
if(WepSet_FromWeapon(Weapons_from(wpn)) & WEPSET_SUPERWEAPONS)
{
if(doreduce && g_weapon_stay == 2)
{
// if our weapon is loaded, give its load back to the player
- int i = PS(own).m_weapon.m_id;
- if(own.(weapon_load[i]) > 0)
+ int i = own.(weaponentity).m_weapon.m_id;
+ if(own.(weaponentity).(weapon_load[i]) > 0)
{
- own.(ammotype) += own.(weapon_load[i]);
- own.(weapon_load[i]) = -1; // schedule the weapon for reloading
+ own.(ammotype) += own.(weaponentity).(weapon_load[i]);
+ own.(weaponentity).(weapon_load[i]) = -1; // schedule the weapon for reloading
}
wep.(ammotype) = 0;
else if(doreduce)
{
// if our weapon is loaded, give its load back to the player
- int i = PS(own).m_weapon.m_id;
- if(own.(weapon_load[i]) > 0)
+ int i = own.(weaponentity).m_weapon.m_id;
+ if(own.(weaponentity).(weapon_load[i]) > 0)
{
- own.(ammotype) += own.(weapon_load[i]);
- own.(weapon_load[i]) = -1; // schedule the weapon for reloading
+ own.(ammotype) += own.(weaponentity).(weapon_load[i]);
+ own.(weaponentity).(weapon_load[i]) = -1; // schedule the weapon for reloading
}
thisammo = min(own.(ammotype), wep.(ammotype));
bool W_IsWeaponThrowable(entity this, int w)
{
- if (MUTATOR_CALLHOOK(ForbidDropCurrentWeapon, this))
+ if (MUTATOR_CALLHOOK(ForbidDropCurrentWeapon, this, w))
return false;
if (!autocvar_g_pickup_items)
return false;
// toss current weapon
void W_ThrowWeapon(entity this, .entity weaponentity, vector velo, vector delta, float doreduce)
{
- Weapon w = PS(this).m_weapon;
+ Weapon w = this.(weaponentity).m_weapon;
if (w == WEP_Null)
return; // just in case
- if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon, this))
+ if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon, this, this.(weaponentity)))
return;
if(!autocvar_g_weapon_throwable)
return;
if(!(this.weapons & set)) return;
this.weapons &= ~set;
- W_SwitchWeapon_Force(this, w_getbestweapon(this));
- string a = W_ThrowNewWeapon(this, w.m_id, doreduce, this.origin + delta, velo);
+ W_SwitchWeapon_Force(this, w_getbestweapon(this, weaponentity), weaponentity);
+ string a = W_ThrowNewWeapon(this, w.m_id, doreduce, this.origin + delta, velo, weaponentity);
if(!a) return;
Send_Notification(NOTIF_ONE, this, MSG_MULTI, ITEM_WEAPON_DROP, a, w.m_id);
}
-void SpawnThrownWeapon(entity this, vector org, float w)
+void SpawnThrownWeapon(entity this, vector org, float w, .entity weaponentity)
{
- if(this.weapons & WepSet_FromWeapon(PS(this).m_weapon))
- if(W_IsWeaponThrowable(this, PS(this).m_weapon.m_id))
- W_ThrowNewWeapon(this, PS(this).m_weapon.m_id, false, org, randomvec() * 125 + '0 0 200');
+ entity wep = this.(weaponentity).m_weapon;
+
+ if(this.weapons & WepSet_FromWeapon(wep))
+ if(W_IsWeaponThrowable(this, wep.m_id))
+ W_ThrowNewWeapon(this, wep.m_id, false, org, randomvec() * 125 + '0 0 200', weaponentity);
}