Infomessages panel update; Display command names everywhere in the HUD instead of the commands theirselves
* Infomessages panel:
* Allow message wrapping
* Reduce number of messages displayed when spectating / observing by showing 3 less important messages in rotation
* Make command names translatable
* Display command names everywhere in the HUD instead of the commands theirselves
See merge request !342
- 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=9b2513f29762de886296f998ac8725c9
+ - EXPECT=56bf79831a0a35ab6a3a420e26b09945
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
set g_balance_blaster_primary_animtime 0.2
set g_balance_blaster_primary_damage 25
set g_balance_blaster_primary_delay 0
-set g_balance_blaster_primary_edgedamage 17
+set g_balance_blaster_primary_edgedamage 12.5
set g_balance_blaster_primary_force 300
set g_balance_blaster_primary_force_zscale 1.25
set g_balance_blaster_primary_lifetime 5
// {{{ #2: Shotgun
set g_balance_shotgun_primary_ammo 1
set g_balance_shotgun_primary_animtime 0.2
-set g_balance_shotgun_primary_bullets 8
-set g_balance_shotgun_primary_damage 6
+set g_balance_shotgun_primary_bullets 12
+set g_balance_shotgun_primary_damage 4
set g_balance_shotgun_primary_force 15
set g_balance_shotgun_primary_refire 0.75
set g_balance_shotgun_primary_solidpenetration 3.8
set g_balance_shotgun_reload_ammo 0
set g_balance_shotgun_reload_time 2
set g_balance_shotgun_secondary 1
-set g_balance_shotgun_secondary_animtime 1
+set g_balance_shotgun_secondary_animtime 1.15
set g_balance_shotgun_secondary_damage 70
set g_balance_shotgun_secondary_force 200
set g_balance_shotgun_secondary_melee_delay 0.25
set g_balance_shotgun_switchdelay_drop 0.2
set g_balance_shotgun_switchdelay_raise 0.2
set g_balance_shotgun_weaponreplace ""
-set g_balance_shotgun_weaponstart 1
+set g_balance_shotgun_weaponstart 0
set g_balance_shotgun_weaponstartoverride -1
set g_balance_shotgun_weaponthrowable 1
// }}}
set g_balance_machinegun_sustained_spread 0.03
set g_balance_machinegun_switchdelay_drop 0.2
set g_balance_machinegun_switchdelay_raise 0.2
-set g_balance_machinegun_weaponreplace ""
+set g_balance_machinegun_weaponreplace "arc"
set g_balance_machinegun_weaponstart 0
set g_balance_machinegun_weaponstartoverride -1
set g_balance_machinegun_weaponthrowable 1
set g_balance_mortar_bouncestop 0.075
set g_balance_mortar_primary_ammo 2
set g_balance_mortar_primary_animtime 0.3
-set g_balance_mortar_primary_damage 40
+set g_balance_mortar_primary_damage 55
set g_balance_mortar_primary_damageforcescale 0
set g_balance_mortar_primary_edgedamage 25
set g_balance_mortar_primary_force 250
set g_balance_electro_primary_force 200
set g_balance_electro_primary_lifetime 5
set g_balance_electro_primary_midaircombo_explode 1
-set g_balance_electro_primary_midaircombo_interval 0.3
-set g_balance_electro_primary_midaircombo_radius 150
+set g_balance_electro_primary_midaircombo_interval 0.1
+set g_balance_electro_primary_midaircombo_radius 0
set g_balance_electro_primary_radius 100
set g_balance_electro_primary_refire 0.6
set g_balance_electro_primary_speed 2500
set g_balance_electro_secondary_speed 1000
set g_balance_electro_secondary_speed_up 200
set g_balance_electro_secondary_speed_z 0
-set g_balance_electro_secondary_spread 0.04
-set g_balance_electro_secondary_touchexplode 0
+set g_balance_electro_secondary_spread 0
+set g_balance_electro_secondary_stick 1
+set g_balance_electro_secondary_touchexplode 1
set g_balance_electro_switchdelay_drop 0.2
set g_balance_electro_switchdelay_raise 0.2
set g_balance_electro_weaponreplace ""
set g_balance_crylink_primary_animtime 0.3
set g_balance_crylink_primary_bouncedamagefactor 0.5
set g_balance_crylink_primary_bounces 1
-set g_balance_crylink_primary_damage 8
+set g_balance_crylink_primary_damage 12
set g_balance_crylink_primary_edgedamage 6
set g_balance_crylink_primary_force -50
set g_balance_crylink_primary_joindelay 0.1
set g_balance_crylink_primary_joinexplode_force 0
set g_balance_crylink_primary_joinexplode_radius 0
set g_balance_crylink_primary_joinspread 0.2
-set g_balance_crylink_primary_linkexplode 1
+set g_balance_crylink_primary_linkexplode 0
set g_balance_crylink_primary_middle_fadetime 5
set g_balance_crylink_primary_middle_lifetime 5
set g_balance_crylink_primary_other_fadetime 5
set g_balance_crylink_secondary_animtime 0.2
set g_balance_crylink_secondary_bouncedamagefactor 0.5
set g_balance_crylink_secondary_bounces 0
-set g_balance_crylink_secondary_damage 10
+set g_balance_crylink_secondary_damage 35
set g_balance_crylink_secondary_edgedamage 5
-set g_balance_crylink_secondary_force -200
+set g_balance_crylink_secondary_force -300
set g_balance_crylink_secondary_joindelay 0
set g_balance_crylink_secondary_joinexplode 0
set g_balance_crylink_secondary_joinexplode_damage 0
set g_balance_crylink_secondary_other_lifetime 5
set g_balance_crylink_secondary_radius 100
set g_balance_crylink_secondary_refire 0.7
-set g_balance_crylink_secondary_shots 5
+set g_balance_crylink_secondary_shots 1
set g_balance_crylink_secondary_speed 3000
set g_balance_crylink_secondary_spread 0.01
set g_balance_crylink_secondary_spreadtype 1
set g_balance_crylink_weaponthrowable 1
// }}}
// {{{ #8: Vortex
-set g_balance_vortex_charge 1
+set g_balance_vortex_charge 0
set g_balance_vortex_charge_animlimit 0.5
set g_balance_vortex_charge_limit 1
set g_balance_vortex_charge_maxspeed 800
-set g_balance_vortex_charge_mindmg 50
+set g_balance_vortex_charge_mindmg 40
set g_balance_vortex_charge_minspeed 400
set g_balance_vortex_charge_rate 0.6
set g_balance_vortex_charge_rot_pause 0
set g_balance_vortex_charge_velocity_rate 0
set g_balance_vortex_primary_ammo 6
set g_balance_vortex_primary_animtime 0.4
-set g_balance_vortex_primary_damage 80
+set g_balance_vortex_primary_damage 70
set g_balance_vortex_primary_damagefalloff_forcehalflife 0
set g_balance_vortex_primary_damagefalloff_halflife 0
set g_balance_vortex_primary_damagefalloff_maxdist 0
set g_balance_vortex_primary_damagefalloff_mindist 0
-set g_balance_vortex_primary_force 450
+set g_balance_vortex_primary_force 400
set g_balance_vortex_primary_refire 1.5
set g_balance_vortex_reload_ammo 0
set g_balance_vortex_reload_time 2
set g_balance_vortex_secondary_damagefalloff_mindist 0
set g_balance_vortex_secondary_force 0
set g_balance_vortex_secondary_refire 0
-set g_balance_vortex_switchdelay_drop 0.2
-set g_balance_vortex_switchdelay_raise 0.2
+set g_balance_vortex_switchdelay_drop 0.25
+set g_balance_vortex_switchdelay_raise 0.25
set g_balance_vortex_weaponreplace ""
set g_balance_vortex_weaponstart 0
set g_balance_vortex_weaponstartoverride -1
set g_balance_hagar_secondary_load_hold 4
set g_balance_hagar_secondary_load_linkexplode 0
set g_balance_hagar_secondary_load_max 4
-set g_balance_hagar_secondary_load_releasedeath 1
+set g_balance_hagar_secondary_load_releasedeath 0
set g_balance_hagar_secondary_load_speed 0.5
set g_balance_hagar_secondary_load_spread 0.075
set g_balance_hagar_secondary_load_spread_bias 0.5
set g_balance_hagar_secondary_radius 80
set g_balance_hagar_secondary_refire 0.5
set g_balance_hagar_secondary_speed 2000
-set g_balance_hagar_secondary_spread 0.05
+set g_balance_hagar_secondary_spread 0
set g_balance_hagar_switchdelay_drop 0.2
set g_balance_hagar_switchdelay_raise 0.2
set g_balance_hagar_weaponreplace ""
set g_balance_devastator_damageforcescale 1
set g_balance_devastator_detonatedelay 0.02
set g_balance_devastator_edgedamage 40
-set g_balance_devastator_force 450
+set g_balance_devastator_force 400
set g_balance_devastator_guidedelay 0.2
set g_balance_devastator_guidegoal 512
set g_balance_devastator_guiderate 90
set g_balance_devastator_guidestop 0
set g_balance_devastator_health 30
set g_balance_devastator_lifetime 10
-set g_balance_devastator_radius 150
+set g_balance_devastator_radius 110
set g_balance_devastator_refire 1.1
set g_balance_devastator_reload_ammo 0
set g_balance_devastator_reload_time 2
set g_balance_devastator_remote_damage 70
set g_balance_devastator_remote_edgedamage 35
-set g_balance_devastator_remote_force 400
+set g_balance_devastator_remote_force 450
set g_balance_devastator_remote_jump_damage 70
set g_balance_devastator_remote_jump_radius 0
set g_balance_devastator_remote_jump_velocity_z_add 400
set g_balance_devastator_remote_jump_velocity_z_max 1500
set g_balance_devastator_remote_jump_velocity_z_min 400
-set g_balance_devastator_remote_radius 150
-set g_balance_devastator_speed 1200
-set g_balance_devastator_speedaccel 1200
+set g_balance_devastator_remote_radius 110
+set g_balance_devastator_speed 1300
+set g_balance_devastator_speedaccel 1300
set g_balance_devastator_speedstart 1000
set g_balance_devastator_switchdelay_drop 0.2
set g_balance_devastator_switchdelay_raise 0.2
// {{{ #12: Vaporizer
set g_balance_vaporizer_primary_ammo 10
set g_balance_vaporizer_primary_animtime 0.3
+set g_balance_vaporizer_primary_damage 150
set g_balance_vaporizer_primary_refire 1
set g_balance_vaporizer_reload_ammo 0
set g_balance_vaporizer_reload_time 0
// }}}
// {{{ #19: Shockwave (MUTATOR WEAPON)
set g_balance_shockwave_blast_animtime 0.3
-set g_balance_shockwave_blast_damage 20
+set g_balance_shockwave_blast_damage 40
set g_balance_shockwave_blast_distance 1000
set g_balance_shockwave_blast_edgedamage 0
-set g_balance_shockwave_blast_force 200
+set g_balance_shockwave_blast_force 15
set g_balance_shockwave_blast_force_forwardbias 50
-set g_balance_shockwave_blast_force_zscale 2
+set g_balance_shockwave_blast_force_zscale 1
set g_balance_shockwave_blast_jump_damage 20
set g_balance_shockwave_blast_jump_edgedamage 0
-set g_balance_shockwave_blast_jump_force 300
-set g_balance_shockwave_blast_jump_force_velocitybias 0
-set g_balance_shockwave_blast_jump_force_zscale 1.25
+set g_balance_shockwave_blast_jump_force 100
+set g_balance_shockwave_blast_jump_force_velocitybias 1
+set g_balance_shockwave_blast_jump_force_zscale 1
set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5
set g_balance_shockwave_blast_jump_multiplier_distance 0.5
set g_balance_shockwave_blast_jump_multiplier_min 0
set g_balance_shockwave_blast_jump_radius 150
-set g_balance_shockwave_blast_multiplier_accuracy 0.5
-set g_balance_shockwave_blast_multiplier_distance 0.5
+set g_balance_shockwave_blast_multiplier_accuracy 0.45
+set g_balance_shockwave_blast_multiplier_distance 0.2
set g_balance_shockwave_blast_multiplier_min 0
set g_balance_shockwave_blast_refire 0.75
set g_balance_shockwave_blast_splash_damage 15
set g_balance_shockwave_switchdelay_drop 0.2
set g_balance_shockwave_switchdelay_raise 0.2
set g_balance_shockwave_weaponreplace ""
-set g_balance_shockwave_weaponstart 0
+set g_balance_shockwave_weaponstart 1
set g_balance_shockwave_weaponstartoverride -1
set g_balance_shockwave_weaponthrowable 0
// }}}
// {{{ #20: Arc
-set g_balance_arc_beam_ammo 4
-set g_balance_arc_beam_animtime 0.2
+set g_balance_arc_beam_ammo 6
+set g_balance_arc_beam_animtime 0.1
set g_balance_arc_beam_botaimlifetime 0
set g_balance_arc_beam_botaimspeed 0
-set g_balance_arc_beam_damage 115
+set g_balance_arc_beam_damage 100
set g_balance_arc_beam_degreespersegment 1
set g_balance_arc_beam_distancepersegment 0
set g_balance_arc_beam_falloff_halflifedist 0
set g_balance_arc_beam_falloff_maxdist 0
set g_balance_arc_beam_falloff_mindist 0
-set g_balance_arc_beam_force 900
-set g_balance_arc_beam_healing_amax 100
+set g_balance_arc_beam_force 600
+set g_balance_arc_beam_healing_amax 0
set g_balance_arc_beam_healing_aps 50
set g_balance_arc_beam_healing_hmax 150
set g_balance_arc_beam_healing_hps 50
set g_balance_arc_cooldown 2.5
+set g_balance_arc_cooldown_release 0
set g_balance_arc_overheat_max 5
set g_balance_arc_overheat_min 3
-set g_balance_arc_beam_heat 1
+set g_balance_arc_beam_heat 0
set g_balance_arc_burst_heat 5
set g_balance_arc_beam_maxangle 10
set g_balance_arc_beam_nonplayerdamage 80
set g_balance_arc_beam_range 1000
-set g_balance_arc_beam_refire 0.5
+set g_balance_arc_beam_refire 0.25
set g_balance_arc_beam_returnspeed 8
set g_balance_arc_beam_tightness 0.5
+set g_balance_arc_bolt 1
+set g_balance_arc_bolt_ammo 1
+set g_balance_arc_bolt_damage 25
+set g_balance_arc_bolt_damageforcescale 0
+set g_balance_arc_bolt_edgedamage 12.5
+set g_balance_arc_bolt_force 120
+set g_balance_arc_bolt_health 15
+set g_balance_arc_bolt_lifetime 5
+set g_balance_arc_bolt_radius 65
+set g_balance_arc_bolt_refire 0.16667
+set g_balance_arc_bolt_speed 2300
+set g_balance_arc_bolt_spread 0
set g_balance_arc_burst_ammo 15
set g_balance_arc_burst_damage 250
set g_balance_arc_burst_healing_aps 100
set g_balance_electro_secondary_speed_up 200
set g_balance_electro_secondary_speed_z 0
set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
set g_balance_electro_secondary_touchexplode 0
set g_balance_electro_switchdelay_drop 0.15
set g_balance_electro_switchdelay_raise 0.15
set g_balance_electro_secondary_speed_up 200
set g_balance_electro_secondary_speed_z 0
set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
set g_balance_electro_secondary_touchexplode 0
set g_balance_electro_switchdelay_drop 0.2
set g_balance_electro_switchdelay_raise 0.2
set g_balance_electro_secondary_speed_up 200
set g_balance_electro_secondary_speed_z 0
set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
set g_balance_electro_secondary_touchexplode 0
set g_balance_electro_switchdelay_drop 0.2
set g_balance_electro_switchdelay_raise 0.2
set g_balance_electro_secondary_speed_up 200
set g_balance_electro_secondary_speed_z 0
set g_balance_electro_secondary_spread 0.05
+set g_balance_electro_secondary_stick 0
set g_balance_electro_secondary_touchexplode 0
set g_balance_electro_switchdelay_drop 0
set g_balance_electro_switchdelay_raise 0
set g_balance_electro_secondary_speed_up 200
set g_balance_electro_secondary_speed_z 0
set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
set g_balance_electro_secondary_touchexplode 1
set g_balance_electro_switchdelay_drop 0.2
set g_balance_electro_switchdelay_raise 0.2
set g_balance_electro_secondary_speed_up 200
set g_balance_electro_secondary_speed_z 0
set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
set g_balance_electro_secondary_touchexplode 1
set g_balance_electro_switchdelay_drop 0.2
set g_balance_electro_switchdelay_raise 0.2
// {{{ starting gear
set g_balance_health_start 100
-set g_balance_armor_start 50
-set g_start_ammo_shells 20
+set g_balance_armor_start 0
+set g_start_ammo_shells 15
set g_start_ammo_nails 0
set g_start_ammo_rockets 0
set g_start_ammo_cells 0
// {{{ pickup items
set g_pickup_ammo_anyway 1
set g_pickup_weapons_anyway 1
-set g_pickup_shells 20
+set g_pickup_shells 15
set g_pickup_shells_weapon 15
set g_pickup_shells_max 60
set g_pickup_nails 80
// }}}
// {{{ misc
-set g_balance_selfdamagepercent 0.5
+set g_balance_selfdamagepercent 0.65
set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
set g_weaponratefactor 1 "weapon fire rate multiplier"
set g_weapondamagefactor 1 "weapon damage multiplier"
// 7: forward + circle with (1-r)(2-r) falloff
set g_balance_falldamage_deadminspeed 250
set g_balance_falldamage_minspeed 900
-set g_balance_falldamage_factor 0.07
-set g_balance_falldamage_maxdamage 250
+set g_balance_falldamage_factor 0.20
+set g_balance_falldamage_maxdamage 40
set g_balance_damagepush_speedfactor 2.5
set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
setorigin(debugmodel_entity, view_origin);
debugmodel_entity.angles = view_angles;
debugmodel_entity.draw = DrawDebugModel;
+ IL_PUSH(g_drawables, debugmodel_entity);
return;
}
redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0;
}
+int autocvar__teams_available;
void HUD_Mod_CTF(vector pos, vector mySize)
{
vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos;
float fs, fs2, fs3, size1, size2;
vector e1, e2;
+ int nteams = autocvar__teams_available;
+
redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3;
blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3;
yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3;
if (autocvar__hud_configure) {
redflag = 1;
blueflag = 2;
- if (team_count >= 3)
+ if (nteams & BIT(2))
yellowflag = 2;
- if (team_count >= 4)
+ if (nteams & BIT(3))
pinkflag = 3;
ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor?
}
break; \
} \
} MACRO_END
- X(red, myteam != NUM_TEAM_1);
- X(blue, myteam != NUM_TEAM_2);
- X(yellow, myteam != NUM_TEAM_3 && team_count >= 3);
- X(pink, myteam != NUM_TEAM_4 && team_count >= 4);
+ X(red, myteam != NUM_TEAM_1 && (nteams & BIT(0)));
+ X(blue, myteam != NUM_TEAM_2 && (nteams & BIT(1)));
+ X(yellow, myteam != NUM_TEAM_3 && (nteams & BIT(2)));
+ X(pink, myteam != NUM_TEAM_4 && (nteams & BIT(3)));
X(neutral, ctf_oneflag);
#undef X
+ int tcount = 2;
+ if(nteams & BIT(2))
+ tcount = 3;
+ if(nteams & BIT(3))
+ tcount = 4;
+
if (ctf_oneflag) {
// hacky, but these aren't needed
red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null;
fs = fs2 = fs3 = 1;
- } else switch (team_count) {
+ } else switch (tcount) {
default:
case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
#define X(team) MACRO_BEGIN { \
f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \
+ if (team##_icon && ctf_stalemate) \
+ drawpic_aspect_skin(team##flag_pos, "flag_stalemate", flag_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); \
if (team##_icon_prevstatus && f < 1) \
drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \
if (team##_icon) \
draw_teamradar_background(hud_panel_radar_foreground_alpha);
- FOREACH_ENTITY_CLASS("radarlink", true, draw_teamradar_link(it.origin, it.velocity, it.team));
+ IL_EACH(g_radarlinks, true, draw_teamradar_link(it.origin, it.velocity, it.team));
- FOREACH_ENTITY_FLAGS(teamradar_icon, 0xFFFFFF, {
+ IL_EACH(g_radaricons, it.teamradar_icon, {
if ( hud_panel_radar_mouse )
if ( it.health >= 0 )
if ( it.team == myteam+1 || gametype == MAPINFO_TYPE_RACE || !(serverflags & SERVERFLAG_TEAMPLAY) )
this.drawmask = MASK_NORMAL;
//this.move_movetype = MOVETYPE_NOCLIP;
//this.draw = Spawn_Draw;
+ IL_PUSH(g_drawables, this);
}*/
if(autocvar_cl_spawn_point_particles)
{
else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); }
this.draw = Spawn_Draw;
+ if (is_new) IL_PUSH(g_drawables, this);
setpredraw(this, Spawn_PreDraw);
this.fade_start = autocvar_cl_spawn_point_dist_min;
this.fade_end = autocvar_cl_spawn_point_dist_max;
.float eliminated;
.void(entity) draw;
+IntrusiveList g_drawables;
+STATIC_INIT(g_drawables) { g_drawables = IL_NEW(); }
.void(entity) draw2d;
+IntrusiveList g_drawables_2d;
+STATIC_INIT(g_drawables_2d) { g_drawables_2d = IL_NEW(); }
.void(entity) entremove;
float drawframetime;
vector view_origin, view_forward, view_right, view_up;
+IntrusiveList g_radarlinks;
+STATIC_INIT(g_radarlinks) { g_radarlinks = IL_NEW(); }
+IntrusiveList g_radaricons;
+STATIC_INIT(g_radaricons) { g_radaricons = IL_NEW(); }
+
bool button_zoom;
bool spectatorbutton_zoom;
bool button_attack2;
this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
this.classname = "radarlink";
+ if (isnew) IL_PUSH(g_radarlinks, this);
if(sendflags & 1)
{
{
entity e = new_pure(porto);
e.draw = Porto_Draw;
+ IL_PUSH(g_drawables, e);
e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
}
{
case 1: // crosshair_color_per_weapon
{
- if(this != WEP_Null)
+ if(this != WEP_Null && hud == HUD_NORMAL)
{
wcross_color = this.wpcolor;
break;
mousepos = mousepos*0.5 + getmousepos();
*/
- FOREACH_ENTITY_FLOAT(pure_data, false,
- {
- if(it.draw) { it.draw(it); }
- });
+ IL_EACH(g_drawables, it.draw, it.draw(it));
addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);
renderscene();
} else */
// draw 2D entities
- FOREACH_ENTITY_FLOAT(pure_data, false,
- {
- if(it.draw2d) { it.draw2d(it); }
- });
+ IL_EACH(g_drawables_2d, it.draw2d, it.draw2d(it));
Draw_ShowNames_All();
Debug_Draw();
// let's reset the view back to normal for the end
setproperty(VF_MIN, '0 0 0');
setproperty(VF_SIZE, '1 0 0' * w + '0 1 0' * h);
+
+ IL_ENDFRAME();
}
this.entremove = Ent_Wall_Remove;
this.draw = Ent_Wall_Draw;
+ if (isnew) IL_PUSH(g_drawables, this);
setpredraw(this, Ent_Wall_PreDraw);
}
case PROJECTILE_ELECTRO:
// only new engines support sound moving with object
loopsound(this, CH_SHOTS_SINGLE, SND(ELECTRO_FLY), VOL_BASE, ATTEN_NORM);
- this.mins = '0 0 -4';
- this.maxs = '0 0 -4';
+ this.mins = '-4 -4 -4';
+ this.maxs = '4 4 4';
this.move_movetype = MOVETYPE_BOUNCE;
settouch(this, func_null);
this.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor);
this.classname = "csqcprojectile";
this.draw = Projectile_Draw;
+ if (isnew) IL_PUSH(g_drawables, this);
this.entremove = Ent_RemoveProjectile;
}
{
entity e = TRACE_ENT = new_pure(TRACE_ENT);
e.draw2d = Trace_draw2d;
+ IL_PUSH(g_drawables_2d, e);
}
#endif
casing.drawmask = MASK_NORMAL;
casing.draw = Casing_Draw;
+ if (isNew) IL_PUSH(g_drawables, casing);
casing.velocity = casing.velocity + 2 * prandomvec();
casing.avelocity = '0 250 0' + 100 * prandomvec();
casing.move_movetype = MOVETYPE_BOUNCE;
setsize (gib, '-8 -8 -8', '8 8 8');
gib.draw = Gib_Draw;
+ IL_PUSH(g_drawables, gib);
if(destroyontouch)
settouch(gib, Gib_Touch);
else
e.cnt = ReadByte() / 255.0; // actually alpha
e.draw = ModelEffect_Draw;
+ if (isnew) IL_PUSH(g_drawables, e);
if (!isnew) remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then
return true;
void RubbleLimit(string cname, float limit, void(entity) deleteproc)
{
- entity e;
- entity oldest;
- float c;
- float oldesttime;
-
// remove rubble of the same type if it's at the limit
// remove multiple rubble if the limit has been decreased
while (1)
{
- e = findchain(classname, cname);
- if (e == NULL) break;
// walk the list and count the entities, find the oldest
// initialize our search with the first entity
- c = 1;
- oldest = e;
- oldesttime = e.creationtime;
- e = e.chain;
+ int c = 0;
+ entity oldest = NULL;
+ float oldesttime = 0;
// compare to all other matching entities
- while (e)
+ FOREACH_ENTITY_CLASS(cname, true,
{
- c = c + 1;
- if (oldesttime > e.creationtime)
+ ++c;
+ if(!oldest || oldesttime > it.creationtime)
{
- oldesttime = e.creationtime;
- oldest = e;
+ oldest = it;
+ oldesttime = it.creationtime;
}
- e = e.chain;
- }
+ });
// stop if there are less than the limit already
if (c <= limit) break;
const float ST_NEXBALL_GOALS = 1;
const float SP_NEXBALL_GOALS = 4;
const float SP_NEXBALL_FAULTS = 5;
-void nb_ScoreRules(float teams)
+void nb_ScoreRules(int teams)
{
ScoreRules_basics(teams, 0, 0, true);
ScoreInfo_SetLabel_TeamScore( ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY);
EXACTTRIGGER_TOUCH(this, toucher);
- if(nb_teams == 2)
+ if(NumTeams(nb_teams) == 2)
otherteam = OtherTeam(ball.team);
else
otherteam = 0;
else if(this.team == GOAL_FAULT)
{
LogNB("fault", ball.pusher);
- if(nb_teams == 2)
+ if(NumTeams(nb_teams) == 2)
bprint(Team_ColoredFullName(otherteam), " gets a point due to ", pname, "^7's silliness.\n");
else
bprint(Team_ColoredFullName(ball.team), " loses a point due to ", pname, "^7's silliness.\n");
if(ball.team && pscore)
{
- if(nb_teams == 2 && pscore < 0)
+ if(NumTeams(nb_teams) == 2 && pscore < 0)
TeamScore_AddToTeam(otherteam, ST_NEXBALL_GOALS, -pscore);
else
TeamScore_AddToTeam(ball.team, ST_NEXBALL_GOALS, pscore);
e.netname = teamname;
e.cnt = teamcolor;
e.team = e.cnt + 1;
- nb_teams += 1;
+ //nb_teams += 1;
}
void nb_spawnteams()
if(!t_red)
{
nb_spawnteam("Red", e.team-1) ;
+ nb_teams |= BIT(0);
t_red = true;
}
break;
{
nb_spawnteam("Blue", e.team-1) ;
t_blue = true;
+ nb_teams |= BIT(1);
}
break;
case NUM_TEAM_3:
{
nb_spawnteam("Yellow", e.team-1);
t_yellow = true;
+ nb_teams |= BIT(2);
}
break;
case NUM_TEAM_4:
{
nb_spawnteam("Pink", e.team-1) ;
t_pink = true;
+ nb_teams |= BIT(3);
}
break;
}
e.move_time = time + 0.05;
e.drawmask = MASK_NORMAL;
e.draw = ons_generator_ray_draw;
+ IL_PUSH(g_drawables, e);
}
void generator_draw(entity this)
}
ons_ControlPoint_UpdateSprite(l);
}
- l = findchain(classname, "ons_captureshield");
- while(l)
+ FOREACH_ENTITY_CLASS("ons_captureshield", true,
{
- l.team = l.enemy.team;
- l.colormap = l.enemy.colormap;
- l = l.chain;
- }
+ it.team = it.enemy.team;
+ it.colormap = it.enemy.colormap;
+ });
}
void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius)
{
- entity head;
- float t, c;
bool needarmor = false, needweapons = false;
// Needs armor/health?
needarmor = true;
// Needs weapons?
- c = 0;
+ int c = 0;
FOREACH(Weapons, it != WEP_Null, {
if(this.weapons & (it.m_wepset))
if(++c >= 4)
LOG_DEBUG(strcat(this.netname, " needs armor ", ftos(needarmor) , "\n"));
// See what is around
- head = findchainfloat(bot_pickup, true);
- while (head)
+ FOREACH_ENTITY_FLOAT(bot_pickup, true,
{
// gather health and armor only
- if (head.solid)
- if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
- if (vdist(head.origin - org, <, sradius))
+ if (it.solid)
+ if ( ((it.health || it.armorvalue) && needarmor) || (it.weapons && needweapons ) )
+ if (vdist(it.origin - org, <, sradius))
{
- t = head.bot_pickupevalfunc(this, head);
+ int t = it.bot_pickupevalfunc(this, it);
if (t > 0)
- navigation_routerating(this, head, t * ratingscale, 500);
+ navigation_routerating(this, it, t * ratingscale, 500);
}
- head = head.chain;
- }
+ });
}
void havocbot_role_ons_setrole(entity this, int role)
*/
entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist)
{
- entity tmp_entity, closest_target = NULL;
- tmp_entity = findchain(classname, "onslaught_controlpoint");
- while(tmp_entity)
+ entity closest_target = NULL;
+ FOREACH_ENTITY_CLASS("onslaught_controlpoint", true,
{
- if(SAME_TEAM(tmp_entity, this))
- if(tmp_entity.iscaptured)
- if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <=, max_dist))
- if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
- closest_target = tmp_entity;
- tmp_entity = tmp_entity.chain;
- }
- tmp_entity = findchain(classname, "onslaught_generator");
- while(tmp_entity)
+ if(SAME_TEAM(it, this))
+ if(it.iscaptured)
+ if(max_dist <= 0 || vdist(it.origin - pos, <=, max_dist))
+ if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
+ closest_target = it;
+ });
+ FOREACH_ENTITY_CLASS("onslaught_generator", true,
{
- if(SAME_TEAM(tmp_entity, this))
- if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <, max_dist))
- if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
- closest_target = tmp_entity;
- tmp_entity = tmp_entity.chain;
- }
+ if(SAME_TEAM(it, this))
+ if(max_dist <= 0 || vdist(it.origin - pos, <, max_dist))
+ if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
+ closest_target = it;
+ });
return closest_target;
}
*/
entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist)
{
- entity tmp_entity, closest_target = NULL;
+ entity closest_target = NULL;
vector delta;
float smallest_distance = 0, distance;
- tmp_entity = findchain(classname, "onslaught_controlpoint");
- while(tmp_entity)
+ FOREACH_ENTITY_CLASS("onslaught_controlpoint", true,
{
- delta = tmp_entity.origin - pos;
+ delta = it.origin - pos;
delta_z = 0;
distance = vlen(delta);
- if(SAME_TEAM(tmp_entity, this))
- if(tmp_entity.iscaptured)
+ if(SAME_TEAM(it, this))
+ if(it.iscaptured)
if(max_dist <= 0 || distance <= max_dist)
if(closest_target == NULL || distance <= smallest_distance )
{
- closest_target = tmp_entity;
+ closest_target = it;
smallest_distance = distance;
}
-
- tmp_entity = tmp_entity.chain;
- }
- tmp_entity = findchain(classname, "onslaught_generator");
- while(tmp_entity)
+ });
+ FOREACH_ENTITY_CLASS("onslaught_generator", true,
{
- delta = tmp_entity.origin - pos;
+ delta = it.origin - pos;
delta_z = 0;
distance = vlen(delta);
- if(SAME_TEAM(tmp_entity, this))
+ if(SAME_TEAM(it, this))
if(max_dist <= 0 || distance <= max_dist)
if(closest_target == NULL || distance <= smallest_distance )
{
- closest_target = tmp_entity;
+ closest_target = it;
smallest_distance = distance;
}
-
- tmp_entity = tmp_entity.chain;
- }
+ });
return closest_target;
}
*/
int ons_Count_SelfControlPoints(entity this)
{
- entity tmp_entity;
- tmp_entity = findchain(classname, "onslaught_controlpoint");
int n = 0;
- while(tmp_entity)
+ FOREACH_ENTITY_CLASS("onslaught_controlpoint", true,
{
- if(SAME_TEAM(tmp_entity, this))
- if(tmp_entity.iscaptured)
+ if(SAME_TEAM(it, this))
+ if(it.iscaptured)
n++;
- tmp_entity = tmp_entity.chain;
- }
- tmp_entity = findchain(classname, "onslaught_generator");
- while(tmp_entity)
+ });
+ FOREACH_ENTITY_CLASS("onslaught_generator", true,
{
- if(SAME_TEAM(tmp_entity, this))
+ if(SAME_TEAM(it, this))
n++;
- tmp_entity = tmp_entity.chain;
- }
+ });
return n;
}
void ons_ScoreRules()
{
CheckAllowedTeams(NULL);
- ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true);
+ int teams = 0;
+ if(c1 >= 0) teams |= BIT(0);
+ if(c2 >= 0) teams |= BIT(1);
+ if(c3 >= 0) teams |= BIT(2);
+ if(c4 >= 0) teams |= BIT(3);
+ ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY);
ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY);
ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0);
if(!autocvar_g_monsters) { Monster_Remove(this); return false; }
+ if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
+ IL_PUSH(g_monsters, this);
+
if(Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
if(!this.monster_skill)
this.m_group = _group;
this.m_friendlyfire = _friendlyfire;
DamageText_update(this, _origin, _health, _armor, _deathtype);
+ IL_PUSH(g_drawables_2d, this);
}
ENDCLASS(DamageText)
#endif
void Item_ItemsTime_SetTimesForAllPlayers()
{
- FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it)), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it)));
+ FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it) || autocvar_sv_itemstime == 2), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it)));
}
float Item_ItemsTime_UpdateTime(entity e, float t)
{
// client became player on connection skipping putObserverInServer step
if (IS_REAL_CLIENT(player))
- if (warmup_stage)
+ if (warmup_stage || autocvar_sv_itemstime == 2)
Item_ItemsTime_SetTimesForPlayer(player);
}
}
MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn)
{
- if (warmup_stage) return;
+ if (warmup_stage || autocvar_sv_itemstime == 2) return;
entity player = M_ARGV(0, entity);
Item_ItemsTime_ResetTimesForPlayer(player);
{
if (!(
(autocvar_hud_panel_itemstime == 1 && spectatee_status != 0)
- || (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage))
+ || (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage || STAT(ITEMSTIME) == 2))
)) { return; }
}
else
e.orb_radius = e.orb_radius/model_radius*0.6;
e.draw = orb_draw;
+ IL_PUSH(g_drawables, e);
e.health = 255;
e.move_movetype = MOVETYPE_NONE;
e.solid = SOLID_NOT;
W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
if (autocvar_g_casings >= 2) // casing code
+ {
+ makevectors(actor.v_angle); // for some reason, this is lost
SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+ }
int slot = weaponslot(weaponentity);
ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor(actor);
#endif
#ifdef CSQC
-void Ent_WaypointSprite(entity this);
+void Ent_WaypointSprite(entity this, bool isnew);
NET_HANDLE(waypointsprites, bool isnew) {
- Ent_WaypointSprite(this);
+ Ent_WaypointSprite(this, isnew);
return true;
}
}
/** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
-void Ent_WaypointSprite(entity this)
+void Ent_WaypointSprite(entity this, bool isnew)
{
int sendflags = ReadByte();
this.wp_extra = ReadByte();
this.spawntime = time;
this.draw2d = Draw_WaypointSprite;
+ if (isnew) {
+ IL_PUSH(g_drawables_2d, this);
+ IL_PUSH(g_radaricons, this);
+ }
InterpolateOrigin_Undo(this);
this.iflags |= IFLAG_ORIGIN;
case SPRITERULE_SPECTATOR:
if (!(
(autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
- || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
+ || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage || STAT(ITEMSTIME) == 2))
))
return;
spriteimage = this.netname;
{
if (!autocvar_sv_itemstime)
return false;
- if (!warmup_stage && IS_PLAYER(view))
+ if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2)
return false;
}
else if (this.team && this.rule == SPRITERULE_DEFAULT)
void Ent_RemoveWaypointSprite(entity this);
-void Ent_WaypointSprite(entity this);
+void Ent_WaypointSprite(entity this, bool isnew);
void WaypointSprite_Load_Frames(string ext);
}
SET_ONGROUND(this);
- this.move_groundentity = trace_ent;
+ this.groundentity = trace_ent;
}
}
else if(stepheight)
this.origin = trace_endpos;
if(trace_fraction < 1)
- if(this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || (this.move_groundentity != trace_ent)))
+ if(this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || (this.groundentity != trace_ent)))
_Movetype_Impact(this, trace_ent);
return trace_fraction;
.float bouncefactor;
#endif
-#ifdef SVQC
-.bool move_qcphysics;
-#endif
-
void set_movetype(entity this, int mt);
.float move_movetype;
.entity aiment;
.vector punchangle;
-.entity move_groundentity; // FIXME add move_groundnetworkentity?
+.entity groundentity; // FIXME add move_groundnetworkentity?
.float move_suspendedinair;
.float move_didgravity;
const int FL_ITEM = 256;
const int FL_ONGROUND = 512;
+#elif defined(SVQC)
+const int MOVETYPE_ANGLENOCLIP = 1;
+const int MOVETYPE_ANGLECLIP = 2;
#endif
const int MOVETYPE_FAKEPUSH = 13;
{
UNSET_ONGROUND(this);
}
- else if (!this.move_groundentity)
+ else if (!this.groundentity)
{
return;
}
- else if (this.move_suspendedinair && wasfreed(this.move_groundentity))
+ else if (this.move_suspendedinair && wasfreed(this.groundentity))
{
- this.move_groundentity = NULL;
+ this.groundentity = NULL;
return;
}
}
if (trace_plane_normal.z > 0.7 && d < bstop && d > -bstop)
{
SET_ONGROUND(this);
- this.move_groundentity = trace_ent;
+ this.groundentity = trace_ent;
this.velocity = '0 0 0';
this.avelocity = '0 0 0';
}
if (trace_plane_normal.z > 0.7)
{
SET_ONGROUND(this);
- this.move_groundentity = trace_ent;
+ this.groundentity = trace_ent;
if (trace_ent.solid == SOLID_BSP)
this.move_suspendedinair = true;
this.velocity = '0 0 0';
vector originalvelocity = this.velocity;
// originalmove_clip = clip;
int originalflags = this.flags;
- entity originalmove_groundentity = this.move_groundentity;
+ entity originalmove_groundentity = this.groundentity;
// if move didn't block on a step, return
if (clip & 2)
this.velocity = originalvelocity;
// clip = originalmove_clip;
this.flags = originalflags;
- this.move_groundentity = originalmove_groundentity;
+ this.groundentity = originalmove_groundentity;
// now try to unstick if needed
// clip = SV_TryUnstick (ent, oldvel);
return;
this.origin = originalorigin;
this.velocity = originalvelocity;
this.flags = originalflags;
- this.move_groundentity = originalmove_groundentity;
+ this.groundentity = originalmove_groundentity;
}
_Movetype_CheckVelocity(this);
do_crouch = false;
if(STAT(FROZEN, this))
do_crouch = false;
- if((activeweapon == WEP_SHOCKWAVE || activeweapon == WEP_SHOTGUN) && viewmodel.animstate_startframe == viewmodel.anim_fire2_x && time < viewmodel.weapon_nextthink)
+ 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)
}
}
- if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0)
- pmove_waterjumptime = 0;
+ if (IS_ONGROUND(this) || this.velocity.z <= 0 || PHYS_WATERJUMP_TIME(this) <= 0)
+ PHYS_WATERJUMP_TIME(this) = 0;
#endif
}
#ifdef SVQC
PHYS_TELEPORT_TIME(this) = time + 2; // safety net
#elif defined(CSQC)
- pmove_waterjumptime = 2;
+ PHYS_WATERJUMP_TIME(this) = 2;
#endif
}
}
#endif
}
-void PM_fly(entity this, float maxspd_mod)
-{
- // noclipping or flying
- UNSET_ONGROUND(this);
-
- this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
- makevectors(this.v_angle);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y
- + '0 0 1' * this.movement.z;
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
- if(pmove_waterjumptime <= 0)
-#endif
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-}
-
-void PM_swim(entity this, float maxspd_mod)
-{
- // swimming
- UNSET_ONGROUND(this);
-
- float jump = PHYS_INPUT_BUTTON_JUMP(this);
- // water jump only in certain situations
- // this mimics quakeworld code
- if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc)
- {
- vector yawangles = '0 1 0' * this.v_angle.y;
- makevectors(yawangles);
- vector forward = v_forward;
- vector spot = this.origin + 24 * forward;
- spot_z += 8;
- traceline(spot, spot, MOVE_NOMONSTERS, this);
- if (trace_startsolid)
- {
- spot_z += 24;
- traceline(spot, spot, MOVE_NOMONSTERS, this);
- if (!trace_startsolid)
- {
- this.velocity = forward * 50;
- this.velocity_z = 310;
- UNSET_ONGROUND(this);
- SET_JUMP_HELD(this);
- }
- }
- }
- makevectors(this.v_angle);
- float wishdown = this.movement.z;
- if(PHYS_INPUT_BUTTON_CROUCH(this))
- wishdown = -PHYS_MAXSPEED(this);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y
- + '0 0 1' * wishdown;
- if(this.viewloc)
- wishvel.z = -160; // drift anyway
- else if (wishvel == '0 0 0')
- wishvel = '0 0 -60'; // drift towards bottom
-
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7;
-
-// if (pmove_waterjumptime <= 0) // TODO: use
- {
- // water friction
- float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this);
- f = min(max(0, f), 1);
- this.velocity *= f;
-
- f = wishspeed - this.velocity * wishdir;
- if (f > 0)
- {
- float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f);
- this.velocity += accelspeed * wishdir;
- }
-
- // holding jump button swims upward slowly
- if (jump && !this.viewloc)
- {
-#if 0
- if (this.watertype & CONTENT_LAVA)
- this.velocity_z = 50;
- else if (this.watertype & CONTENT_SLIME)
- this.velocity_z = 80;
- else
- {
- if (IS_NEXUIZ_DERIVED(gamemode))
-#endif
- if(this.waterlevel >= WATERLEVEL_SUBMERGED)
- this.velocity_z = PHYS_MAXSPEED(this);
- else
- this.velocity_z = 200;
-#if 0
- else
- this.velocity_z = 100;
- }
-#endif
- }
- }
- if(this.viewloc)
- {
- const float addspeed = wishspeed - this.velocity * wishdir;
- if (addspeed > 0)
- {
- const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
- this.velocity += accelspeed * wishdir;
- }
- }
- else
- {
- // water acceleration
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
- }
-}
-
.vector oldmovement;
-void PM_ladder(entity this, float maxspd_mod)
-{
- // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
- UNSET_ONGROUND(this);
-
- float g;
- g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH;
- if (PHYS_ENTGRAVITY(this))
- g *= PHYS_ENTGRAVITY(this);
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- {
- g *= 0.5;
- this.velocity_z += g;
- }
-
- this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
- makevectors(this.v_angle);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement_x
- + v_right * this.movement_y
- + '0 0 1' * this.movement_z;
- if(this.viewloc)
- wishvel.z = this.oldmovement.x;
- this.velocity_z += g;
- if (this.ladder_entity.classname == "func_water")
- {
- float f = vlen(wishvel);
- if (f > this.ladder_entity.speed)
- wishvel *= (this.ladder_entity.speed / f);
-
- this.watertype = this.ladder_entity.skin;
- f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
- if ((this.origin_z + this.view_ofs_z) < f)
- this.waterlevel = WATERLEVEL_SUBMERGED;
- else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f)
- this.waterlevel = WATERLEVEL_SWIMMING;
- else if ((this.origin_z + this.mins_z + 1) < f)
- this.waterlevel = WATERLEVEL_WETFEET;
- else
- {
- this.waterlevel = WATERLEVEL_NONE;
- this.watertype = CONTENT_EMPTY;
- }
- }
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
- if(pmove_waterjumptime <= 0)
-#endif
- // water acceleration
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
-}
void PM_jetpack(entity this, float maxspd_mod)
{
}
}
-void PM_walk(entity this, float maxspd_mod)
-{
- if (!WAS_ONGROUND(this))
- {
-#ifdef SVQC
- if (autocvar_speedmeter)
- LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
-#endif
- if (this.lastground < time - 0.3)
- this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
-#ifdef SVQC
- if (this.jumppadcount > 1)
- LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
- this.jumppadcount = 0;
-#endif
- }
-
- // walking
- makevectors(this.v_angle.y * '0 1 0');
- const vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y;
- // acceleration
- const vector wishdir = normalize(wishvel);
- float wishspeed = vlen(wishvel);
- wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod);
- if (IS_DUCKED(this)) wishspeed *= 0.5;
-
- // apply edge friction
- const float f2 = vlen2(vec2(this.velocity));
- if (f2 > 0)
- {
- trace_dphitq3surfaceflags = 0;
- tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
- // TODO: apply edge friction
- // apply ground friction
- const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
- ? PHYS_FRICTION_SLICK(this)
- : PHYS_FRICTION(this);
-
- float f = sqrt(f2);
- f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
- f = max(0, f);
- this.velocity *= f;
- /*
- Mathematical analysis time!
-
- Our goal is to invert this mess.
-
- For the two cases we get:
- v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
- = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
- v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
- and
- v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-
- These cases would be chosen ONLY if:
- v0 < PHYS_STOPSPEED(this)
- v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
- v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- and, respectively:
- v0 >= PHYS_STOPSPEED(this)
- v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
- v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- */
- }
- const float addspeed = wishspeed - this.velocity * wishdir;
- if (addspeed > 0)
- {
- const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
- this.velocity += accelspeed * wishdir;
- }
-}
-
-void PM_air(entity this, float buttons_prev, float maxspd_mod)
-{
- makevectors(this.v_angle.y * '0 1 0');
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y;
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = vlen(wishvel);
-
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
- if(pmove_waterjumptime <= 0)
-#endif
- {
- float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1);
-
- // apply air speed limit
- float airaccelqw = PHYS_AIRACCEL_QW(this);
- float wishspeed0 = wishspeed;
- wishspeed = min(wishspeed, maxairspd);
- if (IS_DUCKED(this))
- wishspeed *= 0.5;
- float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1);
-
- float accelerating = (this.velocity * wishdir > 0);
- float wishspeed2 = wishspeed;
-
- // CPM: air control
- if (PHYS_AIRSTOPACCELERATE(this))
- {
- vector curdir = normalize(vec2(this.velocity));
- airaccel += (PHYS_AIRSTOPACCELERATE(this)*maxspd_mod - airaccel) * max(0, -(curdir * wishdir));
- }
- // note that for straight forward jumping:
- // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
- // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
- // -->
- // dv/dt = accel * maxspeed (when slow)
- // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
- // log dv/dt = logaccel + logmaxspeed (when slow)
- // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
- float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
- if (PHYS_MAXAIRSTRAFESPEED(this))
- wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod));
- if (PHYS_AIRSTRAFEACCELERATE(this))
- airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod);
- if (PHYS_AIRSTRAFEACCEL_QW(this))
- airaccelqw =
- (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
- *
- (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
- // !CPM
-
- if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0)
- PM_AirAccelerate(this, wishdir, wishspeed2);
- else {
- float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0;
- PM_Accelerate(this, 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);
- }
-}
-
// used for calculating airshots
bool IsFlying(entity this)
{
return true;
}
-void PM_Main(entity this)
-{
- int buttons = PHYS_INPUT_BUTTON_MASK(this);
-#ifdef CSQC
- this.items = STAT(ITEMS);
-
- this.movement = PHYS_INPUT_MOVEVALUES(this);
-
- this.spectatorspeed = STAT(SPECTATORSPEED);
-
- this.team = myteam + 1; // is this correct?
- if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
- UNSET_JUMP_HELD(this); // canjump = true
- pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
-
- PM_ClientMovement_UpdateStatus(this);
-#endif
-
- this.oldmovement = this.movement;
-
-
-#ifdef SVQC
- WarpZone_PlayerPhysics_FixVAngle(this);
-#endif
- float maxspeed_mod = 1;
- maxspeed_mod *= PHYS_HIGHSPEED(this);
-
-#ifdef SVQC
- Physics_UpdateStats(this, maxspeed_mod);
-
- if (this.PlayerPhysplug)
- if (this.PlayerPhysplug(this))
- return;
-#elif defined(CSQC)
- if(hud != HUD_NORMAL)
- return; // no vehicle prediction (yet)
-#endif
-
-#ifdef SVQC
- anticheat_physics(this);
-#endif
-
- if (PM_check_specialcommand(this, buttons))
- return;
-#ifdef SVQC
- if (sv_maxidle > 0)
- {
- if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old)
- this.parm_idlesince = time;
- }
-#endif
- int buttons_prev = this.buttons_old;
- this.buttons_old = buttons;
- this.movement_old = this.movement;
- this.v_angle_old = this.v_angle;
-
- PM_check_nickspam(this);
-
- PM_check_punch(this);
-#ifdef SVQC
- if (IS_BOT_CLIENT(this))
- {
- if (playerdemo_read(this))
- return;
- bot_think(this);
- }
-#endif
-
-#ifdef SVQC
- if (IS_PLAYER(this))
- {
- const bool allowed_to_move = (time >= game_starttime);
- if (!allowed_to_move)
- {
- this.velocity = '0 0 0';
- set_movetype(this, MOVETYPE_NONE);
- this.disableclientprediction = 2;
- }
- else if (this.disableclientprediction == 2)
- {
- if (this.move_movetype == MOVETYPE_NONE)
- set_movetype(this, MOVETYPE_WALK);
- this.disableclientprediction = 0;
- }
- }
-#endif
-
-#ifdef SVQC
- if (this.move_movetype == MOVETYPE_NONE)
- return;
-
- // when we get here, disableclientprediction cannot be 2
- this.disableclientprediction = (this.move_qcphysics) ? -1 : 0;
-#endif
-
- viewloc_PlayerPhysics(this);
-
- PM_check_frozen(this);
-
- PM_check_blocked(this);
-
- maxspeed_mod = 1;
-
- if (this.in_swamp)
- maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate");
-
- // conveyors: first fix velocity
- if (this.conveyor.state)
- this.velocity -= this.conveyor.movedir;
-
- MUTATOR_CALLHOOK(PlayerPhysics, this);
-
- if (!IS_PLAYER(this))
- {
-#ifdef SVQC
- maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
- if (!this.spectatorspeed)
- this.spectatorspeed = maxspeed_mod;
- if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229))
- {
- if (this.lastclassname != STR_PLAYER)
- {
- if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209))
- this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
- else if (this.impulse == 11)
- this.spectatorspeed = maxspeed_mod;
- else if (this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229))
- this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
- else if (this.impulse >= 1 && this.impulse <= 9)
- this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
- } // otherwise just clear
- this.impulse = 0;
- }
-#endif
- maxspeed_mod = this.spectatorspeed;
- }
-#ifdef SVQC
-
- float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
- if(this.speed != spd)
- {
- this.speed = spd;
- string temps = ftos(spd);
- stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
- }
-
- if(this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min)
- {
- this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
- stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
- }
- if(this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max)
- {
- this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
- stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
- }
-#endif
-
- if(IS_DEAD(this))
- {
- // handle water here
- vector midpoint = ((this.absmin + this.absmax) * 0.5);
- if(pointcontents(midpoint) == CONTENT_WATER)
- {
- this.velocity = this.velocity * 0.5;
-
- // do we want this?
- //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
- //{ this.velocity_z = 70; }
- }
- goto end;
- }
-
-#ifdef SVQC
- if (!this.fixangle)
- this.angles = '0 1 0' * this.v_angle.y;
-#endif
-
- if (IS_PLAYER(this) && IS_ONGROUND(this))
- {
- PM_check_hitground(this);
- PM_Footsteps(this);
- }
-
-#ifdef SVQC
- if(IsFlying(this))
- this.wasFlying = 1;
-#endif
-
- if (IS_PLAYER(this))
- CheckPlayerJump(this);
-
- if (this.flags & FL_WATERJUMP)
- {
- this.velocity_x = this.movedir.x;
- this.velocity_y = this.movedir.y;
- if (this.waterlevel == WATERLEVEL_NONE
- #ifdef CSQC
- || pmove_waterjumptime <= 0
- #elif defined(SVQC)
- || time > PHYS_TELEPORT_TIME(this)
- #endif
- )
- {
- this.flags &= ~FL_WATERJUMP;
- #ifdef CSQC
- pmove_waterjumptime = 0;
- #elif defined(CSQC)
- PHYS_TELEPORT_TIME(this) = 0;
- #endif
- }
- }
-
- else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod))
- { }
-
-#ifdef SVQC
- else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#elif defined(CSQC)
- else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#endif
- PM_fly(this, maxspeed_mod);
-
- else if (this.waterlevel >= WATERLEVEL_SWIMMING)
- PM_swim(this, maxspeed_mod);
-
- else if (time < this.ladder_time)
- PM_ladder(this, maxspeed_mod);
-
- else if (ITEMS_STAT(this) & IT_USING_JETPACK)
- PM_jetpack(this, maxspeed_mod);
-
- else if (IS_ONGROUND(this))
- PM_walk(this, maxspeed_mod);
-
- else
- PM_air(this, buttons_prev, maxspeed_mod);
-
-LABEL(end)
- if (IS_ONGROUND(this))
- this.lastground = time;
-
- // conveyors: then break velocity again
- if(this.conveyor.state)
- this.velocity += this.conveyor.movedir;
-
- this.lastflags = this.flags;
-
- this.lastclassname = this.classname;
-}
+void sys_phys_update(entity this, float dt);
#if defined(SVQC)
void SV_PlayerPhysics(entity this)
#elif defined(CSQC)
void CSQC_ClientMovement_PlayerMove_Frame(entity this)
#endif
{
- PM_Main(this);
+ sys_phys_update(this, PHYS_INPUT_TIMELENGTH);
#ifdef SVQC
this.pm_frametime = frametime;
#define ITEMS_STAT(s) ((s).items)
.float teleport_time;
+#define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
+
+.float waterjump_time;
+#define PHYS_WATERJUMP_TIME(s) ((s).waterjump_time)
#ifdef CSQC
+ #define PHYS_FIXANGLE(s) ('0 0 0')
+ #define PHYS_MOVETYPE(s) ((s).move_movetype)
+
string autocvar_cl_jumpspeedcap_min;
string autocvar_cl_jumpspeedcap_max;
- noref float pmove_waterjumptime;
-
const int FL_WATERJUMP = 2048; // player jumping out of water
const int FL_JUMPRELEASED = 4096; // for jump debouncing
#define PHYS_GRAVITY(s) STAT(MOVEVARS_GRAVITY, s)
- #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
-
#define TICRATE ticrate
#define PHYS_INPUT_ANGLES(s) input_angles
#elif defined(SVQC)
+ #define PHYS_FIXANGLE(s) ((s).fixangle)
+ #define PHYS_MOVETYPE(s) ((s).movetype)
+
bool Physics_Valid(string thecvar);
.float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW);
.string jumpspeedcap_min;
.string jumpspeedcap_max;
- #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
-
#define PHYS_GRAVITY(s) autocvar_sv_gravity
#define TICRATE sys_frametime
REGISTER_STAT(CTF_FLAGSTATUS, int)
REGISTER_STAT(ENTRAP_ORB, float)
REGISTER_STAT(ENTRAP_ORB_ALPHA, float)
+REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime)
#ifdef SVQC
int autocvar_g_multijump;
{
this.drawmask = MASK_NORMAL;
this.move_movetype = MOVETYPE_TOSS;
+ if (isnew) IL_PUSH(g_drawables, this);
this.draw = ItemDraw;
this.solid = SOLID_TRIGGER;
//this.flags |= FL_ITEM;
void conveyor_init(entity this)
{
this.draw = conveyor_draw;
+ IL_PUSH(g_drawables, this);
this.drawmask = MASK_NORMAL;
this.move_movetype = MOVETYPE_NONE;
this.move_movetype = MOVETYPE_PUSH;
this.drawmask = MASK_NORMAL;
this.draw = plat_draw;
+ if (isnew) IL_PUSH(g_drawables, this);
this.use = plat_use;
this.entremove = trigger_remove_generic;
setsize(this, this.mins, this.maxs);
this.solid = SOLID_NOT;
this.draw = Draw_PointParticles;
+ if (isnew) IL_PUSH(g_drawables, this);
this.entremove = Ent_PointParticles_Remove;
}
#endif
setorigin(this, this.origin);
setsize(this, this.mins, this.maxs);
this.solid = SOLID_NOT;
+ if (isnew) IL_PUSH(g_drawables, this);
if(this.impulse)
this.draw = Draw_Rain;
else
this.move_movetype = MOVETYPE_PUSH;
this.drawmask = MASK_NORMAL;
this.draw = train_draw;
+ if (isnew) IL_PUSH(g_drawables, this);
this.entremove = trigger_remove_generic;
if(set_platmovetype(this, this.platmovetype))
InterpolateOrigin_Note(this);
this.draw = Draw_Laser;
+ if (isnew) IL_PUSH(g_drawables, this);
}
#endif
if (toucher.flags & FL_PROJECTILE)
{
toucher.angles = vectoangles (toucher.velocity);
+ toucher.com_phys_gravity_factor = 1;
switch(toucher.move_movetype)
{
case MOVETYPE_FLY:
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;
}
- e = findchainfloat(railgunhit, 1);
- while (e) {
- e.railgunhit = 0;
- e = e.chain;
- }
-
+ FOREACH_ENTITY_FLOAT(railgunhit, 1,
+ {
+ it.railgunhit = 0;
+ });
}
}
NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew)
{
+ int sf = ReadByte();
+
int axh_id = bound(0, ReadByte(), MAX_AXH);
- entity axh = AuxiliaryXhair[axh_id];
+ entity axh = AuxiliaryXhair[axh_id];
- if(axh == NULL || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
+ if(axh == NULL || wasfreed(axh))
{
- axh = spawn();
+ axh = new(auxiliary_crosshair);
axh.draw2d = func_null;
axh.drawmask = MASK_NORMAL;
axh.axh_drawflag = DRAWFLAG_ADDITIVE;
axh.axh_image = vCROSS_HINT;
axh.alpha = 1;
AuxiliaryXhair[axh_id] = axh;
+ IL_PUSH(g_drawables_2d, axh);
+ }
+
+ if(sf & 2)
+ {
+ axh.origin_x = ReadCoord();
+ axh.origin_y = ReadCoord();
+ axh.origin_z = ReadCoord();
+ }
+
+ if(sf & 4)
+ {
+ axh.colormod_x = ReadByte() / 255;
+ axh.colormod_y = ReadByte() / 255;
+ axh.colormod_z = ReadByte() / 255;
}
- axh.origin_x = ReadCoord();
- axh.origin_y = ReadCoord();
- axh.origin_z = ReadCoord();
- axh.colormod_x = ReadByte() / 255;
- axh.colormod_y = ReadByte() / 255;
- axh.colormod_z = ReadByte() / 255;
axh.cnt = time;
axh.draw2d = AuxiliaryXhair_Draw2D;
return true;
return = true;
// hud_id == 0 means we exited a vehicle, so stop alarm sound/s
- if(hud_id == 0)
+ // note: HUD_NORMAL is set to 0 currently too, but we'll check both just in case
+ if(hud_id == 0 || hud_id == HUD_NORMAL)
{
sound(this, CH_TRIGGER_SINGLE, SND_Null, VOL_BASEVOICE, ATTEN_NONE);
sound(this, CH_PAIN_SINGLE, SND_Null, VOL_BASEVOICE, ATTEN_NONE);
+
+ for(int i = 0; i < MAX_AXH; ++i)
+ {
+ entity axh = AuxiliaryXhair[i];
+
+ if(axh != NULL && !wasfreed(axh))
+ {
+ AuxiliaryXhair[i] = NULL;
+ remove(axh);
+ }
+ }
return;
}
axh.axh_image = vCROSS_HINT;
axh.alpha = 1;
AuxiliaryXhair[i] = axh;
+ IL_PUSH(g_drawables_2d, axh);
}
if(hud_id == HUD_BUMBLEBEE_GUN)
bool SendAuxiliaryXhair(entity this, entity to, int sf)
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
+ WriteByte(MSG_ENTITY, sf);
WriteByte(MSG_ENTITY, this.cnt);
- WriteCoord(MSG_ENTITY, this.origin_x);
- WriteCoord(MSG_ENTITY, this.origin_y);
- WriteCoord(MSG_ENTITY, this.origin_z);
+ if(sf & 2)
+ {
+ WriteCoord(MSG_ENTITY, this.origin_x);
+ WriteCoord(MSG_ENTITY, this.origin_y);
+ WriteCoord(MSG_ENTITY, this.origin_z);
+ }
- WriteByte(MSG_ENTITY, rint(this.colormod_x * 255));
- WriteByte(MSG_ENTITY, rint(this.colormod_y * 255));
- WriteByte(MSG_ENTITY, rint(this.colormod_z * 255));
+ if(sf & 4)
+ {
+ WriteByte(MSG_ENTITY, rint(this.colormod_x * 255));
+ WriteByte(MSG_ENTITY, rint(this.colormod_y * 255));
+ WriteByte(MSG_ENTITY, rint(this.colormod_z * 255));
+ }
return true;
}
+.vector axh_prevorigin;
+.vector axh_prevcolors;
+
void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, int axh_id)
{
if(!IS_REAL_CLIENT(own))
if(axh == NULL || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist? Mario: because of sloppy code like this)
{
- axh = spawn();
- axh.cnt = axh_id;
- axh.drawonlytoclient = own;
- axh.owner = own;
+ axh = new(auxiliary_xhair);
+ axh.cnt = axh_id;
+ axh.drawonlytoclient = own;
+ axh.owner = own;
Net_LinkEntity(axh, false, 0, SendAuxiliaryXhair);
}
- setorigin(axh, loc);
- axh.colormod = clr;
- axh.SendFlags = 0x01;
- own.(AuxiliaryXhair[axh_id]) = axh;
+ if(loc != axh.axh_prevorigin)
+ {
+ setorigin(axh, loc);
+ axh.SendFlags |= 2;
+ }
+
+ if(clr != axh.axh_prevcolors)
+ {
+ axh.colormod = clr;
+ axh.SendFlags |= 4;
+ }
+
+ own.(AuxiliaryXhair[axh_id]) = axh; // set it anyway...?
}
void CSQCVehicleSetup(entity own, int vehicle_id)
this.lip = particleeffectnum(EFFECT_BUMBLEBEE_HEAL_IMPACT);
this.draw = bumble_raygun_draw;
+ if (isnew) IL_PUSH(g_drawables, this);
}
void raptor_flare_think(entity this)
{
this.nextthink = time + 0.1;
- entity _missile = findchainentity(enemy, this.owner);
- while(_missile)
+ FOREACH_ENTITY_ENT(enemy, this.owner,
{
- if(_missile.flags & FL_PROJECTILE)
- if(vdist(this.origin - _missile.origin, <, autocvar_g_vehicle_raptor_flare_range))
+ if(it.flags & FL_PROJECTILE)
+ if(vdist(this.origin - it.origin, <, autocvar_g_vehicle_raptor_flare_range))
if(random() > autocvar_g_vehicle_raptor_flare_chase)
- _missile.enemy = this;
- _missile = _missile.chain;
- }
+ it.enemy = this;
+ });
if(this.tur_impacttime < time)
remove(this);
sfrag.solid = SOLID_CORPSE;
sfrag.draw = RaptorCBShellfragDraw;
+ IL_PUSH(g_drawables, sfrag);
sfrag.velocity = _vel;
sfrag.avelocity = prandomvec() * vlen(sfrag.velocity);
void spiderbot_guide_release(entity this)
{
- entity rkt;
- rkt = findchainentity(realowner, this.owner);
- if(!rkt)
- return;
-
- crosshair_trace(this.owner);
- while(rkt)
+ FOREACH_ENTITY_ENT(realowner, this.owner,
{
- if(getthink(rkt) == spiderbot_rocket_guided)
+ if(i == 0) // something exists, let's trace!
+ crosshair_trace(this.owner);
+
+ if(getthink(it) == spiderbot_rocket_guided)
{
- rkt.pos1 = trace_endpos;
- setthink(rkt, spiderbot_rocket_unguided);
+ it.pos1 = trace_endpos;
+ setthink(it, spiderbot_rocket_unguided);
}
- rkt = rkt.chain;
- }
+ });
}
float spiberbot_calcartillery_flighttime;
const int WEP_FLAG_RELOADABLE = 0x80; // can has reload
const int WEP_FLAG_SUPERWEAPON = 0x100; // powerup timer
const int WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
+const int WEP_TYPE_MELEE_PRI = 0x400; // primary attack is melee swing (for animation)
+const int WEP_TYPE_MELEE_SEC = 0x800; // secondary attack is melee swing (for animation)
// variables:
string weaponorder_byid;
// set other main attributes of the beam
this.draw = Draw_ArcBeam;
+ IL_PUSH(g_drawables, this);
this.entremove = Remove_ArcBeam;
this.move_time = time;
loopsound(this, CH_SHOTS_SINGLE, SND(ARC_LOOP), VOL_BASE, ATTEN_NORM);
if(skill >= 2) // skill 0 and 1 bots won't detonate rockets!
{
// decide whether to detonate rockets
- entity targetlist, targ;
- float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+ float edgedamage, coredamage, edgeradius, recipricoledgeradius;
float selfdamage, teamdamage, enemydamage;
edgedamage = WEP_CVAR(devastator, edgedamage);
coredamage = WEP_CVAR(devastator, damage);
selfdamage = 0;
teamdamage = 0;
enemydamage = 0;
- targetlist = findchainfloat(bot_attack, true);
FOREACH_ENTITY_ENT(realowner, actor,
{
if(it.classname != "rocket") continue;
- targ = targetlist;
- while(targ)
+ entity rocket = it;
+ FOREACH_ENTITY_FLOAT(bot_attack, true,
{
- d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - it.origin);
- d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
- // count potential damage according to type of target
- if(targ == actor)
- selfdamage = selfdamage + d;
- else if(targ.team == actor.team && teamplay)
- teamdamage = teamdamage + d;
- else if(bot_shouldattack(actor, targ))
- enemydamage = enemydamage + d;
- targ = targ.chain;
- }
+ float d = vlen(it.origin + (it.mins + it.maxs) * 0.5 - rocket.origin);
+ d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+ // count potential damage according to type of target
+ if(it == actor)
+ selfdamage = selfdamage + d;
+ else if(SAME_TEAM(it, actor))
+ teamdamage = teamdamage + d;
+ else if(bot_shouldattack(actor, it))
+ enemydamage = enemydamage + d;
+ });
});
float desirabledamage;
desirabledamage = enemydamage;
if(teamplay && actor.team)
desirabledamage = desirabledamage - teamdamage;
+ makevectors(actor.v_angle);
FOREACH_ENTITY_ENT(realowner, actor,
{
if(it.classname != "rocket") continue;
- makevectors(it.v_angle);
- targ = targetlist;
if(skill > 9) // normal players only do this for the target they are tracking
{
- targ = targetlist;
- while(targ)
- {
- if(
- (v_forward * normalize(it.origin - targ.origin)< 0.1)
- && desirabledamage > 0.1*coredamage
- ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
- targ = targ.chain;
- }
- }
- else
- {
- float distance; distance= bound(300,vlen(actor.origin-actor.enemy.origin),30000);
+ entity rocket = it;
+ FOREACH_ENTITY_FLOAT(bot_attack, true,
+ {
+ if((v_forward * normalize(rocket.origin - it.origin) < 0.1)
+ && desirabledamage > 0.1 * coredamage
+ ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+ });
+ }
+ else
+ {
//As the distance gets larger, a correct detonation gets near imposible
//Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player
- if(v_forward * normalize(it.origin - actor.enemy.origin)< 0.1)
- if(IS_PLAYER(actor.enemy))
- if(desirabledamage >= 0.1*coredamage)
- if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
- PHYS_INPUT_BUTTON_ATCK2(actor) = true;
- // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
- }
+ if((v_forward * normalize(it.origin - actor.enemy.origin) < 0.1)
+ && IS_PLAYER(actor.enemy)
+ && (desirabledamage >= 0.1 * coredamage)
+ )
+ {
+ float distance = bound(300, vlen(actor.origin - actor.enemy.origin), 30000);
+ if(random() / distance * 300 > frametime * bound(0, (10 - skill) * 0.2, 1))
+ PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+ }
+ }
});
// if we would be doing at X percent of the core damage, detonate it
// but don't fire a new shot at the same time!
P(class, prefix, speed_up, float, SEC) \
P(class, prefix, speed_z, float, SEC) \
P(class, prefix, spread, float, BOTH) \
+ P(class, prefix, stick, float, SEC) \
P(class, prefix, switchdelay_drop, float, NONE) \
P(class, prefix, switchdelay_raise, float, NONE) \
P(class, prefix, touchexplode, float, SEC) \
this.event_damage = func_null;
this.takedamage = DAMAGE_NO;
- if(this.move_movetype == MOVETYPE_BOUNCE)
+ if(this.move_movetype == MOVETYPE_BOUNCE || this.classname == "electro_orb") // TODO: classname is more reliable anyway?
{
RadiusDamage(
this,
W_Electro_Explode(this, toucher);
}
+
+void sys_phys_update_single(entity this);
+
void W_Electro_Bolt_Think(entity this)
{
+ // sys_phys_update_single(this);
if(time >= this.ltime)
{
this.use(this, NULL, NULL);
{ this.nextthink = min(time + WEP_CVAR_PRI(electro, midaircombo_interval), this.ltime); }
}
else { this.nextthink = this.ltime; }
+ // this.nextthink = time;
}
void W_Electro_Attack_Bolt(Weapon thiswep, entity actor)
proj.projectiledeathtype = WEP_ELECTRO.m_id;
setorigin(proj, w_shotorg);
+ // if (IS_CSQC)
set_movetype(proj, MOVETYPE_FLY);
W_SetupProjVelocity_PRI(proj, electro);
proj.angles = vectoangles(proj.velocity);
CSQCProjectile(proj, true, PROJECTILE_ELECTRO_BEAM, true);
MUTATOR_CALLHOOK(EditProjectile, actor, proj);
+ // proj.com_phys_pos = proj.origin;
+ // proj.com_phys_vel = proj.velocity;
+}
+
+void W_Electro_Orb_Stick(entity this, entity to)
+{
+ entity newproj = spawn();
+ newproj.classname = this.classname;
+
+ newproj.bot_dodge = this.bot_dodge;
+ newproj.bot_dodgerating = this.bot_dodgerating;
+
+ newproj.owner = this.owner;
+ newproj.realowner = this.realowner;
+ setsize(newproj, this.mins, this.maxs);
+ setorigin(newproj, this.origin);
+ setmodel(newproj, MDL_PROJECTILE_ELECTRO);
+ newproj.angles = vectoangles(-trace_plane_normal); // face against the surface
+
+ newproj.takedamage = this.takedamage;
+ newproj.damageforcescale = this.damageforcescale;
+ newproj.health = this.health;
+ newproj.event_damage = this.event_damage;
+ newproj.spawnshieldtime = this.spawnshieldtime;
+ newproj.damagedbycontents = true;
+
+ set_movetype(newproj, MOVETYPE_NONE); // lock the orb in place
+ newproj.projectiledeathtype = this.projectiledeathtype;
+
+ settouch(newproj, func_null);
+ setthink(newproj, getthink(this));
+ newproj.nextthink = this.nextthink;
+ newproj.use = this.use;
+ newproj.flags = this.flags;
+
+ remove(this);
+
+ if(to)
+ SetMovetypeFollow(this, to);
}
void W_Electro_Orb_Touch(entity this, entity toucher)
//UpdateCSQCProjectile(this);
spamsound(this, CH_SHOTS, SND(ELECTRO_BOUNCE), VOL_BASE, ATTEN_NORM);
this.projectiledeathtype |= HITTYPE_BOUNCE;
+
+ if(WEP_CVAR_SEC(electro, stick))
+ W_Electro_Orb_Stick(this, toucher);
}
}
W_SetupShot_ProjectileSize(
actor,
- '0 0 -4',
- '0 0 -4',
+ '-4 -4 -4',
+ '4 4 4',
false,
2,
SND_ELECTRO_FIRE2,
set_movetype(proj, MOVETYPE_BOUNCE);
W_SetupProjVelocity_UP_SEC(proj, electro);
settouch(proj, W_Electro_Orb_Touch);
- setsize(proj, '0 0 -4', '0 0 -4');
+ setsize(proj, '-4 -4 -4', '4 4 4');
proj.takedamage = DAMAGE_YES;
proj.damageforcescale = WEP_CVAR_SEC(electro, damageforcescale);
proj.health = WEP_CVAR_SEC(electro, health);
if(bIsNew || !this.teleport_time)
{
this.draw = Draw_GrapplingHook;
+ IL_PUSH(g_drawables, this);
this.entremove = Remove_GrapplingHook;
switch(this.HookType)
// casing code
if(autocvar_g_casings >= 2)
+ {
+ makevectors(actor.v_angle); // for some reason, this is lost
SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+ }
if(actor.misc_bulletcounter == 1)
W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo));
W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
if(autocvar_g_casings >= 2) // casing code
+ {
+ makevectors(actor.v_angle); // for some reason, this is lost
SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+ }
int slot = weaponslot(weaponentity);
ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
if(autocvar_g_casings >= 2) // casing code
+ {
+ makevectors(actor.v_angle); // for some reason, this is lost
SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+ }
actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
if(actor.misc_bulletcounter == 0)
if(skill >= 2) // skill 0 and 1 bots won't detonate mines!
{
// decide whether to detonate mines
- entity targetlist, targ;
- float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+ float edgedamage, coredamage, edgeradius, recipricoledgeradius;
float selfdamage, teamdamage, enemydamage;
edgedamage = WEP_CVAR(minelayer, edgedamage);
coredamage = WEP_CVAR(minelayer, damage);
selfdamage = 0;
teamdamage = 0;
enemydamage = 0;
- targetlist = findchainfloat(bot_attack, true);
- entity mine = find(NULL, classname, "mine");
- while(mine)
+
+ FOREACH_ENTITY_ENT(realowner, actor,
{
- if(mine.realowner != actor)
- {
- mine = find(mine, classname, "mine");
- continue;
- }
- targ = targetlist;
- while(targ)
- {
- d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin);
- d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+ if(it.classname != "mine") continue;
+
+ entity mine = it;
+ FOREACH_ENTITY_FLOAT(bot_attack, true,
+ {
+ float d = vlen(it.origin + (it.mins + it.maxs) * 0.5 - mine.origin);
+ d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
// count potential damage according to type of target
- if(targ == actor)
+ if(it == actor)
selfdamage = selfdamage + d;
- else if(targ.team == actor.team && teamplay)
+ else if(SAME_TEAM(it, actor))
teamdamage = teamdamage + d;
- else if(bot_shouldattack(actor, targ))
+ else if(bot_shouldattack(actor, it))
enemydamage = enemydamage + d;
- targ = targ.chain;
- }
- mine = find(mine, classname, "mine");
- }
+ });
+ });
+
float desirabledamage;
desirabledamage = enemydamage;
if(time > actor.invincible_finished && time > actor.spawnshieldtime)
if(teamplay && actor.team)
desirabledamage = desirabledamage - teamdamage;
- mine = find(NULL, classname, "mine");
- while(mine)
+ makevectors(actor.v_angle);
+ FOREACH_ENTITY_ENT(realowner, actor,
{
- if(mine.realowner != actor)
- {
- mine = find(mine, classname, "mine");
- continue;
- }
- makevectors(mine.v_angle);
- targ = targetlist;
+ if(it.classname != "mine") continue;
+
if(skill > 9) // normal players only do this for the target they are tracking
{
- targ = targetlist;
- while(targ)
- {
- if(
- (v_forward * normalize(mine.origin - targ.origin)< 0.1)
- && desirabledamage > 0.1*coredamage
- ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
- targ = targ.chain;
- }
- }else{
- float distance; distance= bound(300,vlen(actor.origin-actor.enemy.origin),30000);
+ entity mine = it;
+ FOREACH_ENTITY_FLOAT(bot_attack, true,
+ {
+ if((v_forward * normalize(mine.origin - it.origin) < 0.1)
+ && desirabledamage > 0.1 * coredamage
+ ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+ });
+ }
+ else
+ {
//As the distance gets larger, a correct detonation gets near imposible
//Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player
- if(v_forward * normalize(mine.origin - actor.enemy.origin)< 0.1)
- if(IS_PLAYER(actor.enemy))
- if(desirabledamage >= 0.1*coredamage)
- if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
- PHYS_INPUT_BUTTON_ATCK2(actor) = true;
- // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
- }
-
- mine = find(mine, classname, "mine");
- }
+ if((v_forward * normalize(it.origin - actor.enemy.origin) < 0.1)
+ && IS_PLAYER(actor.enemy)
+ && (desirabledamage >= 0.1 * coredamage)
+ )
+ {
+ float distance = bound(300, vlen(actor.origin - actor.enemy.origin), 30000);
+ if(random() / distance * 300 > frametime * bound(0, (10 - skill) * 0.2, 1))
+ PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+ }
+ }
+ });
+
// if we would be doing at X percent of the core damage, detonate it
// but don't fire a new shot at the same time!
if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
fireBullet(actor, w_shotorg, w_shotdir, pSpread, pSolidPenetration, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE));
if(autocvar_g_casings >= 2)
+ {
+ makevectors(actor.v_angle); // for some reason, this is lost
SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+ }
}
void W_Rifle_Attack(entity actor)
CLASS(Shockwave, Weapon)
/* ammotype */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none)
/* impulse */ ATTRIB(Shockwave, impulse, int, 2)
-/* flags */ ATTRIB(Shockwave, spawnflags, int, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_FLAG_MUTATORBLOCKED);
+/* flags */ ATTRIB(Shockwave, spawnflags, int, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_TYPE_MELEE_SEC);
/* rating */ ATTRIB(Shockwave, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW);
/* color */ ATTRIB(Shockwave, wpcolor, vector, '0.5 0.25 0');
/* modelname */ ATTRIB(Shockwave, mdl, string, "shotgun");
if(lag)
{
FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_takeback(it, CS(it), time - lag));
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
- if (it != actor)
- antilag_takeback(it, it, time - lag);
+ IL_EACH(g_monsters, it != actor,
+ {
+ antilag_takeback(it, it, time - lag);
});
}
if(lag)
{
FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_restore(it, CS(it)));
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
- if (it != actor)
- antilag_restore(it, it);
+ IL_EACH(g_monsters, it != actor,
+ {
+ antilag_restore(it, it);
});
}
}
entity shockwave;
shockwave = spawn();
shockwave.draw = Draw_Shockwave;
+ IL_PUSH(g_drawables, shockwave);
shockwave.sw_shotorg_x = ReadCoord(); shockwave.sw_shotorg_y = ReadCoord(); shockwave.sw_shotorg_z = ReadCoord();
shockwave.sw_shotdir_x = ReadCoord(); shockwave.sw_shotdir_y = ReadCoord(); shockwave.sw_shotdir_z = ReadCoord();
CLASS(Shotgun, Weapon)
/* ammotype */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells)
/* impulse */ ATTRIB(Shotgun, impulse, int, 2)
-/* flags */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
+/* flags */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_TYPE_MELEE_SEC);
/* rating */ ATTRIB(Shotgun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW);
/* color */ ATTRIB(Shotgun, wpcolor, vector, '0.5 0.25 0');
/* modelname */ ATTRIB(Shotgun, mdl, string, "shotgun");
// casing code
if(autocvar_g_casings >= 1)
+ {
+ makevectors(actor.v_angle); // for some reason, this is lost
for(sc = 0;sc < WEP_CVAR_PRI(shotgun, ammo);sc = sc + 1)
SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, actor);
+ }
// muzzle flash for 1st person view
flash = spawn();
setthink(this, SUB_Remove);
this.nextthink = time + bound(0, autocvar_cl_vaporizerbeam_lifetime, 10);
this.draw = VaporizerBeam_Draw;
+ if (isNew) IL_PUSH(g_drawables, this);
this.drawmask = MASK_NORMAL;
this.vorg1_x = ReadCoord(); this.vorg1_y = ReadCoord(); this.vorg1_z = ReadCoord();
# SVQC
+Main loop:
+* SV_Physics()
+ * StartFrame()
+ * if (force_retouch)
+ * foreach entity:
+ * .touch()
+ * foreach client:
+ * PlayerPreThink()
+ * .think()
+ * PlayerPostThink()
+ * foreach nonclient:
+ * .think()
+ * EndFrame()
+
+
```
.entity clientcamera;
int() _buf_create = #440;
#define buf_create _buf_create
+bool(entity ent) wasfreed = #353;
+
#pragma noref 0
#endif
#undef error
#undef movetogoal
#undef objerror
+#undef remove
#undef walkmove
#ifdef MENUQC
#define error builtin_error
#define movetogoal builtin_movetogoal
#define objerror builtin_objerror
+#define remove builtin_remove
#define walkmove builtin_walkmove
### listening
entity listener = new_pure(someListener);
- listener.evt_$event = void(entity this) { code; };
- subscribe(listener, $event);
+ subscribe(listener, $event, void(entity this) { code; });
-/** Components always interpolate from the previous state */
-#define COMPONENT(com) \
- void com_##com##_interpolate(entity it, float a); \
- .bool com_##com
-
-#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body)
-
-
-#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T
-
-#define emit(T, ...) \
- MACRO_BEGIN \
- FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \
- MACRO_END
-
-#define subscribe(listener, T) \
- MACRO_BEGIN \
- listener.evt_##T##_listener = true; \
- MACRO_END
-
-
-/**
- * framelimit 0 is no limit, interpolation does not apply
- * framerate below minfps will result in less than 100% speed
- */
-#define SYSTEM(sys, frameLimit, minfps) \
- void sys_##sys##_update(entity this, float dt); \
- float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
- float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))
-
-#define SYSTEM_UPDATE(sys) \
- MACRO_BEGIN \
- static float t = 0; \
- float dt = autocvar_xon_sys_##sys##_dt; \
- float minfps = autocvar_xon_sys_##sys##_minfps; \
- static float accumulator = 0; \
- float a = 0; \
- if (dt) { \
- accumulator += min(frametime, 1 / (minfps)); \
- } else { \
- accumulator += frametime; \
- dt = accumulator; \
- a = 1; \
- } \
- while (accumulator >= dt) \
- { \
- time = t; \
- FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \
- t += dt; \
- accumulator -= dt; \
- } \
- if (!a) a = accumulator / dt; \
- FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \
- MACRO_END
-
+#include "_lib.qh"
#include "_mod.inc"
#include "components/_mod.inc"
--- /dev/null
+#pragma once
+
+/** Components always interpolate from the previous state */
+#define COMPONENT(com) \
+ void com_##com##_interpolate(entity it, float a); \
+ .bool com_##com
+
+#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body)
+
+
+#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T
+
+#define emit(T, ...) \
+ MACRO_BEGIN \
+ FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \
+ MACRO_END
+
+#define subscribe(listener, T, fn) \
+ MACRO_BEGIN \
+ listener.evt_##T = (fn); \
+ listener.evt_##T##_listener = true; \
+ MACRO_END
+
+
+/**
+ * framelimit 0 is no limit, interpolation does not apply
+ * framerate below minfps will result in less than 100% speed
+ */
+#define SYSTEM(sys, frameLimit, minfps) \
+ void sys_##sys##_update(entity this, float dt); \
+ float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
+ float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))
+
+#define SYSTEM_UPDATE(sys) \
+ MACRO_BEGIN \
+ static float t = 0; \
+ float dt = autocvar_xon_sys_##sys##_dt; \
+ float minfps = autocvar_xon_sys_##sys##_minfps; \
+ static float accumulator = 0; \
+ float a = 0; \
+ if (dt) { \
+ accumulator += min(frametime, 1 / (minfps)); \
+ } else { \
+ accumulator += frametime; \
+ dt = accumulator; \
+ a = 1; \
+ } \
+ while (accumulator >= dt) \
+ { \
+ time = t; \
+ FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \
+ t += dt; \
+ accumulator -= dt; \
+ } \
+ if (!a) a = accumulator / dt; \
+ FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \
+ MACRO_END
#pragma once
COMPONENT(in);
-.vector com_in_move;
+.vector com_in_move, com_in_move_prev;
.vector com_in_angles;
.bool com_in_jump;
+.bool com_in_crouch;
.vector com_phys_pos, com_phys_pos_prev;
.vector com_phys_ang, com_phys_ang_prev;
.vector com_phys_vel;
+.float com_phys_vel_max;
+.float com_phys_vel_max_air;
+.float com_phys_vel_max_air_strafe;
.vector com_phys_acc;
+.float com_phys_acc_rate;
+.float com_phys_acc_rate_air;
+.float com_phys_acc_rate_air_strafe;
+.float com_phys_acc_rate_air_stop;
+.float com_phys_friction;
+
+.vector com_phys_gravity;
+.float com_phys_gravity_factor;
+// TODO: remove
+.bool com_phys_ground;
+.bool com_phys_air;
+.bool com_phys_ladder;
+.bool com_phys_vel_2d;
+.bool com_phys_water;
+.bool com_phys_friction_air;
+.bool move_qcphysics;
// generated file; do not modify
+#include <ecs/events/physics.qc>
// generated file; do not modify
+#include <ecs/events/physics.qh>
--- /dev/null
+#include "physics.qh"
--- /dev/null
+#pragma once
+
+EVENT(phys_land, (entity this));
// generated file; do not modify
+#include <ecs/systems/input.qc>
#include <ecs/systems/physics.qc>
+#ifdef CSQC
+ #include <ecs/systems/cl_physics.qc>
+#endif
+#ifdef SVQC
+ #include <ecs/systems/sv_physics.qc>
+#endif
// generated file; do not modify
+#include <ecs/systems/input.qh>
#include <ecs/systems/physics.qh>
--- /dev/null
+#include "physics.qh"
+
+void sys_phys_fix(entity this, float dt)
+{
+ this.team = myteam + 1; // is this correct?
+ PHYS_WATERJUMP_TIME(this) -= dt;
+ this.oldmovement = this.movement;
+ this.movement = PHYS_INPUT_MOVEVALUES(this);
+ this.items = STAT(ITEMS, this);
+ this.spectatorspeed = STAT(SPECTATORSPEED, this);
+ if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
+ UNSET_JUMP_HELD(this); // canjump = true
+ PM_ClientMovement_UpdateStatus(this);
+}
+
+bool sys_phys_override(entity this)
+{
+ // no vehicle prediction
+ return hud != HUD_NORMAL;
+}
+
+void sys_phys_monitor(entity this) {}
+
+void sys_phys_ai(entity this) {}
+
+void sys_phys_pregame_hold(entity this) {}
+
+void sys_phys_spectator_control(entity this) {}
+
+void sys_phys_fixspeed(entity this, float maxspeed_mod) {}
--- /dev/null
+#include "input.qh"
+
+void sys_in_update(entity this, float dt)
+{
+ this.com_in_jump = PHYS_INPUT_BUTTON_JUMP(this);
+ this.com_in_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
+}
--- /dev/null
+#pragma once
+
+SYSTEM(in, 30, 10);
#include "physics.qh"
+#include "input.qh"
+
+.int disableclientprediction;
+
+void sys_phys_simulate(entity this, float dt);
+void sys_phys_simulate_simple(entity this, float dt);
void sys_phys_update(entity this, float dt)
{
- PM_Main(this);
+ if (!IS_CLIENT(this)) {
+ sys_phys_simulate_simple(this, dt);
+ return;
+ }
+ sys_in_update(this, dt);
+
+ sys_phys_fix(this, dt);
+ if (sys_phys_override(this)) { return; } sys_phys_monitor(this);
+
+ this.buttons_old = PHYS_INPUT_BUTTON_MASK(this);
+ this.movement_old = this.movement;
+ this.v_angle_old = this.v_angle;
+
+ sys_phys_ai(this);
+
+ sys_phys_pregame_hold(this);
+
+ if (IS_SVQC) {
+ if (PHYS_MOVETYPE(this) == MOVETYPE_NONE) { return; }
+ // when we get here, disableclientprediction cannot be 2
+ this.disableclientprediction = (this.move_qcphysics) ? -1 : 0;
+ }
+
+ viewloc_PlayerPhysics(this);
+
+ PM_check_frozen(this);
+
+ PM_check_blocked(this);
+
+ float maxspeed_mod = (!this.in_swamp) ? 1 : this.swamp_slowdown; // cvar("g_balance_swamp_moverate");
+
+// conveyors: first fix velocity
+ if (this.conveyor.state) { this.velocity -= this.conveyor.movedir; }
+ MUTATOR_CALLHOOK(PlayerPhysics, this);
+
+ if (!IS_PLAYER(this)) {
+ sys_phys_spectator_control(this);
+ maxspeed_mod = this.spectatorspeed;
+ }
+ sys_phys_fixspeed(this, maxspeed_mod);
+
+ if (IS_DEAD(this)) {
+ // handle water here
+ vector midpoint = ((this.absmin + this.absmax) * 0.5);
+ if (pointcontents(midpoint) == CONTENT_WATER) {
+ this.velocity = this.velocity * 0.5;
+
+ // do we want this?
+ // if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
+ // { this.velocity_z = 70; }
+ }
+ goto end;
+ }
+
+ if (IS_SVQC && !PHYS_FIXANGLE(this)) { this.angles = '0 1 0' * this.v_angle.y; }
+ if (IS_PLAYER(this)) {
+ if (IS_ONGROUND(this)) {
+ PM_check_hitground(this);
+ PM_Footsteps(this);
+ } else if (IsFlying(this)) {
+ this.wasFlying = true;
+ }
+ CheckPlayerJump(this);
+ }
+
+ if (this.flags & FL_WATERJUMP) {
+ this.velocity_x = this.movedir.x;
+ this.velocity_y = this.movedir.y;
+ if (this.waterlevel == WATERLEVEL_NONE
+ || time > PHYS_TELEPORT_TIME(this)
+ || PHYS_WATERJUMP_TIME(this) <= 0
+ ) {
+ this.flags &= ~FL_WATERJUMP;
+ PHYS_TELEPORT_TIME(this) = 0;
+ PHYS_WATERJUMP_TIME(this) = 0;
+ }
+ } else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod)) {
+ // handled
+ } else if (PHYS_MOVETYPE(this) == MOVETYPE_NOCLIP
+ || PHYS_MOVETYPE(this) == MOVETYPE_FLY
+ || PHYS_MOVETYPE(this) == MOVETYPE_FLY_WORLDONLY
+ || MUTATOR_CALLHOOK(IsFlying, this)) {
+ this.com_phys_friction = PHYS_FRICTION(this);
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+ this.com_phys_friction_air = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_friction_air = false;
+ } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+ this.com_phys_water = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_water = false;
+ } else if (time < this.ladder_time) {
+ this.com_phys_friction = PHYS_FRICTION(this);
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+ this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt;
+ if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); }
+ this.com_phys_ladder = true;
+ this.com_phys_friction_air = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_friction_air = false;
+ 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);
+ } else if (IS_ONGROUND(this)) {
+ if (!WAS_ONGROUND(this)) {
+ emit(phys_land, this);
+ if (this.lastground < time - 0.3) {
+ this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
+ }
+ }
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt;
+ if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); }
+ this.com_phys_ground = true;
+ this.com_phys_vel_2d = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_vel_2d = false;
+ this.com_phys_ground = false;
+ this.com_phys_gravity = '0 0 0';
+ } else {
+ this.com_phys_acc_rate_air = PHYS_AIRACCELERATE(this) * min(maxspeed_mod, 1);
+ this.com_phys_acc_rate_air_stop = PHYS_AIRSTOPACCELERATE(this) * maxspeed_mod;
+ this.com_phys_acc_rate_air_strafe = PHYS_AIRSTRAFEACCELERATE(this) * maxspeed_mod;
+ this.com_phys_vel_max_air_strafe = PHYS_MAXAIRSTRAFESPEED(this) * maxspeed_mod;
+ this.com_phys_vel_max_air = PHYS_MAXAIRSPEED(this) * maxspeed_mod;
+ this.com_phys_vel_max = PHYS_MAXAIRSPEED(this) * min(maxspeed_mod, 1);
+ this.com_phys_air = true;
+ this.com_phys_vel_2d = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_vel_2d = false;
+ this.com_phys_air = false;
+ }
+
+ LABEL(end)
+ if (IS_ONGROUND(this)) { this.lastground = time; }
+// conveyors: then break velocity again
+ if (this.conveyor.state) { this.velocity += this.conveyor.movedir; }
+ this.lastflags = this.flags;
+
+ this.lastclassname = this.classname;
+}
+
+/** for players */
+void sys_phys_simulate(entity this, float dt)
+{
+ const vector g = -this.com_phys_gravity;
+ const bool jump = this.com_in_jump;
+
+ if (!this.com_phys_ground && !this.com_phys_air) {
+ // noclipping
+ // flying
+ // on a spawnfunc_func_ladder
+ // swimming in spawnfunc_func_water
+ // swimming
+ UNSET_ONGROUND(this);
+
+ if (this.com_phys_friction_air) {
+ this.velocity_z += g.z / 2;
+ this.velocity = this.velocity * (1 - dt * this.com_phys_friction);
+ this.velocity_z += g.z / 2;
+ }
+ }
+
+ if (this.com_phys_water) {
+ // water jump only in certain situations
+ // this mimics quakeworld code
+ if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc) {
+ vector yawangles = '0 1 0' * this.v_angle.y;
+ makevectors(yawangles);
+ vector forward = v_forward;
+ vector spot = this.origin + 24 * forward;
+ spot_z += 8;
+ traceline(spot, spot, MOVE_NOMONSTERS, this);
+ if (trace_startsolid) {
+ spot_z += 24;
+ traceline(spot, spot, MOVE_NOMONSTERS, this);
+ if (!trace_startsolid) {
+ this.velocity = forward * 50;
+ this.velocity_z = 310;
+ UNSET_ONGROUND(this);
+ SET_JUMP_HELD(this);
+ }
+ }
+ }
+ }
+ makevectors(vmul(this.v_angle, (this.com_phys_vel_2d ? '0 1 0' : '1 1 1')));
+ // wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+ vector wishvel = v_forward * this.movement.x
+ + v_right * this.movement.y
+ + '0 0 1' * this.movement.z * (this.com_phys_vel_2d ? 0 : 1);
+ if (this.com_phys_water) {
+ if (PHYS_INPUT_BUTTON_CROUCH(this)) {
+ wishvel.z = -PHYS_MAXSPEED(this);
+ }
+ if (this.viewloc) {
+ wishvel.z = -160; // drift anyway
+ } else if (wishvel == '0 0 0') {
+ wishvel = '0 0 -60'; // drift towards bottom
+ }
+ }
+ if (this.com_phys_ladder) {
+ if (this.viewloc) {
+ wishvel.z = this.oldmovement.x;
+ }
+ if (this.ladder_entity.classname == "func_water") {
+ float f = vlen(wishvel);
+ if (f > this.ladder_entity.speed) {
+ wishvel *= (this.ladder_entity.speed / f);
+ }
+
+ this.watertype = this.ladder_entity.skin;
+ f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
+ if ((this.origin_z + this.view_ofs_z) < f) {
+ this.waterlevel = WATERLEVEL_SUBMERGED;
+ } else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f) {
+ this.waterlevel = WATERLEVEL_SWIMMING;
+ } else if ((this.origin_z + this.mins_z + 1) < f) {
+ this.waterlevel = WATERLEVEL_WETFEET;
+ } else {
+ this.waterlevel = WATERLEVEL_NONE;
+ this.watertype = CONTENT_EMPTY;
+ }
+ }
+ }
+ // acceleration
+ const vector wishdir = normalize(wishvel);
+ float wishspeed = min(vlen(wishvel), this.com_phys_vel_max);
+
+ if (this.com_phys_air) {
+ if ((IS_SVQC && time >= PHYS_TELEPORT_TIME(this))
+ || (IS_CSQC && PHYS_WATERJUMP_TIME(this) <= 0)) {
+ // apply air speed limit
+ float airaccelqw = PHYS_AIRACCEL_QW(this);
+ float wishspeed0 = wishspeed;
+ const float maxairspd = this.com_phys_vel_max;
+ wishspeed = min(wishspeed, maxairspd);
+ if (IS_DUCKED(this)) {
+ wishspeed *= 0.5;
+ }
+ float airaccel = this.com_phys_acc_rate_air;
+
+ float accelerating = (this.velocity * wishdir > 0);
+ float wishspeed2 = wishspeed;
+
+ // CPM: air control
+ if (PHYS_AIRSTOPACCELERATE(this)) {
+ vector curdir = normalize(vec2(this.velocity));
+ 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;
+ // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
+ // -->
+ // dv/dt = accel * maxspeed (when slow)
+ // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
+ // log dv/dt = logaccel + logmaxspeed (when slow)
+ // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
+ float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
+ if (PHYS_MAXAIRSTRAFESPEED(this)) {
+ wishspeed =
+ min(wishspeed,
+ GeomLerp(this.com_phys_vel_max_air, strafity, this.com_phys_vel_max_air_strafe));
+ }
+ if (PHYS_AIRSTRAFEACCELERATE(this)) {
+ airaccel = GeomLerp(airaccel, strafity, this.com_phys_acc_rate_air_strafe);
+ }
+ if (PHYS_AIRSTRAFEACCEL_QW(this)) {
+ airaccelqw =
+ (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
+ *
+ (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
+ }
+ // !CPM
+
+ if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0) {
+ PM_AirAccelerate(this, wishdir, wishspeed2);
+ } else {
+ float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0;
+ PM_Accelerate(this, 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);
+ }
+ }
+ } else {
+ if (this.com_phys_ground && IS_DUCKED(this)) { wishspeed *= 0.5; }
+ if (this.com_phys_water) {
+ wishspeed *= 0.7;
+
+ // if (PHYS_WATERJUMP_TIME(this) <= 0) // TODO: use
+ {
+ // water friction
+ float f = 1 - dt * PHYS_FRICTION(this);
+ f = min(max(0, f), 1);
+ this.velocity *= f;
+
+ f = wishspeed - this.velocity * wishdir;
+ if (f > 0) {
+ float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, f);
+ this.velocity += accelspeed * wishdir;
+ }
+
+ // holding jump button swims upward slowly
+ if (jump && !this.viewloc) {
+ // was:
+ // lava: 50
+ // slime: 80
+ // water: 100
+ // idea: double those
+ this.velocity_z = 200;
+ if (this.waterlevel >= WATERLEVEL_SUBMERGED) {
+ this.velocity_z = PHYS_MAXSPEED(this) * 0.7;
+ }
+ }
+ }
+ if (this.viewloc) {
+ const float addspeed = wishspeed - this.velocity * wishdir;
+ if (addspeed > 0) {
+ const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed);
+ this.velocity += accelspeed * wishdir;
+ }
+ } else {
+ // water acceleration
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
+ }
+ return;
+ }
+ if (this.com_phys_ground) {
+ // apply edge friction
+ const float f2 = vlen2(vec2(this.velocity));
+ if (f2 > 0) {
+ trace_dphitq3surfaceflags = 0;
+ tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+ // TODO: apply edge friction
+ // apply ground friction
+ const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+ ? PHYS_FRICTION_SLICK(this)
+ : PHYS_FRICTION(this);
+
+ float f = sqrt(f2);
+ f = 1 - dt * realfriction
+ * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
+ f = max(0, f);
+ this.velocity *= f;
+ /*
+ Mathematical analysis time!
+
+ Our goal is to invert this mess.
+
+ For the two cases we get:
+ v = v0 * (1 - dt * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
+ = v0 - dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+ v0 = v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+ and
+ v = v0 * (1 - dt * PHYS_FRICTION(this))
+ v0 = v / (1 - dt * PHYS_FRICTION(this))
+
+ These cases would be chosen ONLY if:
+ v0 < PHYS_STOPSPEED(this)
+ v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
+ v < PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this))
+ and, respectively:
+ v0 >= PHYS_STOPSPEED(this)
+ v / (1 - dt * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
+ v >= PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this))
+ */
+ }
+ const float addspeed = wishspeed - this.velocity * wishdir;
+ if (addspeed > 0) {
+ const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed);
+ this.velocity += accelspeed * wishdir;
+ }
+ return;
+ }
+
+ 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);
+ }
+ }
+}
+
+.entity groundentity;
+/** for other entities */
+void sys_phys_simulate_simple(entity this, float dt)
+{
+ vector mn = this.mins;
+ vector mx = this.maxs;
+
+ vector g = '0 0 0';
+ if (this.com_phys_gravity_factor && !g) g = '0 0 -1' * PHYS_GRAVITY(NULL);
+
+ vector acc = this.com_phys_acc;
+ vector vel = this.com_phys_vel;
+ vector pos = this.com_phys_pos;
+
+ // SV_Physics_Toss
+
+ vel += g * dt;
+
+ this.angles += dt * this.avelocity;
+ float movetime = dt;
+ for (int i = 0; i < MAX_CLIP_PLANES && movetime > 0; i++) {
+ vector push = vel * movetime;
+ vector p0 = pos;
+ vector p1 = p0 + push;
+ // SV_PushEntity
+ tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+ if (!trace_startsolid) {
+ bool hit = trace_fraction < 1;
+ pos = trace_endpos;
+ entity ent = trace_ent;
+ // SV_LinkEdict_TouchAreaGrid
+ if (this.solid != SOLID_NOT) {
+ FOREACH_ENTITY_RADIUS_ORDERED(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
+ if (it.solid != SOLID_TRIGGER || it == this) continue;
+ if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax)) {
+ // SV_LinkEdict_TouchAreaGrid_Call
+ trace_allsolid = false;
+ trace_startsolid = false;
+ trace_fraction = 1;
+ trace_inwater = false;
+ trace_inopen = true;
+ trace_endpos = it.origin;
+ trace_plane_normal = '0 0 1';
+ trace_plane_dist = 0;
+ trace_ent = this;
+ trace_dpstartcontents = 0;
+ trace_dphitcontents = 0;
+ trace_dphitq3surfaceflags = 0;
+ trace_dphittexturename = string_null;
+ gettouch(it)(this, it);
+ vel = this.velocity;
+ }
+ });
+ }
+ if (hit && this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || this.groundentity != ent)) {
+ // SV_Impact (ent, trace);
+ tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+ void(entity, entity) touched = gettouch(this);
+ if (touched && this.solid != SOLID_NOT) {
+ touched(ent, this);
+ }
+ void(entity, entity) touched2 = gettouch(ent);
+ if (this && ent && touched2 && ent.solid != SOLID_NOT) {
+ trace_endpos = ent.origin;
+ trace_plane_normal *= -1;
+ trace_plane_dist *= -1;
+ trace_ent = this;
+ trace_dpstartcontents = 0;
+ trace_dphitcontents = 0;
+ trace_dphitq3surfaceflags = 0;
+ trace_dphittexturename = string_null;
+ touched2(this, ent);
+ }
+ }
+ }
+ // end SV_PushEntity
+ if (wasfreed(this)) { return; }
+ tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+ if (trace_fraction == 1) { break; }
+ movetime *= 1 - min(1, trace_fraction);
+ ClipVelocity(vel, trace_plane_normal, vel, 1);
+ }
+
+ this.com_phys_acc = acc;
+ this.com_phys_vel = vel;
+ this.com_phys_pos = pos;
+ setorigin(this, this.com_phys_pos);
+}
+
+void sys_phys_update_single(entity this)
+{
+ sys_phys_simulate_simple(this, frametime);
}
#pragma once
SYSTEM(phys, 30, 10);
+
+void sys_phys_fix(entity this, float dt);
+bool sys_phys_override(entity this);
+void sys_phys_monitor(entity this);
+void sys_phys_pregame_hold(entity this);
+void sys_phys_ai(entity this);
+void sys_phys_spectator_control(entity this);
+void sys_phys_fixspeed(entity this, float maxspeed_mod);
--- /dev/null
+#include "physics.qh"
+
+void sys_phys_fix(entity this, float dt)
+{
+ WarpZone_PlayerPhysics_FixVAngle(this);
+ Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
+}
+
+bool sys_phys_override(entity this)
+{
+ int buttons = PHYS_INPUT_BUTTON_MASK(this);
+ if (PM_check_specialcommand(this, buttons)) { return true; }
+ if (this.PlayerPhysplug && this.PlayerPhysplug(this)) { return true; }
+ return false;
+}
+
+void sys_phys_monitor(entity this)
+{
+ int buttons = PHYS_INPUT_BUTTON_MASK(this);
+ anticheat_physics(this);
+ if (sv_maxidle > 0) {
+ if (buttons != this.buttons_old
+ || this.movement != this.movement_old
+ || this.v_angle != this.v_angle_old) { this.parm_idlesince = time; }
+ }
+ PM_check_nickspam(this);
+ PM_check_punch(this);
+}
+
+void sys_phys_ai(entity this)
+{
+ if (!IS_BOT_CLIENT(this)) { return; }
+ if (playerdemo_read(this)) { return; }
+ bot_think(this);
+}
+
+void sys_phys_pregame_hold(entity this)
+{
+ if (!IS_PLAYER(this)) { return; }
+ const bool allowed_to_move = (time >= game_starttime);
+ if (!allowed_to_move) {
+ this.velocity = '0 0 0';
+ this.movetype = MOVETYPE_NONE;
+ this.disableclientprediction = 2;
+ } else if (this.disableclientprediction == 2) {
+ if (this.movetype == MOVETYPE_NONE) { this.movetype = MOVETYPE_WALK; }
+ this.disableclientprediction = 0;
+ }
+}
+
+void sys_phys_spectator_control(entity this)
+{
+ float maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
+ if (!this.spectatorspeed) { this.spectatorspeed = maxspeed_mod; }
+ if ((this.impulse >= 1 && this.impulse <= 19)
+ || (this.impulse >= 200 && this.impulse <= 209)
+ || (this.impulse >= 220 && this.impulse <= 229)
+ ) {
+ if (this.lastclassname != STR_PLAYER) {
+ if (this.impulse == 10
+ || this.impulse == 15
+ || this.impulse == 18
+ || (this.impulse >= 200 && this.impulse <= 209)
+ ) { this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5); } else if (this.impulse == 11) {
+ this.spectatorspeed = maxspeed_mod;
+ } else if (this.impulse == 12
+ || this.impulse == 16
+ || this.impulse == 19
+ || (this.impulse >= 220 && this.impulse <= 229)
+ ) {
+ this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
+ } else if (this.impulse >= 1 && this.impulse <= 9) {
+ this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
+ }
+ } // otherwise just clear
+ this.impulse = 0;
+ }
+}
+
+void sys_phys_fixspeed(entity this, float maxspeed_mod)
+{
+ float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
+ if (this.speed != spd) {
+ this.speed = spd;
+ string temps = ftos(spd);
+ stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
+ }
+
+ if (this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min) {
+ this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
+ stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
+ }
+ if (this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max) {
+ this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
+ stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
+ }
+}
+
+void sys_phys_land(entity this)
+{
+ if (autocvar_speedmeter) {
+ LOG_TRACEF("landing velocity: %v (abs: %f)", this.velocity, vlen(this.velocity));
+ }
+ if (this.jumppadcount > 1) {
+ LOG_TRACEF("%dx jumppad combo", this.jumppadcount);
+ }
+ this.jumppadcount = 0;
+}
+
+STATIC_INIT(sys_phys)
+{
+ entity listener = new_pure(sys_phys);
+ subscribe(listener, phys_land, sys_phys_land);
+}
#define COMPAT_NO_MOD_IS_XONOTIC
#endif
+#ifdef CSQC
+#define IS_CSQC 1
+#else
+#define IS_CSQC 0
+#endif
+
+#ifdef SVQC
+#define IS_SVQC 1
+#else
+#define IS_SVQC 0
+#endif
+
#include "compiler.qh"
#ifndef QCC_SUPPORT_INT
#include "file.qh"
#include "functional.qh"
#include "i18n.qh"
+#include "intrusivelist.qh"
#include "iter.qh"
#include "json.qc"
#include "lazy.qh"
#include "matrix/_mod.inc"
+#ifndef SVQC
+#define objerror_safe(e)
+#else
+void make_safe_for_remove(entity this);
+ #define objerror_safe(e) make_safe_for_remove(e)
+#endif
+
+#define objerror(this, msg) MACRO_BEGIN { \
+ LOG_WARNING("======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); \
+} MACRO_END
+
#ifdef MENUQC
void _m_init();
void m_init() { if (_m_init) _m_init(); }
#define CSQC_Ent_Remove _CSQC_Ent_Remove
#endif
#undef ENGINE_EVENT
+
+#ifndef MENUQC
+ #include <ecs/_lib.qh>
+ #include <ecs/components/_mod.qh>
+#endif
--- /dev/null
+#pragma once
+
+#include "iter.qh"
+
+const int IL_MAX = 128;
+
+void IL_INIT(entity this);
+void IL_DTOR(entity this);
+void IL_ENDFRAME();
+
+/**
+ * limitations:
+ * NULL cannot be present
+ * elements can only be present once
+ * a maximum of `IL_MAX` lists can exist at one time
+ * freed entities must be removed from the list
+ */
+CLASS(IntrusiveList, Object)
+ ATTRIB(IntrusiveList, il_head, entity, NULL);
+ ATTRIB(IntrusiveList, il_tail, entity, NULL);
+ ATTRIB(IntrusiveList, il_nextfld, .entity, nil);
+ ATTRIB(IntrusiveList, il_prevfld, .entity, nil);
+ INIT(IntrusiveList) { IL_INIT(this); }
+ DESTRUCTOR(IntrusiveList) { IL_DTOR(this); }
+ENDCLASS(IntrusiveList)
+
+// bitflags
+.vector il_lists;
+// bitflags
+.vector il_listmask;
+
+#define IL_NEW() NEW(IntrusiveList)
+
+#define IL_EMPTY(this) (this.il_head == NULL)
+
+#define IL_FIRST(this) (this.il_head)
+#define IL_LAST(this) (this.il_tail)
+#define IL_PEEK(this) (this.il_tail)
+
+bool IL_CONTAINS(IntrusiveList this, entity it)
+{
+ assert(this, return false);
+ return it.(this.il_nextfld) || this.il_head == it || this.il_tail == it;
+}
+
+/**
+ * Push to tail
+ */
+entity IL_PUSH(IntrusiveList this, entity it)
+{
+ assert(this, return NULL);
+ assert(it, return NULL);
+ .entity il_next = this.il_nextfld;
+ .entity il_prev = this.il_prevfld;
+ assert(!IL_CONTAINS(this, it), return NULL);
+
+ entity tail = it.(il_prev) = this.il_tail;
+ tail ? (tail.(il_next) = it) : this.il_head = it;
+ this.il_tail = it;
+ it.il_lists |= this.il_listmask;
+ return it;
+}
+
+/**
+ * Push to head
+ */
+entity IL_UNSHIFT(IntrusiveList this, entity it)
+{
+ assert(this, return NULL);
+ assert(it, return NULL);
+ .entity il_next = this.il_nextfld;
+ .entity il_prev = this.il_prevfld;
+ assert(!IL_CONTAINS(this, it), return NULL);
+
+ entity head = it.(il_next) = this.il_head;
+ head ? (head.(il_prev) = it) : this.il_tail = it;
+ this.il_head = it;
+ it.il_lists |= this.il_listmask;
+ return it;
+}
+
+/**
+ * Pop from tail
+ */
+entity IL_POP(IntrusiveList this)
+{
+ assert(this, return NULL);
+ .entity il_next = this.il_nextfld;
+ .entity il_prev = this.il_prevfld;
+
+ if (!this.il_tail) return NULL;
+ entity it = this.il_tail;
+ entity prev = it.(il_prev);
+ if (prev) (this.il_tail = prev).(il_next) = NULL;
+ else this.il_head = this.il_tail = NULL;
+ return it;
+}
+
+/**
+ * Pop from head
+ */
+entity IL_SHIFT(IntrusiveList this)
+{
+ assert(this, return NULL);
+ .entity il_next = this.il_nextfld;
+ .entity il_prev = this.il_prevfld;
+
+ if (!this.il_head) return NULL;
+ entity it = this.il_head;
+ entity next = it.(il_next);
+ if (next) (this.il_head = next).(il_prev) = NULL;
+ else this.il_head = this.il_tail = NULL;
+ return it;
+}
+
+/**
+ * Remove any element, anywhere in the list
+ */
+void IL_REMOVE(IntrusiveList this, entity it)
+{
+ assert(this, return);
+ .entity il_next = this.il_nextfld;
+ .entity il_prev = this.il_prevfld;
+ entity next = it.(il_next);
+ entity prev = it.(il_prev);
+ entity ohead = this.il_head;
+ entity otail = this.il_tail;
+ next ? next.(il_prev) = prev : this.il_tail = prev;
+ prev ? prev.(il_next) = next : this.il_head = next;
+ LOG_DEBUGF("remove %i (%i :: %i), head: %i -> %i, tail: %i -> %i\n", it, it.(il_prev), it.(il_next), ohead, this.il_head, otail, this.il_tail);
+ it.(il_next) = it.(il_prev) = NULL;
+}
+
+/**
+ * Remove all elements
+ */
+#define IL_CLEAR(this) \
+ MACRO_BEGIN \
+ { \
+ IntrusiveList __il = this; \
+ assert(__il); \
+ .entity il_prev = __il.il_prevfld; \
+ IL_EACH(__il, true, it.(il_next) = it.(il_prev) = NULL); \
+ __il.il_head = __il.il_tail = NULL; \
+ } MACRO_END
+
+/**
+ * Delete the list
+ */
+#define IL_DELETE(this, dtor) \
+ MACRO_BEGIN \
+ { \
+ remove(this); \
+ this = NULL; \
+ } MACRO_END
+
+#define IL_EACH(this, cond, body) \
+ MACRO_BEGIN \
+ { \
+ IntrusiveList _il = this; \
+ assert(_il); \
+ .entity il_next = _il.il_nextfld; \
+ noref int i = 0; \
+ for (entity _next, _it = _il.il_head; _it; (_it = _next, ++i)) \
+ { \
+ const noref entity it = _it; \
+ _next = it.(il_next); \
+ if (cond) { LAMBDA(body) } \
+ } \
+ } MACRO_END
+
+.int il_id;
+IntrusiveList il_links[IL_MAX];
+.entity il_links_flds[IL_MAX * 2];
+int il_links_ptr;
+
+#define IL_FLOOR(n) ((n) | 0)
+#define IL_CEIL(n) IL_FLOOR((n) + 0.5)
+
+#define IL_LISTS_PER_BIT IL_CEIL(IL_MAX / (3 * 24))
+
+void IL_INIT(IntrusiveList this)
+{
+ .entity nextfld, prevfld;
+ for (int i = il_links_ptr; i < il_links_ptr + IL_MAX; ++i) {
+ int idx = i;
+ if (idx >= IL_MAX) idx -= IL_MAX;
+ int id = idx;
+ idx *= 2;
+ if (!il_links[idx]) {
+ il_links[idx] = this;
+ nextfld = il_links_flds[idx + 0];
+ prevfld = il_links_flds[idx + 1];
+ this.il_id = id;
+ int bit = IL_FLOOR(id / IL_LISTS_PER_BIT);
+ if (bit < (1 * 24)) this.il_listmask = '1 0 0' * (1 << (bit - (0 * 24)));
+ else if (bit < (2 * 24)) this.il_listmask = '0 1 0' * (1 << (bit - (1 * 24)));
+ else if (bit < (3 * 24)) this.il_listmask = '0 0 1' * (1 << (bit - (2 * 24)));
+ else assert(false);
+ il_links_ptr = id + 1;
+ if (il_links_ptr >= IL_MAX) il_links_ptr -= IL_MAX;
+ this.il_nextfld = nextfld;
+ this.il_prevfld = prevfld;
+ return;
+ }
+ }
+ LOG_WARNINGF("IntrusiveList overflow");
+}
+
+void IL_DTOR(IntrusiveList this)
+{
+ IL_CLEAR(this);
+ il_links[this.il_id] = NULL;
+}
+
+void IL_ENDFRAME()
+{
+#if 0
+ // incompatible with CSQC, remove() clears entities
+ for (int i = 0; i < IL_MAX; ++i) {
+ IntrusiveList list = il_links[i];
+ if (list) {
+ .entity nextfld = list.il_nextfld;
+ for (entity next, it = list.il_head; it; it = next) {
+ next = it.(nextfld);
+ if (wasfreed(it)) {
+ IL_REMOVE(list, it);
+ }
+ }
+ }
+ }
+#endif
+}
+
+void ONREMOVE(entity this)
+{
+ if (this.il_lists) {
+ for (int i = 0; i < IL_MAX; ++i) {
+ IntrusiveList list = il_links[i];
+ if (this.il_lists & list.il_listmask && IL_CONTAINS(list, this)) {
+ IL_REMOVE(list, this);
+ }
+ }
+ }
+}
MACRO_BEGIN \
{ \
int _i = 0; \
- for (entity _it = list##_first; _it; (_it = _it.next, ++_i)) \
+ for (entity _it = list##_first, _next = NULL; _it; (_it = _next, ++_i)) \
{ \
const noref int i = _i; \
ITER_CONST noref entity it = _it; \
+ _next = _it.next; \
if (cond) { LAMBDA(body) } \
} \
} MACRO_END
if (cond) LAMBDA(body) \
} \
} MACRO_END
+#define MUTEX_LOCK(this) MACRO_BEGIN \
+ if (this) LOG_SEVEREF("Loop mutex held by %s", this); \
+ this = __FUNC__; \
+MACRO_END
+#define MUTEX_UNLOCK(this) MACRO_BEGIN \
+ this = string_null; \
+MACRO_END
#define _FOREACH_ENTITY_FIND_UNORDERED(id, T, fld, match, cond, body) \
MACRO_BEGIN { \
- if (_FOREACH_ENTITY_FIND_##T##_##id##mutex) LOG_SEVEREF("Loop mutex held by %s", _FOREACH_ENTITY_FIND_##T##_##id##mutex); \
- _FOREACH_ENTITY_FIND_##T##_##id##mutex = __FUNC__; \
+ MUTEX_LOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \
entity _foundchain_first = _findchain##T##_tofield(fld, match, _FOREACH_ENTITY_FIND_##T##_next##id); \
FOREACH_LIST(_foundchain, _FOREACH_ENTITY_FIND_##T##_next##id, cond, body); \
- _FOREACH_ENTITY_FIND_##T##_##id##mutex = string_null; \
+ MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \
} MACRO_END
#define FOREACH_ENTITY(cond, body) ORDERED(FOREACH_ENTITY)(cond, body)
#ifndef MENUQC
entity(vector org, float rad, .entity tofield) _findchainradius_tofield = #22;
-#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body)
+#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) ORDERED(FOREACH_ENTITY_RADIUS)(org, dist, cond, body)
.entity _FOREACH_ENTITY_FIND_radius_next; noref string _FOREACH_ENTITY_FIND_radius_mutex;
#define FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body) _FOREACH_ENTITY_FIND_UNORDERED(, radius, org, dist, cond, body)
+.entity _FOREACH_ENTITY_FIND_radius_nexttmp; noref string _FOREACH_ENTITY_FIND_radius_tmpmutex;
+#define FOREACH_ENTITY_RADIUS_ORDERED(org, dist, cond, body) \
+MACRO_BEGIN \
+ entity _rev_first = NULL; \
+ _FOREACH_ENTITY_FIND_UNORDERED(tmp, radius, org, dist, cond, (it._FOREACH_ENTITY_FIND_radius_nexttmp = _rev_first, _rev_first = it)); \
+ MUTEX_LOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \
+ FOREACH_LIST(_rev, _FOREACH_ENTITY_FIND_radius_nexttmp, true, body); \
+ MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \
+MACRO_END
#endif
#define FOREACH_ENTITY_FLOAT(fld, match, body) ORDERED(FOREACH_ENTITY_FLOAT)(fld, match, body)
} MACRO_END
// TODO: this sucks, lets find a better way to do backtraces?
-#ifdef SVQC
- void builtin_remove(entity);
- #define _backtrace() builtin_remove(NULL)
-#else
- void remove(entity);
- #define _backtrace() remove(NULL)
-#endif
+#define _backtrace() builtin_remove(NULL)
noref int autocvar_developer;
noref bool autocvar_prvm_backtraceforwarnings;
#define new_pure(class) _new(class, true)
#define spawn() __spawn("entity", __FILE__ ":" STR(__LINE__), false)
+[[accumulate]] void ONREMOVE(entity this) {}
+
+#ifndef SVQC
+ #define delete_fn builtin_remove
+#endif
+
#define delete(this) MACRO_BEGIN { \
entity _this = (this); \
void(entity) _dtor = _this.dtor; \
- if (_dtor) _dtor(_this); else remove(_this); \
+ ONREMOVE(this); \
+ if (_dtor) _dtor(_this); else delete_fn(_this); \
/* this = NULL; */ \
} MACRO_END
#define spawn_1(this)
#define _vtbl NULL
CLASS(Object, );
- DESTRUCTOR(Object) { remove(this); }
+ DESTRUCTOR(Object) { builtin_remove(this); }
#define remove(this) delete(this)
METHOD(Object, describe, string(Object this))
{
#endif
#define error(...) (__self = (NULL), builtin_error(__VA_ARGS__))
#define movetogoal(e, ...) (__self = (e), builtin_movetogoal(__VA_ARGS__))
-#ifndef SVQC
- #define objerror(e, ...) (__self = (e), builtin_objerror(__VA_ARGS__))
-#else
- void make_safe_for_remove(entity this);
- #define objerror(e, ...) (__self = (e), make_safe_for_remove(__self), builtin_objerror(__VA_ARGS__))
-#endif
#define walkmove(e, ...) (__self = (e), builtin_walkmove(__VA_ARGS__))
#ifndef MENUQC
#define _STAT(id) g_stat_##id
#define REGISTER_STAT_2(id, T) \
T _STAT(id); \
- T CAT(_STAT(id), _prev); \
+ /* T CAT(_STAT(id), _prev); */ \
REGISTER(Stats, STAT_##id, m_id, new_pure(stat)) \
{ \
if (#T == "vector" || #T == "vectori") { \
[[accumulate]] void stats_get() \
{ \
T it = getstat_##T(STAT_##id.m_id); \
- if (it != CAT(_STAT(id), _prev)) \
- CAT(_STAT(id), _prev) = _STAT(id) = it; \
+ /* if (it != CAT(_STAT(id), _prev)) \
+ CAT(_STAT(id), _prev) = */ _STAT(id) = it; \
}
#define REGISTER_STAT_3(x, T, expr) REGISTER_STAT_2(x, T)
#elif defined(SVQC)
}
#endif
+noref vector _vmul_a, _vmul_b;
+#define vmul(a, b) \
+ (_vmul_a = (a), _vmul_b = (b), \
+ '1 0 0' * (_vmul_a.x * _vmul_b.x) \
+ + '0 1 0' * (_vmul_a.y * _vmul_b.y) \
+ + '0 0 1' * (_vmul_a.z * _vmul_b.z))
+
const vector eX = '1 0 0';
const vector eY = '0 1 0';
const vector eZ = '0 0 1';
return vel - (1 + bounce) * (vel * norm) * norm;
}
+vector vec_epsilon(vector this, float eps)
+{
+ if (this.x > -eps && this.x < eps) this.x = 0;
+ if (this.y > -eps && this.y < eps) this.y = 0;
+ if (this.z > -eps && this.z < eps) this.z = 0;
+ return this;
+}
+
+#define ClipVelocity(in, normal, out, overbounce) \
+ (out = vec_epsilon(vec_reflect(in, normal, (overbounce) - 1), 0.1))
+
#ifndef MENUQC
vector get_corner_position(entity box, int corner)
{
postMenuDraw();
frametime = 0;
+ IL_ENDFRAME();
}
void m_display()
#define FOREACH_CLIENT(cond, body) FOREACH_CLIENTSLOT(IS_CLIENT(it) && (cond), body)
-// NOTE: FOR_EACH_MONSTER deprecated! Use the following instead: FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { code; });
+// NOTE: FOR_EACH_MONSTER deprecated! Use the following instead: IL_EACH(g_monsters, true, { code; });
#include <common/effects/all.qh>
#include <common/models/all.qh>
int autocvar_sv_gentle;
#define autocvar_sv_gravity cvar("sv_gravity")
string autocvar_sv_intermission_cdtrack;
-float autocvar_sv_itemstime;
+int autocvar_sv_itemstime;
string autocvar_sv_jumpspeedcap_max;
float autocvar_sv_jumpspeedcap_max_disable_on_ramps;
string autocvar_sv_jumpspeedcap_min;
void bot_relinkplayerlist()
{
- entity e;
- entity prevbot;
player_count = 0;
currentbots = 0;
- player_list = e = findchainflags(flags, FL_CLIENT);
bot_list = NULL;
- prevbot = NULL;
- while (e)
+
+ entity prevbot = NULL;
+ FOREACH_CLIENT(true,
{
- player_count = player_count + 1;
- e.nextplayer = e.chain;
- if (IS_BOT_CLIENT(e))
+ ++player_count;
+
+ if(IS_BOT_CLIENT(it))
{
- if (prevbot)
- prevbot.nextbot = e;
+ if(prevbot)
+ prevbot.nextbot = it;
else
{
- bot_list = e;
+ bot_list = it;
bot_list.nextbot = NULL;
}
- prevbot = e;
- currentbots = currentbots + 1;
+ prevbot = it;
+ ++currentbots;
}
- e = e.chain;
- }
+ });
LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
bot_strategytoken = bot_list;
bot_strategytoken_taken = true;
void bot_removefromlargestteam()
{
- float besttime, bestcount, thiscount;
- entity best, head;
CheckAllowedTeams(NULL);
GetTeamCounts(NULL);
- head = findchainfloat(isbot, true);
- if (!head)
- return;
- best = head;
- besttime = head.createdtime;
- bestcount = 0;
- while (head)
+
+ entity best = NULL;
+ float besttime = 0;
+ int bestcount = 0;
+
+ int bcount = 0;
+ FOREACH_ENTITY_FLOAT(isbot, true,
{
- if(head.team == NUM_TEAM_1)
- thiscount = c1;
- else if(head.team == NUM_TEAM_2)
- thiscount = c2;
- else if(head.team == NUM_TEAM_3)
- thiscount = c3;
- else if(head.team == NUM_TEAM_4)
- thiscount = c4;
- else
- thiscount = 0;
- if (thiscount > bestcount)
+ ++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 = head.createdtime;
- best = head;
+ besttime = it.createdtime;
+ best = it;
}
- else if (thiscount == bestcount && besttime < head.createdtime)
+ else if(thiscount == bestcount && besttime < it.createdtime)
{
- besttime = head.createdtime;
- best = head;
+ besttime = it.createdtime;
+ best = it;
}
- head = head.chain;
- }
+ });
+ if(!bcount)
+ return; // no bots to remove
currentbots = currentbots - 1;
dropclient(best);
}
void bot_removenewest()
{
- float besttime;
- entity best, head;
-
if(teamplay)
{
bot_removefromlargestteam();
return;
}
- head = findchainfloat(isbot, true);
- if (!head)
- return;
- best = head;
- besttime = head.createdtime;
- while (head)
+ float besttime = 0;
+ entity best = NULL;
+ int bcount = 0;
+
+ FOREACH_ENTITY_FLOAT(isbot, true,
{
- if (besttime < head.createdtime)
+ ++bcount;
+
+ if(!best)
{
- besttime = head.createdtime;
- best = head;
+ best = it;
+ besttime = it.createdtime;
}
- head = head.chain;
- }
+
+ if(besttime < it.createdtime)
+ {
+ besttime = it.createdtime;
+ best = it;
+ }
+ });
+
+ if(!bcount)
+ return; // no bots to remove
+
currentbots = currentbots - 1;
dropclient(best);
}
// 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 && (c3==-1 && c4==-1))
+ 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)))
{
else
{
// TODO: Make this check cleaner
- entity wp = findchain(classname, "waypoint");
- if(time - wp.nextthink > 10)
+ IL_EACH(g_waypoints, time - it.nextthink > 10,
+ {
waypoint_save_links();
+ break;
+ });
}
}
else
float bot_weapons_close[Weapons_MAX];
entity bot_list;
-entity player_list;
.entity nextbot;
-.entity nextplayer;
.string cleanname;
.string netname_freeme;
.string playermodel_freeme;
// 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==NULL)
- if(this.waterlevel==WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
+ if(!this.goalcurrent)
+ if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
{
// Look for the closest waypoint out of water
- entity newgoal, head;
- float bestdistance, distance;
-
- newgoal = NULL;
- bestdistance = 10000;
- for (head = findchain(classname, "waypoint"); head; head = head.chain)
+ entity newgoal = NULL;
+ IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 10000),
{
- distance = vlen(head.origin - this.origin);
- if(distance>10000)
- continue;
-
- if(head.origin.z < this.origin.z)
+ if(it.origin.z < this.origin.z)
continue;
- if(head.origin.z - this.origin.z - this.view_ofs.z > 100)
+ if(it.origin.z - this.origin.z - this.view_ofs.z > 100)
continue;
- if (pointcontents(head.origin + head.maxs + '0 0 1') != CONTENT_EMPTY)
+ if (pointcontents(it.origin + it.maxs + '0 0 1') != CONTENT_EMPTY)
continue;
- traceline(this.origin + this.view_ofs , head.origin, true, head);
+ traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
- if(trace_fraction<1)
+ if(trace_fraction < 1)
continue;
- if(distance<bestdistance)
- {
- newgoal = head;
- bestdistance = distance;
- }
- }
+ if(!newgoal || vlen2(it.origin - this.origin) < vlen2(newgoal.origin - this.origin))
+ newgoal = it;
+ });
if(newgoal)
{
{
if(fabs(this.velocity.z)<50)
{
- entity head, newgoal = NULL;
- float distance, bestdistance = 0;
-
- for (head = findchain(classname, "waypoint"); head; head = head.chain)
+ 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);
- distance = vlen(head.origin - this.origin);
- if(distance>1000)
+ if(trace_fraction < 1)
continue;
- traceline(this.origin + this.view_ofs , ( ( head.absmin + head.absmax ) * 0.5 ), true, NULL);
-
- if(trace_fraction<1)
- continue;
-
- if(distance>bestdistance)
- {
- newgoal = head;
- bestdistance = distance;
- }
- }
+ if(!newgoal || vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))
+ newgoal = it;
+ });
if(newgoal)
{
void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius)
{
- entity head;
- float rating, d, discard, distance, friend_distance, enemy_distance;
+ float rating, d, discard, friend_distance, enemy_distance;
vector o;
ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
- head = findchainfloat(bot_pickup, true);
- while (head)
+ FOREACH_ENTITY_FLOAT(bot_pickup, true,
{
- o = (head.absmin + head.absmax) * 0.5;
- distance = vlen(o - org);
+ o = (it.absmin + it.absmax) * 0.5;
friend_distance = 10000; enemy_distance = 10000;
rating = 0;
- if(!head.solid || distance > sradius || (head == this.ignoregoal && time < this.ignoregoaltime) )
- {
- head = head.chain;
+ if(!it.solid || vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) )
continue;
- }
// Check if the item can be picked up safely
- if(head.classname == "droppedweapon")
+ 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)
- {
- head = head.chain;
continue;
- }
- if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
- {
- head = head.chain;
+ if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
continue;
- }
}
else
{
// Ignore items under water
- traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
+ traceline(it.origin + it.maxs, it.origin + it.maxs, MOVE_NORMAL, it);
if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
- {
- head = head.chain;
continue;
- }
}
if(teamplay)
{
discard = false;
- FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it), LAMBDA(
+ 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 )
discard = true;
- if( head.health && it.health > this.health )
+ if( picker.health && it.health > this.health )
continue;
- if( head.armorvalue && it.armorvalue > this.armorvalue)
+ if( picker.armorvalue && it.armorvalue > this.armorvalue)
continue;
- if( head.weapons )
- if( head.weapons & ~it.weapons )
+ if( picker.weapons )
+ if( picker.weapons & ~it.weapons )
continue;
- if (head.ammo_shells && it.ammo_shells > this.ammo_shells)
+ if (picker.ammo_shells && it.ammo_shells > this.ammo_shells)
continue;
- if (head.ammo_nails && it.ammo_nails > this.ammo_nails)
+ if (picker.ammo_nails && it.ammo_nails > this.ammo_nails)
continue;
- if (head.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
+ if (picker.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
continue;
- if (head.ammo_cells && it.ammo_cells > this.ammo_cells)
+ if (picker.ammo_cells && it.ammo_cells > this.ammo_cells)
continue;
- if (head.ammo_plasma && it.ammo_plasma > this.ammo_plasma)
+ if (picker.ammo_plasma && it.ammo_plasma > this.ammo_plasma)
continue;
discard = false;
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 && distance < enemy_distance) ||
+ if ( (enemy_distance < friend_distance && vdist(o - org, <, enemy_distance)) ||
(friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
- rating = head.bot_pickupevalfunc(this, head);
+ rating = it.bot_pickupevalfunc(this, it);
}
else
- rating = head.bot_pickupevalfunc(this, head);
+ rating = it.bot_pickupevalfunc(this, it);
if(rating > 0)
- navigation_routerating(this, head, rating * ratingscale, 2000);
- head = head.chain;
- }
+ navigation_routerating(this, it, rating * ratingscale, 2000);
+ });
}
void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius)
{
- entity head;
- head = findchain(classname, "dom_controlpoint");
- while (head)
+ FOREACH_ENTITY_CLASS("dom_controlpoint", vdist((((it.absmin + it.absmax) * 0.5) - org), <, sradius),
{
- if(vdist((((head.absmin + head.absmax) * 0.5) - org), <, sradius))
- {
- if(head.cnt > -1) // this is just being fought for
- navigation_routerating(this, head, ratingscale, 5000);
- else if(head.goalentity.cnt == 0) // unclaimed point
- navigation_routerating(this, head, ratingscale * 0.5, 5000);
- else if(head.goalentity.team != this.team) // other team's point
- navigation_routerating(this, head, ratingscale * 0.2, 5000);
- }
- head = head.chain;
- }
+ 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)
// 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)
{
- entity waylist, w, best;
- vector v, org, pm1, pm2;
-
- pm1 = ent.origin + ent.mins;
- pm2 = ent.origin + ent.maxs;
- waylist = findchain(classname, "waypoint");
+ vector pm1 = ent.origin + ent.mins;
+ vector pm2 = ent.origin + ent.maxs;
// do two scans, because box test is cheaper
- w = waylist;
- while (w)
+ IL_EACH(g_waypoints, it != ent && it != except,
{
- // if object is touching spawnfunc_waypoint
- if(w != ent && w != except)
- if (boxesoverlap(pm1, pm2, w.absmin, w.absmax))
- return w;
- w = w.chain;
- }
+ if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
+ return it;
+ });
- org = ent.origin + 0.5 * (ent.mins + ent.maxs);
+ 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)
if (navigation_testtracewalk)
te_plasmaburn(org);
- best = NULL;
+ entity best = NULL;
+ vector v;
// box check failed, try walk
- w = waylist;
- while (w)
+ IL_EACH(g_waypoints, it != ent,
{
- // if object can walk from spawnfunc_waypoint
- if(w != ent)
+ if(it.wpisbox)
{
- if (w.wpisbox)
- {
- vector wm1, wm2;
- wm1 = w.origin + w.mins;
- wm2 = w.origin + w.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 = w.origin;
- if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist))
- {
- bestdist = vlen(v - org);
- best = w;
- }
+ 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);
}
- w = w.chain;
- }
+ 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)
}
// finds the waypoints near the bot initiating a navigation query
-float navigation_markroutes_nearestwaypoints(entity this, entity waylist, float maxdist)
+float navigation_markroutes_nearestwaypoints(entity this, float maxdist)
{
- entity head;
- vector v, m1, m2, diff;
- float c;
+ vector v, m1, m2;
// navigation_testtracewalk = true;
- c = 0;
- head = waylist;
- while (head)
+ int c = 0;
+ IL_EACH(g_waypoints, !it.wpconsidered,
{
- if (!head.wpconsidered)
+ if (it.wpisbox)
{
- if (head.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))
{
- m1 = head.origin + head.mins;
- m2 = head.origin + head.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 = head.origin;
- diff = v - this.origin;
- diff.z = max(0, diff.z);
- if(vdist(diff, <, maxdist))
- {
- head.wpconsidered = true;
- if (tracewalk(this, this.origin, this.mins, this.maxs, v, bot_navigation_movemode))
- {
- head.wpnearestpoint = v;
- head.wpcost = vlen(v - this.origin) + head.dmg;
- head.wpfire = 1;
- head.enemy = NULL;
- c = c + 1;
- }
+ it.wpnearestpoint = v;
+ it.wpcost = vlen(v - this.origin) + it.dmg;
+ it.wpfire = 1;
+ it.enemy = NULL;
+ c = c + 1;
}
}
- head = head.chain;
- }
+ });
//navigation_testtracewalk = false;
return c;
}
// queries the entire spawnfunc_waypoint network for pathes leading away from the bot
void navigation_markroutes(entity this, entity fixed_source_waypoint)
{
- entity w, wp, waylist;
- float searching, cost, cost2;
+ float cost, cost2;
vector p;
- w = waylist = findchain(classname, "waypoint");
- while (w)
+
+ IL_EACH(g_waypoints, true,
{
- w.wpconsidered = false;
- w.wpnearestpoint = '0 0 0';
- w.wpcost = 10000000;
- w.wpfire = 0;
- w.enemy = NULL;
- w = w.chain;
- }
+ it.wpconsidered = false;
+ it.wpnearestpoint = '0 0 0';
+ it.wpcost = 10000000;
+ it.wpfire = 0;
+ it.enemy = NULL;
+ });
if(fixed_source_waypoint)
{
{
// 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 i, increment, maxdistance;
+ float increment, maxdistance;
if(IS_ONGROUND(this))
{
increment = 750;
maxdistance = 1500;
}
- for(i=increment;!navigation_markroutes_nearestwaypoints(this, waylist, i)&&i<maxdistance;i+=increment);
+ for(int j = increment; !navigation_markroutes_nearestwaypoints(this, j) && j < maxdistance; j += increment);
}
- searching = true;
+ bool searching = true;
while (searching)
{
searching = false;
- w = waylist;
- while (w)
+ IL_EACH(g_waypoints, it.wpfire,
{
- if (w.wpfire)
- {
- searching = true;
- w.wpfire = 0;
- cost = w.wpcost;
- p = w.wpnearestpoint;
- wp = w.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp00mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp01mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp02mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp03mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp04mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp05mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp06mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp07mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp08mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp09mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp10mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp11mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp12mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp13mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp14mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp15mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp16mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp17mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp18mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp19mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp20mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp21mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp22mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp23mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp24mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp25mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp26mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp27mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp28mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp29mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp30mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- wp = w.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp31mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
- }
- w = w.chain;
- }
+ 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)
{
- entity w, wp, waylist;
- float searching, cost, cost2;
+ float cost, cost2;
vector p;
- w = waylist = findchain(classname, "waypoint");
- while (w)
+ IL_EACH(g_waypoints, true,
{
- w.wpconsidered = false;
- w.wpnearestpoint = '0 0 0';
- w.wpcost = 10000000;
- w.wpfire = 0;
- w.enemy = NULL;
- w = w.chain;
- }
+ it.wpconsidered = false;
+ it.wpnearestpoint = '0 0 0';
+ it.wpcost = 10000000;
+ it.wpfire = 0;
+ it.enemy = NULL;
+ });
if(fixed_source_waypoint)
{
error("need to start with a waypoint\n");
}
- searching = true;
+ bool searching = true;
while (searching)
{
searching = false;
- w = waylist;
- while (w)
+ IL_EACH(g_waypoints, it.wpfire,
{
- if (w.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,
{
- searching = true;
- w.wpfire = 0;
- cost = w.wpcost; // cost to walk from w to home
- p = w.wpnearestpoint;
- for(wp = waylist; wp; wp = wp.chain)
- {
- if(w != wp.wp00) if(w != wp.wp01) if(w != wp.wp02) if(w != wp.wp03)
- if(w != wp.wp04) if(w != wp.wp05) if(w != wp.wp06) if(w != wp.wp07)
- if(w != wp.wp08) if(w != wp.wp09) if(w != wp.wp10) if(w != wp.wp11)
- if(w != wp.wp12) if(w != wp.wp13) if(w != wp.wp14) if(w != wp.wp15)
- if(w != wp.wp16) if(w != wp.wp17) if(w != wp.wp18) if(w != wp.wp19)
- if(w != wp.wp20) if(w != wp.wp21) if(w != wp.wp22) if(w != wp.wp23)
- if(w != wp.wp24) if(w != wp.wp25) if(w != wp.wp26) if(w != wp.wp27)
- if(w != wp.wp28) if(w != wp.wp29) if(w != wp.wp30) if(w != wp.wp31)
- continue;
- cost2 = cost + wp.dmg;
- navigation_markroutes_checkwaypoint(w, wp, cost2, p);
- }
- }
- w = w.chain;
- }
+ 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);
+ });
+ });
}
}
void botframe_updatedangerousobjects(float maxupdate)
{
- entity head, bot_dodgelist;
vector m1, m2, v, o;
float c, d, danger;
c = 0;
- bot_dodgelist = findchainfloat(bot_dodge, true);
- botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint");
- while (botframe_dangerwaypoint != NULL)
+ IL_EACH(g_waypoints, true,
{
danger = 0;
- m1 = botframe_dangerwaypoint.mins;
- m2 = botframe_dangerwaypoint.maxs;
- head = bot_dodgelist;
- while (head)
+ m1 = it.mins;
+ m2 = it.maxs;
+ FOREACH_ENTITY_FLOAT(bot_dodge, true,
{
- v = head.origin;
+ 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 = (head.absmin + head.absmax) * 0.5;
- d = head.bot_dodgerating - vlen(o - v);
+ 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;
}
- head = head.chain;
- }
- botframe_dangerwaypoint.dmg = danger;
+ });
+ it.dmg = danger;
c = c + 1;
if (c >= maxupdate)
break;
- botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint");
- }
+ });
}
void navigation_unstuck(entity this)
// build a new queue
LOG_DEBUG(strcat(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu\n"));
- entity head, first;
-
- first = NULL;
- head = findradius(this.origin, search_radius);
+ entity first = NULL;
- while(head)
+ FOREACH_ENTITY_RADIUS(this.origin, search_radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
{
- if(head.classname=="waypoint")
- // if(!(head.wpflags & WAYPOINTFLAG_GENERATED))
- {
- if(bot_waypoint_queue_goal)
- bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = head;
- else
- first = head;
-
- bot_waypoint_queue_goal = head;
- bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
- }
+ if(bot_waypoint_queue_goal)
+ bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = it;
+ else
+ first = it;
- head = head.chain;
- }
+ bot_waypoint_queue_goal = it;
+ bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
+ });
if (first)
bot_waypoint_queue_goal = first;
vector jumpstepheightvec;
vector stepheightvec;
-entity botframe_dangerwaypoint;
entity navigation_bestgoal;
// stack of current goals (the last one of which may be an item or other
float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
-float navigation_markroutes_nearestwaypoints(entity this, entity waylist, float maxdist);
+float navigation_markroutes_nearestwaypoints(entity this, float maxdist);
float navigation_routetogoal(entity this, entity e, vector startposition);
void navigation_clearroute(entity this);
if(!number)
return NULL;
- bot = findchainflags(flags, FL_CLIENT);
+ bot = findchainflags(flags, FL_CLIENT); // TODO: doesn't findchainflags loop backwards through entities?
while (bot)
{
if(IS_BOT_CLIENT(bot))
{
if(!(f & WAYPOINTFLAG_PERSONAL))
{
- for(entity wp = findchain(classname, "waypoint"); wp; wp = wp.chain)
+ IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax),
{
- // if a matching spawnfunc_waypoint already exists, don't add a duplicate
- if(boxesoverlap(m1, m2, wp.absmin, wp.absmax))
- return wp;
- }
+ 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;
//dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
sm1 = this.origin + this.mins;
sm2 = this.origin + this.maxs;
- FOREACH_ENTITY_CLASS("waypoint", true,
+ IL_EACH(g_waypoints, true,
{
if (boxesoverlap(this.absmin, this.absmax, it.absmin, it.absmax))
{
// 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);
// empties the map of waypoints
void waypoint_removeall()
{
- entity head, next;
- head = findchain(classname, "waypoint");
- while (head)
+ IL_EACH(g_waypoints, true,
{
- next = head.chain;
- remove(head);
- head = next;
- }
+ remove(it);
+ });
}
// tell all waypoints to relink
// (is this useful at all?)
void waypoint_schedulerelinkall()
{
- entity head;
relink_total = relink_walkculled = relink_pvsculled = relink_lengthculled = 0;
- head = findchain(classname, "waypoint");
- while (head)
+ IL_EACH(g_waypoints, true,
{
- waypoint_schedulerelink(head);
- head = head.chain;
- }
+ waypoint_schedulerelink(it);
+ });
}
// Load waypoint links from file
// Save all waypoint links to a file
void waypoint_save_links()
{
- string filename, s;
- float file, c, i;
- entity w, link;
- filename = strcat("maps/", mapname);
- filename = strcat(filename, ".waypoints.cache");
- file = fopen(filename, FILE_WRITE);
+ string filename = sprintf("maps/%s.waypoints.cache", mapname);
+ int file = fopen(filename, FILE_WRITE);
if (file < 0)
{
- LOG_INFO("waypoint links save to ");
- LOG_INFO(filename);
- LOG_INFO(" failed\n");
+ LOG_INFOF("waypoint link save to %s failed\n", filename);
+ return;
}
- c = 0;
- w = findchain(classname, "waypoint");
- while (w)
+
+ int c = 0;
+ IL_EACH(g_waypoints, true,
{
- for(i=0;i<32;++i)
+ for(int j = 0; j < 32; ++j)
{
- // :S
- link = waypoint_get_link(w, i);
- if(link==NULL)
- continue;
-
- s = strcat(vtos(w.origin), "*", vtos(link.origin), "\n");
- fputs(file, s);
- ++c;
+ entity link = waypoint_get_link(it, j);
+ if(link)
+ {
+ string s = strcat(vtos(it.origin), "*", vtos(link.origin), "\n");
+ fputs(file, s);
+ ++c;
+ }
}
- w = w.chain;
- }
+ });
fclose(file);
botframe_cachedwaypointlinks = true;
- LOG_INFO("saved ");
- LOG_INFO(ftos(c));
- LOG_INFO(" waypoints links to maps/");
- LOG_INFO(mapname);
- LOG_INFO(".waypoints.cache\n");
+ 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, s;
- float file, c;
- entity w;
- filename = strcat("maps/", mapname);
- filename = strcat(filename, ".waypoints");
- file = fopen(filename, FILE_WRITE);
- if (file >= 0)
+ string filename = sprintf("maps/%s.waypoints", mapname);
+ int file = fopen(filename, FILE_WRITE);
+ if (file < 0)
{
- c = 0;
- w = findchain(classname, "waypoint");
- while (w)
- {
- if (!(w.wpflags & WAYPOINTFLAG_GENERATED))
- {
- s = strcat(vtos(w.origin + w.mins), "\n");
- s = strcat(s, vtos(w.origin + w.maxs));
- s = strcat(s, "\n");
- s = strcat(s, ftos(w.wpflags));
- s = strcat(s, "\n");
- fputs(file, s);
- c = c + 1;
- }
- w = w.chain;
- }
- fclose(file);
- bprint("saved ");
- bprint(ftos(c));
- bprint(" waypoints to maps/");
- bprint(mapname);
- bprint(".waypoints\n");
+ waypoint_save_links(); // save anyway?
+ botframe_loadedforcedlinks = false;
+
+ LOG_INFOF("waypoint links: save to %s failed\n", filename);
+ return;
}
- else
+
+ int c = 0;
+ IL_EACH(g_waypoints, true,
{
- bprint("waypoint save to ");
- bprint(filename);
- bprint(" failed\n");
- }
+ 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
void waypoint_spawnforitem_force(entity e, vector org)
{
- entity w;
-
// Fix the waypoint altitude if necessary
org = waypoint_fixorigin(org);
// don't spawn an item spawnfunc_waypoint if it already exists
- w = findchain(classname, "waypoint");
- while (w)
+ IL_EACH(g_waypoints, true,
{
- if (w.wpisbox)
+ if(it.wpisbox)
{
- if (boxesoverlap(org, org, w.absmin, w.absmax))
+ if(boxesoverlap(org, org, it.absmin, it.absmax))
{
- e.nearestwaypoint = w;
+ e.nearestwaypoint = it;
return;
}
}
else
{
- if (vlen(w.origin - org) < 16)
+ if(vdist(it.origin - org, <, 16))
{
- e.nearestwaypoint = w;
+ e.nearestwaypoint = it;
return;
}
}
- w = w.chain;
- }
+ });
+
e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM);
}
void botframe_showwaypointlinks()
{
- entity head, w;
if (time < botframe_waypointeditorlightningtime)
return;
botframe_waypointeditorlightningtime = time + 0.5;
- FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot, LAMBDA(
+ FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot,
+ {
if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE)
{
//navigation_testtracewalk = true;
- head = navigation_findnearestwaypoint(it, false);
+ 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.wp31;if (w) te_lightning2(NULL, w.origin, head.origin);
}
}
- ));
+ });
}
float botframe_autowaypoints_fixdown(vector v)
float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f)
{
- entity w;
-
- w = find(NULL, classname, "waypoint");
- while (w)
+ 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
- if (boxesoverlap(v - '32 32 32', v + '32 32 32', w.absmin, w.absmax))
- //if (boxesoverlap(v - '4 4 4', v + '4 4 4', w.absmin, w.absmax))
- return 0;
- w = find(w, classname, "waypoint");
- }
+ return 0;
+ });
waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f));
return 1;
maxdist = 2100;
}
- float bestdist;
- bestdist = maxdist;
- w = find(NULL, classname, "waypoint");
- while (w)
+ float bestdist = maxdist;
+ IL_EACH(g_waypoints, it != wp && !(it.wpflags & WAYPOINTFLAG_NORELINK),
{
- if(w != wp && !(w.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))
{
- float d;
- d = vlen(wp.origin - w.origin) + vlen(w.origin - porg);
- if(d < bestdist)
- if(navigation_waypoint_will_link(wp.origin, w.origin, p, walkfromwp, 1050))
- if(navigation_waypoint_will_link(w.origin, porg, p, walkfromwp, 1050))
- {
- bestdist = d;
- p.(fld) = w;
- }
+ bestdist = d;
+ p.(fld) = it;
}
- w = find(w, classname, "waypoint");
- }
+ });
if(bestdist < maxdist)
{
LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n");
void botframe_deleteuselesswaypoints()
{
- entity w, w1, w2;
- float i, j, k;
- for (w = NULL; (w = findfloat(w, bot_pickup, true)); )
+ FOREACH_ENTITY_FLOAT(bot_pickup, true,
{
// NOTE: this protects waypoints if they're the ONLY nearest
// waypoint. That's the intention.
- navigation_findnearestwaypoint(w, false); // Walk TO item.
- navigation_findnearestwaypoint(w, true); // Walk FROM item.
- }
- for (w = NULL; (w = find(w, classname, "waypoint")); )
+ navigation_findnearestwaypoint(it, false); // Walk TO item.
+ navigation_findnearestwaypoint(it, true); // Walk FROM item.
+ });
+ IL_EACH(g_waypoints, true,
{
- w.wpflags |= WAYPOINTFLAG_DEAD_END;
- w.wpflags &= ~WAYPOINTFLAG_USEFUL;
+ it.wpflags |= WAYPOINTFLAG_DEAD_END;
+ it.wpflags &= ~WAYPOINTFLAG_USEFUL;
// WP is useful if:
- if (w.wpflags & WAYPOINTFLAG_ITEM)
- w.wpflags |= WAYPOINTFLAG_USEFUL;
- if (w.wpflags & WAYPOINTFLAG_TELEPORT)
- w.wpflags |= WAYPOINTFLAG_USEFUL;
- if (w.wpflags & WAYPOINTFLAG_PROTECTED)
- w.wpflags |= WAYPOINTFLAG_USEFUL;
+ 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.
- for (w1 = NULL; (w1 = find(w1, classname, "waypoint")); )
+ IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_PERSONAL),
{
- if (w1.wpflags & WAYPOINTFLAG_PERSONAL)
- continue;
- for (i = 0; i < 32; ++i)
+ for (int m = 0; m < 32; ++m)
{
- w = waypoint_get_link(w1, i);
+ entity w = waypoint_get_link(it, m);
if (!w)
break;
if (w.wpflags & WAYPOINTFLAG_PERSONAL)
continue;
if (w.wpflags & WAYPOINTFLAG_USEFUL)
continue;
- for (j = 0; j < 32; ++j)
+ for (int j = 0; j < 32; ++j)
{
- w2 = waypoint_get_link(w, j);
+ entity w2 = waypoint_get_link(w, j);
if (!w2)
break;
- if (w1 == w2)
+ if (it == w2)
continue;
if (w2.wpflags & WAYPOINTFLAG_PERSONAL)
continue;
- // If we got here, w1 != w2 exist with w1 -> w
+ // 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 (k = 0; k < 32; ++k)
+ for (int k = 0; k < 32; ++k)
{
- if (waypoint_get_link(w1, k) == w2)
+ if (waypoint_get_link(it, k) == w2)
continue;
// IF WE GET HERE, w is proven useful
- // to get from w1 to w2!
+ // 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.
- for (w = NULL; (w = find(w, classname, "waypoint")); )
+ IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)),
{
- if (!(w.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)))
- {
- LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", w.origin);
- te_explosion(w.origin);
- waypoint_remove(w);
- break;
- }
- }
- for (w = NULL; (w = find(w, classname, "waypoint")); )
- w.wpflags &= ~(WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END); // temp flag
+ 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()
if (!sv_foginterval && world.fog != "")
stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
- if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)))
+ if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AvailableTeams() == 2))
if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
send_CSQC_teamnagger();
do_crouch = false;
} else if (STAT(FROZEN, this)) {
do_crouch = false;
- } else if ((PS(this).m_weapon == WEP_SHOTGUN || PS(this).m_weapon == WEP_SHOCKWAVE) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) {
- // WEAPONTODO: predict
+ } 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;
}
IMPULSE(navwaypoint_unreachable)
{
if (!autocvar_g_waypointeditor) return;
- for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
+ IL_EACH(g_waypoints, true,
{
- e.colormod = '0.5 0.5 0.5';
- e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
- }
+ it.colormod = '0.5 0.5 0.5';
+ it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
+ });
entity e2 = navigation_findnearestwaypoint(this, false);
navigation_markroutes(this, e2);
- int i, m;
+ int j, m;
- i = 0;
+ j = 0;
m = 0;
- for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
+ IL_EACH(g_waypoints, it.wpcost >= 10000000,
{
- if (e.wpcost < 10000000) continue;
- LOG_INFO("unreachable: ", etos(e), " ", vtos(e.origin), "\n");
- e.colormod_z = 8;
- e.effects |= EF_NODEPTHTEST | EF_BLUE;
- ++i;
+ LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
+ it.colormod_z = 8;
+ it.effects |= EF_NODEPTHTEST | EF_BLUE;
+ ++j;
++m;
- }
- if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i);
+ });
+ if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
navigation_markroutes_inverted(e2);
- i = 0;
- for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
+ j = 0;
+ IL_EACH(g_waypoints, it.wpcost >= 10000000,
{
- if (e.wpcost < 10000000) continue;
- LOG_INFO("cannot reach me: ", etos(e), " ", vtos(e.origin), "\n");
- e.colormod_x = 8;
- if (!(e.effects & EF_NODEPTHTEST)) // not already reported before
+ LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n");
+ it.colormod_x = 8;
+ if (!(it.effects & EF_NODEPTHTEST)) // not already reported before
++m;
- e.effects |= EF_NODEPTHTEST | EF_RED;
- ++i;
- }
- if (i) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", i);
+ it.effects |= EF_NODEPTHTEST | EF_RED;
+ ++j;
+ });
+ if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j);
if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
- i = 0;
- for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain)
+ j = 0;
+ FOREACH_ENTITY_CLASS("info_player_deathmatch", true,
{
- vector org = e.origin;
- tracebox(e.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), e.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
- setorigin(e, trace_endpos);
- if (navigation_findnearestwaypoint(e, false))
+ vector org = it.origin;
+ tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
+ setorigin(it, trace_endpos);
+ if (navigation_findnearestwaypoint(it, false))
{
- setorigin(e, org);
- e.effects &= ~EF_NODEPTHTEST;
- e.model = "";
+ setorigin(it, org);
+ it.effects &= ~EF_NODEPTHTEST;
+ it.model = "";
}
else
{
- setorigin(e, org);
- LOG_INFO("spawn without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
- e.effects |= EF_NODEPTHTEST;
- _setmodel(e, this.model);
- e.frame = this.frame;
- e.skin = this.skin;
- e.colormod = '8 0.5 8';
- setsize(e, '0 0 0', '0 0 0');
- ++i;
+ setorigin(it, org);
+ LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
+ it.effects |= EF_NODEPTHTEST;
+ _setmodel(it, this.model);
+ it.frame = this.frame;
+ it.skin = this.skin;
+ it.colormod = '8 0.5 8';
+ setsize(it, '0 0 0', '0 0 0');
+ ++j;
}
- }
- if (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i);
+ });
+ if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
- i = 0;
- entity start = findchainflags(flags, FL_ITEM);
- for (entity e = start; e; e = e.chain)
+ j = 0;
+ FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
{
- e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
- e.colormod = '0.5 0.5 0.5';
- }
- for (entity e = start; e; e = e.chain)
+ it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
+ it.colormod = '0.5 0.5 0.5';
+ });
+ FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
{
- if (navigation_findnearestwaypoint(e, false)) continue;
- LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
- e.effects |= EF_NODEPTHTEST | EF_RED;
- e.colormod_x = 8;
- ++i;
- }
- if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", i);
+ if (navigation_findnearestwaypoint(it, false)) continue;
+ LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
+ it.effects |= EF_NODEPTHTEST | EF_RED;
+ it.colormod_x = 8;
+ ++j;
+ });
+ if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j);
- i = 0;
- for (entity e = start; e; e = e.chain)
+ j = 0;
+ FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
{
- if (navigation_findnearestwaypoint(e, true)) continue;
- LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
- e.effects |= EF_NODEPTHTEST | EF_BLUE;
- e.colormod_z = 8;
- ++i;
- }
- if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", i);
+ if (navigation_findnearestwaypoint(it, true)) continue;
+ LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
+ it.effects |= EF_NODEPTHTEST | EF_BLUE;
+ it.colormod_z = 8;
+ ++j;
+ });
+ if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);
}
if (arg_lower == "list") { print_to(caller, monsterlist_reply); return; }
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(
- if(it.realowner == caller)
- ++tmp_moncount;
- ));
+ IL_EACH(g_monsters, it.realowner == caller,
+ {
+ ++tmp_moncount;
+ });
if (!autocvar_g_monsters) { print_to(caller, "Monsters are disabled"); return; }
if (autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { print_to(caller, "Monster spawning is disabled"); return; }
int tmp_remcount = 0;
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(
+ IL_EACH(g_monsters, true,
+ {
Monster_Remove(it);
++tmp_remcount;
- ));
+ });
+ IL_CLEAR(g_monsters);
monsters_total = monsters_killed = totalspawned = 0;
.bool init_for_player_needed;
.void(entity this, entity player) init_for_player;
+
+IntrusiveList g_monsters;
+STATIC_INIT(g_monsters) { g_monsters = IL_NEW(); }
+
+IntrusiveList g_waypoints;
+STATIC_INIT(g_waypoints) { g_waypoints = IL_NEW(); }
{
// take players back into the past
FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_takeback(it, CS(it), time - lag));
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
- if(it != forent)
- antilag_takeback(it, it, time - lag);
+ IL_EACH(g_monsters, it != forent,
+ {
+ antilag_takeback(it, it, time - lag);
});
}
if (lag)
{
FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_restore(it, CS(it)));
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
- if (it != forent)
- antilag_restore(it, it);
+ IL_EACH(g_monsters, it != forent,
+ {
+ antilag_restore(it, it);
});
}
cvar_string = cvar_string_normal;
cvar_set = cvar_set_normal;
- remove = remove_unsafely;
+ delete_fn = remove_unsafely;
entity e = spawn();
setthink(e, GotoFirstMap);
error("world already spawned - you may have EXACTLY ONE worldspawn!");
world_already_spawned = true;
- remove = remove_safely; // during spawning, watch what you remove!
+ delete_fn = remove_safely; // during spawning, watch what you remove!
cvar_changes_init(); // do this very early now so it REALLY matches the server config
it.movetype = mt;
continue;
}
-
+
it.movetype = ((it.move_qcphysics) ? MOVETYPE_NONE : it.move_movetype);
if(it.move_movetype == MOVETYPE_NONE)
});
}
+void systems_update();
void EndFrame()
{
anticheat_endframe();
it.damage_dealt = 0;
antilag_record(it, CS(it), altime);
});
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
+ IL_EACH(g_monsters, true,
+ {
antilag_record(it, it, altime);
});
FOREACH_CLIENT(PS(it), {
PlayerState s = PS(it);
s.ps_push(s, it);
});
+ systems_update();
+ IL_ENDFRAME();
}
{
traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
}
+.bool ctrace_solidchanged;
void crosshair_trace_plusvisibletriggers(entity pl)
{
- entity first;
- entity e;
- first = findchainfloat(solid, SOLID_TRIGGER);
-
- for (e = first; e; e = e.chain)
- if (e.model != "")
- e.solid = SOLID_BSP;
+ FOREACH_ENTITY_FLOAT(solid, SOLID_TRIGGER,
+ {
+ if(it.model != "")
+ {
+ it.solid = SOLID_BSP;
+ it.ctrace_solidchanged = true;
+ }
+ });
crosshair_trace(pl);
- for (e = first; e; e = e.chain)
- e.solid = SOLID_TRIGGER;
+ FOREACH_ENTITY_FLOAT(ctrace_solidchanged, true,
+ {
+ it.solid = SOLID_TRIGGER;
+ it.ctrace_solidchanged = false;
+ });
}
void WarpZone_crosshair_trace(entity pl)
{
{
entity startoflist = initialize_entity_first;
initialize_entity_first = NULL;
- remove = remove_except_protected;
+ delete_fn = remove_except_protected;
for (entity e = startoflist; e; e = e.initialize_entity_next)
{
e.remove_except_protected_forbidden = 1;
}
e = next;
}
- remove = remove_unsafely;
+ delete_fn = remove_unsafely;
}
.float(entity) isEliminated;
// these stop the projectile from moving, so...
if(trace_dphitcontents == 0)
{
- //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
- LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %d, classname: %s, origin: %s)\n", etof(this), this.classname, vtos(this.origin));
+ LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin);
checkclient(this);
}
if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
// legacy bot code
void havocbot_goalrating_ast_targets(entity this, float ratingscale)
{
- entity ad, best, wp;
- float radius, bestvalue;
- bool found;
- vector p;
-
- ad = findchain(classname, "func_assault_destructible");
-
- for (; ad; ad = ad.chain)
+ FOREACH_ENTITY_CLASS("func_assault_destructible", it.bot_attack,
{
- if (ad.target == "")
- continue;
-
- if (!ad.bot_attack)
+ if (it.target == "")
continue;
- found = false;
- FOREACH_ENTITY_STRING(targetname, ad.target,
+ bool found = false;
+ FOREACH_ENTITY_STRING(targetname, it.target,
{
- if(it.classname == "target_objective_decrease")
+ if(it.classname != "target_objective_decrease")
+ continue;
+
+ if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE)
{
- if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE)
- {
- // dprint(etos(ad),"\n");
- found = true;
- break;
- }
+ found = true;
+ break;
}
});
if(!found)
- {
- /// dprint("target not found\n");
continue;
- }
- /// dprint("target #", etos(ad), " found\n");
-
- p = 0.5 * (ad.absmin + ad.absmax);
- // dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n");
- // te_knightspike(p);
- // te_lightning2(NULL, '0 0 0', p);
+ vector p = 0.5 * (it.absmin + it.absmax);
// Find and rate waypoints around it
found = false;
- best = NULL;
- bestvalue = 99999999999;
- for(radius=0; radius<1500 && !found; radius+=500)
+ entity best = NULL;
+ float bestvalue = 99999999999;
+ entity des = it;
+ for(float radius = 0; radius < 1500 && !found; radius += 500)
{
- for(wp=findradius(p, radius); wp; wp=wp.chain)
+ FOREACH_ENTITY_RADIUS(p, radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
{
- if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
- if(wp.classname=="waypoint")
- if(checkpvs(wp.origin, ad))
+ if(checkpvs(it.origin, des))
{
found = true;
- if(wp.cnt<bestvalue)
+ if(it.cnt < bestvalue)
{
- best = wp;
- bestvalue = wp.cnt;
+ best = it;
+ bestvalue = it.cnt;
}
}
- }
+ });
}
if(best)
this.havocbot_attack_time = 0;
- if(checkpvs(this.view_ofs,ad))
+ if(checkpvs(this.view_ofs,it))
if(checkpvs(this.view_ofs,best))
{
// dprint("increasing attack time for this target\n");
this.havocbot_attack_time = time + 2;
}
}
- }
+ });
}
void havocbot_role_ast_offense(entity this)
// scoreboard setup
void assault_ScoreRules()
{
- ScoreRules_basics(2, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, true);
+ int teams = 0;
+ teams |= BIT(0);
+ teams |= BIT(1); // always red vs blue
+
+ ScoreRules_basics(teams, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, true);
ScoreInfo_SetLabel_TeamScore( ST_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY);
ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY);
ScoreRules_basics_end();
if (ca_teams < 2) ca_teams = autocvar_g_ca_teams;
ca_teams = bound(2, ca_teams, 4);
- ScoreRules_basics(ca_teams, SFL_SORT_PRIO_PRIMARY, 0, true);
+ int teams = 0;
+ if(ca_teams >= 1) teams |= BIT(0);
+ if(ca_teams >= 2) teams |= BIT(1);
+ if(ca_teams >= 3) teams |= BIT(2);
+ if(ca_teams >= 4) teams |= BIT(3);
+
+ ca_teams = teams; // now set it?
+
+ ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY);
ScoreRules_basics_end();
void nades_Clear(entity player);
#define CA_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
-#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
+#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == NumTeams(ca_teams))
float CA_CheckWinner()
{
if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
prev_missing_teams_mask = -1;
return false;
}
- int missing_teams_mask = (!redalive) + (!bluealive) * 2;
- if(ca_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
- if(ca_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+ int missing_teams_mask = 0;
+ if(ca_teams & BIT(0))
+ missing_teams_mask += (!redalive) * 1;
+ if(ca_teams & BIT(1))
+ missing_teams_mask += (!bluealive) * 2;
+ if(ca_teams & BIT(2))
+ missing_teams_mask += (!yellowalive) * 4;
+ if(ca_teams & BIT(3))
+ missing_teams_mask += (!pinkalive) * 8;
if(prev_missing_teams_mask != missing_teams_mask)
{
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
void havocbot_goalrating_ctf_carrieritems(entity this, float ratingscale, vector org, float sradius)
{
- entity head;
- float t;
- head = findchainfloat(bot_pickup, true);
- while (head)
+ FOREACH_ENTITY_FLOAT(bot_pickup, true,
{
// gather health and armor only
- if (head.solid)
- if (head.health || head.armorvalue)
- if (vdist(head.origin - org, <, sradius))
+ if (it.solid)
+ if (it.health || it.armorvalue)
+ if (vdist(it.origin - org, <, sradius))
{
// get the value of the item
- t = head.bot_pickupevalfunc(this, head) * 0.0001;
+ float t = it.bot_pickupevalfunc(this, it) * 0.0001;
if (t > 0)
- navigation_routerating(this, head, t * ratingscale, 500);
+ navigation_routerating(this, it, t * ratingscale, 500);
}
- head = head.chain;
- }
+ });
}
void havocbot_ctf_reset_role(entity this)
{
switch(argv(1))
{
- case "red": _team = NUM_TEAM_1; break;
- case "blue": _team = NUM_TEAM_2; break;
- case "yellow": if(ctf_teams >= 3) _team = NUM_TEAM_3; break;
- case "pink": if(ctf_teams >= 4) _team = NUM_TEAM_4; break;
+ case "red": if(ctf_teams & BIT(0)) _team = NUM_TEAM_1; break;
+ case "blue": if(ctf_teams & BIT(1)) _team = NUM_TEAM_2; break;
+ case "yellow": if(ctf_teams & BIT(2)) _team = NUM_TEAM_3; break;
+ case "pink": if(ctf_teams & BIT(3)) _team = NUM_TEAM_4; break;
}
}
void ctf_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up.
{
- ctf_teams = 2;
+ ctf_teams = 0;
entity tmp_entity;
for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
{
- if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); }
- if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); }
+ //if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); }
+ //if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); }
+
+ switch(tmp_entity.team)
+ {
+ case NUM_TEAM_1: BITSET_ASSIGN(ctf_teams, BIT(0)); break;
+ case NUM_TEAM_2: BITSET_ASSIGN(ctf_teams, BIT(1)); break;
+ case NUM_TEAM_3: BITSET_ASSIGN(ctf_teams, BIT(2)); break;
+ case NUM_TEAM_4: BITSET_ASSIGN(ctf_teams, BIT(3)); break;
+ }
if(tmp_entity.team == 0) { ctf_oneflag = true; }
}
- ctf_teams = bound(2, ctf_teams, 4);
+ if(NumTeams(ctf_teams) < 2) // somehow, there's not enough flags!
+ {
+ ctf_teams = 0; // so set the default red and blue teams
+ BITSET_ASSIGN(ctf_teams, BIT(0));
+ BITSET_ASSIGN(ctf_teams, BIT(1));
+ }
+
+ //ctf_teams = bound(2, ctf_teams, 4);
// if no teams are found, spawn defaults
if(find(NULL, classname, "ctf_team") == NULL)
{
LOG_TRACE("No \"ctf_team\" entities found on this map, creating them anyway.\n");
- ctf_SpawnTeam("Red", NUM_TEAM_1);
- ctf_SpawnTeam("Blue", NUM_TEAM_2);
- if(ctf_teams >= 3)
+ if(ctf_teams & BIT(0))
+ ctf_SpawnTeam("Red", NUM_TEAM_1);
+ if(ctf_teams & BIT(1))
+ ctf_SpawnTeam("Blue", NUM_TEAM_2);
+ if(ctf_teams & BIT(2))
ctf_SpawnTeam("Yellow", NUM_TEAM_3);
- if(ctf_teams >= 4)
+ if(ctf_teams & BIT(3))
ctf_SpawnTeam("Pink", NUM_TEAM_4);
}
}
// scoreboard setup
-void ScoreRules_dom(float teams)
+void ScoreRules_dom(int teams)
{
if(domination_roundbased)
{
}
CheckAllowedTeams(NULL);
- domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2);
+ //domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2);
+
+ int teams = 0;
+ if(c1 >= 0) teams |= BIT(0);
+ if(c2 >= 0) teams |= BIT(1);
+ if(c3 >= 0) teams |= BIT(2);
+ if(c4 >= 0) teams |= BIT(3);
+ domination_teams = teams;
domination_roundbased = autocvar_g_domination_roundbased;
float autocvar_g_freezetag_warmup;
const float SP_FREEZETAG_REVIVALS = 4;
-void freezetag_ScoreRules(float teams)
+void freezetag_ScoreRules(int teams)
{
ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); // SFL_SORT_PRIO_PRIMARY
ScoreInfo_SetLabel_PlayerScore(SP_FREEZETAG_REVIVALS, "revivals", 0);
eliminatedPlayers.SendFlags |= 1;
}
#define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
-#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
+#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == NumTeams(freezetag_teams))
float freezetag_CheckTeams()
{
prev_missing_teams_mask = -1;
return 0;
}
- float missing_teams_mask = (!redalive) + (!bluealive) * 2;
- if(freezetag_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
- if(freezetag_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+ int missing_teams_mask = 0;
+ if(freezetag_teams & BIT(0))
+ missing_teams_mask += (!redalive) * 1;
+ if(freezetag_teams & BIT(1))
+ missing_teams_mask += (!bluealive) * 2;
+ if(freezetag_teams & BIT(2))
+ missing_teams_mask += (!yellowalive) * 4;
+ if(freezetag_teams & BIT(3))
+ missing_teams_mask += (!pinkalive) * 8;
if(prev_missing_teams_mask != missing_teams_mask)
{
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
if(freezetag_teams < 2)
freezetag_teams = autocvar_g_freezetag_teams;
freezetag_teams = bound(2, freezetag_teams, 4);
+
+ int teams = 0;
+ if(freezetag_teams >= 1) teams |= BIT(0);
+ if(freezetag_teams >= 2) teams |= BIT(1);
+ if(freezetag_teams >= 3) teams |= BIT(2);
+ if(freezetag_teams >= 4) teams |= BIT(3);
+
+ freezetag_teams = teams; // now set it?
freezetag_ScoreRules(freezetag_teams);
round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null);
{
if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
{
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it)));
+ IL_EACH(g_monsters, true,
+ {
+ Monster_Remove(it);
+ });
+ IL_CLEAR(g_monsters);
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER);
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER);
float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0;
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(
- if(it.health > 0)
- {
- if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
- ++supermonster_count;
- ++total_alive_monsters;
+ IL_EACH(g_monsters, it.health > 0,
+ {
+ if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
+ ++supermonster_count;
+ ++total_alive_monsters;
- if(teamplay)
- switch(it.team)
- {
- case NUM_TEAM_1: ++red_alive; break;
- case NUM_TEAM_2: ++blue_alive; break;
- case NUM_TEAM_3: ++yellow_alive; break;
- case NUM_TEAM_4: ++pink_alive; break;
- }
+ if(teamplay)
+ switch(it.team)
+ {
+ case NUM_TEAM_1: ++red_alive; break;
+ case NUM_TEAM_2: ++blue_alive; break;
+ case NUM_TEAM_3: ++yellow_alive; break;
+ case NUM_TEAM_4: ++pink_alive; break;
}
- ));
+ });
if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned)
{
));
}
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it)));
+ IL_EACH(g_monsters, true,
+ {
+ Monster_Remove(it);
+ });
+ IL_CLEAR(g_monsters);
if(teamplay)
{
return true;
}
-void invasion_ScoreRules(float inv_teams)
+void invasion_ScoreRules(int inv_teams)
{
if(inv_teams) { CheckAllowedTeams(NULL); }
ScoreRules_basics(inv_teams, 0, 0, false);
void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up.
{
if(autocvar_g_invasion_teams)
+ {
invasion_teams = bound(2, autocvar_g_invasion_teams, 4);
+ int teams = 0;
+ if(invasion_teams >= 1) teams |= BIT(0);
+ if(invasion_teams >= 2) teams |= BIT(1);
+ if(invasion_teams >= 3) teams |= BIT(2);
+ if(invasion_teams >= 4) teams |= BIT(3);
+
+ invasion_teams = teams; // now set it?
+ }
else
invasion_teams = 0;
const float SP_KH_PICKUPS = 7;
const float SP_KH_KCKILLS = 8;
const float SP_KH_LOSSES = 9;
-void kh_ScoreRules(float teams)
+void kh_ScoreRules(int teams)
{
ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
ScoreInfo_SetLabel_TeamScore( ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY);
}
else
{
- float missing_teams_mask = boolean(p1) + boolean(p2) * 2;
- if(kh_teams >= 3) missing_teams_mask += boolean(p3) * 4;
- if(kh_teams >= 4) missing_teams_mask += boolean(p4) * 8;
+ int missing_teams_mask = 0;
+ if(kh_teams & BIT(0))
+ missing_teams_mask += boolean(p1) * 1;
+ if(kh_teams & BIT(1))
+ missing_teams_mask += boolean(p2) * 2;
+ if(kh_teams & BIT(2))
+ missing_teams_mask += boolean(p3) * 4;
+ if(kh_teams & BIT(3))
+ missing_teams_mask += boolean(p4) * 8;
if(prev_missing_teams_mask != missing_teams_mask)
{
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
kh_teams = autocvar_g_keyhunt_teams;
kh_teams = bound(2, kh_teams, 4);
+ int teams = 0;
+ if(kh_teams >= 1) teams |= BIT(0);
+ if(kh_teams >= 2) teams |= BIT(1);
+ if(kh_teams >= 3) teams |= BIT(2);
+ if(kh_teams >= 4) teams |= BIT(3);
+
+ kh_teams = teams; // now set it?
+
// make a KH entity for controlling the game
kh_controller = spawn();
setthink(kh_controller, kh_Controller_Think);
{
ActivateTeamplay();
race_teams = bound(2, autocvar_g_race_teams, 4);
+ int teams = 0;
+ if(race_teams >= 1) teams |= BIT(0);
+ if(race_teams >= 2) teams |= BIT(1);
+ if(race_teams >= 3) teams |= BIT(2);
+ if(race_teams >= 4) teams |= BIT(3);
+
+ race_teams = teams; // now set it?
+
have_team_spawns = -1; // request team spawns
}
else
}
// code from here on is just to support maps that don't have team entities
-void tdm_SpawnTeam (string teamname, float teamcolor)
+void tdm_SpawnTeam (string teamname, int teamcolor)
{
entity this = new_pure(tdm_team);
this.netname = teamname;
- this.cnt = teamcolor;
- this.team = this.cnt + 1;
+ this.cnt = teamcolor - 1;
+ this.team = teamcolor;
this.spawnfunc_checked = true;
//spawnfunc_tdm_team(this);
}
{
LOG_TRACE("No \"tdm_team\" entities found on this map, creating them anyway.\n");
- int numteams = min(4, autocvar_g_tdm_teams_override);
+ int numteams = autocvar_g_tdm_teams_override;
if(numteams < 2) { numteams = autocvar_g_tdm_teams; }
numteams = bound(2, numteams, 4);
- float i;
- for(i = 1; i <= numteams; ++i)
- tdm_SpawnTeam(Team_ColorName(Team_NumberToTeam(i)), Team_NumberToTeam(i) - 1);
+ int teams = 0;
+ if(numteams >= 1) teams |= BIT(0);
+ if(numteams >= 2) teams |= BIT(1);
+ if(numteams >= 3) teams |= BIT(2);
+ if(numteams >= 4) teams |= BIT(3);
+
+ if(teams & BIT(0))
+ tdm_SpawnTeam("Red", NUM_TEAM_1);
+ if(teams & BIT(1))
+ tdm_SpawnTeam("Blue", NUM_TEAM_2);
+ if(teams & BIT(2))
+ tdm_SpawnTeam("Yellow", NUM_TEAM_3);
+ if(teams & BIT(3))
+ tdm_SpawnTeam("Pink", NUM_TEAM_4);
}
}
void pathlib_deletepath(entity start)
{
- entity e;
-
- e = findchainentity(owner, start);
- while(e)
+ FOREACH_ENTITY_ENT(owner, start,
{
- setthink(e, SUB_Remove);
- e.nextthink = time;
- e = e.chain;
- }
+ setthink(it, SUB_Remove);
+ it.nextthink = time;
+ });
}
//#define PATHLIB_NODEEXPIRE 0.05
entity pathlib_getbestopen()
{
- entity node;
- entity bestnode;
-
if(best_open_node)
{
++pathlib_bestcash_hits;
return best_open_node;
}
- node = findchainentity(owner,openlist);
- if(!node)
- return NULL;
-
- bestnode = node;
- while(node)
+ entity bestnode = NULL;
+ FOREACH_ENTITY_ENT(owner, openlist,
{
- ++pathlib_bestopen_seached;
- if(node.pathlib_node_f < bestnode.pathlib_node_f)
- bestnode = node;
+ ++pathlib_bestopen_searched;
- node = node.chain;
- }
+ if(!bestnode || it.pathlib_node_f < bestnode.pathlib_node_f)
+ bestnode = it;
+ });
return bestnode;
}
pathlib_made_cnt = 0;
pathlib_merge_cnt = 0;
pathlib_searched_cnt = 0;
- pathlib_bestopen_seached = 0;
+ pathlib_bestopen_searched = 0;
pathlib_bestcash_hits = 0;
pathlib_bestcash_saved = 0;
LOG_TRACE("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n");
LOG_TRACE("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n");
LOG_TRACE("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n");
- LOG_TRACE("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n");
+ LOG_TRACE("Nodes bestopen searched: ", ftos(pathlib_bestopen_searched),"\n");
LOG_TRACE("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n");
LOG_TRACE("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n");
LOG_TRACE("AStar done.\n");
entity pathlib_wpp_bestopen()
{
- entity n, best;
-
if(best_open_node)
return best_open_node;
- n = findchainentity(pathlib_list, openlist);
- best = n;
- while(n)
- {
- if(n.pathlib_node_f < best.pathlib_node_f)
- best = n;
+ entity best = NULL;
- n = n.chain;
- }
+ FOREACH_ENTITY_ENT(pathlib_list, openlist,
+ {
+ if(!best || it.pathlib_node_f < best.pathlib_node_f)
+ best = it;
+ });
return best;
entity pathlib_waypointpath(entity wp_from, entity wp_to, float callback)
{
- entity n;
float ptime;
ptime = gettime(GETTIME_REALTIME);
LOG_TRACE("pathlib_waypointpath init\n");
// Initialize waypoint grid
- // FIXME! presisted chain for better preformance
- for(n = findchain(classname, "waypoint"); n; n = n.chain)
+ IL_EACH(g_waypoints, true,
{
- n.pathlib_list = NULL;
- n.pathlib_node_g = 0;
- n.pathlib_node_f = 0;
- n.pathlib_node_h = 0;
-
- //setmodel(n, "models/runematch/rune.mdl");
- //n.effects = EF_LOWPRECISION;
- //n.colormod = '0 0 0';
- //n.scale = 1;
-
- }
+ it.pathlib_list = NULL;
+ it.pathlib_node_g = 0;
+ it.pathlib_node_f = 0;
+ it.pathlib_node_h = 0;
+
+ //setmodel(it, "models/runematch/rune.mdl");
+ //it.effects = EF_LOWPRECISION;
+ //it.colormod = '0 0 0';
+ //it.scale = 1;
+ });
goal_node = wp_to;
start_node = wp_from;
float pathlib_made_cnt;
float pathlib_merge_cnt;
float pathlib_searched_cnt;
-float pathlib_bestopen_seached;
+float pathlib_bestopen_searched;
float pathlib_bestcash_hits;
float pathlib_bestcash_saved;
float pathlib_gridsize;
return true;
}
-void ScoreInfo_Init(float teams)
+void ScoreInfo_Init(int teams)
{
if(scores_initialized)
{
scores_initialized = new_pure(ent_client_scoreinfo);
Net_LinkEntity(scores_initialized, false, 0, ScoreInfo_SendEntity);
}
- if(teams >= 1)
+ if(teams & BIT(0))
TeamScore_Spawn(NUM_TEAM_1, "Red");
- if(teams >= 2)
+ if(teams & BIT(1))
TeamScore_Spawn(NUM_TEAM_2, "Blue");
- if(teams >= 3)
+ if(teams & BIT(2))
TeamScore_Spawn(NUM_TEAM_3, "Yellow");
- if(teams >= 4)
+ if(teams & BIT(3))
TeamScore_Spawn(NUM_TEAM_4, "Pink");
}
#include "cl_client.qh"
#include "scores.qh"
+int ScoreRules_teams;
+
void CheckAllowedTeams (entity for_whom);
+int NumTeams(int teams)
+{
+ return boolean(teams & BIT(0)) + boolean(teams & BIT(1)) + boolean(teams & BIT(2)) + boolean(teams & BIT(3));
+}
+
+int AvailableTeams()
+{
+ return NumTeams(ScoreRules_teams);
+ // NOTE: this method is unreliable, as forced teams set the c* globals to weird values
+ //return boolean(c1 >= 0) + boolean(c2 >= 0) + boolean(c3 >= 0) + boolean(c4 >= 0);
+}
+
// NOTE: SP_ constants may not be >= MAX_SCORE; ST_constants may not be >= MAX_TEAMSCORE
// scores that should be in all modes:
-float ScoreRules_teams;
-void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabled)
+void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled)
{
float i;
for(i = 0; i < MAX_SCORE; ++i)
if(teamplay)
{
CheckAllowedTeams(NULL);
- ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
+ int teams = 0;
+ if(c1 >= 0) teams |= BIT(0);
+ if(c2 >= 0) teams |= BIT(1);
+ if(c3 >= 0) teams |= BIT(2);
+ if(c4 >= 0) teams |= BIT(3);
+ ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
}
else
ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
#pragma once
-void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabled);
+int NumTeams(int teams);
+int AvailableTeams();
+void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled);
void ScoreRules_basics_end();
void ScoreRules_generic();
void flocker_hunter_think(entity this)
{
vector dodgemove,attractmove,newmove;
- entity e,ee;
- float d,bd;
+ entity ee;
this.angles_x = this.angles.x * -1;
makevectors(this.angles);
if(!this.enemy)
{
- e = findchainfloat(flock_id,this.flock_id);
- while(e)
+ FOREACH_ENTITY_FLOAT(flock_id, this.flock_id,
{
- d = vlen(this.origin - e.origin);
-
- if(e != this.owner)
- if(e != ee)
- if(d > bd)
- {
- this.enemy = e;
- bd = d;
- }
- e = e.chain;
- }
+ if(it == this.owner || it == ee)
+ continue;
+
+ if(!this.enemy || vlen2(this.origin - it.origin) > vlen2(this.origin - this.enemy.origin))
+ this.enemy = it;
+ });
}
if(this.enemy)
this.velocity = movelib_inertmove_byspeed(this, newmove,1250,0.3,0.7);
this.velocity = movelib_dragvec(this, 0.01,0.5);
-
this.angles = vectoangles(this.velocity);
this.nextthink = time + 0.1;
}
float game_delay;
float game_delay_last;
-bool autocvar_sv_autopause = true;
+bool autocvar_sv_autopause = false;
float RedirectionThink();
-void PM_Main(Client this);
+void systems_update();
+void sys_phys_update(entity this, float dt);
void StartFrame()
{
// TODO: if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
- FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), PM_Main(it));
+ FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), sys_phys_update(it, frametime));
FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), PlayerPreThink(it));
execute_next_frame();
if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(true);
- remove = remove_unsafely; // not during spawning!
+ delete_fn = remove_unsafely; // not during spawning!
serverprevtime = servertime;
servertime = time;
serverframetime = frametime;
var float(string name) cvar;
var string(string name) cvar_string;
var void(string name, string value) cvar_set;
-var void remove(entity e);
+var void delete_fn(entity e);
#undef IT_SHOTGUN
#undef IT_SUPER_SHOTGUN
#pragma once
#define droptofloor builtin_droptofloor
-#define remove builtin_remove
#define cvar_set builtin_cvar_set
#define cvar_string builtin_cvar_string
#define cvar builtin_cvar
if(!mutator_returnvalue)
{
- if(dm >= 4)
- c1 = c2 = c3 = c4 = 0;
- else if(dm >= 3)
- c1 = c2 = c3 = 0;
- else
- c1 = c2 = 0;
+ if(dm & BIT(0)) c1 = 0;
+ if(dm & BIT(1)) c2 = 0;
+ if(dm & BIT(2)) c3 = 0;
+ if(dm & BIT(3)) c4 = 0;
}
// find out what teams are allowed if necessary
}
// TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line)
- if(c3==-1 && c4==-1)
+ if(AvailableTeams() == 2)
if(autocvar_bot_vs_human && for_whom)
{
if(autocvar_bot_vs_human > 0)
{
- // bots are all blue
+ // find last team available
+
if(IS_BOT_CLIENT(for_whom))
- c1 = c3 = c4 = -1;
+ {
+ if(c4 >= 0) { c3 = c2 = c1 = -1; }
+ else if(c3 >= 0) { c4 = c2 = c1 = -1; }
+ else { c4 = c3 = c1 = -1; }
+ // no further cases, we know at least 2 teams exist
+ }
else
- c2 = -1;
+ {
+ if(c1 >= 0) { c2 = c3 = c4 = -1; }
+ else if(c2 >= 0) { c1 = c3 = c4 = -1; }
+ else { c1 = c2 = c4 = -1; }
+ // no further cases, bots have one of the teams
+ }
}
else
{
- // bots are all red
+ // find first team available
+
if(IS_BOT_CLIENT(for_whom))
- c2 = c3 = c4 = -1;
+ {
+ if(c1 >= 0) { c2 = c3 = c4 = -1; }
+ else if(c2 >= 0) { c1 = c3 = c4 = -1; }
+ else { c1 = c2 = c4 = -1; }
+ // no further cases, we know at least 2 teams exist
+ }
else
- c1 = -1;
+ {
+ if(c4 >= 0) { c3 = c2 = c1 = -1; }
+ else if(c3 >= 0) { c4 = c2 = c1 = -1; }
+ else { c4 = c3 = c1 = -1; }
+ // no further cases, bots have one of the teams
+ }
}
}
// NOTE: Assumes CheckAllowedTeams has already been called!
float FindSmallestTeam(entity pl, float ignore_pl)
{
- float totalteams, t;
- totalteams = 0;
+ int totalteams = 0;
+ int t = 1; // initialize with a random team?
+ if(c4 >= 0) t = 4;
+ if(c3 >= 0) t = 3;
+ if(c2 >= 0) t = 2;
+ if(c1 >= 0) t = 1;
// find out what teams are available
//CheckAllowedTeams();
RandomSelection_Init();
- t = 1;
+ if(TeamSmallerEqThanTeam(1, t, pl))
+ t = 1;
if(TeamSmallerEqThanTeam(2, t, pl))
t = 2;
if(TeamSmallerEqThanTeam(3, t, pl))
if(lag)
{
FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_takeback(it, CS(it), time - lag));
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
- if (it != this)
- antilag_takeback(it, it, time - lag);
+ IL_EACH(g_monsters, it != this,
+ {
+ antilag_takeback(it, it, time - lag);
});
}
if(lag)
{
FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_restore(it, CS(it)));
- FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
- if (it != this)
- antilag_restore(it, it);
+ IL_EACH(g_monsters, it != this,
+ {
+ antilag_restore(it, it);
});
}
if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
{
- int act = (fr == WFRAME_FIRE2 && (PS(actor).m_weapon == WEP_SHOCKWAVE || PS(actor).m_weapon == WEP_SHOTGUN))
+ bool primary_melee = boolean(fr == WFRAME_FIRE1 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI));
+ bool secondary_melee = boolean(fr == WFRAME_FIRE2 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC));
+ int act = (primary_melee || secondary_melee)
? ANIMACTION_MELEE
: ANIMACTION_SHOOT
;
sp_permit_cpp11_shift = false # false/true
# Add or remove space before '(' of 'if', 'for', 'switch', and 'while'
-sp_before_sparen = add # ignore/add/remove/force #force
+sp_before_sparen = force # ignore/add/remove/force #force
# Add or remove space inside if-condition '(' and ')'
# NOTE: is 68 worse than ignore
sp_inside_sparen_open = ignore # ignore/add/remove/force #force
# Add or remove space after ')' of 'if', 'for', 'switch', and 'while'
-sp_after_sparen = add # ignore/add/remove/force
+sp_after_sparen = force # ignore/add/remove/force
# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
-sp_sparen_brace = add # ignore/add/remove/force
+sp_sparen_brace = force # ignore/add/remove/force
# Add or remove space between 'invariant' and '(' in the D language.
sp_invariant_paren = ignore # ignore/add/remove/force #ignore
sp_macro_func = remove # ignore/add/remove/force #force
# Add or remove space between 'else' and '{' if on the same line
-sp_else_brace = add # ignore/add/remove/force
+sp_else_brace = force # ignore/add/remove/force
# Add or remove space between '}' and 'else' if on the same line
# WARNING: Code doesn't seem to use this feature - delete from the config?
-sp_brace_else = ignore # ignore/add/remove/force
+sp_brace_else = force # ignore/add/remove/force
# Add or remove space between '}' and the name of a typedef on the same line
sp_brace_typedef = add # ignore/add/remove/force
# Add or remove newline between 'if' and '{'
# NOTE: is 136 worse than ignore
-nl_if_brace = add # ignore/add/remove/force
+nl_if_brace = remove # ignore/add/remove/force
# Add or remove newline between '}' and 'else'
-nl_brace_else = add # ignore/add/remove/force
+nl_brace_else = remove # ignore/add/remove/force
# Add or remove newline between 'else if' and '{'
# If set to ignore, nl_if_brace is used instead
-nl_elseif_brace = add # ignore/add/remove/force
+nl_elseif_brace = remove # ignore/add/remove/force
# Add or remove newline between 'else' and '{'
-nl_else_brace = add # ignore/add/remove/force
+nl_else_brace = remove # ignore/add/remove/force
# Add or remove newline between 'else' and 'if'
nl_else_if = remove # ignore/add/remove/force
# Add or remove newline between 'finally' and '{'
# WARNING: Code doesn't seem to use this feature - delete from the config?
-nl_finally_brace = ignore # ignore/add/remove/force
+nl_finally_brace = remove # ignore/add/remove/force
# Add or remove newline between 'try' and '{'
# WARNING: Code doesn't seem to use this feature - delete from the config?
# Add or remove newline between 'for' and '{'
# NOTE: is 109 worse than ignore
-nl_for_brace = add # ignore/add/remove/force
+nl_for_brace = remove # ignore/add/remove/force
# Add or remove newline between 'catch' and '{'
# WARNING: Code doesn't seem to use this feature - delete from the config?
# Add or remove newline between 'while' and '{'
# NOTE: is 22 worse than ignore
-nl_while_brace = add # ignore/add/remove/force
+nl_while_brace = remove # ignore/add/remove/force
# Add or remove newline between 'scope (x)' and '{' (D)
# WARNING: Code doesn't seem to use this feature - delete from the config?
#
# Add or remove braces on single-line 'do' statement
-mod_full_brace_do = add # ignore/add/remove/force
+mod_full_brace_do = force # ignore/add/remove/force
# Add or remove braces on single-line 'for' statement
# NOTE: is 3 worse than ignore
-mod_full_brace_for = remove # ignore/add/remove/force
+mod_full_brace_for = force # ignore/add/remove/force
# Add or remove braces on single-line function definitions. (Pawn)
mod_full_brace_function = ignore # ignore/add/remove/force #ignore
# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'.
-mod_full_brace_if = add # ignore/add/remove/force
+mod_full_brace_if = force # ignore/add/remove/force
# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if.
# If any must be braced, they are all braced. If all can be unbraced, then the braces are removed.
-mod_full_brace_if_chain = true # false/true #force
+mod_full_brace_if_chain = false # false/true #force
# Don't remove braces around statements that span N newlines
mod_full_brace_nl = 2 # number #force
# Add or remove braces on single-line 'while' statement
-mod_full_brace_while = remove # ignore/add/remove/force
+mod_full_brace_while = force # ignore/add/remove/force
# Add or remove braces on single-line 'using ()' statement
# WARNING: Code doesn't seem to use this feature - delete from the config?
mod_full_paren_if_bool = false # false/true
# Whether to remove superfluous semicolons
-mod_remove_extra_semicolon = false # false/true
+mod_remove_extra_semicolon = true # false/true
# If a function body exceeds the specified number of newlines and doesn't have a comment after
# the close brace, a comment will be added.