}
if(sf & ARC_SF_BEAMDIR) // beam direction
{
- WriteAngle(MSG_ENTITY, this.beam_dir.x);
- WriteAngle(MSG_ENTITY, this.beam_dir.y);
- WriteAngle(MSG_ENTITY, this.beam_dir.z);
+ WriteAngleVector(MSG_ENTITY, this.beam_dir);
}
if(sf & ARC_SF_BEAMTYPE) // beam type
{
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);
- Send_Effect(EFFECT_ARC_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
+ 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)
Weapon thiswep = WEP_ARC;
+ // TODO: use standard weapon use checks here!
if(
!IS_PLAYER(own)
||
IS_DEAD(own)
||
+ game_stopped
+ ||
!weapon_prepareattack_check(thiswep, own, weaponentity, this.beam_bursting, -1)
||
own.(weaponentity).m_switchweapon != WEP_ARC
}
if(this == own.(weaponentity).arc_beam) { own.(weaponentity).arc_beam = NULL; }
- if(!thiswep.wr_checkammo1(thiswep, own, weaponentity) && !(own.items & IT_UNLIMITED_WEAPON_AMMO))
+ if(!thiswep.wr_checkammo1(thiswep, own, weaponentity) && !(own.items & IT_UNLIMITED_AMMO))
{
// note: this doesn't force the switch
W_SwitchToOtherWeapon(own, weaponentity);
// decrease ammo
float coefficient = frametime;
- if(!(own.items & IT_UNLIMITED_WEAPON_AMMO))
+ if(!(own.items & IT_UNLIMITED_AMMO))
{
float rootammo;
if(burst)
Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 );
}
- if ( actor.arc_smoke_sound && ( actor.arc_overheat <= time ||
- !( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) ) || actor.(weaponentity).m_switchweapon != thiswep )
+ bool attacking = PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor);
+ bool stop_smoke_sound = actor.arc_overheat <= time || !attacking;
+ if ((actor.arc_smoke_sound && stop_smoke_sound) || actor.(weaponentity).m_switchweapon != thiswep)
{
actor.arc_smoke_sound = 0;
sound(actor, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
}
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); }
}
}
Draw_CylindricLine(start, end, thickness, beam.beam_image, 0.25, -time * 3, beam.beam_color, beam.beam_alpha, DRAWFLAG_NORMAL, transformed_view_org);
else
{
- R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE
+ R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL, false); // DRAWFLAG_ADDITIVE
R_PolygonVertex(
top,
'0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)),
// into a weapon system for client code.
// find where we are aiming
- makevectors(((autocvar_chase_active) ? warpzone_save_view_angles : view_angles));
- vector forward = v_forward;
- vector right = v_right;
- vector up = v_up;
+ vector myviewangle = ((autocvar_chase_active) ? warpzone_save_view_angles : view_angles);
+ vector forward, right, up;
+ MAKE_VECTORS(myviewangle, forward, right, up);
entity wepent = viewmodels[this.beam_slot];
- if(autocvar_chase_active)
- this.beam_usevieworigin = 1;
- else
- this.beam_usevieworigin = 2;
+ this.beam_usevieworigin = (autocvar_chase_active) ? 1 : 2;
// decide upon start position
if(this.beam_usevieworigin == 2)
else
{ start_pos = this.origin; }
- int v_shot_idx; // used later
- (v_shot_idx = gettagindex(wepent, "shot")) || (v_shot_idx = gettagindex(wepent, "tag_shot"));
- if(v_shot_idx && this.beam_usevieworigin == 2)
- start_pos = gettaginfo(wepent, v_shot_idx) - '0 0 2';
-
// trace forward with an estimation
WarpZone_TraceLine(
start_pos,
this
);
+ int v_shot_idx; // used later
+ (v_shot_idx = gettagindex(wepent, "shot")) || (v_shot_idx = gettagindex(wepent, "tag_shot"));
+ if(v_shot_idx && this.beam_usevieworigin == 2)
+ start_pos = gettaginfo(wepent, v_shot_idx) - '0 0 2';
+
// untransform in case our trace went through a warpzone
vector end_pos = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
if(!v_shot_idx || this.beam_usevieworigin != 2)
{
this.beam_shotorigin = wepent.movedir;
- origin_offset =
- right * -this.beam_shotorigin.y
- + up * this.beam_shotorigin.z;
+ origin_offset = right * -this.beam_shotorigin.y + up * this.beam_shotorigin.z;
}
else
this.beam_shotorigin = '0 0 0';
{
this.beam_dir = wantdir;
this.beam_initialized = true;
+
+ this.beam_muzzleentity.drawmask = MASK_NORMAL; // NOTE: this works around the muzzle entity flashing on the middle of the screen for a frame
}
if(this.beam_dir != wantdir)
// if the angle is greater than maxangle, force the blendfactor to make this the maximum factor
float blendfactor = bound(
0,
- (1 - (this.beam_returnspeed * frametime)),
+ (1 - (this.beam_returnspeed * dt)),
min(this.beam_maxangle / angle, 1)
);
this.beam_dir = normalize((wantdir * (1 - blendfactor)) + (this.beam_dir * blendfactor));
// the radius is not too far yet, no worries :D
float blendfactor = bound(
0,
- (1 - (this.beam_returnspeed * frametime)),
+ (1 - (this.beam_returnspeed * dt)),
1
);
this.beam_dir = normalize((wantdir * (1 - blendfactor)) + (this.beam_dir * blendfactor));
this.beam_hiteffect,
last_origin,
beamdir * -1,
- frametime * 2
+ dt * 2
);
}
if(this.beam_hitlight[0])
this.beam_muzzleeffect,
original_start_pos + wantdir * 20,
wantdir * 1000,
- frametime * 0.1
+ dt * 0.1
);
}
if(this.beam_muzzlelight[0])
flash = spawn();
flash.owner = this;
flash.effects = EF_ADDITIVE | EF_FULLBRIGHT;
- flash.drawmask = MASK_NORMAL;
+ //flash.drawmask = MASK_NORMAL;
flash.solid = SOLID_NOT;
flash.avelocity_z = 5000;
setattachment(flash, this, "");
if(ReadByte())
{
- if(autocvar_chase_active)
- { this.beam_usevieworigin = 1; }
- else // use view origin
- { this.beam_usevieworigin = 2; }
+ this.beam_usevieworigin = (autocvar_chase_active) ? 1 : 2;
}
else
{
if(sf & ARC_SF_BEAMDIR) // beam direction
{
- this.angles_x = ReadAngle();
- this.angles_y = ReadAngle();
- this.angles_z = ReadAngle();
+ this.angles = ReadAngleVector();
}
if(sf & ARC_SF_BEAMTYPE) // beam type