- 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=d17f62e08ff11b8b25116fa4a64f7e86
+ - EXPECT=56bf79831a0a35ab6a3a420e26b09945
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
seta hud_panel_quickmenu_translatecommands 0 "when the game is translated, translate strings inside commands too (useful for chat commands)"
seta hud_panel_quickmenu_time 5 "quickmenu expires after this number of seconds in the same page"
+seta hud_panel_infomessages_group_time 6 "number of seconds a message of a group lasts before it gets changed"
+seta hud_panel_infomessages_group_fadetime 0.4 "group message fade in/out time"
+
// hud panel aliases
alias quickmenu "cl_cmd hud quickmenu ${* ?}"
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_stick 0
-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
// {{{ 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
seta hud_panel_engineinfo_framecounter_time "0.1"
seta hud_panel_engineinfo_framecounter_decimals "0"
-seta hud_panel_infomessages_pos "0.720000 0.100000"
+seta hud_panel_infomessages_pos "0.680000 0.100000"
seta hud_panel_infomessages_size "0.280000 0.080000"
seta hud_panel_infomessages_bg "0"
seta hud_panel_infomessages_bg_color ""
seta hud_panel_engineinfo_framecounter_time "0.1"
seta hud_panel_engineinfo_framecounter_decimals "0"
-seta hud_panel_infomessages_pos "0.720000 0.100000"
+seta hud_panel_infomessages_pos "0.680000 0.100000"
seta hud_panel_infomessages_size "0.280000 0.080000"
seta hud_panel_infomessages_bg "0"
seta hud_panel_infomessages_bg_color ""
seta hud_panel_engineinfo_framecounter_decimals "0"
seta hud_panel_infomessages_pos "0.710000 0"
-seta hud_panel_infomessages_size "0.290000 0.100000"
+seta hud_panel_infomessages_size "0.280000 0.090000"
seta hud_panel_infomessages_bg "0"
seta hud_panel_infomessages_bg_color ""
seta hud_panel_infomessages_bg_color_team ""
seta hud_panel_engineinfo_framecounter_decimals "0"
seta hud_panel_infomessages_pos "0.710000 0"
-seta hud_panel_infomessages_size "0.290000 0.100000"
+seta hud_panel_infomessages_size "0.280000 0.090000"
seta hud_panel_infomessages_bg "0"
seta hud_panel_infomessages_bg_color ""
seta hud_panel_infomessages_bg_color_team ""
// Info messages panel (#14)
-#define drawInfoMessage(s) MACRO_BEGIN { \
- if(autocvar_hud_panel_infomessages_flip) \
- o.x = pos.x + mySize.x - stringwidth(s, true, fontsize); \
- drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); \
- o.y += fontsize.y; \
+float autocvar_hud_panel_infomessages_group_fadetime = 0.4;
+float autocvar_hud_panel_infomessages_group_time = 6;
+const int IMG_COUNT = 1; // number of InfoMessage Groups
+float img_fade[IMG_COUNT];
+int img_cur_msg[IMG_COUNT];
+float img_time[IMG_COUNT];
+
+int img_select(int group_id)
+{
+ float fadetime = max(0.001, autocvar_hud_panel_infomessages_group_fadetime);
+ if(time > img_time[group_id])
+ {
+ img_fade[group_id] = max(0, img_fade[group_id] - frametime / fadetime);
+ if(!img_fade[group_id])
+ {
+ ++img_cur_msg[group_id];
+ img_time[group_id] = floor(time) + autocvar_hud_panel_infomessages_group_time;
+ }
+ }
+ else
+ img_fade[group_id] = min(1, img_fade[group_id] + frametime / fadetime);
+ return img_cur_msg[group_id];
+}
+
+float stringwidth_colors(string s, vector theSize);
+vector InfoMessages_drawstring(string s, vector pos, vector sz, float a, vector fontsize)
+{
+ getWrappedLine_remaining = s;
+ float offset = 0;
+ while(getWrappedLine_remaining)
+ {
+ s = getWrappedLine(sz.x - offset, fontsize, stringwidth_colors);
+ if(autocvar_hud_panel_infomessages_flip)
+ offset = sz.x - stringwidth_colors(s, fontsize) - offset;
+ drawcolorcodedstring(pos + eX * offset, s, fontsize, a, DRAWFLAG_NORMAL);
+ pos.y += fontsize.y;
+ offset = fontsize.x;
+ }
+ pos.y += fontsize.y * 0.25;
+ return pos;
+}
+
+#define InfoMessage(s) MACRO_BEGIN { \
+ pos = InfoMessages_drawstring(s, pos, mySize, ((img_curr_group >= 0) ? panel_fg_alpha * img_fade[img_curr_group] : panel_fg_alpha), fontsize); \
+ img_curr_group = -1; \
} MACRO_END
+
void HUD_InfoMessages()
{
if(!autocvar__hud_configure)
mySize -= '2 2 0' * panel_bg_padding;
}
- // always force 5:1 aspect
- vector newSize = '0 0 0';
- if(mySize.x/mySize.y > 5)
- {
- newSize.x = 5 * mySize.y;
- newSize.y = mySize.y;
-
- pos.x = pos.x + (mySize.x - newSize.x) / 2;
- }
- else
- {
- newSize.y = 1/5 * mySize.x;
- newSize.x = mySize.x;
-
- pos.y = pos.y + (mySize.y - newSize.y) / 2;
- }
-
- mySize = newSize;
- entity tm;
- vector o;
- o = pos;
-
- vector fontsize;
- fontsize = '0.20 0.20 0' * mySize.y;
-
- float a;
- a = panel_fg_alpha;
-
+ vector fontsize = '0.2 0.2 0' * mySize.y;
string s;
+ int img_curr_group = -1;
if(!autocvar__hud_configure)
{
if(spectatee_status)
{
- a = 1;
if(spectatee_status == -1)
s = _("^1Observing");
else
s = sprintf(_("^1Spectating: ^7%s"), entcs_GetName(current_player));
- drawInfoMessage(s);
+ InfoMessage(s);
- if(spectatee_status == -1)
- s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
- else
- s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
- drawInfoMessage(s);
-
- if(spectatee_status == -1)
- s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
- else
- s = sprintf(_("^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode"), getcommandkey("secondary fire", "+fire2"), getcommandkey("drop weapon", "dropweapon"));
- drawInfoMessage(s);
-
- s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info"));
- drawInfoMessage(s);
+ img_curr_group = 0;
+ switch(img_select(img_curr_group) % 3)
+ {
+ default:
+ case 0:
+ if(spectatee_status == -1)
+ s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey(_("primary fire"), "+fire"));
+ else
+ s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey(_("next weapon"), "weapnext"), getcommandkey(_("previous weapon"), "weapprev"));
+ break;
+ case 1:
+ if(spectatee_status == -1)
+ s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey(_("next weapon"), "weapnext"), getcommandkey(_("previous weapon"), "weapprev"));
+ else
+ s = sprintf(_("^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode"), getcommandkey(_("secondary fire"), "+fire2"), getcommandkey(_("drop weapon"), "dropweapon"));
+ break;
+ case 2:
+ s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey(_("server info"), "+show_info"));
+ break;
+ }
+ InfoMessage(s);
if(gametype == MAPINFO_TYPE_LMS)
{
else if(sk.(scores[ps_primary]) > 0)
s = _("^1You have no more lives left");
else
- s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+ s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump"));
}
else
- s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
- drawInfoMessage(s);
+ s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump"));
+ InfoMessage(s);
}
if (time < STAT(GAMESTARTTIME))
//we need to ceil, otherwise the countdown would be off by .5 when using round()
float countdown = ceil(STAT(GAMESTARTTIME) - time);
s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown);
- drawInfoMessage(s);
+ InfoMessage(s);
}
if(warmup_stage)
{
s = _("^2Currently in ^1warmup^2 stage!");
- drawInfoMessage(s);
+ InfoMessage(s);
}
string blinkcolor;
if(ready_waiting_for_me)
{
if(warmup_stage)
- s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+ s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey(_("ready"), "ready"), blinkcolor);
else
- s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+ s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey(_("ready"), "ready"), blinkcolor);
}
else
{
else
s = _("^2Waiting for others to ready up...");
}
- drawInfoMessage(s);
+ InfoMessage(s);
}
else if(warmup_stage && !spectatee_status)
{
- s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready"));
- drawInfoMessage(s);
+ s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey(_("ready"), "ready"));
+ InfoMessage(s);
}
if(teamplay && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger)
{
float ts_min = 0, ts_max = 0;
- tm = teams.sort_next;
+ entity tm = teams.sort_next;
if (tm)
{
for (; tm.sort_next; tm = tm.sort_next)
{
s = strcat(blinkcolor, _("Teamnumbers are unbalanced!"));
tm = GetTeam(myteam, false);
- if (tm)
- if (tm.team != NUM_SPECTATOR)
- if (tm.team_size == ts_max)
- s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor));
- drawInfoMessage(s);
+ if (tm && tm.team != NUM_SPECTATOR && tm.team_size == ts_max)
+ s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey(_("team menu"), "menu_showteamselect"), blinkcolor));
+ InfoMessage(s);
}
}
}
}
else
{
- s = _("^7Press ^3ESC ^7to show HUD options.");
- drawInfoMessage(s);
- s = _("^3Doubleclick ^7a panel for panel-specific options.");
- drawInfoMessage(s);
- s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and");
- drawInfoMessage(s);
- s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.");
- drawInfoMessage(s);
+ InfoMessage(_("^7Press ^3ESC ^7to show HUD options."));
+ InfoMessage(_("^3Doubleclick ^7a panel for panel-specific options."));
+ InfoMessage(_("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and"));
+ InfoMessage(_("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments."));
}
}
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) \
panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight;
}
- // these must be below above block
vector pos, mySize;
pos = panel_pos;
mySize = panel_size;
drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL);
// print the yes/no counts
- s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount);
- drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL);
- s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount);
- drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL);
+ s = sprintf("^2%s ^7(%d)", getcommandkey_forcename(_("Yes"), "vyes"), vote_yescount);
+ drawcolorcodedstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, a, DRAWFLAG_NORMAL);
+ s = sprintf("^1%s ^7(%d)", getcommandkey_forcename(_("No"), "vno"), vote_nocount);
+ drawcolorcodedstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, a, DRAWFLAG_NORMAL);
// draw the progress bar backgrounds
drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
}
}
-string getcommandkey(string text, string command)
+string _getcommandkey(string cmd_name, string command, bool forcename)
{
string keys;
float n, j, k, l = 0;
if (!autocvar_hud_showbinds)
- return text;
+ return cmd_name;
keys = db_get(binddb, command);
if (keys == "")
if (keys == "NO_KEY") {
if (autocvar_hud_showbinds > 1)
- return sprintf(_("%s (not bound)"), text);
+ return sprintf(_("%s (not bound)"), cmd_name);
else
- return text;
+ return cmd_name;
}
- else if (autocvar_hud_showbinds > 1)
- return sprintf("%s (%s)", text, keys);
+ else if (autocvar_hud_showbinds > 1 || forcename)
+ return sprintf("%s (%s)", cmd_name, keys);
else
return keys;
}
void Fog_Force();
-string getcommandkey(string text, string command);
+string _getcommandkey(string text, string command, bool forcename);
+#define getcommandkey(cmd_name, command) _getcommandkey(cmd_name, command, false)
+#define getcommandkey_forcename(cmd_name, command) _getcommandkey(cmd_name, command, true)
string vote_called_vote;
float ready_waiting;
);
}
else if(time >= respawn_time)
- str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
+ str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey(_("jump"), "+jump"));
pos.y += 1.2 * hud_fontsize.y;
drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
{
case 1: // crosshair_color_per_weapon
{
- if(this != WEP_Null)
+ if(this != WEP_Null && hud == HUD_NORMAL)
{
wcross_color = this.wpcolor;
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;
}
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);
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
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);
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)
MSG_CENTER_NOTIF(CTF_PASS_OTHER_NEUTRAL, 1, 2, 0, "s1 s2", CPID_CTF_PASS, "0 0", _("^BG%s^BG passed the flag to %s"), "")
MULTITEAM_CENTER(CTF_PASS_RECEIVED, 4, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou received the ^TC^TT^BG flag from %s"), "", FLAG)
MSG_CENTER_NOTIF(CTF_PASS_RECEIVED_NEUTRAL, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou received the flag from %s"), "")
- MSG_CENTER_NOTIF(CTF_PASS_REQUESTED, 1, 1, 0, "s1 pass_key", CPID_CTF_PASS, "0 0", _("^BG%s^BG requests you to pass the flag%s"), "")
+ MSG_CENTER_NOTIF(CTF_PASS_REQUESTED, 1, 1, 0, "pass_key s1", CPID_CTF_PASS, "0 0", _("^BGPress ^F2%s^BG to receive the flag from %s^BG"), "")
MSG_CENTER_NOTIF(CTF_PASS_REQUESTING, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGRequesting %s^BG to pass you the flag"), "")
MULTITEAM_CENTER(CTF_PASS_SENT, 4, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou passed the ^TC^TT^BG flag to %s"), "", FLAG)
MSG_CENTER_NOTIF(CTF_PASS_SENT_NEUTRAL, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou passed the flag to %s"), "")
MSG_CENTER_NOTIF(ONS_GENERATOR_SHIELDED, 1, 0, 0, "", CPID_ONS_CAPSHIELD, "0 0", _("^BGThe enemy generator cannot be destroyed yet\n^F2Capture some control points to unshield it"), "")
MULTITEAM_CENTER(ONS_NOTSHIELDED, 4, 1, 0, 0, "", CPID_ONSLAUGHT, "0 0", _("^BGThe ^TCenemy^BG generator is no longer shielded!"), "", NAME)
MSG_CENTER_NOTIF(ONS_NOTSHIELDED_TEAM, 1, 0, 0, "", CPID_ONSLAUGHT, "0 0", _("^K1Your generator is NOT shielded!\n^BGRe-capture control points to shield it!"), "")
- MSG_CENTER_NOTIF(ONS_TELEPORT, 1, 0, 0, "pass_key", CPID_ONSLAUGHT, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to teleport"), "")
+ MSG_CENTER_NOTIF(ONS_TELEPORT, 1, 0, 0, "pass_key", CPID_ONSLAUGHT, "0 0", _("^BGPress ^F2%s^BG to teleport"), "")
MSG_CENTER_NOTIF(ONS_TELEPORT_ANTISPAM, 1, 0, 1, "f1secs", CPID_ONSLAUGHT, "0 0", _("^BGTeleporting disabled for %s"), "")
MSG_CENTER_NOTIF(OVERTIME_FRAG, 1, 0, 0, "", CPID_OVERTIME, "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!"))
MSG_CENTER_NOTIF(JOIN_PREVENT_MINIGAME, 1, 0, 0, "", CPID_Null, "0 0", _("^K1Cannot join given minigame session!"), "" )
- MSG_CENTER_NOTIF(VEHICLE_ENTER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to enter/exit the vehicle"), "")
- MSG_CENTER_NOTIF(VEHICLE_ENTER_GUNNER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to enter the vehicle gunner"), "")
- MSG_CENTER_NOTIF(VEHICLE_ENTER_STEAL, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to steal this vehicle"), "")
+ MSG_CENTER_NOTIF(VEHICLE_ENTER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2%s^BG to enter/exit the vehicle"), "")
+ MSG_CENTER_NOTIF(VEHICLE_ENTER_GUNNER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2%s^BG to enter the vehicle gunner"), "")
+ MSG_CENTER_NOTIF(VEHICLE_ENTER_STEAL, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2%s^BG to steal this vehicle"), "")
MSG_CENTER_NOTIF(VEHICLE_STEAL, 1, 0, 0, "", CPID_VEHICLES_OTHER, "0 0", _("^F2The enemy is stealing one of your vehicles!\n^F4Stop them!"), "")
MSG_CENTER_NOTIF(VEHICLE_STEAL_SELF, 1, 0, 0, "", CPID_VEHICLES_OTHER, "4 0", _("^F2You have stolen the enemy's vehicle, you are now visible on their radar!"), "")
ARG_CASE(ARG_CS_SV, "race_col", CCR(((f1 == 1) ? "^F1" : "^F2"))) \
ARG_CASE(ARG_CS_SV, "race_diff", ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
ARG_CASE(ARG_CS, "missing_teams", notif_arg_missing_teams(f1)) \
- ARG_CASE(ARG_CS, "pass_key", ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
+ ARG_CASE(ARG_CS, "pass_key", getcommandkey(_("drop flag"), "+use")) \
ARG_CASE(ARG_CS, "frag_ping", notif_arg_frag_ping(true, f2)) \
ARG_CASE(ARG_CS, "frag_stats", notif_arg_frag_stats(f2, f3, f4)) \
/*ARG_CASE(ARG_CS, "frag_pos", ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : ""))*/ \
}
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);
}
}
- 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;
if (toucher.flags & FL_PROJECTILE)
{
toucher.angles = vectoangles (toucher.velocity);
+ toucher.com_phys_gravity_factor = 1;
switch(toucher.move_movetype)
{
case MOVETYPE_FLY:
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;
- if (isnew) IL_PUSH(g_drawables_2d, axh);
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)
tmpPos.x = vehicleHud_Pos.x + vehicleHud_Size.x * (96/256) - tmpSize.x;
tmpPos.y = vehicleHud_Pos.y;
tmpSize = '1 1 1' * hud_fontsize;
- drawstring(tmpPos, sprintf(_("Press %s"), getcommandkey("dropweapon", "dropweapon")), tmpSize, '1 0 0' + '0 1 1' * tmpblinkValue, hudAlpha, DRAWFLAG_NORMAL);
+ drawstring(tmpPos, sprintf(_("Press %s"), getcommandkey(_("drop weapon"), "dropweapon")), tmpSize, '1 0 0' + '0 1 1' * tmpblinkValue, hudAlpha, DRAWFLAG_NORMAL);
}
// Model
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)
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)
// 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)
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 | WEP_TYPE_MELEE_SEC);
+/* 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");
// 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();
# SVQC
+Main loop:
+* SV_Physics()
+ * StartFrame()
+ * if (force_retouch)
+ * foreach entity:
+ * .touch()
+ * foreach client:
+ * PlayerPreThink()
+ * .think()
+ * PlayerPostThink()
+ * foreach nonclient:
+ * .think()
+ * EndFrame()
+
+
```
.entity clientcamera;
### 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
#define CSQC_Ent_Remove _CSQC_Ent_Remove
#endif
#undef ENGINE_EVENT
+
+#ifndef MENUQC
+ #include <ecs/_lib.qh>
+ #include <ecs/components/_mod.qh>
+#endif
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)
}
#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)
{
KEYBIND_DEF("+fire2" , _("secondary fire"));
KEYBIND_DEF("" , "");
KEYBIND_DEF("" , _("Weapon switching"));
- KEYBIND_DEF("weapprev" , _("previous"));
- KEYBIND_DEF("weapnext" , _("next"));
- KEYBIND_DEF("weaplast" , _("previously used"));
- KEYBIND_DEF("weapbest" , _("best"));
+ KEYBIND_DEF("weapprev" , CTX(_("WEAPON^previous")));
+ KEYBIND_DEF("weapnext" , CTX(_("WEAPON^next")));
+ KEYBIND_DEF("weaplast" , CTX(_("WEAPON^previously used")));
+ KEYBIND_DEF("weapbest" , CTX(_("WEAPON^best")));
KEYBIND_DEF("reload" , _("reload"));
int i;
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;
// 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)))
{
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();
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();
PlayerState s = PS(it);
s.ps_push(s, it);
});
+ systems_update();
IL_ENDFRAME();
}
// 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)
// 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);
{
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);
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);
}
}
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();
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(!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))
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.