WriteByte(MSG_ENTITY, bound(1, this.dmg, 255));
WriteByte(MSG_ENTITY, bound(0, this.dmg_radius, 255));
WriteByte(MSG_ENTITY, bound(1, this.dmg_edge, 255));
- WriteVector(MSG_ENTITY, this.velocity);
+ // we can't send the force vector compressed with compressShortVector as it's too inaccurate
+ // it would break decals when hit angle on a surface is small
+ // (the traceline performed by the client to spawn a decal wouldn't hit the surface at all)
+ WriteShort(MSG_ENTITY, floor(this.velocity.x / 4));
+ WriteShort(MSG_ENTITY, floor(this.velocity.y / 4));
+ WriteShort(MSG_ENTITY, floor(this.velocity.z / 4));
WriteByte(MSG_ENTITY, this.species);
return true;
}
- void Damage_DamageInfo(vector org, bool is_solid_hit, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
+ void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
{
// TODO maybe call this from non-edgedamage too?
// TODO maybe make the client do the particle effects for the weapons and the impact sounds using this info?
e.dmg_force = vlen(force);
e.velocity = force;
e.species = bloodtype;
- if(is_solid_hit)
- e.species |= 0x80;
Net_LinkEntity(e, false, 0.2, Damage_DamageInfo_SendEntity);
}
}
if(this.state && !this.owner.csqcmodel_isdead)
{
- // if the player was dead but is now alive, it means he respawned
- // if so, clear his damage effects, or damages from his dead body will be copied back
+ // if the player was dead but is now alive, it means they respawned
+ // if so, clear their damage effects, or damages from their dead body will be copied back
this.owner.total_damages = max(0, this.owner.total_damages - 1);
delete(this);
return;
thedamage = ReadByte();
rad = ReadByte();
edge = ReadByte();
- force = ReadVector();
+ force.x = ReadShort() * 4 + 2;
+ force.y = ReadShort() * 4 + 2;
+ force.z = ReadShort() * 4 + 2;
+
species = ReadByte();
- bool is_solid_hit = (species & 0x80);
- species = (species & 0x7F);
return = true;
DamageEffect(it, w_org, thisdmg, w_deathtype, species);
- if((it.isplayermodel & ISPLAYER_MODEL))
- hitplayer = true; // this impact damaged a player
+ if(it != csqcplayer && (it.isplayermodel & ISPLAYER_MODEL))
+ hitplayer = true; // this impact damaged another player
});
if(DEATH_ISVEHICLE(w_deathtype))
w_random = prandom();
vector force_dir = normalize(force);
- if (is_solid_hit) // traceline not needed
- w_backoff = -force_dir;
+ // this traceline usually starts in solid when a hitscan shot hits a surface with a very small angle
+ // if so, try another traceline starting further back (may still start in solid but only with extremely small angles)
+ traceline(w_org - force_dir * 16, w_org + force_dir * 16, MOVE_NOMONSTERS, NULL);
+ if(trace_startsolid)
+ traceline(w_org - force_dir * 40, w_org + force_dir * 16, MOVE_NOMONSTERS, NULL);
+ if(trace_fraction < 1)
+ w_backoff = trace_plane_normal;
else
- {
- traceline(w_org - force_dir * 16, w_org + force_dir * 16, MOVE_NOMONSTERS, NULL);
- if(trace_fraction < 1 && !(hitwep.spawnflags & WEP_TYPE_HITSCAN))
- w_backoff = trace_plane_normal;
- else
- w_backoff = -force_dir;
- }
+ w_backoff = -force_dir;
setorigin(this, w_org + w_backoff * 2); // for sound() calls
if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
{
// Secondary uses it's own refire timer if refire_type is 1.
actor.jump_interval = time + WEP_CVAR_SEC(okhmg, refire) * W_WeaponRateFactor(actor);
- BLASTER_SECONDARY_ATTACK(okhmg, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
(actor.(weaponentity).wframe == WFRAME_FIRE2))
{
{
return;
}
- BLASTER_SECONDARY_ATTACK(okhmg, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okhmg, animtime), w_ready);
}
}
METHOD(OverkillHeavyMachineGun, wr_impacteffect, void(entity thiswep, entity actor))
{
- vector org2;
- org2 = w_org + w_backoff * 2;
+ vector org2 = w_org + w_backoff * 2;
pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent)
sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
{
// Secondary uses it's own refire timer if refire_type is 1.
actor.jump_interval = time + WEP_CVAR_SEC(okmachinegun, refire) * W_WeaponRateFactor(actor);
- BLASTER_SECONDARY_ATTACK(okmachinegun, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
(actor.(weaponentity).wframe == WFRAME_FIRE2))
{
{
return;
}
- BLASTER_SECONDARY_ATTACK(okmachinegun, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okmachinegun, animtime), w_ready);
}
}
METHOD(OverkillMachineGun, wr_impacteffect, void(entity thiswep, entity actor))
{
- vector org2;
- org2 = w_org + w_backoff * 2;
+ vector org2 = w_org + w_backoff * 2;
pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent)
sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);
{
// Secondary uses it's own refire timer if refire_type is 1.
actor.jump_interval = time + WEP_CVAR_SEC(oknex, refire) * W_WeaponRateFactor(actor);
- BLASTER_SECONDARY_ATTACK(oknex, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
(actor.(weaponentity).wframe == WFRAME_FIRE2))
{
{
return;
}
- BLASTER_SECONDARY_ATTACK(oknex, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(oknex, animtime), w_ready);
return;
}
METHOD(OverkillNex, wr_impacteffect, void(entity thiswep, entity actor))
{
entity this = actor;
- vector org2 = w_org + w_backoff * 6;
+ vector org2 = w_org + w_backoff * 2;
pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1);
if(!w_issilent)
sound(this, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM);
{
// Secondary uses it's own refire timer if refire_type is 1.
actor.jump_interval = time + WEP_CVAR_SEC(okrpc, refire) * W_WeaponRateFactor(actor);
- BLASTER_SECONDARY_ATTACK(okrpc, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
(actor.(weaponentity).wframe == WFRAME_FIRE2))
{
{
return;
}
- BLASTER_SECONDARY_ATTACK(okrpc, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okrpc, animtime), w_ready);
}
}
METHOD(OverkillRocketPropelledChainsaw, wr_impacteffect, void(entity thiswep, entity actor))
{
- vector org2;
- org2 = w_org + w_backoff * 12;
+ vector org2 = w_org + w_backoff * 2;
pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(actor, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
PROJECTILE_TOUCH(this, toucher);
this.event_damage = func_null;
+ bool isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
RadiusDamageForSource(
this,
(this.origin + (this.mins + this.maxs) * 0.5),
this.velocity,
this.realowner,
- this.blaster_damage,
- this.blaster_edgedamage,
- this.blaster_radius,
+ WEP_CVAR_BOTH(blaster, isprimary, damage),
+ WEP_CVAR_BOTH(blaster, isprimary, edgedamage),
+ WEP_CVAR_BOTH(blaster, isprimary, radius),
NULL,
NULL,
false,
- this.blaster_force,
- this.blaster_force_zscale,
+ WEP_CVAR_BOTH(blaster, isprimary, force),
+ WEP_CVAR_BOTH(blaster, isprimary, force_zscale),
this.projectiledeathtype,
this.weaponentity_fld,
toucher
{
set_movetype(this, MOVETYPE_FLY);
setthink(this, SUB_Remove);
- this.nextthink = time + this.blaster_lifetime;
+ bool isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
+ this.nextthink = time + WEP_CVAR_BOTH(blaster, isprimary, lifetime);
CSQCProjectile(this, true, PROJECTILE_BLASTER, true);
}
void W_Blaster_Attack(
entity actor,
.entity weaponentity,
- float atk_deathtype,
- float atk_shotangle,
- float atk_damage,
- float atk_edgedamage,
- float atk_radius,
- float atk_force,
- float atk_force_zscale,
- float atk_speed,
- float atk_spread,
- float atk_delay,
- float atk_lifetime)
+ float atk_deathtype)
{
+ bool isprimary = !(atk_deathtype & HITTYPE_SECONDARY);
+ float atk_shotangle = WEP_CVAR_BOTH(blaster, isprimary, shotangle);
+ float atk_damage = WEP_CVAR_BOTH(blaster, isprimary, damage);
vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
W_SetupShot_Dir(actor, weaponentity, s_forward, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, atk_damage, atk_deathtype);
missile.bot_dodgerating = atk_damage;
PROJECTILE_MAKETRIGGER(missile);
- missile.blaster_damage = atk_damage;
- missile.blaster_edgedamage = atk_edgedamage;
- missile.blaster_radius = atk_radius;
- missile.blaster_force = atk_force;
- missile.blaster_force_zscale = atk_force_zscale;
- missile.blaster_lifetime = atk_lifetime;
-
setorigin(missile, w_shotorg);
setsize(missile, '0 0 0', '0 0 0');
- W_SetupProjVelocity_Explicit(
- missile,
- w_shotdir,
- v_up,
- atk_speed,
- 0,
- 0,
- atk_spread,
- false
- );
+ float atk_speed = WEP_CVAR_BOTH(blaster, isprimary, speed);
+ float atk_spread = WEP_CVAR_BOTH(blaster, isprimary, spread);
+ W_SetupProjVelocity_Explicit(missile, w_shotdir, v_up, atk_speed, 0, 0, atk_spread, false);
missile.angles = vectoangles(missile.velocity);
missile.projectiledeathtype = atk_deathtype;
missile.weaponentity_fld = weaponentity;
setthink(missile, W_Blaster_Think);
- missile.nextthink = time + atk_delay;
+ missile.nextthink = time + WEP_CVAR_BOTH(blaster, isprimary, delay);
MUTATOR_CALLHOOK(EditProjectile, actor, missile);
{
if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(blaster, refire)))
{
- W_Blaster_Attack(
- actor,
- weaponentity,
- WEP_BLASTER.m_id,
- WEP_CVAR_PRI(blaster, shotangle),
- WEP_CVAR_PRI(blaster, damage),
- WEP_CVAR_PRI(blaster, edgedamage),
- WEP_CVAR_PRI(blaster, radius),
- WEP_CVAR_PRI(blaster, force),
- WEP_CVAR_PRI(blaster, force_zscale),
- WEP_CVAR_PRI(blaster, speed),
- WEP_CVAR_PRI(blaster, spread),
- WEP_CVAR_PRI(blaster, delay),
- WEP_CVAR_PRI(blaster, lifetime)
- );
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready);
}
}
{
if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(blaster, refire)))
{
- W_Blaster_Attack(
- actor,
- weaponentity,
- WEP_BLASTER.m_id | HITTYPE_SECONDARY,
- WEP_CVAR_SEC(blaster, shotangle),
- WEP_CVAR_SEC(blaster, damage),
- WEP_CVAR_SEC(blaster, edgedamage),
- WEP_CVAR_SEC(blaster, radius),
- WEP_CVAR_SEC(blaster, force),
- WEP_CVAR_SEC(blaster, force_zscale),
- WEP_CVAR_SEC(blaster, speed),
- WEP_CVAR_SEC(blaster, spread),
- WEP_CVAR_SEC(blaster, delay),
- WEP_CVAR_SEC(blaster, lifetime)
- );
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
}
}
actor.jump_interval = time + WEP_CVAR_SEC(blaster, refire) * W_WeaponRateFactor(actor);
.entity weaponentity = weaponentities[1];
- BLASTER_SECONDARY_ATTACK(blaster, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
}
#endif
METHOD(Blaster, wr_impacteffect, void(entity thiswep, entity actor))
{
- vector org2;
- org2 = w_org + w_backoff * 6;
+ vector org2 = w_org + w_backoff * 2;
pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent) { sound(actor, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
}
void W_MineLayer_ProximityExplode(entity this)
{
- // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance
+ // make sure no friend is in the mine's radius. If there is any, explosion is delayed until they're at a safe distance
if(WEP_CVAR(minelayer, protection) && this.mine_explodeanyway == 0)
{
entity head;
this.mine_explodeanyway = 1; // make the mine super aggressive -- Samual: Rather, make it not care if a team mate is near.
}
- // a player's mines shall explode if he disconnects or dies
+ // a player's mines shall explode if they disconnect or die
// TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
if(!IS_PLAYER(this.realowner) || IS_DEAD(this.realowner) || STAT(FROZEN, this.realowner))
{
METHOD(MineLayer, wr_impacteffect, void(entity thiswep, entity actor))
{
- vector org2;
- org2 = w_org + w_backoff * 12;
+ vector org2 = w_org + w_backoff * 2;
pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(actor, CH_SHOTS, SND_MINE_EXP, VOL_BASE, ATTN_NORM);
if(WEP_CVAR_SEC(vaporizer, ammo))
W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(vaporizer, ammo), weaponentity);
- BLASTER_SECONDARY_ATTACK(vaporizer, actor, weaponentity);
+ makevectors(actor.v_angle);
+ W_Blaster_Attack(actor, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
// now do normal refire
weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready);
METHOD(Vaporizer, wr_impacteffect, void(entity thiswep, entity actor))
{
- vector org2 = w_org + w_backoff * 6;
+ vector org2 = w_org + w_backoff * 2;
if(w_deathtype & HITTYPE_SECONDARY)
{
pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
// These are ALWAYS lethal
// No damage modification here
- // Instead, prepare the victim for his death...
+ // Instead, prepare the victim for their death...
if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
{
SetResourceExplicit(targ, RES_ARMOR, 0);
else
force = normalize(force);
if(forceintensity >= 0)
- Damage_DamageInfo(inflictororigin, false, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
+ Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
else
- Damage_DamageInfo(inflictororigin, false, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
+ Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
}
stat_damagedone = 0;
// TODO for fireBullet, find how far the shot will penetrate and aim at that
// for fireRailgunbullet, find the farthest target and aim at that
// this will avoid issues when another player is passing in front of you when you shoot
- // (currently such a shot will hit him but then miss the original target)
+ // (currently such a shot will hit them but then miss the original target)
ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
}
else
}
if(trace_ent.solid == SOLID_BSP || trace_ent.solid == SOLID_SLIDEBOX)
- Damage_DamageInfo(trace_endpos, (trace_ent.solid == SOLID_BSP), bdamage, 0, 0, force, deathtype, trace_ent.species, this);
+ Damage_DamageInfo(trace_endpos, bdamage, 0, 0, force, deathtype, trace_ent.species, this);
// if it is NULL we can't hurt it so stop now
if (trace_ent == NULL || trace_fraction == 1)
is_weapclip = true;
if(!hit || hit.solid == SOLID_BSP || hit.solid == SOLID_SLIDEBOX)
- Damage_DamageInfo(start, (hit && hit.solid == SOLID_BSP), damage * damage_fraction, 0, 0, max(1, force) * dir * damage_fraction, dtype, hit.species, this);
+ Damage_DamageInfo(start, damage * damage_fraction, 0, 0, max(1, force) * dir * damage_fraction, dtype, hit.species, this);
if (hit && hit != WarpZone_trace_forent && hit != fireBullet_last_hit) // Avoid self-damage (except after going through a warp); avoid hitting the same entity twice (engine bug).
{
start = trace_endpos;
if(hit.solid == SOLID_BSP)
- Damage_DamageInfo(start, true, 0, 0, 0, max(1, force) * normalize(dir) * -damage_fraction, dtype, 0, this);
+ Damage_DamageInfo(start, 0, 0, 0, max(1, force) * normalize(dir) * -damage_fraction, dtype, 0, this);
}
if(headshot)