X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fw_laser.qc;h=25e240357e2a2e812ada00b18906045c29329464;hb=a9a7db34cf93f756ae42925c302cec84f9f9b7d2;hp=6b7fcdf2d4fde0f9fbe5f4871eb68d346562f057;hpb=f4c07fdddf2d3e44403186fa218717f39b879708;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/w_laser.qc b/qcsrc/server/w_laser.qc index 6b7fcdf2d..25e240357 100644 --- a/qcsrc/server/w_laser.qc +++ b/qcsrc/server/w_laser.qc @@ -7,9 +7,10 @@ void() W_LastWeapon; .float swing_prev; .entity swing_alreadyhit; -void SendCSQCShockwaveParticle(float spread, vector endpos, entity transform) +void SendCSQCShockwaveParticle(vector endpos) { - WarpZone_UnTransformOrigin(transform, endpos); + //endpos = WarpZone_UnTransformOrigin(transform, endpos); + WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); WriteByte(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE); WriteCoord(MSG_BROADCAST, w_shotorg_x); @@ -18,7 +19,9 @@ void SendCSQCShockwaveParticle(float spread, vector endpos, entity transform) WriteCoord(MSG_BROADCAST, endpos_x); WriteCoord(MSG_BROADCAST, endpos_y); WriteCoord(MSG_BROADCAST, endpos_z); - WriteByte(MSG_BROADCAST, bound(0, 255 * spread, 255)); + WriteByte(MSG_BROADCAST, bound(0, autocvar_g_balance_laser_shockwave_spread_max, 255)); + WriteByte(MSG_BROADCAST, bound(0, autocvar_g_balance_laser_shockwave_spread_min, 255)); + WriteByte(MSG_BROADCAST, num_for_edict(self)); } void W_Laser_Touch() @@ -54,13 +57,11 @@ float W_Laser_Shockwave_CheckSpread(vector targetorg, vector nearest_on_line, ve float spreadlimit; float distance_of_attack = vlen(sw_shotorg - attack_endpos); float distance_from_line = vlen(targetorg - nearest_on_line); - - float total_angle = (vlen(normalize(targetorg - sw_shotorg) - normalize(attack_endpos - sw_shotorg)) * RAD2DEG); spreadlimit = (distance_of_attack ? min(1, (vlen(sw_shotorg - nearest_on_line) / distance_of_attack)) : 1); - spreadlimit = (autocvar_g_balance_laser_primary_spread_min * (1 - spreadlimit) + autocvar_g_balance_laser_primary_spread_max * spreadlimit); + spreadlimit = (autocvar_g_balance_laser_shockwave_spread_min * (1 - spreadlimit) + autocvar_g_balance_laser_shockwave_spread_max * spreadlimit); - if(spreadlimit && (distance_from_line <= spreadlimit) && (total_angle <= 90)) + if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(targetorg - sw_shotorg) - normalize(attack_endpos - sw_shotorg)) * RAD2DEG) <= 90)) return bound(0, (distance_from_line / spreadlimit), 1); else return FALSE; @@ -76,14 +77,14 @@ float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw // STEP ONE: Check if the nearest point is clear if(W_Laser_Shockwave_CheckSpread(nearest_to_attacker, nearest_on_line, sw_shotorg, attack_endpos)) { - WarpZone_TraceLine(sw_shotorg, nearest_to_attacker, MOVE_WORLDONLY, self); + WarpZone_TraceLine(sw_shotorg, nearest_to_attacker, MOVE_NOMONSTERS, self); if(trace_fraction == 1) { return TRUE; } // yes, the nearest point is clear and we can allow the damage } // STEP TWO: Check if shotorg to center point is clear if(W_Laser_Shockwave_CheckSpread(center, nearest_on_line, sw_shotorg, attack_endpos)) { - WarpZone_TraceLine(sw_shotorg, center, MOVE_WORLDONLY, self); + WarpZone_TraceLine(sw_shotorg, center, MOVE_NOMONSTERS, self); if(trace_fraction == 1) { return TRUE; } // yes, the center point is clear and we can allow the damage } @@ -93,7 +94,7 @@ float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw corner = get_corner_position(head, i); if(W_Laser_Shockwave_CheckSpread(corner, nearest_on_line, sw_shotorg, attack_endpos)) { - WarpZone_TraceLine(sw_shotorg, corner, MOVE_WORLDONLY, self); + WarpZone_TraceLine(sw_shotorg, corner, MOVE_NOMONSTERS, self); if(trace_fraction == 1) { return TRUE; } // yes, this corner is clear and we can allow the damage } } @@ -101,61 +102,116 @@ float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw return FALSE; } +#define PLAYER_CENTER(ent) (ent.origin + ((ent.classname == "player") ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5))) + +entity shockwave_hit[32]; +float shockwave_hit_damage[32]; +vector shockwave_hit_force[32]; + +float W_Laser_Shockwave_CheckHit(float queue, entity head, vector final_force, float final_damage) +{ + if not(head) { return FALSE; } + float i; + + ++queue; + + for(i = 1; i <= queue; ++i) + { + if(shockwave_hit[i] == head) + { + if(vlen(final_force) > vlen(shockwave_hit_force[i])) { shockwave_hit_force[i] = final_force; } + if(final_damage > shockwave_hit_damage[i]) { shockwave_hit_damage[i] = final_damage; } + return FALSE; + } + } + + shockwave_hit[queue] = head; + shockwave_hit_force[queue] = final_force; + shockwave_hit_damage[queue] = final_damage; + return TRUE; +} + void W_Laser_Shockwave() { // declarations float multiplier, multiplier_from_accuracy, multiplier_from_distance; float final_damage, final_spread; - vector final_force, center; + vector final_force, center, vel; entity head, next; - - // set up the shot direction - vector wanted_shot_direction = (v_forward * cos(autocvar_g_balance_laser_primary_shotangle * DEG2RAD) + v_up * sin(autocvar_g_balance_laser_primary_shotangle * DEG2RAD)); - W_SetupShot_Dir(self, wanted_shot_direction, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage); - vector attack_endpos = (w_shotorg + (w_shotdir * autocvar_g_balance_laser_primary_radius)); - // find out what we're pointing at and acquire the warpzone transform - WarpZone_TraceLine(w_shotorg, attack_endpos, FALSE, self); - entity aim_ent = trace_ent; - entity transform = WarpZone_trace_transform; + float i, queue; + // set up the shot direction + W_SetupShot(self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_shockwave_damage); + vector attack_endpos = (w_shotorg + (w_shotdir * autocvar_g_balance_laser_shockwave_distance)); + WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, self); vector attack_hitpos = trace_endpos; - float distance_to_hit = vlen(w_shotorg - attack_hitpos); float distance_to_end = vlen(w_shotorg - attack_endpos); - - // do the jump explosion now (also handles the impact effect) - RadiusDamageForSource(self, trace_endpos, '0 0 0', self, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_jumpradius, world, self, TRUE, autocvar_g_balance_laser_primary_force, WEP_LASER, world); - - // also do the firing effect now - SendCSQCShockwaveParticle(autocvar_g_balance_laser_primary_spread, attack_hitpos, transform); - - // did we hit a player directly? - if(aim_ent.takedamage) + float distance_to_hit = vlen(w_shotorg - attack_hitpos); + entity transform = WarpZone_trace_transform; + + // do the firing effect now + SendCSQCShockwaveParticle(attack_endpos); + Damage_DamageInfo(attack_hitpos, autocvar_g_balance_laser_shockwave_splash_damage, autocvar_g_balance_laser_shockwave_splash_edgedamage, autocvar_g_balance_laser_shockwave_splash_radius, w_shotdir * autocvar_g_balance_laser_shockwave_splash_force, WEP_LASER, 0, self); + + // splash damage/jumping trace + head = WarpZone_FindRadius(attack_hitpos, max(autocvar_g_balance_laser_shockwave_splash_radius, autocvar_g_balance_laser_shockwave_jump_radius), FALSE); + while(head) { - // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) - center = (aim_ent.origin + ((aim_ent.classname == "player") ? aim_ent.view_ofs : ((aim_ent.mins + aim_ent.maxs) * 0.5))); - - multiplier_from_accuracy = 1; - multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0)); - multiplier = max(autocvar_g_balance_laser_primary_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_primary_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_primary_multiplier_distance))); - - final_force = ((normalize(center - attack_hitpos) * autocvar_g_balance_laser_primary_force) * multiplier); - final_damage = (autocvar_g_balance_laser_primary_damage * multiplier + autocvar_g_balance_laser_primary_edgedamage * (1 - multiplier)); - Damage(aim_ent, self, self, final_damage, WEP_LASER, aim_ent.origin, final_force); - - print("debug: DIRECT HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); + next = head.chain; + + if(head.takedamage) + { + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + center = PLAYER_CENTER(head); + + float distance_to_head = vlen(attack_hitpos - head.WarpZone_findradius_nearest); + + if((head == self) && (distance_to_head <= autocvar_g_balance_laser_shockwave_jump_radius)) + { + multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / autocvar_g_balance_laser_shockwave_jump_radius)) : 0)); + multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0)); + multiplier = max(autocvar_g_balance_laser_shockwave_jump_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_jump_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_jump_multiplier_distance))); + + final_force = ((normalize(center - attack_hitpos) * autocvar_g_balance_laser_shockwave_jump_force) * multiplier); + vel = head.velocity; vel_z = 0; + vel = normalize(vel) * bound(0, vlen(vel) / autocvar_sv_maxspeed, 1) * autocvar_g_balance_laser_shockwave_jump_force_velocitybias; + final_force = (vlen(final_force) * normalize(normalize(final_force) + vel)); + final_force_z *= autocvar_g_balance_laser_shockwave_jump_force_zscale; + final_damage = (autocvar_g_balance_laser_shockwave_jump_damage * multiplier + autocvar_g_balance_laser_shockwave_jump_edgedamage * (1 - multiplier)); + + Damage(head, self, self, final_damage, WEP_LASER, head.origin, final_force); + //print("SELF HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); + } + else if (distance_to_head <= autocvar_g_balance_laser_shockwave_splash_radius) + { + multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / autocvar_g_balance_laser_shockwave_splash_radius)) : 0)); + multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0)); + multiplier = max(autocvar_g_balance_laser_shockwave_splash_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_splash_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_splash_multiplier_distance))); + + final_force = normalize(center - (attack_hitpos - (w_shotdir * autocvar_g_balance_laser_shockwave_splash_force_forwardbias))); + //te_lightning2(world, attack_hitpos, (attack_hitpos + (final_force * 200))); + final_force = ((final_force * autocvar_g_balance_laser_shockwave_splash_force) * multiplier); + final_force_z *= autocvar_g_balance_laser_shockwave_force_zscale; + final_damage = (autocvar_g_balance_laser_shockwave_splash_damage * multiplier + autocvar_g_balance_laser_shockwave_splash_edgedamage * (1 - multiplier)); + + if(W_Laser_Shockwave_CheckHit(queue, head, final_force, final_damage)) { ++queue; } + //print("SPLASH HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); + } + } + head = next; } - // now figure out if I hit anything else than what my aim directly pointed at... - head = WarpZone_FindRadius(w_shotorg, autocvar_g_balance_laser_primary_radius, FALSE); + // cone damage trace + head = WarpZone_FindRadius(w_shotorg, autocvar_g_balance_laser_shockwave_distance, FALSE); while(head) { next = head.chain; - if((head != self && head != aim_ent) && (head.takedamage)) + if((head != self) && head.takedamage) { // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) - center = (head.origin + ((head.classname == "player") ? head.view_ofs : ((head.mins + head.maxs) * 0.5))); + center = PLAYER_CENTER(head); // find the closest point on the enemy to the center of the attack float ang; // angle between shotdir and h @@ -168,24 +224,42 @@ void W_Laser_Shockwave() vector nearest_on_line = (w_shotorg + a * w_shotdir); vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line); - float distance_to_target = vlen(w_shotorg - nearest_to_attacker); + float distance_to_target = vlen(w_shotorg - nearest_to_attacker); // todo: use the findradius function for this - if((distance_to_target <= autocvar_g_balance_laser_primary_radius) + if((distance_to_target <= autocvar_g_balance_laser_shockwave_distance) && (W_Laser_Shockwave_IsVisible(head, nearest_on_line, w_shotorg, attack_endpos))) { multiplier_from_accuracy = (1 - W_Laser_Shockwave_CheckSpread(nearest_to_attacker, nearest_on_line, w_shotorg, attack_endpos)); multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_target / distance_to_end)) : 0)); - multiplier = max(autocvar_g_balance_laser_primary_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_primary_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_primary_multiplier_distance))); + multiplier = max(autocvar_g_balance_laser_shockwave_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_multiplier_distance))); - final_force = ((normalize(center - nearest_on_line) * autocvar_g_balance_laser_primary_force) * multiplier); - final_damage = (autocvar_g_balance_laser_primary_damage * multiplier + autocvar_g_balance_laser_primary_edgedamage * (1 - multiplier)); - Damage(head, self, self, final_damage, WEP_LASER, head.origin, final_force); + final_force = normalize(center - (nearest_on_line - (w_shotdir * autocvar_g_balance_laser_shockwave_force_forwardbias))); + //te_lightning2(world, nearest_on_line, (attack_hitpos + (final_force * 200))); + final_force = ((final_force * autocvar_g_balance_laser_shockwave_force) * multiplier); + final_force_z *= autocvar_g_balance_laser_shockwave_force_zscale; + final_damage = (autocvar_g_balance_laser_shockwave_damage * multiplier + autocvar_g_balance_laser_shockwave_edgedamage * (1 - multiplier)); - print("debug: EDGE HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); + if(W_Laser_Shockwave_CheckHit(queue, head, final_force, final_damage)) { ++queue; } + //print("CONE HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); } } head = next; } + + for(i = 1; i <= queue; ++i) + { + head = shockwave_hit[i]; + final_force = shockwave_hit_force[i]; + final_damage = shockwave_hit_damage[i]; + + Damage(head, self, self, final_damage, WEP_LASER, head.origin, final_force); + print("SHOCKWAVE by ", self.netname, ": damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force)), ".\n"); + + shockwave_hit[i] = world; + shockwave_hit_force = '0 0 0'; + shockwave_hit_damage = 0; + } + //print("queue was ", ftos(queue), ".\n\n"); } void W_Laser_Melee_Think() @@ -204,12 +278,12 @@ void W_Laser_Melee_Think() makevectors(self.realowner.v_angle); // update values for v_* vectors // calculate swing percentage based on time - meleetime = autocvar_g_balance_laser_secondary_melee_time * W_WeaponRateFactor(); + meleetime = autocvar_g_balance_laser_melee_time * W_WeaponRateFactor(); swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10); - f = ((1 - swing) * autocvar_g_balance_laser_secondary_melee_traces); + f = ((1 - swing) * autocvar_g_balance_laser_melee_traces); // check to see if we can still continue, otherwise give up now - if((self.realowner.deadflag != DEAD_NO) && autocvar_g_balance_laser_secondary_melee_no_doubleslap) + if((self.realowner.deadflag != DEAD_NO) && autocvar_g_balance_laser_melee_no_doubleslap) { remove(self); return; @@ -218,12 +292,12 @@ void W_Laser_Melee_Think() // if okay, perform the traces needed for this frame for(i=self.swing_prev; i < f; ++i) { - swing_factor = ((1 - (i / autocvar_g_balance_laser_secondary_melee_traces)) * 2 - 1); + swing_factor = ((1 - (i / autocvar_g_balance_laser_melee_traces)) * 2 - 1); targpos = (self.realowner.origin + self.realowner.view_ofs - + (v_forward * autocvar_g_balance_laser_secondary_melee_range) - + (v_up * swing_factor * autocvar_g_balance_laser_secondary_melee_swing_up) - + (v_right * swing_factor * autocvar_g_balance_laser_secondary_melee_swing_side)); + + (v_forward * autocvar_g_balance_laser_melee_range) + + (v_up * swing_factor * autocvar_g_balance_laser_melee_swing_up) + + (v_right * swing_factor * autocvar_g_balance_laser_melee_swing_side)); WarpZone_traceline_antilag(self.realowner, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self.realowner, ANTILAG_LATENCY(self.realowner)); @@ -236,25 +310,25 @@ void W_Laser_Melee_Think() if((trace_fraction < 1) // if trace is good, apply the damage and remove self && (trace_ent.takedamage == DAMAGE_AIM) && (trace_ent != self.swing_alreadyhit) - && (is_player || autocvar_g_balance_laser_secondary_melee_nonplayerdamage)) + && (is_player || autocvar_g_balance_laser_melee_nonplayerdamage)) { target_victim = trace_ent; // so it persists through other calls if(is_player) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught. - swing_damage = (autocvar_g_balance_laser_secondary_damage * min(1, swing_factor + 1)); + swing_damage = (autocvar_g_balance_laser_melee_damage * min(1, swing_factor + 1)); else - swing_damage = (autocvar_g_balance_laser_secondary_melee_nonplayerdamage * min(1, swing_factor + 1)); + swing_damage = (autocvar_g_balance_laser_melee_nonplayerdamage * min(1, swing_factor + 1)); //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n")); Damage(target_victim, self.realowner, self.realowner, swing_damage, WEP_LASER | HITTYPE_SECONDARY, self.realowner.origin + self.realowner.view_ofs, - v_forward * autocvar_g_balance_laser_secondary_force); + v_forward * autocvar_g_balance_laser_melee_force); if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_LASER, 0, swing_damage); } - if(autocvar_g_balance_laser_secondary_melee_multihit) // allow multiple hits with one swing, but not against the same player twice. + if(autocvar_g_balance_laser_melee_multihit) // allow multiple hits with one swing, but not against the same player twice. { self.swing_alreadyhit = target_victim; continue; // move along to next trace @@ -284,14 +358,14 @@ void W_Laser_Melee_Think() void W_Laser_Melee() { sound(self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTN_NORM); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready); + weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_melee_animtime, w_ready); entity meleetemp; meleetemp = spawn(); meleetemp.owner = meleetemp.realowner = self; meleetemp.think = W_Laser_Melee_Think; - meleetemp.nextthink = time + autocvar_g_balance_laser_secondary_melee_delay * W_WeaponRateFactor(); - W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_laser_secondary_damage, autocvar_g_balance_laser_secondary_melee_range); + meleetemp.nextthink = time + autocvar_g_balance_laser_melee_delay * W_WeaponRateFactor(); + W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_laser_melee_damage, autocvar_g_balance_laser_melee_range); } void W_Laser_Attack(float issecondary) @@ -368,7 +442,7 @@ float W_Laser(float request) { case WR_AIM: { - if((autocvar_g_balance_laser_secondary == 2) && (vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_laser_secondary_melee_range)) + if((autocvar_g_balance_laser_secondary == 2) && (vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_laser_melee_range)) self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); else self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE); @@ -385,8 +459,7 @@ float W_Laser(float request) { W_DecreaseAmmo(ammo_none, 1, TRUE); - - if not(autocvar_g_balance_laser_oldprimary) + if not(autocvar_g_balance_laser_primary) W_Laser_Shockwave(); else W_Laser_Attack(FALSE); @@ -421,11 +494,11 @@ float W_Laser(float request) case 2: // melee attack secondary { if(!self.crouch) // we are not currently crouching; this fixes an exploit where your melee anim is not visible, and besides wouldn't make much sense - if(weapon_prepareattack(1, autocvar_g_balance_laser_secondary_refire)) + if(weapon_prepareattack(1, autocvar_g_balance_laser_melee_refire)) { // attempt forcing playback of the anim by switching to another anim (that we never play) here... W_Laser_Melee(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_secondary_animtime, w_ready); + weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_melee_animtime, w_ready); } } }