- wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
- wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
- make
- - EXPECT=040aeef53953a85c5891c0c39cf9860f
+ - EXPECT=35117dbed178a88ab65576740023dc4b
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
set g_balance_arc_beam_tightness 0.6
set g_balance_arc_bolt 1
set g_balance_arc_bolt_ammo 1
+set g_balance_arc_bolt_bounce_count 0
+set g_balance_arc_bolt_bounce_explode 0
+set g_balance_arc_bolt_bounce_lifetime 0
+set g_balance_arc_bolt_count 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_lifetime 5
set g_balance_arc_bolt_radius 65
set g_balance_arc_bolt_refire 0.16667
+set g_balance_arc_bolt_refire2 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_beam_tightness 0.5
set g_balance_arc_bolt 0
set g_balance_arc_bolt_ammo 1
+set g_balance_arc_bolt_bounce_count 0
+set g_balance_arc_bolt_bounce_explode 0
+set g_balance_arc_bolt_bounce_lifetime 0
+set g_balance_arc_bolt_count 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_lifetime 5
set g_balance_arc_bolt_radius 65
set g_balance_arc_bolt_refire 0.16667
+set g_balance_arc_bolt_refire2 0.16667
set g_balance_arc_bolt_speed 2200
set g_balance_arc_bolt_spread 0.03
set g_balance_arc_burst_ammo 15
set g_balance_arc_beam_tightness 0.5
set g_balance_arc_bolt 0
set g_balance_arc_bolt_ammo 1
+set g_balance_arc_bolt_bounce_count 0
+set g_balance_arc_bolt_bounce_explode 0
+set g_balance_arc_bolt_bounce_lifetime 0
+set g_balance_arc_bolt_count 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_lifetime 5
set g_balance_arc_bolt_radius 65
set g_balance_arc_bolt_refire 0.16667
+set g_balance_arc_bolt_refire2 0.16667
set g_balance_arc_bolt_speed 2200
set g_balance_arc_bolt_spread 0.03
set g_balance_arc_burst_ammo 0
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_bounce_count 0
+set g_balance_arc_bolt_bounce_explode 0
+set g_balance_arc_bolt_bounce_lifetime 0
+set g_balance_arc_bolt_count 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_lifetime 5
set g_balance_arc_bolt_radius 65
set g_balance_arc_bolt_refire 0.033333
+set g_balance_arc_bolt_refire2 0.03333
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_beam_tightness 0.6
set g_balance_arc_bolt 1
set g_balance_arc_bolt_ammo 1
+set g_balance_arc_bolt_bounce_count 0
+set g_balance_arc_bolt_bounce_explode 0
+set g_balance_arc_bolt_bounce_lifetime 0
+set g_balance_arc_bolt_count 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_lifetime 5
set g_balance_arc_bolt_radius 65
set g_balance_arc_bolt_refire 0.16667
+set g_balance_arc_bolt_refire2 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_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
set g_freezetag_revive_nade 1 "Enable reviving from own nade explosion"
set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
+set g_freezetag_revive_time_to_score 1.5 "every this amount of seconds give players reviving a frozen teammate 1 point"
set g_freezetag_round_timelimit 360 "round time limit in seconds"
+set g_freezetag_revive_auto 1 "automatically revive frozen players after some time (g_freezetag_frozen_maxtime)"
+set g_freezetag_revive_auto_progress 1 "start the automatic reviving progress as soon as the player gets frozen"
+set g_freezetag_revive_auto_reducible 1 "reduce auto-revival time when frozen players are hit by enemies; set to -1 to reduce it even when they are hit by teammates"
+set g_freezetag_revive_auto_reducible_forcefactor 0.025 "hit force to time reduction conversion factor"
set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
set g_freezetag_teams_override 0
set g_freezetag_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
else if(STAT(FROZEN))
{
vector col = '0.25 0.90 1';
- if(STAT(REVIVE_PROGRESS))
- col += vec3(STAT(REVIVE_PROGRESS), -STAT(REVIVE_PROGRESS), -STAT(REVIVE_PROGRESS));
- drawfill('0 0 0', vec2(vid_conwidth, vid_conheight), col, autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+ float col_fade = max(0, STAT(REVIVE_PROGRESS) * 2 - 1);
+ float alpha_fade = 0.3 + 0.7 * (1 - max(0, STAT(REVIVE_PROGRESS) * 4 - 3));
+ if(col_fade)
+ col += vec3(col_fade, -col_fade, -col_fade);
+ drawfill('0 0 0', vec2(vid_conwidth, vid_conheight), col, autocvar_hud_colorflash_alpha * alpha_fade, DRAWFLAG_ADDITIVE);
}
HUD_Scale_Enable();
HANDLE(GRENADE_BOUNCING) this.traileffect = EFFECT_TR_GRENADE.m_id; break;
HANDLE(MINE) this.traileffect = EFFECT_TR_GRENADE.m_id; break;
HANDLE(BLASTER) this.traileffect = EFFECT_Null.m_id; break;
- HANDLE(ARC_BOLT) this.traileffect = EFFECT_Null.m_id; break;
+ HANDLE(ARC_BOLT) this.traileffect = EFFECT_TR_WIZSPIKE.m_id; break;
HANDLE(HLAC) this.traileffect = EFFECT_Null.m_id; break;
HANDLE(PORTO_RED) this.traileffect = EFFECT_TR_WIZSPIKE.m_id; this.scale = 4; break;
HANDLE(PORTO_BLUE) this.traileffect = EFFECT_TR_WIZSPIKE.m_id; this.scale = 4; break;
this.mins = '-4 -4 -4';
this.maxs = '4 4 4';
break;
+ case PROJECTILE_ARC_BOLT:
+ set_movetype(this, MOVETYPE_BOUNCE);
+ settouch(this, func_null);
+ break;
case PROJECTILE_RAPTORBOMB:
this.mins = '-3 -3 -3';
this.maxs = '3 3 3';
EFFECT(0, SMOKE_LARGE, "smoke_large")
-EFFECT(0, ARC_MUZZLEFLASH, "arc_muzzleflash")
+EFFECT(0, ARC_MUZZLEFLASH, "electro_muzzleflash")
EFFECT(0, BLASTER_IMPACT, "laser_impact")
EFFECT(0, BLASTER_MUZZLEFLASH, "laser_muzzleflash")
EFFECT(0, ARC_BEAM_HEAL, "arc_beam_heal")
EFFECT(0, ARC_BEAM_HEAL_IMPACT, "arc_beam_healimpact")
EFFECT(0, ARC_BEAM_HEAL_IMPACT2, "healray_impact")
-EFFECT(0, ARC_BOLT_EXPLODE, "arc_bolt_explode")
+// TODO: effect needs updating
+//EFFECT(0, ARC_BOLT_EXPLODE, "arc_bolt_explode")
EFFECT(0, ARC_OVERHEAT, "arc_overheat")
EFFECT(0, ARC_OVERHEAT_FIRE, "arc_overheat_fire")
EFFECT(0, ARC_SMOKE, "arc_smoke")
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER);
FOREACH_CLIENT(IS_PLAYER(it), {
it.freezetag_frozen_timeout = 0;
+ it.freezetag_revive_time = 0;
nades_Clear(it);
});
game_stopped = true;
FOREACH_CLIENT(IS_PLAYER(it), {
it.freezetag_frozen_timeout = 0;
+ it.freezetag_revive_time = 0;
nades_Clear(it);
});
{
if(attacker == targ)
{
- // you froze your own dumb targ
+ // you froze your own dumb self
// counted as "suicide" already
GameRules_scoring_add(targ, SCORE, -1);
}
if(STAT(FROZEN, targ))
return;
- if(autocvar_g_freezetag_frozen_maxtime > 0)
+ targ.freezetag_frozen_time = time;
+ if (autocvar_g_freezetag_revive_auto && autocvar_g_freezetag_frozen_maxtime > 0)
targ.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
Freeze(targ, 0, FROZEN_NORMAL, true);
{
FOREACH_CLIENT(IS_PLAYER(it), {
CS(it).killcount = 0;
+ it.freezetag_revive_time = 0;
it.freezetag_frozen_timeout = -1;
PutClientInServer(it);
it.freezetag_frozen_timeout = 0;
targ.freezetag_frozen_timeout = 0;
}
+MUTATOR_HOOKFUNCTION(ft, Damage_Calculate)
+{
+ entity frag_attacker = M_ARGV(1, entity);
+ entity frag_target = M_ARGV(2, entity);
+ //float frag_deathtype = M_ARGV(3, float);
+ //float frag_damage = M_ARGV(4, float);
+ vector frag_force = M_ARGV(6, vector);
+
+ if (STAT(FROZEN, frag_target) == FROZEN_NORMAL && autocvar_g_freezetag_revive_auto_reducible
+ && autocvar_g_freezetag_frozen_maxtime > 0 && autocvar_g_freezetag_revive_auto)
+ {
+ float t = 0;
+ if ((autocvar_g_freezetag_revive_auto_reducible < 0 || DIFF_TEAM(frag_attacker, frag_target))
+ && frag_target.freezetag_frozen_timeout > time)
+ {
+ if (fabs(autocvar_g_freezetag_revive_auto_reducible) == 1)
+ t = vlen(frag_force) * autocvar_g_freezetag_revive_auto_reducible_forcefactor;
+ frag_target.freezetag_frozen_timeout -= t;
+ if (frag_target.freezetag_frozen_timeout < time)
+ frag_target.freezetag_frozen_timeout = time;
+ }
+ }
+}
+
#ifdef IS_REVIVING
#undef IS_REVIVING
#endif
int n = 0;
vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
FOREACH_CLIENT(IS_PLAYER(it) && IS_REVIVING(player, it, revive_extra_size), {
+ if (autocvar_g_freezetag_revive_time_to_score > 0 && STAT(FROZEN, player) == FROZEN_NORMAL)
+ {
+ it.freezetag_revive_time += frametime / autocvar_g_freezetag_revive_time_to_score;
+ while (it.freezetag_revive_time > 1)
+ {
+ GameRules_scoring_add(it, SCORE, +1);
+ it.freezetag_revive_time -= 1;
+ }
+ }
if (reviving_players_last)
reviving_players_last.chain = it;
reviving_players_last = it;
if (!n && player.freezetag_frozen_timeout > 0 && time >= player.freezetag_frozen_timeout)
n = -1;
+ float base_progress = 0;
+ if (STAT(FROZEN, player) == FROZEN_NORMAL && autocvar_g_freezetag_revive_auto
+ && autocvar_g_freezetag_frozen_maxtime > 0 && autocvar_g_freezetag_revive_auto_progress)
+ {
+ // NOTE if auto-revival is in progress, manual revive speed is reduced so that it always takes the same amount of time
+ base_progress = bound(0, (1 - (player.freezetag_frozen_timeout - time) / autocvar_g_freezetag_frozen_maxtime), 1);
+ }
+
if (!n) // no teammate nearby
{
+ float clearspeed = autocvar_g_freezetag_revive_clearspeed;
+ if (autocvar_g_freezetag_revive_time_to_score > 0)
+ clearspeed = 0; // prevent stacking points by entering and exiting the revival zone many times
if (STAT(FROZEN, player) == FROZEN_NORMAL)
- {
- STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
- SetResourceExplicit(player, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)));
- }
+ STAT(REVIVE_PROGRESS, player) = bound(base_progress, STAT(REVIVE_PROGRESS, player) - frametime * clearspeed * (1 - base_progress), 1);
else if (!STAT(FROZEN, player))
- STAT(REVIVE_PROGRESS, player) = 0; // thawing nobody
+ STAT(REVIVE_PROGRESS, player) = base_progress; // thawing nobody
}
else if (STAT(FROZEN, player) == FROZEN_NORMAL) // OK, there is at least one teammate reviving us
{
- STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
- SetResourceExplicit(player, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)));
+ STAT(REVIVE_PROGRESS, player) = bound(base_progress, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed * (1 - base_progress)), 1);
if(STAT(REVIVE_PROGRESS, player) >= 1)
{
+ float frozen_time = time - player.freezetag_frozen_time;
Unfreeze(player, false);
+ SetResourceExplicit(player, RES_HEALTH, ((warmup_stage) ? warmup_start_health : start_health));
freezetag_count_alive_players();
if(n == -1)
{
- Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime);
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, player.netname, autocvar_g_freezetag_frozen_maxtime);
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, frozen_time);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, player.netname, frozen_time);
return true;
}
STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player);
}
+ if (STAT(FROZEN, player) == FROZEN_NORMAL)
+ {
+ entity player_wp = player.waypointsprite_attached;
+ if (n > 0 || (n == 0 && STAT(REVIVE_PROGRESS, player) > 0.95))
+ {
+ WaypointSprite_UpdateSprites(player_wp, WP_Reviving, WP_Null, WP_Null);
+ WaypointSprite_UpdateTeamRadar(player_wp, RADARICON_WAYPOINT, WP_REVIVING_COLOR);
+ }
+ else
+ {
+ WaypointSprite_UpdateSprites(player_wp, WP_Frozen, WP_Null, WP_Null);
+ WaypointSprite_UpdateTeamRadar(player_wp, RADARICON_WAYPOINT, WP_FROZEN_COLOR);
+ }
+
+ WaypointSprite_UpdateMaxHealth(player_wp, 1);
+ WaypointSprite_UpdateHealth(player_wp, STAT(REVIVE_PROGRESS, player));
+ }
+
return true;
}
return 0;
}
+.float freezetag_revive_time;
.float freezetag_frozen_time;
.float freezetag_frozen_timeout;
const float ICE_MAX_ALPHA = 1;
const float ICE_MIN_ALPHA = 0.1;
float freezetag_teams;
+bool autocvar_g_freezetag_revive_auto = 1;
+int autocvar_g_freezetag_revive_auto_progress = 1;
+int autocvar_g_freezetag_revive_auto_reducible;
+float autocvar_g_freezetag_revive_auto_reducible_forcefactor;
float autocvar_g_freezetag_revive_extra_size;
float autocvar_g_freezetag_revive_speed;
+float autocvar_g_freezetag_revive_time_to_score = 1.5;
bool autocvar_g_freezetag_revive_nade;
float autocvar_g_freezetag_revive_nade_health;
MODEL(PROJECTILE_HAGAR, "models/hagarmissile.mdl");
MODEL(PROJECTILE_HAGAR_BOUNCING, "models/hagarmissile.mdl");
-MODEL(PROJECTILE_ARC_BOLT, "models/arctrail.mdl");
+MODEL(PROJECTILE_ARC_BOLT, "models/ebomb.mdl"); // TODO: remove models/arctrail.mdl right before 0.8.3 release!
// napalm grenade
MODEL(PROJECTILE_NAPALM_FOUNTAIN, "null");
REGISTER_WAYPOINT(Here, _("Here"), "", '0 1 0', 1);
REGISTER_WAYPOINT(Danger, _("DANGER"), "", '1 0.5 0', 1);
-REGISTER_WAYPOINT(Frozen, _("Frozen!"), "", '0.25 0.90 1', 1);
+const vector WP_FROZEN_COLOR = '0.25 0.9 1';
+const vector WP_REVIVING_COLOR = '1 0.5 0';
+REGISTER_WAYPOINT(Frozen, _("Frozen!"), "", WP_FROZEN_COLOR, 1);
+REGISTER_WAYPOINT(Reviving, _("Reviving"), "", WP_REVIVING_COLOR, 1);
REGISTER_WAYPOINT(Item, _("Item"), "", '1 0 1', 1);
{
// no check, as this is never called without doing an actual change (usually only once)
int i = icon.m_id;
- e.cnt = (e.cnt & BIT(7)) | (i & BITS(7));
- e.colormod = col;
- e.SendFlags |= 32;
+ int new_cnt = (e.cnt & BIT(7)) | (i & BITS(7));
+ if (new_cnt != e.cnt || col != e.colormod)
+ {
+ e.cnt = new_cnt;
+ e.colormod = col;
+ e.SendFlags |= 32;
+ }
}
void WaypointSprite_Ping(entity e)
void W_Arc_Bolt_Touch(entity this, entity toucher)
{
PROJECTILE_TOUCH(this, toucher);
- this.use(this, NULL, toucher);
+ if(this.cnt >= WEP_CVAR(arc, bolt_bounce_count) || !WEP_CVAR(arc, bolt_bounce_count) || toucher.takedamage == DAMAGE_AIM) {
+ this.use(this, NULL, toucher);
+ } else {
+ this.cnt++;
+ Send_Effect(EFFECT_BALL_SPARKS, this.origin, this.velocity, 1);
+ this.angles = vectoangles(this.velocity);
+ this.owner = NULL;
+ this.projectiledeathtype |= HITTYPE_BOUNCE;
+ if(WEP_CVAR(arc, bolt_bounce_explode))
+ RadiusDamage(this, this.realowner, WEP_CVAR(arc, bolt_damage), WEP_CVAR(arc, bolt_edgedamage), WEP_CVAR(arc, bolt_radius), NULL, NULL, WEP_CVAR(arc, bolt_force), this.projectiledeathtype, this.weaponentity_fld, toucher);
+ if(this.cnt == 1 && WEP_CVAR(arc, bolt_bounce_lifetime))
+ this.nextthink = time + WEP_CVAR(arc, bolt_bounce_lifetime);
+ }
}
-void W_Arc_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity)
+void W_Arc_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
- entity missile;
-
- W_DecreaseAmmo(thiswep, actor, WEP_CVAR(arc, bolt_ammo), weaponentity);
-
- W_SetupShot(actor, weaponentity, false, 2, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR(arc, bolt_damage), WEP_ARC.m_id | HITTYPE_SECONDARY);
+ W_SetupShot(actor, weaponentity, false, 2, SND_ELECTRO_FIRE2, CH_WEAPON_A, WEP_CVAR(arc, bolt_damage), thiswep.m_id | HITTYPE_SECONDARY);
W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
- missile = new(missile);
+ entity missile = new(missile);
missile.owner = missile.realowner = actor;
missile.bot_dodge = true;
IL_PUSH(g_bot_dodge, missile);
IL_PUSH(g_damagedbycontents, missile);
settouch(missile, W_Arc_Bolt_Touch);
+ missile.cnt = 0;
missile.use = W_Arc_Bolt_Explode_use;
setthink(missile, adaptor_think2use_hittype_splash);
missile.nextthink = time + WEP_CVAR(arc, bolt_lifetime);
PROJECTILE_MAKETRIGGER(missile);
- missile.projectiledeathtype = WEP_ARC.m_id | HITTYPE_SECONDARY;
+ missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
missile.weaponentity_fld = weaponentity;
setorigin(missile, w_shotorg);
setsize(missile, '0 0 0', '0 0 0');
- set_movetype(missile, MOVETYPE_FLY);
+ set_movetype(missile, MOVETYPE_BOUNCEMISSILE);
W_SetupProjVelocity_PRE(missile, arc, bolt_);
missile.angles = vectoangles(missile.velocity);
missile.flags = FL_PROJECTILE;
+ IL_PUSH(g_projectiles, missile);
missile.missile_flags = MIF_SPLASH;
CSQCProjectile(missile, true, PROJECTILE_ARC_BOLT, true);
MUTATOR_CALLHOOK(EditProjectile, actor, missile);
+
+ actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
+ if(actor.(weaponentity).misc_bulletcounter == 0)
+ {
+ ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(arc, bolt_refire2) * W_WeaponRateFactor(actor);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), w_ready);
+ }
+ else
+ {
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), W_Arc_Attack_Bolt);
+ }
}
void W_Arc_Beam_Think(entity this)
}
else if(fire & 2)
{
- if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(arc, bolt_refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
{
- W_Arc_Attack_Bolt(thiswep, actor, weaponentity);
- weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), w_ready);
+ if(!thiswep.wr_checkammo2(thiswep, actor, weaponentity))
+ if(!(actor.items & IT_UNLIMITED_AMMO))
+ {
+ W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
+ w_ready(thiswep, actor, weaponentity, fire);
+ return;
+ }
+ float ammo_available = GetResource(actor, thiswep.ammo_type);
+ // We don't want to shoot 3 rounds if there's 2 left in the mag, so we'll use a fraction.
+ // Also keep the fraction <= 1 otherwise we'd mag dump in one burst.
+ float burst_fraction = min(1, ammo_available / WEP_CVAR(arc, bolt_ammo));
+ int to_shoot = floor(WEP_CVAR(arc, bolt_count) * burst_fraction);
+
+ // We also don't want to use 3 rounds if there's only 2 left.
+ int to_use = min(WEP_CVAR(arc, bolt_ammo), ammo_available);
+ W_DecreaseAmmo(thiswep, actor, to_use, weaponentity);
+
+ // Bursting counts up to 0 from a negative.
+ actor.(weaponentity).misc_bulletcounter = -to_shoot;
+ W_Arc_Attack_Bolt(thiswep, actor, weaponentity, fire);
}
}
{
vector org2;
org2 = w_org + w_backoff * 6;
- pointparticles(EFFECT_ARC_BOLT_EXPLODE, org2, w_backoff * 1000, 1);
- if(!w_issilent) { sound(actor, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
+ pointparticles(EFFECT_ELECTRO_IMPACT, org2, w_backoff * 1000, 1);
+ if(!w_issilent) { sound(actor, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTN_NORM); }
}
}
BEGIN(class) \
P(class, prefix, bolt, float, NONE) \
P(class, prefix, bolt_ammo, float, NONE) \
+ P(class, prefix, bolt_bounce_count, float, NONE) \
+ P(class, prefix, bolt_bounce_explode, float, NONE) \
+ P(class, prefix, bolt_bounce_lifetime, float, NONE) \
+ P(class, prefix, bolt_count, float, NONE) \
P(class, prefix, bolt_damageforcescale, float, NONE) \
P(class, prefix, bolt_damage, float, NONE) \
P(class, prefix, bolt_edgedamage, float, NONE) \
P(class, prefix, bolt_lifetime, float, NONE) \
P(class, prefix, bolt_radius, float, NONE) \
P(class, prefix, bolt_refire, float, NONE) \
+ P(class, prefix, bolt_refire2, float, NONE) \
P(class, prefix, bolt_speed, float, NONE) \
P(class, prefix, bolt_spread, float, NONE) \
P(class, prefix, beam_ammo, float, NONE) \
slist.controlledTextbox = e;
me.gotoRC(me, 0.5, 2.6);
- me.TD(me, 1, 0.9, e = makeXonoticCheckBox(0, "menu_slist_categories", ZCTX(_("SRVS^Categories"))));
+ me.TD(me, 1, 0.75, e = makeXonoticCheckBox(0, "menu_slist_categories", ZCTX(_("SRVS^Categories"))));
e.onClickEntity = slist;
e.onClick = ServerList_Categories_Click;
me.TD(me, 1, 0.6, e = makeXonoticCheckBox_T(0, "menu_slist_showempty", ZCTX(_("SRVS^Empty")),
slist.filterShowEmpty = e.checked;
e.onClickEntity = slist;
e.onClick = ServerList_ShowEmpty_Click;
- me.TD(me, 1, 0.6, e = makeXonoticCheckBox_T(0, "menu_slist_showfull", ZCTX(_("SRVS^Full")),
+ me.TD(me, 1, 0.5, e = makeXonoticCheckBox_T(0, "menu_slist_showfull", ZCTX(_("SRVS^Full")),
_("Show full servers that have no slots available")));
slist.filterShowFull = e.checked;
e.onClickEntity = slist;
e.onClick = ServerList_ShowFull_Click;
+ me.TD(me, 1, 0.6, e = makeXonoticCheckBox_T(0, "menu_slist_showlaggy", ZCTX(_("SRVS^Laggy")),
+ _("Show high latency servers")));
+ slist.filterShowLaggy = e.checked;
+ e.onClickEntity = slist;
+ e.onClick = ServerList_ShowLaggy_Click;
me.TD(me, 1, 0.6, e = makeXonoticCheckBox_T(0, "net_slist_pause", _("Pause"),
_("Pause updating the server list to prevent servers from \"jumping around\"")));
me.TD(me, 1, 1, e = makeXonoticButton_T(_("Refresh"), '0 0 0', _("Reload the server list")));
if(!me.filterShowEmpty)
sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
+ // show laggy button
+ if(!me.filterShowLaggy && autocvar_menu_slist_maxping > 0)
+ sethostcachemasknumber(++m, SLIST_FIELD_PING, autocvar_menu_slist_maxping, SLIST_TEST_LESSEQUAL);
+
// gametype filtering
if(typestr != "")
sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
me.ipAddressBox.cursorPos = 0;
me.ipAddressBoxFocused = -1;
}
+void ServerList_ShowLaggy_Click(entity box, entity me)
+{
+ box.setChecked(box, me.filterShowLaggy = !me.filterShowLaggy);
+ me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
+
+ me.ipAddressBox.setText(me.ipAddressBox, "");
+ me.ipAddressBox.cursorPos = 0;
+ me.ipAddressBoxFocused = -1;
+}
void XonoticServerList_setSortOrder(entity me, int fld, int direction)
{
if(me.currentSortField == fld)
METHOD(XonoticServerList, setSortOrder, void(entity, float, float));
ATTRIB(XonoticServerList, filterShowEmpty, float, 1);
ATTRIB(XonoticServerList, filterShowFull, float, 1);
+ ATTRIB(XonoticServerList, filterShowLaggy, float, 0);
ATTRIB(XonoticServerList, filterString, string);
ATTRIB(XonoticServerList, controlledTextbox, entity);
ATTRIB(XonoticServerList, ipAddressBox, entity);
void ServerList_Categories_Click(entity box, entity me);
void ServerList_ShowEmpty_Click(entity box, entity me);
void ServerList_ShowFull_Click(entity box, entity me);
+void ServerList_ShowLaggy_Click(entity box, entity me);
void ServerList_Connect_Click(entity btn, entity me);
void ServerList_Update_favoriteButton(entity btn, entity me);
void ServerList_Favorite_Click(entity btn, entity me);
float autocvar_menu_slist_categories_onlyifmultiple;
float autocvar_menu_slist_purethreshold;
float autocvar_menu_slist_modimpurity;
+float autocvar_menu_slist_maxping = 300;
float autocvar_menu_slist_recommendations;
float autocvar_menu_slist_recommendations_maxping;
float autocvar_menu_slist_recommendations_minfreeslots;
damage = 0;
force = '0 0 0';
}
- else if(SAME_TEAM(attacker, targ))
+ else if(!STAT(FROZEN, targ) && SAME_TEAM(attacker, targ))
{
if(autocvar_teamplay_mode == 1)
damage = 0;
if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
{
- if(DIFF_TEAM(victim, attacker) && !STAT(FROZEN, victim))
+ if (DIFF_TEAM(victim, attacker))
{
if(damage > 0)
{
}
}
}
- else if(IS_PLAYER(attacker))
+ else if (IS_PLAYER(attacker) && !STAT(FROZEN, victim)) // same team
{
- // if enemy gets frozen in this frame and receives other damage don't
- // play the typehitsound e.g. when hit by multiple bullets of the shotgun
- if (deathtype != DEATH_FIRE.m_id && (!STAT(FROZEN, victim) || time > victim.freeze_time))
+ if (deathtype != DEATH_FIRE.m_id)
{
attacker.typehitsound += 1;
}
}
}
- if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
+ if (STAT(FROZEN, this))
+ {
+ if (!ITEM_DAMAGE_NEEDKILL(deathtype))
+ damage = 0;
+ }
+ else if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage);
if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears
if(take)
this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
- if (time > this.pain_finished) //Don't switch pain sequences like crazy
+ if (time > this.pain_finished && !STAT(FROZEN, this)) // Don't switch pain sequences like crazy
{
this.pain_finished = time + 0.5; //Supajoe
}
float realdmg = damage - excess;
- if (this != attacker && realdmg)
- if (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime)
+ if (this != attacker && realdmg && !STAT(FROZEN, this)
+ && (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime))
{
if (IS_PLAYER(attacker) && DIFF_TEAM(attacker, this)) {
GameRules_scoring_add(attacker, DMG, realdmg);
if(vbot || IS_REAL_CLIENT(this))
if(abot || IS_REAL_CLIENT(attacker))
if(attacker && this != attacker)
- if(DIFF_TEAM(this, attacker))
+ if (DIFF_TEAM(this, attacker) && (!STAT(FROZEN, this) || this.freeze_time > time))
{
if(DEATH_ISSPECIAL(deathtype))
awep = attacker.(weaponentity).m_weapon;
valid_damage_for_weaponstats = true;
}
- dh = dh - max(GetResource(this, RES_HEALTH), 0);
- da = da - max(GetResource(this, RES_ARMOR), 0);
+ dh -= max(GetResource(this, RES_HEALTH), 0); // health difference
+ da -= max(GetResource(this, RES_ARMOR), 0); // armor difference
if(valid_damage_for_weaponstats)
{
WeaponStats_LogDamage(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot, dh + da);
vector md = ent.(weaponentity).movedir;
vector vecs = ((md.x > 0) ? md : '0 0 0');
- // TODO this is broken - see 637056bea7bf7f5c9c0fc6113e94731a2767476 for an attempted fix
- // which fixes issue #1957 but causes #2129
vector dv = right * -vecs.y + up * vecs.z;
- w_shotorg = ent.origin + ent.view_ofs + dv;
+ w_shotorg = ent.origin + ent.view_ofs;
+ if(antilag)
+ {
+ if(CS(ent).antilag_debug)
+ tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + dv, MOVE_NORMAL, ent, CS(ent).antilag_debug);
+ else
+ tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + dv, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent));
+ }
+ else
+ tracebox(w_shotorg, mi, ma, w_shotorg + dv, MOVE_NORMAL, ent);
+ w_shotorg = trace_endpos;
// now move the shotorg forward as much as requested if possible
if(antilag)
alias test_rocket_flying "settemp g_balance_devastator_remote_jump 1"
+alias test_arc_bounce "settemp g_balance_arc_bolt_bounce_count 1 ; settemp g_balance_arc_bolt_bounce_lifetime 0.5 ; settemp g_balance_arc_bolt_bounce_explode 1 ; settemp g_balance_arc_bolt_ammo 2"
+alias test_arc_bounce_burst "settemp g_balance_arc_bolt_bounce_count 1 ; settemp g_balance_arc_bolt_bounce_lifetime 0.5 ; settemp g_balance_arc_bolt_bounce_explode 1 ; settemp g_balance_arc_bolt_ammo 3 ; settemp g_balance_arc_bolt_refire2 0.33333 ; settemp g_balance_arc_bolt_count 3"
+
// https://forums.xonotic.org/showthread.php?tid=8192
// https://gitlab.com/xonotic/xonotic-data.pk3dir/merge_requests/736
alias test_ctf_stalemate90 "settemp g_ctf_stalemate_time 90"
alias test_ctf_stalemate120 "settemp g_ctf_stalemate_time 120"
-alias testing_enable "addvote test_blaster_switch ; addvote test_crylink_sec_horizontal ; addvote test_rocket_flying ; addvote test_ctf_stalemate90 ; addvote test_ctf_stalemate120"
-alias testing_disable "delvote test_blaster_switch ; delvote test_crylink_sec_horizontal ; delvote test_rocket_flying ; delvote test_ctf_stalemate90 ; delvote test_ctf_stalemate120"
+alias testing_enable "addvote test_blaster_switch ; addvote test_crylink_sec_horizontal ; addvote test_rocket_flying ; addvote test_ctf_stalemate90 ; addvote test_ctf_stalemate120 ; addvote test_arc_bounce ; addvote test_arc_bounce_burst"
+alias testing_disable "delvote test_blaster_switch ; delvote test_crylink_sec_horizontal ; delvote test_rocket_flying ; delvote test_ctf_stalemate90 ; delvote test_ctf_stalemate120 ; delvote test_arc_bounce ; delvote test_arc_bounce_burst"
// for menu server list (eventually make them have engine support?)
seta menu_slist_showfull 1 "show servers even if they are full and have no slots to join"
seta menu_slist_showempty 1 "show servers even if they are no empty and have no opponents to play against"
+seta menu_slist_showlaggy 0 "show servers even if they are very high latency (see menu_slist_maxping)"
seta menu_slist_modfilter "" // set to either: !modname or modname. modname of = means "same as we are running now".
// other serverlist cvars
seta menu_slist_categories 1
seta menu_slist_categories_onlyifmultiple 1
seta menu_slist_purethreshold 0
+seta menu_slist_maxping 300
seta menu_slist_modimpurity 0
seta menu_slist_recommendations 3
seta menu_slist_recommendations_maxping 150