X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fw_laser.qc;h=25e240357e2a2e812ada00b18906045c29329464;hb=a9a7db34cf93f756ae42925c302cec84f9f9b7d2;hp=1e067647b86d51e359148bbd58f0db091dc4aa8f;hpb=5ebc99271d5778d4eafced015dedc05224e502cc;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/w_laser.qc b/qcsrc/server/w_laser.qc index 1e067647b..25e240357 100644 --- a/qcsrc/server/w_laser.qc +++ b/qcsrc/server/w_laser.qc @@ -1,5 +1,5 @@ #ifdef REGISTER_WEAPON -REGISTER_WEAPON(LASER, w_laser, 0, 1, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, 0, "laser", "laser", _("Laser")) +REGISTER_WEAPON(LASER, W_Laser, 0, 1, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, 0, "laser", "laser", _("Blaster")) #else #ifdef SVQC void(float imp) W_SwitchWeapon; @@ -7,9 +7,10 @@ void() W_LastWeapon; .float swing_prev; .entity swing_alreadyhit; -void SendCSQCShockwaveParticle(float spread, vector endpos) +void SendCSQCShockwaveParticle(vector endpos) { - //WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + //endpos = WarpZone_UnTransformOrigin(transform, endpos); + WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); WriteByte(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE); WriteCoord(MSG_BROADCAST, w_shotorg_x); @@ -18,50 +19,55 @@ void SendCSQCShockwaveParticle(float spread, vector endpos) 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 (void) +void W_Laser_Touch() { PROJECTILE_TOUCH; self.event_damage = SUB_Null; - if (self.dmg) - RadiusDamage (self, self.realowner, autocvar_g_balance_laser_secondary_damage, autocvar_g_balance_laser_secondary_edgedamage, autocvar_g_balance_laser_secondary_radius, world, world, autocvar_g_balance_laser_secondary_force, self.projectiledeathtype, other); + + if(self.dmg) + RadiusDamage(self, self.realowner, autocvar_g_balance_laser_secondary_damage, autocvar_g_balance_laser_secondary_edgedamage, autocvar_g_balance_laser_secondary_radius, world, world, autocvar_g_balance_laser_secondary_force, self.projectiledeathtype, other); else - RadiusDamage (self, self.realowner, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_radius, world, world, autocvar_g_balance_laser_primary_force, self.projectiledeathtype, other); + RadiusDamage(self, self.realowner, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_radius, world, world, autocvar_g_balance_laser_primary_force, self.projectiledeathtype, other); - remove (self); + remove(self); } void W_Laser_Think() { self.movetype = MOVETYPE_FLY; self.think = SUB_Remove; - if (self.dmg) + + if(self.dmg) self.nextthink = time + autocvar_g_balance_laser_secondary_lifetime; else self.nextthink = time + autocvar_g_balance_laser_primary_lifetime; + CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE); } -float W_Laser_Shockwave_CheckSpread(vector targetorg, vector nearest_on_line, vector sw_shotorg, vector attack_hitpos) +float W_Laser_Shockwave_CheckSpread(vector targetorg, vector nearest_on_line, vector sw_shotorg, vector attack_endpos) { float spreadlimit; - float distance_of_attack = vlen(sw_shotorg - attack_hitpos); + float distance_of_attack = vlen(sw_shotorg - attack_endpos); float distance_from_line = vlen(targetorg - nearest_on_line); 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)) + 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; } -float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw_shotorg, vector attack_hitpos) +float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw_shotorg, vector attack_endpos) { vector nearest_to_attacker = head.WarpZone_findradius_nearest; vector center = (head.origin + (head.mins + head.maxs) * 0.5); @@ -69,16 +75,16 @@ float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw float i; // STEP ONE: Check if the nearest point is clear - if(W_Laser_Shockwave_CheckSpread(nearest_to_attacker, nearest_on_line, sw_shotorg, attack_hitpos)) + 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_hitpos)) + 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 } @@ -86,9 +92,9 @@ float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw for(i=1; i<=8; ++i) { corner = get_corner_position(head, i); - if(W_Laser_Shockwave_CheckSpread(corner, nearest_on_line, sw_shotorg, attack_hitpos)) + 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 } } @@ -96,59 +102,116 @@ float W_Laser_Shockwave_IsVisible(entity head, vector nearest_on_line, vector sw return FALSE; } -void W_Laser_Shockwave (void) +#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; + + float i, queue; // 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; + 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_of_attack = vlen(w_shotorg - attack_hitpos); - - // 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); - - // did we hit a player directly? - if(aim_ent.takedamage) + float distance_to_end = vlen(w_shotorg - attack_endpos); + 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_of_attack ? min(1, (distance_of_attack / autocvar_g_balance_laser_primary_radius)) : 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); - Damage(aim_ent, self, self, final_damage, WEP_LASER, aim_ent.origin, final_force); - - print("multiplier = ", ftos(multiplier), ", multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ", "); - print(strcat("direct hit damage = ", ftos(autocvar_g_balance_laser_primary_damage), ", force = ", vtos(final_force), ".\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 @@ -161,33 +224,45 @@ void W_Laser_Shockwave (void) 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))) { - if(W_Laser_Shockwave_IsVisible(head, nearest_on_line, w_shotorg, attack_hitpos)) - { - multiplier_from_accuracy = (1 - W_Laser_Shockwave_CheckSpread(nearest_to_attacker, nearest_on_line, w_shotorg, attack_hitpos)); - multiplier_from_distance = (1 - (distance_of_attack ? min(1, (distance_to_target / autocvar_g_balance_laser_primary_radius)) : 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 - 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); - - print("multiplier = ", ftos(multiplier), ", multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ", "); - print(strcat("edge hit damage = ", ftos(final_damage), ", force = ", vtos(final_force), ".\n")); - - //pointparticles(particleeffectnum("rocket_guide"), w_shotorg, w_shotdir * 1000, 1); - //SendCSQCShockwaveParticle(autocvar_g_balance_laser_primary_spread, trace_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_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 - (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)); + + 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(void) +void W_Laser_Melee_Think() { // declarations float i, f, swing, swing_factor, swing_damage, meleetime, is_player; @@ -203,12 +278,12 @@ void W_Laser_Melee_Think(void) 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; @@ -217,12 +292,12 @@ void W_Laser_Melee_Think(void) // 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)); @@ -235,25 +310,25 @@ void W_Laser_Melee_Think(void) 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 @@ -280,20 +355,20 @@ void W_Laser_Melee_Think(void) } } -void W_Laser_Melee(void) +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) +void W_Laser_Attack(float issecondary) { entity missile; vector s_forward; @@ -309,14 +384,14 @@ void W_Laser_Attack (float issecondary) s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD); if(nodamage) - W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, 0); + W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, 0); else if(issecondary == 1) - W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_secondary_damage); + W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_secondary_damage); else - W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage); + W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage); pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - missile = spawn (); + missile = spawn(); missile.owner = missile.realowner = self; missile.classname = "laserbolt"; missile.dmg = 0; @@ -329,11 +404,11 @@ void W_Laser_Attack (float issecondary) PROJECTILE_MAKETRIGGER(missile); missile.projectiledeathtype = WEP_LASER; - setorigin (missile, w_shotorg); + setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_primary); - missile.angles = vectoangles (missile.velocity); + missile.angles = vectoangles(missile.velocity); //missile.glow_color = 250; // 244, 250 //missile.glow_size = 120; missile.touch = W_Laser_Touch; @@ -356,128 +431,147 @@ void W_Laser_Attack (float issecondary) } } -void spawnfunc_weapon_laser (void) +void spawnfunc_weapon_laser(void) { weapon_defaultspawnfunc(WEP_LASER); } -float w_laser(float req) +float W_Laser(float request) { - if (req == WR_AIM) - { - if((autocvar_g_balance_laser_secondary == 2) && (vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_laser_secondary_melee_range)) - self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); - else - self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE); - } - else if (req == WR_THINK) + switch(request) { - if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if (self.BUTTON_ATCK) + case WR_AIM: { - if (weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire)) - { - W_DecreaseAmmo(ammo_none, 1, TRUE); - - - if not(autocvar_g_balance_laser_oldprimary) - W_Laser_Shockwave(); - else - W_Laser_Attack(FALSE); - - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready); - } + 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); + return TRUE; } - else if (self.BUTTON_ATCK2) + + case WR_THINK: { - switch(autocvar_g_balance_laser_secondary) + if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload + weapon_action(self.weapon, WR_RELOAD); + else if(self.BUTTON_ATCK) { - case 0: // switch to last used weapon + if(weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire)) { - if(self.switchweapon == WEP_LASER) // don't do this if already switching - W_LastWeapon(); + W_DecreaseAmmo(ammo_none, 1, TRUE); - break; - } + if not(autocvar_g_balance_laser_primary) + W_Laser_Shockwave(); + else + W_Laser_Attack(FALSE); - case 1: // normal projectile secondary + weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready); + } + } + else if(self.BUTTON_ATCK2) + { + switch(autocvar_g_balance_laser_secondary) { - if(weapon_prepareattack(0, autocvar_g_balance_laser_secondary_refire)) + case 0: // switch to last used weapon { - W_DecreaseAmmo(ammo_none, 1, TRUE); - W_Laser_Attack(TRUE); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready); + if(self.switchweapon == WEP_LASER) // don't do this if already switching + W_LastWeapon(); + + break; } - break; - } + case 1: // normal projectile secondary + { + if(weapon_prepareattack(1, autocvar_g_balance_laser_secondary_refire)) + { + W_DecreaseAmmo(ammo_none, 1, TRUE); + W_Laser_Attack(TRUE); + weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready); + } + + break; + } - case 2: // melee attack secondary - { - if (self.clip_load >= 0) // we are not currently reloading - 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)) + case 2: // melee attack secondary { - // attempt forcing playback of the anim by switching to another anim (that we never play) here... - weapon_thinkf(WFRAME_FIRE1, 0, W_Laser_Melee); + 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_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_melee_animtime, w_ready); + } } } } + return TRUE; + } + + case WR_PRECACHE: + { + precache_model("models/weapons/g_laser.md3"); + precache_model("models/weapons/v_laser.md3"); + precache_model("models/weapons/h_laser.iqm"); + precache_sound("weapons/lasergun_fire.wav"); + return TRUE; + } + + case WR_SETUP: + { + weapon_setup(WEP_LASER); + self.current_ammo = ammo_none; + return TRUE; + } + + case WR_CHECKAMMO1: + case WR_CHECKAMMO2: + { + return TRUE; // laser has infinite ammo + } + + case WR_RELOAD: + { + W_Reload(0, autocvar_g_balance_laser_reload_ammo, autocvar_g_balance_laser_reload_time, "weapons/reload.wav"); + return TRUE; } } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_laser.md3"); - precache_model ("models/weapons/v_laser.md3"); - precache_model ("models/weapons/h_laser.iqm"); - precache_sound ("weapons/lasergun_fire.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_LASER); - self.current_ammo = ammo_none; - } - else if (req == WR_CHECKAMMO1) - { - return TRUE; - } - else if (req == WR_CHECKAMMO2) - { - return TRUE; - } - else if (req == WR_RELOAD) - { - W_Reload(0, autocvar_g_balance_laser_reload_ammo, autocvar_g_balance_laser_reload_time, "weapons/reload.wav"); - } + return TRUE; } #endif #ifdef CSQC -float w_laser(float req) +float W_Laser(float request) { - if(req == WR_IMPACTEFFECT) + switch(request) { - vector org2; - org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum("new_laser_impact"), org2, w_backoff * 1000, 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/laserimpact.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - w_deathtypestring = _("%s lasered themself to hell"); - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - w_deathtypestring = _("%s was cut in half by %s's gauntlet"); // unchecked: SPLASH // TODO - else - w_deathtypestring = _("%s was lasered to death by %s"); // unchecked: SPLASH + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + pointparticles(particleeffectnum("new_laser_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent) { sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM); } + return TRUE; + } + + case WR_PRECACHE: + { + precache_sound("weapons/laserimpact.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + w_deathtypestring = _("%s lasered themself to hell"); + return TRUE; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + w_deathtypestring = _("%s was cut in half by %s's gauntlet"); // unchecked: SPLASH // TODO + else + w_deathtypestring = _("%s was lasered to death by %s"); // unchecked: SPLASH + return TRUE; + } } + return TRUE; } #endif