]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
More cleanup...
authorSamual Lenks <samual@xonotic.org>
Mon, 10 Jun 2013 23:57:20 +0000 (19:57 -0400)
committerSamual Lenks <samual@xonotic.org>
Mon, 10 Jun 2013 23:57:20 +0000 (19:57 -0400)
qcsrc/client/projectile.qc [deleted file]
qcsrc/client/projectile.qh [deleted file]
qcsrc/client/weapons/projectile.qc [new file with mode: 0644]
qcsrc/client/weapons/projectile.qh [new file with mode: 0644]
qcsrc/common/weapons/calculations.qc
qcsrc/server/accuracy.qc [deleted file]
qcsrc/server/accuracy.qh [deleted file]
qcsrc/server/weapons/accuracy.qc [new file with mode: 0644]
qcsrc/server/weapons/accuracy.qh [new file with mode: 0644]

diff --git a/qcsrc/client/projectile.qc b/qcsrc/client/projectile.qc
deleted file mode 100644 (file)
index fb4fdd5..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-.vector iorigin1, iorigin2;
-.float spawntime;
-.vector trail_oldorigin;
-.float trail_oldtime;
-.float fade_time, fade_rate;
-
-void SUB_Stop()
-{
-       self.move_velocity = self.move_avelocity = '0 0 0';
-       self.move_movetype = MOVETYPE_NONE;
-}
-
-.float alphamod;
-.float count; // set if clientside projectile
-.float cnt; // sound index
-.float gravity;
-.float snd_looping;
-.float silent;
-
-void Projectile_ResetTrail(vector to)
-{
-       self.trail_oldorigin = to;
-       self.trail_oldtime = time;
-}
-
-void Projectile_DrawTrail(vector to)
-{
-       vector from;
-       float t0;
-
-       from = self.trail_oldorigin;
-       t0 = self.trail_oldtime;
-       self.trail_oldorigin = to;
-       self.trail_oldtime = time;
-
-       // force the effect even for stationary firemine
-       if(self.cnt == PROJECTILE_FIREMINE)
-               if(from == to)
-                       from_z += 1;
-
-       if (self.traileffect)
-       {
-               particles_alphamin = particles_alphamax = sqrt(self.alpha);
-               boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, sqrt(self.alpha), PARTICLES_USEALPHA);
-       }
-}
-
-void Projectile_Draw()
-{
-       vector rot;
-       vector trailorigin;
-       float f;
-       float drawn;
-       float t;
-       float a;
-
-       f = self.move_flags;
-
-       if(self.count & 0x80)
-       {
-               //self.move_flags &~= FL_ONGROUND;
-               if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY)
-                       Movetype_Physics_NoMatchServer();
-                       // the trivial movetypes do not have to match the
-                       // server's ticrate as they are ticrate independent
-                       // NOTE: this assumption is only true if MOVETYPE_FLY
-                       // projectiles detonate on impact. If they continue
-                       // moving, we might still be ticrate dependent.
-               else
-                       Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
-               if(!(self.move_flags & FL_ONGROUND))
-                       if(self.velocity != '0 0 0')
-                               self.move_angles = self.angles = vectoangles(self.velocity);
-       }
-       else
-       {
-               InterpolateOrigin_Do();
-       }
-
-       if(self.count & 0x80)
-       {
-               drawn = (time >= self.spawntime - 0.02);
-               t = max(time, self.spawntime);
-       }
-       else
-       {
-               drawn = (self.iflags & IFLAG_VALID);
-               t = time;
-       }
-
-       if(!(f & FL_ONGROUND))
-       {
-               rot = '0 0 0';
-               switch(self.cnt)
-               {
-                       /*
-                       case PROJECTILE_GRENADE:
-                               rot = '-2000 0 0'; // forward
-                               break;
-                       */
-                       case PROJECTILE_GRENADE_BOUNCING:
-                               rot = '0 -1000 0'; // sideways
-                               break;
-                       case PROJECTILE_HOOKBOMB:
-                               rot = '1000 0 0'; // forward
-                               break;
-                       default:
-                               break;
-               }
-               self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime)));
-       }
-
-       vector ang;
-       ang = self.angles;
-       ang_x = -ang_x;
-       makevectors(ang);
-
-       a = 1 - (time - self.fade_time) * self.fade_rate;
-       self.alpha = bound(0, self.alphamod * a, 1);
-       if(self.alpha <= 0)
-               drawn = 0;
-       self.renderflags = 0;
-
-       trailorigin = self.origin;
-       switch(self.cnt)
-       {
-               case PROJECTILE_GRENADE:
-               case PROJECTILE_GRENADE_BOUNCING:
-                       trailorigin += v_right * 1 + v_forward * -10;
-                       break;
-               default:
-                       break;
-       }
-       if(drawn)
-               Projectile_DrawTrail(trailorigin);
-       else
-               Projectile_ResetTrail(trailorigin);
-
-       self.drawmask = 0;
-
-       if(!drawn)
-               return;
-
-       switch(self.cnt)
-       {
-               case PROJECTILE_BULLET_GLOWING:
-               case PROJECTILE_BULLET_GLOWING_TRACER:
-                       adddynamiclight(self.origin, 50 * a, '1 1 0');
-                       break;
-               default:
-                       break;
-       }
-
-       self.drawmask = MASK_NORMAL;
-}
-
-void loopsound(entity e, float ch, string samp, float vol, float attn)
-{
-       if(self.silent)
-               return;
-
-       sound(e, ch, samp, vol, attn);
-       e.snd_looping = ch;
-}
-
-void Ent_RemoveProjectile()
-{
-       if(self.count & 0x80)
-       {
-               tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self);
-               Projectile_DrawTrail(trace_endpos);
-       }
-}
-
-void Ent_Projectile()
-{
-       float f;
-
-       // projectile properties:
-       //   kind (interpolated, or clientside)
-       //
-       //   modelindex
-       //   origin
-       //   scale
-       //   if clientside:
-       //     velocity
-       //     gravity
-       //   soundindex (hardcoded list)
-       //   effects
-       //
-       // projectiles don't send angles, because they always follow the velocity
-
-       f = ReadByte();
-       self.count = (f & 0x80);
-       self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN;
-       self.solid = SOLID_TRIGGER;
-       //self.effects = EF_NOMODELFLAGS;
-
-       // this should make collisions with bmodels more exact, but it leads to
-       // projectiles no longer being able to lie on a bmodel
-       self.move_nomonsters = MOVE_WORLDONLY;
-       if(f & 0x40)
-               self.move_flags |= FL_ONGROUND;
-       else
-               self.move_flags &~= FL_ONGROUND;
-
-       if(!self.move_time)
-       {
-               // for some unknown reason, we don't need to care for
-               // sv_gameplayfix_delayprojectiles here.
-               self.move_time = time;
-               self.spawntime = time;
-       }
-       else
-               self.move_time = max(self.move_time, time);
-
-       if(!(self.count & 0x80))
-               InterpolateOrigin_Undo();
-
-       if(f & 1)
-       {
-               self.origin_x = ReadCoord();
-               self.origin_y = ReadCoord();
-               self.origin_z = ReadCoord();
-               setorigin(self, self.origin);
-               if(self.count & 0x80)
-               {
-                       self.velocity_x = ReadCoord();
-                       self.velocity_y = ReadCoord();
-                       self.velocity_z = ReadCoord();
-                       if(f & 0x10)
-                               self.gravity = ReadCoord();
-                       else
-                               self.gravity = 0; // none
-                       self.move_origin = self.origin;
-                       self.move_velocity = self.velocity;
-               }
-
-               if(time == self.spawntime || (self.count & 0x80) || (f & 0x08))
-               {
-                       self.trail_oldorigin = self.origin;
-                       if(!(self.count & 0x80))
-                               InterpolateOrigin_Reset();
-               }
-
-               if(f & 0x20)
-               {
-                       self.fade_time = time + ReadByte() * ticrate;
-                       self.fade_rate = 1 / (ReadByte() * ticrate);
-               }
-               else
-               {
-                       self.fade_time = 0;
-                       self.fade_rate = 0;
-               }
-       }
-
-       if(f & 2)
-       {
-               self.cnt = ReadByte();
-
-               self.silent = (self.cnt & 0x80);
-               self.cnt = (self.cnt & 0x7F);
-
-               self.scale = 1;
-               self.traileffect = 0;
-               switch(self.cnt)
-               {
-                       case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
-                       case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
-                       case PROJECTILE_BULLET: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_bullet"); break;
-                       case PROJECTILE_BULLET_GLOWING: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle_weak"); break;
-                       case PROJECTILE_BULLET_GLOWING_TRACER: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle"); break;
-                       case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
-                       case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
-                       case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
-                       case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
-                       case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
-                       case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
-                       case PROJECTILE_LASER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break;
-                       case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break;
-                       case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
-                       case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
-                       case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
-                       case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
-                       case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
-                       case PROJECTILE_FIREBALL: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("fireball"); break; // particle effect is good enough
-                       case PROJECTILE_FIREMINE: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("firemine"); break; // particle effect is good enough
-                       case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break;
-                       case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break;
-                       case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break;
-
-                       case PROJECTILE_RAPTORBOMB:    setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
-                       case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3");     self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
-                       case PROJECTILE_RAPTORCANNON:  setmodel(self, "models/plasmatrail.mdl"); self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
-
-                       case PROJECTILE_SPIDERROCKET: setmodel(self, "models/vehicles/rocket02.md3"); self.traileffect = particleeffectnum("spiderbot_rocket_thrust"); break;
-                       case PROJECTILE_WAKIROCKET:   setmodel(self, "models/vehicles/rocket01.md3");  self.traileffect = particleeffectnum("wakizashi_rocket_thrust"); break;
-                       case PROJECTILE_WAKICANNON:   setmodel(self, "models/laser.mdl");  self.traileffect = particleeffectnum(""); break;
-
-                       case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
-                       case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
-
-                       default:
-                               error("Received invalid CSQC projectile, can't work with this!");
-                               break;
-               }
-
-               self.mins = '0 0 0';
-               self.maxs = '0 0 0';
-               self.colormod = '0 0 0';
-               self.move_touch = SUB_Stop;
-               self.move_movetype = MOVETYPE_TOSS;
-               self.alphamod = 1;
-
-               switch(self.cnt)
-               {
-                       case PROJECTILE_ELECTRO:
-                               // only new engines support sound moving with object
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM);
-                               self.mins = '0 0 -4';
-                               self.maxs = '0 0 -4';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
-                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
-                               break;
-                       case PROJECTILE_ROCKET:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTN_NORM);
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               break;
-                       case PROJECTILE_GRENADE:
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               break;
-                       case PROJECTILE_GRENADE_BOUNCING:
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.move_bounce_factor = g_balance_grenadelauncher_bouncefactor;
-                               self.move_bounce_stopspeed = g_balance_grenadelauncher_bouncestop;
-                               break;
-                       case PROJECTILE_MINE:
-                               self.mins = '-4 -4 -4';
-                               self.maxs = '4 4 4';
-                               break;
-                       case PROJECTILE_PORTO_RED:
-                               self.colormod = '2 1 1';
-                               self.alphamod = 0.5;
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               break;
-                       case PROJECTILE_PORTO_BLUE:
-                               self.colormod = '1 1 2';
-                               self.alphamod = 0.5;
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               break;
-                       case PROJECTILE_HAGAR_BOUNCING:
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               break;
-                       case PROJECTILE_CRYLINK_BOUNCING:
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               break;
-                       case PROJECTILE_FIREBALL:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTN_NORM);
-                               self.mins = '-16 -16 -16';
-                               self.maxs = '16 16 16';
-                               break;
-                       case PROJECTILE_FIREMINE:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTN_NORM);
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.mins = '-4 -4 -4';
-                               self.maxs = '4 4 4';
-                               break;
-                       case PROJECTILE_TAG:
-                               self.mins = '-2 -2 -2';
-                               self.maxs = '2 2 2';
-                               break;
-                       case PROJECTILE_FLAC:
-                               self.mins = '-2 -2 -2';
-                               self.maxs = '2 2 2';
-                               break;
-                       case PROJECTILE_SEEKER:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
-                               self.mins = '-4 -4 -4';
-                               self.maxs = '4 4 4';
-                               break;
-            case PROJECTILE_RAPTORBOMB:
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               break;
-            case PROJECTILE_RAPTORBOMBLET:
-                               break;
-            case PROJECTILE_RAPTORCANNON:
-                               break;
-            case PROJECTILE_SPIDERROCKET:
-                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
-                               break;
-            case PROJECTILE_WAKIROCKET:
-                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
-                               break;            
-            /*
-            case PROJECTILE_WAKICANNON:
-                               break;
-                       case PROJECTILE_BUMBLE_GUN:
-                               // only new engines support sound moving with object
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM);
-                               self.mins = '0 0 -4';
-                               self.maxs = '0 0 -4';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
-                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
-                               break;
-                       */
-                       default:
-                               break;
-               }
-               setsize(self, self.mins, self.maxs);
-       }
-
-       if(self.gravity)
-       {
-               if(self.move_movetype == MOVETYPE_FLY)
-                       self.move_movetype = MOVETYPE_TOSS;
-               if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
-                       self.move_movetype = MOVETYPE_BOUNCE;
-       }
-       else
-       {
-               if(self.move_movetype == MOVETYPE_TOSS)
-                       self.move_movetype = MOVETYPE_FLY;
-               if(self.move_movetype == MOVETYPE_BOUNCE)
-                       self.move_movetype = MOVETYPE_BOUNCEMISSILE;
-       }
-
-       if(!(self.count & 0x80))
-               InterpolateOrigin_Note();
-
-       self.draw = Projectile_Draw;
-       self.entremove = Ent_RemoveProjectile;
-}
-
-void Projectile_Precache()
-{
-       precache_model("models/ebomb.mdl");
-       precache_model("models/elaser.mdl");
-       precache_model("models/grenademodel.md3");
-       precache_model("models/mine.md3");
-       precache_model("models/hagarmissile.mdl");
-       precache_model("models/hlac_bullet.md3");
-       precache_model("models/laser.mdl");
-       precache_model("models/plasmatrail.mdl");
-       precache_model("models/rocket.md3");
-       precache_model("models/tagrocket.md3");
-       precache_model("models/tracer.mdl");
-
-       precache_sound("weapons/electro_fly.wav");
-       precache_sound("weapons/rocket_fly.wav");
-       precache_sound("weapons/fireball_fly.wav");
-       precache_sound("weapons/fireball_fly2.wav");
-       precache_sound("weapons/tag_rocket_fly.wav");
-
-}
diff --git a/qcsrc/client/projectile.qh b/qcsrc/client/projectile.qh
deleted file mode 100644 (file)
index 70c8ba0..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-.float traileffect;
-void Projectile_ResetTrail(vector to);
-void Projectile_DrawTrail(vector to);
diff --git a/qcsrc/client/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc
new file mode 100644 (file)
index 0000000..fb4fdd5
--- /dev/null
@@ -0,0 +1,470 @@
+.vector iorigin1, iorigin2;
+.float spawntime;
+.vector trail_oldorigin;
+.float trail_oldtime;
+.float fade_time, fade_rate;
+
+void SUB_Stop()
+{
+       self.move_velocity = self.move_avelocity = '0 0 0';
+       self.move_movetype = MOVETYPE_NONE;
+}
+
+.float alphamod;
+.float count; // set if clientside projectile
+.float cnt; // sound index
+.float gravity;
+.float snd_looping;
+.float silent;
+
+void Projectile_ResetTrail(vector to)
+{
+       self.trail_oldorigin = to;
+       self.trail_oldtime = time;
+}
+
+void Projectile_DrawTrail(vector to)
+{
+       vector from;
+       float t0;
+
+       from = self.trail_oldorigin;
+       t0 = self.trail_oldtime;
+       self.trail_oldorigin = to;
+       self.trail_oldtime = time;
+
+       // force the effect even for stationary firemine
+       if(self.cnt == PROJECTILE_FIREMINE)
+               if(from == to)
+                       from_z += 1;
+
+       if (self.traileffect)
+       {
+               particles_alphamin = particles_alphamax = sqrt(self.alpha);
+               boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, sqrt(self.alpha), PARTICLES_USEALPHA);
+       }
+}
+
+void Projectile_Draw()
+{
+       vector rot;
+       vector trailorigin;
+       float f;
+       float drawn;
+       float t;
+       float a;
+
+       f = self.move_flags;
+
+       if(self.count & 0x80)
+       {
+               //self.move_flags &~= FL_ONGROUND;
+               if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY)
+                       Movetype_Physics_NoMatchServer();
+                       // the trivial movetypes do not have to match the
+                       // server's ticrate as they are ticrate independent
+                       // NOTE: this assumption is only true if MOVETYPE_FLY
+                       // projectiles detonate on impact. If they continue
+                       // moving, we might still be ticrate dependent.
+               else
+                       Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
+               if(!(self.move_flags & FL_ONGROUND))
+                       if(self.velocity != '0 0 0')
+                               self.move_angles = self.angles = vectoangles(self.velocity);
+       }
+       else
+       {
+               InterpolateOrigin_Do();
+       }
+
+       if(self.count & 0x80)
+       {
+               drawn = (time >= self.spawntime - 0.02);
+               t = max(time, self.spawntime);
+       }
+       else
+       {
+               drawn = (self.iflags & IFLAG_VALID);
+               t = time;
+       }
+
+       if(!(f & FL_ONGROUND))
+       {
+               rot = '0 0 0';
+               switch(self.cnt)
+               {
+                       /*
+                       case PROJECTILE_GRENADE:
+                               rot = '-2000 0 0'; // forward
+                               break;
+                       */
+                       case PROJECTILE_GRENADE_BOUNCING:
+                               rot = '0 -1000 0'; // sideways
+                               break;
+                       case PROJECTILE_HOOKBOMB:
+                               rot = '1000 0 0'; // forward
+                               break;
+                       default:
+                               break;
+               }
+               self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime)));
+       }
+
+       vector ang;
+       ang = self.angles;
+       ang_x = -ang_x;
+       makevectors(ang);
+
+       a = 1 - (time - self.fade_time) * self.fade_rate;
+       self.alpha = bound(0, self.alphamod * a, 1);
+       if(self.alpha <= 0)
+               drawn = 0;
+       self.renderflags = 0;
+
+       trailorigin = self.origin;
+       switch(self.cnt)
+       {
+               case PROJECTILE_GRENADE:
+               case PROJECTILE_GRENADE_BOUNCING:
+                       trailorigin += v_right * 1 + v_forward * -10;
+                       break;
+               default:
+                       break;
+       }
+       if(drawn)
+               Projectile_DrawTrail(trailorigin);
+       else
+               Projectile_ResetTrail(trailorigin);
+
+       self.drawmask = 0;
+
+       if(!drawn)
+               return;
+
+       switch(self.cnt)
+       {
+               case PROJECTILE_BULLET_GLOWING:
+               case PROJECTILE_BULLET_GLOWING_TRACER:
+                       adddynamiclight(self.origin, 50 * a, '1 1 0');
+                       break;
+               default:
+                       break;
+       }
+
+       self.drawmask = MASK_NORMAL;
+}
+
+void loopsound(entity e, float ch, string samp, float vol, float attn)
+{
+       if(self.silent)
+               return;
+
+       sound(e, ch, samp, vol, attn);
+       e.snd_looping = ch;
+}
+
+void Ent_RemoveProjectile()
+{
+       if(self.count & 0x80)
+       {
+               tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self);
+               Projectile_DrawTrail(trace_endpos);
+       }
+}
+
+void Ent_Projectile()
+{
+       float f;
+
+       // projectile properties:
+       //   kind (interpolated, or clientside)
+       //
+       //   modelindex
+       //   origin
+       //   scale
+       //   if clientside:
+       //     velocity
+       //     gravity
+       //   soundindex (hardcoded list)
+       //   effects
+       //
+       // projectiles don't send angles, because they always follow the velocity
+
+       f = ReadByte();
+       self.count = (f & 0x80);
+       self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN;
+       self.solid = SOLID_TRIGGER;
+       //self.effects = EF_NOMODELFLAGS;
+
+       // this should make collisions with bmodels more exact, but it leads to
+       // projectiles no longer being able to lie on a bmodel
+       self.move_nomonsters = MOVE_WORLDONLY;
+       if(f & 0x40)
+               self.move_flags |= FL_ONGROUND;
+       else
+               self.move_flags &~= FL_ONGROUND;
+
+       if(!self.move_time)
+       {
+               // for some unknown reason, we don't need to care for
+               // sv_gameplayfix_delayprojectiles here.
+               self.move_time = time;
+               self.spawntime = time;
+       }
+       else
+               self.move_time = max(self.move_time, time);
+
+       if(!(self.count & 0x80))
+               InterpolateOrigin_Undo();
+
+       if(f & 1)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+               if(self.count & 0x80)
+               {
+                       self.velocity_x = ReadCoord();
+                       self.velocity_y = ReadCoord();
+                       self.velocity_z = ReadCoord();
+                       if(f & 0x10)
+                               self.gravity = ReadCoord();
+                       else
+                               self.gravity = 0; // none
+                       self.move_origin = self.origin;
+                       self.move_velocity = self.velocity;
+               }
+
+               if(time == self.spawntime || (self.count & 0x80) || (f & 0x08))
+               {
+                       self.trail_oldorigin = self.origin;
+                       if(!(self.count & 0x80))
+                               InterpolateOrigin_Reset();
+               }
+
+               if(f & 0x20)
+               {
+                       self.fade_time = time + ReadByte() * ticrate;
+                       self.fade_rate = 1 / (ReadByte() * ticrate);
+               }
+               else
+               {
+                       self.fade_time = 0;
+                       self.fade_rate = 0;
+               }
+       }
+
+       if(f & 2)
+       {
+               self.cnt = ReadByte();
+
+               self.silent = (self.cnt & 0x80);
+               self.cnt = (self.cnt & 0x7F);
+
+               self.scale = 1;
+               self.traileffect = 0;
+               switch(self.cnt)
+               {
+                       case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+                       case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
+                       case PROJECTILE_BULLET: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_bullet"); break;
+                       case PROJECTILE_BULLET_GLOWING: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle_weak"); break;
+                       case PROJECTILE_BULLET_GLOWING_TRACER: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle"); break;
+                       case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
+                       case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
+                       case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+                       case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
+                       case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
+                       case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
+                       case PROJECTILE_LASER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break;
+                       case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break;
+                       case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
+                       case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
+                       case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
+                       case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
+                       case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
+                       case PROJECTILE_FIREBALL: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("fireball"); break; // particle effect is good enough
+                       case PROJECTILE_FIREMINE: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("firemine"); break; // particle effect is good enough
+                       case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break;
+                       case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break;
+                       case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break;
+
+                       case PROJECTILE_RAPTORBOMB:    setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
+                       case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3");     self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
+                       case PROJECTILE_RAPTORCANNON:  setmodel(self, "models/plasmatrail.mdl"); self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
+
+                       case PROJECTILE_SPIDERROCKET: setmodel(self, "models/vehicles/rocket02.md3"); self.traileffect = particleeffectnum("spiderbot_rocket_thrust"); break;
+                       case PROJECTILE_WAKIROCKET:   setmodel(self, "models/vehicles/rocket01.md3");  self.traileffect = particleeffectnum("wakizashi_rocket_thrust"); break;
+                       case PROJECTILE_WAKICANNON:   setmodel(self, "models/laser.mdl");  self.traileffect = particleeffectnum(""); break;
+
+                       case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+                       case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+
+                       default:
+                               error("Received invalid CSQC projectile, can't work with this!");
+                               break;
+               }
+
+               self.mins = '0 0 0';
+               self.maxs = '0 0 0';
+               self.colormod = '0 0 0';
+               self.move_touch = SUB_Stop;
+               self.move_movetype = MOVETYPE_TOSS;
+               self.alphamod = 1;
+
+               switch(self.cnt)
+               {
+                       case PROJECTILE_ELECTRO:
+                               // only new engines support sound moving with object
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM);
+                               self.mins = '0 0 -4';
+                               self.maxs = '0 0 -4';
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
+                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
+                               break;
+                       case PROJECTILE_ROCKET:
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTN_NORM);
+                               self.mins = '-3 -3 -3';
+                               self.maxs = '3 3 3';
+                               break;
+                       case PROJECTILE_GRENADE:
+                               self.mins = '-3 -3 -3';
+                               self.maxs = '3 3 3';
+                               break;
+                       case PROJECTILE_GRENADE_BOUNCING:
+                               self.mins = '-3 -3 -3';
+                               self.maxs = '3 3 3';
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               self.move_bounce_factor = g_balance_grenadelauncher_bouncefactor;
+                               self.move_bounce_stopspeed = g_balance_grenadelauncher_bouncestop;
+                               break;
+                       case PROJECTILE_MINE:
+                               self.mins = '-4 -4 -4';
+                               self.maxs = '4 4 4';
+                               break;
+                       case PROJECTILE_PORTO_RED:
+                               self.colormod = '2 1 1';
+                               self.alphamod = 0.5;
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               break;
+                       case PROJECTILE_PORTO_BLUE:
+                               self.colormod = '1 1 2';
+                               self.alphamod = 0.5;
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               break;
+                       case PROJECTILE_HAGAR_BOUNCING:
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               break;
+                       case PROJECTILE_CRYLINK_BOUNCING:
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               break;
+                       case PROJECTILE_FIREBALL:
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTN_NORM);
+                               self.mins = '-16 -16 -16';
+                               self.maxs = '16 16 16';
+                               break;
+                       case PROJECTILE_FIREMINE:
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTN_NORM);
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               self.mins = '-4 -4 -4';
+                               self.maxs = '4 4 4';
+                               break;
+                       case PROJECTILE_TAG:
+                               self.mins = '-2 -2 -2';
+                               self.maxs = '2 2 2';
+                               break;
+                       case PROJECTILE_FLAC:
+                               self.mins = '-2 -2 -2';
+                               self.maxs = '2 2 2';
+                               break;
+                       case PROJECTILE_SEEKER:
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
+                               self.mins = '-4 -4 -4';
+                               self.maxs = '4 4 4';
+                               break;
+            case PROJECTILE_RAPTORBOMB:
+                               self.mins = '-3 -3 -3';
+                               self.maxs = '3 3 3';
+                               break;
+            case PROJECTILE_RAPTORBOMBLET:
+                               break;
+            case PROJECTILE_RAPTORCANNON:
+                               break;
+            case PROJECTILE_SPIDERROCKET:
+                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
+                               break;
+            case PROJECTILE_WAKIROCKET:
+                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
+                               break;            
+            /*
+            case PROJECTILE_WAKICANNON:
+                               break;
+                       case PROJECTILE_BUMBLE_GUN:
+                               // only new engines support sound moving with object
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM);
+                               self.mins = '0 0 -4';
+                               self.maxs = '0 0 -4';
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
+                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
+                               break;
+                       */
+                       default:
+                               break;
+               }
+               setsize(self, self.mins, self.maxs);
+       }
+
+       if(self.gravity)
+       {
+               if(self.move_movetype == MOVETYPE_FLY)
+                       self.move_movetype = MOVETYPE_TOSS;
+               if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
+                       self.move_movetype = MOVETYPE_BOUNCE;
+       }
+       else
+       {
+               if(self.move_movetype == MOVETYPE_TOSS)
+                       self.move_movetype = MOVETYPE_FLY;
+               if(self.move_movetype == MOVETYPE_BOUNCE)
+                       self.move_movetype = MOVETYPE_BOUNCEMISSILE;
+       }
+
+       if(!(self.count & 0x80))
+               InterpolateOrigin_Note();
+
+       self.draw = Projectile_Draw;
+       self.entremove = Ent_RemoveProjectile;
+}
+
+void Projectile_Precache()
+{
+       precache_model("models/ebomb.mdl");
+       precache_model("models/elaser.mdl");
+       precache_model("models/grenademodel.md3");
+       precache_model("models/mine.md3");
+       precache_model("models/hagarmissile.mdl");
+       precache_model("models/hlac_bullet.md3");
+       precache_model("models/laser.mdl");
+       precache_model("models/plasmatrail.mdl");
+       precache_model("models/rocket.md3");
+       precache_model("models/tagrocket.md3");
+       precache_model("models/tracer.mdl");
+
+       precache_sound("weapons/electro_fly.wav");
+       precache_sound("weapons/rocket_fly.wav");
+       precache_sound("weapons/fireball_fly.wav");
+       precache_sound("weapons/fireball_fly2.wav");
+       precache_sound("weapons/tag_rocket_fly.wav");
+
+}
diff --git a/qcsrc/client/weapons/projectile.qh b/qcsrc/client/weapons/projectile.qh
new file mode 100644 (file)
index 0000000..70c8ba0
--- /dev/null
@@ -0,0 +1,3 @@
+.float traileffect;
+void Projectile_ResetTrail(vector to);
+void Projectile_DrawTrail(vector to);
index 0327943f9d440d2ca211e4beeaae3bf9d64e1360..183a5e5ed0f829e6867b23046c8514d52f046c6c 100644 (file)
@@ -1,6 +1,7 @@
 // =============================
 //  Explosion Force Calculation
 // =============================
+
 float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v)
 {
        float a;
diff --git a/qcsrc/server/accuracy.qc b/qcsrc/server/accuracy.qc
deleted file mode 100644 (file)
index 3c737f6..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-.float accuracy_hit[WEP_MAXCOUNT];
-.float accuracy_fired[WEP_MAXCOUNT];
-.float accuracy_cnt_hit[WEP_MAXCOUNT];
-.float accuracy_cnt_fired[WEP_MAXCOUNT];
-
-float accuracy_byte(float n, float d)
-{
-       //print(sprintf("accuracy: %d / %d\n", n, d));
-       if(n <= 0)
-               return 0;
-       if(n > d)
-               return 255;
-       return 1 + rint(n * 100.0 / d);
-}
-
-float accuracy_send(entity to, float sf)
-{
-       float w, f;
-       entity a;
-       WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY);
-
-       a = self.owner;
-       if(IS_SPEC(a))
-               a = a.enemy;
-       a = a.accuracy;
-
-       if(to != a.owner)
-               if not(self.owner.cvar_cl_accuracy_data_share && autocvar_sv_accuracy_data_share)
-                       sf = 0;
-       // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy!
-       WriteInt24_t(MSG_ENTITY, sf);
-       if(sf == 0)
-               return TRUE;
-       // note: we know that client and server agree about SendFlags...
-       for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w)
-       {
-               if(sf & f)
-                       WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w])));
-               if(f == 0x800000)
-                       f = 1;
-               else
-                       f *= 2;
-       }
-       return TRUE;
-}
-
-// init/free
-void accuracy_init(entity e)
-{
-       e.accuracy = spawn();
-       e.accuracy.owner = e;
-       e.accuracy.classname = "accuracy";
-       e.accuracy.drawonlytoclient = e;
-       Net_LinkEntity(e.accuracy, FALSE, 0, accuracy_send);
-}
-
-void accuracy_free(entity e)
-{
-       remove(e.accuracy);
-}
-
-// force a resend of a player's accuracy stats
-void accuracy_resend(entity e)
-{
-       e.accuracy.SendFlags = 0xFFFFFF;
-}
-
-// update accuracy stats
-.float hit_time;
-.float fired_time;
-
-void accuracy_add(entity e, float w, float fired, float hit)
-{
-       entity a;
-       float b;
-       if(IS_INDEPENDENT_PLAYER(e))
-               return;
-       a = e.accuracy;
-       if(!a || !(hit || fired))
-               return;
-       w -= WEP_FIRST;
-       b = accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]));
-       if(hit)
-               a.(accuracy_hit[w]) += hit;
-       if(fired)
-               a.(accuracy_fired[w]) += fired;
-
-    if(hit && a.hit_time != time) // only run this once per frame
-    {
-        a.(accuracy_cnt_hit[w]) += 1;
-        a.hit_time = time;
-    }
-
-    if(fired && a.fired_time != time) // only run this once per frame
-    {
-        a.(accuracy_cnt_fired[w]) += 1;
-        a.fired_time = time;
-    }
-
-       if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])))
-               return;
-       w = pow(2, mod(w, 24));
-       a.SendFlags |= w;
-       FOR_EACH_CLIENT(a)
-               if(IS_SPEC(a))
-                       if(a.enemy == e)
-                               a.SendFlags |= w;
-}
-
-float accuracy_isgooddamage(entity attacker, entity targ)
-{
-       if(!inWarmupStage)
-       if(IS_CLIENT(targ))
-       if(targ.deadflag == DEAD_NO)
-       if(IsDifferentTeam(attacker, targ))
-               return TRUE;
-       return FALSE;
-}
-
-float accuracy_canbegooddamage(entity attacker)
-{
-       if(!inWarmupStage)
-               return TRUE;
-       return FALSE;
-}
diff --git a/qcsrc/server/accuracy.qh b/qcsrc/server/accuracy.qh
deleted file mode 100644 (file)
index 90dbb66..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-.float cvar_cl_accuracy_data_share;
-
-// init/free
-void accuracy_init(entity e);
-void accuracy_free(entity e);
-
-// force a resend of a player's accuracy stats
-void accuracy_resend(entity e);
-
-// update accuracy stats
-void accuracy_add(entity e, float w, float fired, float hit);
-
-// helper
-float accuracy_isgooddamage(entity attacker, entity targ);
-float accuracy_canbegooddamage(entity attacker);
diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc
new file mode 100644 (file)
index 0000000..3c737f6
--- /dev/null
@@ -0,0 +1,125 @@
+.float accuracy_hit[WEP_MAXCOUNT];
+.float accuracy_fired[WEP_MAXCOUNT];
+.float accuracy_cnt_hit[WEP_MAXCOUNT];
+.float accuracy_cnt_fired[WEP_MAXCOUNT];
+
+float accuracy_byte(float n, float d)
+{
+       //print(sprintf("accuracy: %d / %d\n", n, d));
+       if(n <= 0)
+               return 0;
+       if(n > d)
+               return 255;
+       return 1 + rint(n * 100.0 / d);
+}
+
+float accuracy_send(entity to, float sf)
+{
+       float w, f;
+       entity a;
+       WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY);
+
+       a = self.owner;
+       if(IS_SPEC(a))
+               a = a.enemy;
+       a = a.accuracy;
+
+       if(to != a.owner)
+               if not(self.owner.cvar_cl_accuracy_data_share && autocvar_sv_accuracy_data_share)
+                       sf = 0;
+       // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy!
+       WriteInt24_t(MSG_ENTITY, sf);
+       if(sf == 0)
+               return TRUE;
+       // note: we know that client and server agree about SendFlags...
+       for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w)
+       {
+               if(sf & f)
+                       WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w])));
+               if(f == 0x800000)
+                       f = 1;
+               else
+                       f *= 2;
+       }
+       return TRUE;
+}
+
+// init/free
+void accuracy_init(entity e)
+{
+       e.accuracy = spawn();
+       e.accuracy.owner = e;
+       e.accuracy.classname = "accuracy";
+       e.accuracy.drawonlytoclient = e;
+       Net_LinkEntity(e.accuracy, FALSE, 0, accuracy_send);
+}
+
+void accuracy_free(entity e)
+{
+       remove(e.accuracy);
+}
+
+// force a resend of a player's accuracy stats
+void accuracy_resend(entity e)
+{
+       e.accuracy.SendFlags = 0xFFFFFF;
+}
+
+// update accuracy stats
+.float hit_time;
+.float fired_time;
+
+void accuracy_add(entity e, float w, float fired, float hit)
+{
+       entity a;
+       float b;
+       if(IS_INDEPENDENT_PLAYER(e))
+               return;
+       a = e.accuracy;
+       if(!a || !(hit || fired))
+               return;
+       w -= WEP_FIRST;
+       b = accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]));
+       if(hit)
+               a.(accuracy_hit[w]) += hit;
+       if(fired)
+               a.(accuracy_fired[w]) += fired;
+
+    if(hit && a.hit_time != time) // only run this once per frame
+    {
+        a.(accuracy_cnt_hit[w]) += 1;
+        a.hit_time = time;
+    }
+
+    if(fired && a.fired_time != time) // only run this once per frame
+    {
+        a.(accuracy_cnt_fired[w]) += 1;
+        a.fired_time = time;
+    }
+
+       if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])))
+               return;
+       w = pow(2, mod(w, 24));
+       a.SendFlags |= w;
+       FOR_EACH_CLIENT(a)
+               if(IS_SPEC(a))
+                       if(a.enemy == e)
+                               a.SendFlags |= w;
+}
+
+float accuracy_isgooddamage(entity attacker, entity targ)
+{
+       if(!inWarmupStage)
+       if(IS_CLIENT(targ))
+       if(targ.deadflag == DEAD_NO)
+       if(IsDifferentTeam(attacker, targ))
+               return TRUE;
+       return FALSE;
+}
+
+float accuracy_canbegooddamage(entity attacker)
+{
+       if(!inWarmupStage)
+               return TRUE;
+       return FALSE;
+}
diff --git a/qcsrc/server/weapons/accuracy.qh b/qcsrc/server/weapons/accuracy.qh
new file mode 100644 (file)
index 0000000..90dbb66
--- /dev/null
@@ -0,0 +1,15 @@
+.float cvar_cl_accuracy_data_share;
+
+// init/free
+void accuracy_init(entity e);
+void accuracy_free(entity e);
+
+// force a resend of a player's accuracy stats
+void accuracy_resend(entity e);
+
+// update accuracy stats
+void accuracy_add(entity e, float w, float fired, float hit);
+
+// helper
+float accuracy_isgooddamage(entity attacker, entity targ);
+float accuracy_canbegooddamage(entity attacker);