]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'bones_was_here/gunoffset' into 'master'
authorterencehill <piuntn@gmail.com>
Sat, 31 Dec 2022 23:37:05 +0000 (23:37 +0000)
committerterencehill <piuntn@gmail.com>
Sat, 31 Dec 2022 23:37:05 +0000 (23:37 +0000)
Implement cl_gunoffset and related fixes and refactoring

Closes #2374 and #2017

See merge request xonotic/xonotic-data.pk3dir!1100

12 files changed:
qcsrc/client/view.qc
qcsrc/client/view.qh
qcsrc/common/effects/qc/casings.qc
qcsrc/common/effects/qc/casings.qh
qcsrc/common/mutators/mutator/overkill/okhmg.qc
qcsrc/common/mutators/mutator/overkill/okmachinegun.qc
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/all.qh
qcsrc/common/weapons/weapon/machinegun.qc
qcsrc/common/weapons/weapon/rifle.qc
qcsrc/common/weapons/weapon/shotgun.qc
xonotic-client.cfg

index a296bd81d45e14065542e04d7be71d5d9701901c..a9ad0a76bd7e612d58fdeaafa49a40cc27d8fa66 100644 (file)
@@ -269,7 +269,6 @@ void viewmodel_animate(entity this)
                this.origin += bobmodel_ofs(view);
 }
 
-.vector viewmodel_origin, viewmodel_angles;
 .float weapon_nextthink;
 .float weapon_eta_last;
 .float weapon_switchdelay;
@@ -278,8 +277,6 @@ void viewmodel_animate(entity this)
 
 void viewmodel_draw(entity this)
 {
-       if(!this.activeweapon || !autocvar_r_drawviewmodel)
-               return;
        int mask = (intermission || (STAT(HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL;
        float a = ((autocvar_cl_viewmodel_alpha) ? bound(-1, autocvar_cl_viewmodel_alpha, this.m_alpha) : this.m_alpha);
        int wepskin = this.m_skin;
@@ -313,8 +310,7 @@ void viewmodel_draw(entity this)
                {
                        this.name_last = name;
                        CL_WeaponEntity_SetModel(this, name, swap);
-                       this.viewmodel_origin = this.origin;
-                       this.viewmodel_angles = this.angles;
+                       this.origin += autocvar_cl_gunoffset;
                }
                anim_update(this);
                if ((!this.animstate_override && !this.animstate_looping) || time > this.animstate_endtime)
@@ -343,8 +339,6 @@ void viewmodel_draw(entity this)
                }
        }
        this.weapon_eta_last = f;
-       this.origin = this.viewmodel_origin;
-       this.angles = this.viewmodel_angles;
        this.angles_x = (-90 * f * f);
        viewmodel_animate(this);
        MUTATOR_CALLHOOK(DrawViewModel, this);
@@ -1609,8 +1603,10 @@ void CSQC_UpdateView(entity this, float w, float h)
 
        // run viewmodel_draw before updating view_angles to the angles calculated by WarpZone_FixView
        // viewmodel_draw needs to use the view_angles set by the engine on every CSQC_UpdateView call
-       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               viewmodel_draw(viewmodels[slot]);
+       if(autocvar_r_drawviewmodel)
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+                       if(viewmodels[slot].activeweapon)
+                               viewmodel_draw(viewmodels[slot]);
 
        // Render the Scene
        view_origin = getpropertyvec(VF_ORIGIN);
index 45959383bdf38c893dc30abad243237c01629e79..cd33ebfb6a1201e41f9caa706209c1cc2fcb3d92 100644 (file)
@@ -92,6 +92,7 @@ vector autocvar_cl_eventchase_viewoffset = '0 0 20';
 string autocvar__togglezoom;
 int autocvar_cl_nade_timer;
 bool autocvar_r_drawviewmodel;
+vector autocvar_cl_gunoffset;
 
 void calc_followmodel_ofs(entity view);
 
index c0c7f5ac98d41bc95cb90948d4234f84c007f6dc..b49ff60faceaccb3b46a88cde7ad1825907c684c 100644 (file)
@@ -14,7 +14,7 @@ REPLICATE(cvar_cl_casings, bool, "cl_casings");
 REPLICATE(cvar_r_drawviewmodel, int, "r_drawviewmodel");
 
 #ifdef SVQC
-void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner, .entity weaponentity)
+void SpawnCasing(vector vel, vector ang, int casingtype, entity casingowner, .entity weaponentity)
 {
        vector org = casingowner.(weaponentity).spawnorigin;
        org = casingowner.origin + casingowner.view_ofs + org.x * v_forward - org.y * v_right + org.z * v_up;
@@ -22,10 +22,20 @@ void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float ran
        FOREACH_CLIENT(true, {
                if (!(CS_CVAR(it).cvar_cl_casings))
                        continue;
-               if (it == casingowner && !(CS_CVAR(it).cvar_r_drawviewmodel))
+
+               casingtype &= 0x3F; // reset any bitflags that were set for the previous client
+
+               if (it == casingowner || (IS_SPEC(it) && it.enemy == casingowner))
+               {
+                       if (!(CS_CVAR(it).cvar_r_drawviewmodel))
+                               continue;
+
+                       casingtype |= 0x40; // client will apply autocvar_cl_gunoffset in first person
+               }
+               else if (1 & ~checkpvs(it.origin + it.view_ofs, casingowner)) // 1 or 3 means visible
                        continue;
 
-               msg_entity = it;
+               msg_entity = it; // sound_allowed checks this
                if (!sound_allowed(MSG_ONE, it))
                        casingtype |= 0x80; // silent
 
@@ -35,7 +45,7 @@ void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float ran
                WriteShort(MSG_ONE, compressShortVector(vel)); // actually compressed velocity
                WriteByte(MSG_ONE, ang.x * 256 / 360);
                WriteByte(MSG_ONE, ang.y * 256 / 360);
-               WriteByte(MSG_ONE, ang.z * 256 / 360);
+               // weapons only have pitch and yaw, so no need to send ang.z
        });
 }
 #endif
@@ -47,6 +57,7 @@ classfield(Casing) .bool silent;
 classfield(Casing) .int state;
 classfield(Casing) .float cnt;
 
+// this is only needed because LimitedChildrenRubble() takes a func pointer
 void Casing_Delete(entity this)
 {
     delete(this);
@@ -66,14 +77,24 @@ void Casing_Draw(entity this)
 
     if (this.alpha < ALPHA_MIN_VISIBLE)
     {
-        Casing_Delete(this);
+        delete(this);
         this.drawmask = 0;
         return;
     }
 
+    trace_startsolid = 0; // due to cl_casings_ticrate, traces are not always performed
     Movetype_Physics_MatchTicrate(this, autocvar_cl_casings_ticrate, autocvar_cl_casings_sloppy);
     //if (wasfreed(this))
     //    return; // deleted by touch function
+
+    // prevent glitchy casings when the gun model is poking into a wall
+    // doing this here is cheaper than doing it on the server as the client performs the trace anyway
+    if (trace_startsolid)
+    {
+        delete(this);
+        this.drawmask = 0;
+        return;
+    }
 }
 
 SOUND(BRASS1, W_Sound("brass1"));
@@ -93,7 +114,7 @@ void Casing_Touch(entity this, entity toucher)
 {
     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
     {
-        Casing_Delete(this);
+        delete(this);
         return;
     }
 
@@ -133,30 +154,30 @@ void Casing_Damage(entity this, float thisdmg, int hittype, vector org, vector t
 
 NET_HANDLE(casings, bool isNew)
 {
-    int _state = ReadByte();
-    vector org = ReadVector();
-    vector vel = decompressShortVector(ReadShort());
-    vector ang;
-    ang_x = ReadByte() * 360 / 256;
-    ang_y = ReadByte() * 360 / 256;
-    ang_z = ReadByte() * 360 / 256;
+    Casing casing = ListNewChildRubble(CasingsNGibs, new(casing));
+
+    casing.state = ReadByte();
+    casing.origin = ReadVector();
+    casing.velocity = decompressShortVector(ReadShort());
+    casing.angles_x = ReadByte() * 360 / 256;
+    casing.angles_y = ReadByte() * 360 / 256;
+
     return = true;
 
-    Casing casing = ListNewChildRubble(CasingsNGibs, new(casing));
-    casing.silent = (_state & 0x80);
-    casing.state = (_state & 0x7F);
-    casing.origin = org;
+    casing.silent = casing.state & 0x80;
+    if (casing.state & 0x40 && !autocvar_chase_active)
+        casing.origin += autocvar_cl_gunoffset.x * v_forward
+                       - autocvar_cl_gunoffset.y * v_right
+                       + autocvar_cl_gunoffset.z * v_up;
+    casing.state &= 0x3F; // the 2 most significant bits are reserved for the silent and casingowner bitflags
+
     setorigin(casing, casing.origin);
-    casing.velocity = vel;
-    casing.angles = ang;
     casing.drawmask = MASK_NORMAL;
-
     casing.draw = Casing_Draw;
     if (isNew) IL_PUSH(g_drawables, casing);
-    casing.velocity = casing.velocity + 2 * prandomvec();
-    casing.avelocity = '0 250 0' + 100 * prandomvec();
+    casing.velocity += 2 * prandomvec();
+    casing.avelocity = '0 10 0' + 100 * prandomvec();
     set_movetype(casing, MOVETYPE_BOUNCE);
-    casing.bouncefactor = 0.25;
     settouch(casing, Casing_Touch);
     casing.move_time = time;
     casing.event_damage = Casing_Damage;
@@ -166,16 +187,16 @@ NET_HANDLE(casings, bool isNew)
     {
         case 1:
             setmodel(casing, MDL_CASING_SHELL);
+            casing.bouncefactor = 0.25;
             casing.cnt = time + autocvar_cl_casings_shell_time;
             break;
         default:
             setmodel(casing, MDL_CASING_BULLET);
+            casing.bouncefactor = 0.5;
             casing.cnt = time + autocvar_cl_casings_bronze_time;
             break;
     }
 
-    setsize(casing, '0 0 -1', '0 0 -1');
-
     LimitedChildrenRubble(CasingsNGibs, "casing", autocvar_cl_casings_maxcount, Casing_Delete, NULL);
 }
 
index f4884cb233d5424bbede9027db792c7137025185..5e2c0a5ecf2788e9ed8e4bc83e8c522b6b3d981b 100644 (file)
@@ -5,7 +5,7 @@ float autocvar_cl_casings_bronze_time;
 int autocvar_cl_casings_maxcount = 100;
 float autocvar_cl_casings_shell_time;
 bool autocvar_cl_casings_sloppy = 1;
-float autocvar_cl_casings_ticrate = 0.1;
+float autocvar_cl_casings_ticrate;
 #endif
 
 #ifdef GAMEQC
@@ -16,5 +16,5 @@ REPLICATE_INIT(int, cvar_r_drawviewmodel);
 #ifdef SVQC
 int autocvar_g_casings;
 
-void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner, .entity weaponentity);
+void SpawnCasing(vector vel, vector ang, int casingtype, entity casingowner, .entity weaponentity);
 #endif
index d5a2ba2e32f53142357695adbc8834dcab63c1dc..1d8c5e87f9aa043252c00dac81e78152066dd2b6 100644 (file)
@@ -45,7 +45,7 @@ void W_OverkillHeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity
        if (autocvar_g_casings >= 2) // casing code
        {
                makevectors(actor.v_angle); // for some reason, this is lost
-               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+               SpawnCasing(((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
        }
 
        ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(okhmg, refire) * W_WeaponRateFactor(actor);
index 20db8d961863b3a37d6235eee7b38fb891ddad55..a86cdc5194f3f2e21f7c936423a5bc41a913922b 100644 (file)
@@ -39,7 +39,7 @@ void W_OverkillMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weap
        if(autocvar_g_casings >= 2) // casing code
        {
                makevectors(actor.v_angle); // for some reason, this is lost
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
        }
 
        ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(okmachinegun, refire) * W_WeaponRateFactor(actor);
index 07c51f18ddcf7853c19538d3f3784aaf959e1bb4..70937716490921edcd0e01e07b889bf97ab565e0 100644 (file)
@@ -378,6 +378,7 @@ vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn
  *   this.origin, this.angles
  *   this.weaponchild
  *   this.movedir, this.view_ofs, this.movedir_aligned
+ *   this.spawnorigin (SVQC only)
  *   attachment stuff
  *   anim stuff
  * to free:
@@ -394,7 +395,9 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                if (this.weaponchild) delete(this.weaponchild);
                this.weaponchild = NULL;
                this.movedir = '0 0 0';
+#ifdef SVQC
                this.spawnorigin = '0 0 0';
+#endif
                this.oldorigin = '0 0 0';
                this.anim_fire1  = '0 1 0.01';
                this.anim_fire2  = '0 1 0.01';
@@ -466,6 +469,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                                this.movedir = '0 0 0';
                        }
                }
+#ifdef SVQC
                {
                        int idx = 0;
                        // v_ model attached to invisible h_ model
@@ -485,6 +489,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                                this.spawnorigin = this.movedir;
                        }
                }
+#endif
                if (v_shot_idx)
                {
                        this.oldorigin = '0 0 0';  // use regular attachment
@@ -531,7 +536,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                vector v = this.movedir;
                this.movedir = shotorg_adjust(v, false, false, algn);
                this.movedir_aligned = shotorg_adjust(v, false, true, algn);
-               this.view_ofs = shotorg_adjust(v, false, true, algn) - v;
+               this.view_ofs = this.movedir_aligned - v;
        }
        int compressed_shotorg = compressShotOrigin(this.movedir);
        // make them match perfectly
@@ -541,7 +546,9 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
 #endif
        this.movedir = decompressShotOrigin(compressed_shotorg);
 
+#ifdef SVQC
        this.spawnorigin += this.view_ofs;  // offset the casings origin by the same amount
+#endif
 
        // check if an instant weapon switch occurred
        setorigin(this, this.view_ofs);
index acf4366593e62d3a1b72ba012b30e9121d6777e9..141b8b5eee72a77f286571892486e59113b0f624 100644 (file)
@@ -356,7 +356,9 @@ vector weaponentity_glowmod(Weapon wep, int c, entity wepent)
 //.int weapon; // current weapon
 .string weaponname; // name of .weapon
 
+#ifdef SVQC
 .vector spawnorigin; // for casings
+#endif
 
 .vector movedir_aligned; // shot origin based on weapon alignment (unaffected by shootfromeye)
 
index 15a4d6a3c180f809cd9ade5fd6de424c00e972a2..62b2a09eac9774ab7a68349ced551eb5a24cb9fe 100644 (file)
@@ -24,7 +24,7 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we
        if(autocvar_g_casings >= 2)
        {
                makevectors(actor.v_angle); // for some reason, this is lost
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
        }
 
        if(actor.(weaponentity).misc_bulletcounter == 1)
@@ -96,7 +96,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity
        if(autocvar_g_casings >= 2) // casing code
        {
                makevectors(actor.v_angle); // for some reason, this is lost
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
        }
 
        ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
@@ -119,7 +119,7 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentit
        if(autocvar_g_casings >= 2) // casing code
        {
                makevectors(actor.v_angle); // for some reason, this is lost
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
        }
 
        actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
index 803930cc0cfc6880a01df26f36109f5efe794169..aa4a980623a4168791f700cdd21b523967bc1796 100644 (file)
@@ -24,7 +24,7 @@ void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, float pSpread, flo
        if(autocvar_g_casings >= 2)
        {
                makevectors(actor.v_angle); // for some reason, this is lost
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
        }
 }
 
index e304c4c2b4520d663cc6147068f4439cc81ae7a9..65cc0d9d926323840f682a60c5fc046ba1ec7dd7 100644 (file)
@@ -29,7 +29,7 @@ void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float
        {
                makevectors(actor.v_angle); // for some reason, this is lost
                //for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, ammo);sc = sc + 1)
-                       SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, actor, weaponentity);
+                       SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), vectoangles(v_forward), 1, actor, weaponentity);
        }
 }
 
index 4cd709bb6d9689c864affa5f354ad86d99a68c04..6d2550b8b7c6879c446c91252a97dd9c5a85a7da 100644 (file)
@@ -307,6 +307,7 @@ r_shadow_realtime_world_importlightentitiesfrommap 0 // Whether build process us
 cl_decals_fadetime 5
 cl_decals_time 1
 seta cl_gunalign 3 "Gun alignment; 1 = center, 3 = right, 4 = left; requires reconnect"
+seta cl_gunoffset "0 0 0" "Adjust the weapon viewmodel position, applies only to your own first person view and is relative to cl_gunalign"
 seta cl_nogibs 0 "reduce number of violence effects, or remove them totally"
 seta cl_particlegibs 0 "simpler gibs"
 seta cl_gibs_damageforcescale 3.5 "force to push around gibs"
@@ -320,7 +321,7 @@ seta cl_gibs_avelocity_scale 1 "how much angular velocity to use on gibs"
 seta cl_casings 1 "enable or disable bullet casings"
 seta cl_casings_shell_time 30 "shell casing lifetime"
 seta cl_casings_bronze_time 10 "bullet casings lifetime"
-seta cl_casings_ticrate 0.1 "ticrate for casings"
+seta cl_casings_ticrate 0.03125 "ticrate for casings"
 seta cl_casings_sloppy 1 "sloppy casings, may temporarily penetrate walls"
 seta cl_projectiles_sloppy 1 "sloppy projectiles, may temporarily penetrate walls"
 cl_stainmaps 0