string W_Sound(string w_snd)
{
- #define extensions(X) X(wav) X(ogg)
- #define tryext(ext) { if (fexists(strcat("sound/", output = strcat("weapons/", w_snd, "." #ext)))) break; }
- string output;
- do
- {
- extensions(tryext);
- #undef tryext
- #undef extensions
- output = strcat("weapons/", w_snd);
- }
- while (0);
-
+ string output = strcat("weapons/", w_snd);
#ifdef SVQC
- MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
- return weapon_sound_output;
+ MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
+ return weapon_sound_output;
#else
- return output;
+ return output;
#endif
}
{
string output = strcat("models/weapons/", w_mdl);
#ifdef SVQC
- MUTATOR_CALLHOOK(WeaponModel, w_mdl, output);
- return weapon_model_output;
+ MUTATOR_CALLHOOK(WeaponModel, w_mdl, output);
+ return weapon_model_output;
+#else
+ return output;
+#endif
+}
+
+#ifndef MENUQC
+vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
+{
+ switch (algn)
+ {
+ default:
+ case 3:
+ // right alignment
+ break;
+ case 4:
+ // left
+ vecs.y = -vecs.y;
+ break;
+ case 1:
+ case 2:
+ // center
+ vecs.y = 0;
+ vecs.z -= 2;
+ break;
+ }
+ return vecs;
+}
+
+vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn)
+{
+#ifdef SVQC
+ string s;
+#endif
+ if (visual)
+ {
+ vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
+ }
+#ifdef SVQC
+ else if (autocvar_g_shootfromeye)
+ {
+ vecs.y = vecs.z = 0;
+ }
+ else if (autocvar_g_shootfromcenter)
+ {
+ vecs.y = 0;
+ vecs.z -= 2;
+ }
+ else if ((s = autocvar_g_shootfromfixedorigin) != "")
+ {
+ vector v = stov(s);
+ if (y_is_right) v.y = -v.y;
+ if (v.x != 0) vecs.x = v.x;
+ vecs.y = v.y;
+ vecs.z = v.z;
+ }
+#endif
+ else // just do the same as top
+ {
+ vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
+ }
+
+ return vecs;
+}
+
+#define shotorg_adjust shotorg_adjust_values
+
+/**
+ * supported formats:
+ *
+ * 1. simple animated model, muzzle flash handling on h_ model:
+ * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
+ * tags:
+ * shot = muzzle end (shot origin, also used for muzzle flashes)
+ * shell = casings ejection point (must be on the right hand side of the gun)
+ * weapon = attachment for v_tuba.md3
+ * v_tuba.md3 - first and third person model
+ * g_tuba.md3 - pickup model
+ *
+ * 2. simple animated model, muzzle flash handling on v_ model:
+ * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
+ * tags:
+ * weapon = attachment for v_tuba.md3
+ * v_tuba.md3 - first and third person model
+ * tags:
+ * shot = muzzle end (shot origin, also used for muzzle flashes)
+ * shell = casings ejection point (must be on the right hand side of the gun)
+ * g_tuba.md3 - pickup model
+ *
+ * 3. fully animated model, muzzle flash handling on h_ model:
+ * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
+ * tags:
+ * shot = muzzle end (shot origin, also used for muzzle flashes)
+ * shell = casings ejection point (must be on the right hand side of the gun)
+ * handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
+ * v_tuba.md3 - third person model
+ * g_tuba.md3 - pickup model
+ *
+ * 4. fully animated model, muzzle flash handling on v_ model:
+ * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
+ * tags:
+ * shot = muzzle end (shot origin)
+ * shell = casings ejection point (must be on the right hand side of the gun)
+ * v_tuba.md3 - third person model
+ * tags:
+ * shot = muzzle end (for muzzle flashes)
+ * g_tuba.md3 - pickup model
+ *
+ * writes:
+ * this.origin, this.angles
+ * this.weaponchild
+ * this.movedir, this.view_ofs
+ * attachment stuff
+ * anim stuff
+ * to free:
+ * call again with ""
+ * remove the ent
+ */
+void CL_WeaponEntity_SetModel(entity this, string name)
+{
+ if (name == "")
+ {
+ this.model = "";
+ if (this.weaponchild) remove(this.weaponchild);
+ this.weaponchild = NULL;
+ this.movedir = '0 0 0';
+ this.spawnorigin = '0 0 0';
+ this.oldorigin = '0 0 0';
+ this.anim_fire1 = '0 1 0.01';
+ this.anim_fire2 = '0 1 0.01';
+ this.anim_idle = '0 1 0.01';
+ this.anim_reload = '0 1 0.01';
+ }
+ else
+ {
+ // if there is a child entity, hide it until we're sure we use it
+ if (this.weaponchild) this.weaponchild.model = "";
+ _setmodel(this, W_Model(strcat("v_", name, ".md3")));
+ int v_shot_idx; // used later
+ (v_shot_idx = gettagindex(this, "shot")) || (v_shot_idx = gettagindex(this, "tag_shot"));
+
+ _setmodel(this, W_Model(strcat("h_", name, ".iqm")));
+ // preset some defaults that work great for renamed zym files (which don't need an animinfo)
+ this.anim_fire1 = animfixfps(this, '0 1 0.01', '0 0 0');
+ this.anim_fire2 = animfixfps(this, '1 1 0.01', '0 0 0');
+ this.anim_idle = animfixfps(this, '2 1 0.01', '0 0 0');
+ this.anim_reload = animfixfps(this, '3 1 0.01', '0 0 0');
+
+ // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
+ // if we don't, this is a "real" animated model
+ string t;
+ if ((t = "weapon", gettagindex(this, t)) || (t = "tag_weapon", gettagindex(this, t)))
+ {
+ if (!this.weaponchild)
+ {
+ this.weaponchild = new(weaponchild);
+#ifdef CSQC
+ this.weaponchild.drawmask = MASK_NORMAL;
+#endif
+ }
+ _setmodel(this.weaponchild, W_Model(strcat("v_", name, ".md3")));
+ setattachment(this.weaponchild, this, t);
+ }
+ else
+ {
+ if (this.weaponchild) remove(this.weaponchild);
+ this.weaponchild = NULL;
+ }
+
+ setorigin(this, '0 0 0');
+ this.angles = '0 0 0';
+ this.frame = 0;
+#ifdef SVQC
+ this.viewmodelforclient = NULL;
#else
- return output;
+ this.renderflags &= ~RF_VIEWMODEL;
#endif
+ if (v_shot_idx) // v_ model attached to invisible h_ model
+ {
+ this.movedir = gettaginfo(this.weaponchild, v_shot_idx);
+ }
+ else
+ {
+ int idx;
+ if ((idx = gettagindex(this, "shot")) || (idx = gettagindex(this, "tag_shot")))
+ {
+ this.movedir = gettaginfo(this, idx);
+ }
+ else
+ {
+ LOG_WARNINGF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong\n",
+ this.model);
+ this.movedir = '0 0 0';
+ }
+ }
+ {
+ int idx = 0;
+ // v_ model attached to invisible h_ model
+ if (this.weaponchild
+ && ((idx = gettagindex(this.weaponchild, "shell")) || (idx = gettagindex(this.weaponchild, "tag_shell"))))
+ {
+ this.spawnorigin = gettaginfo(this.weaponchild, idx);
+ }
+ else if ((idx = gettagindex(this, "shell")) || (idx = gettagindex(this, "tag_shell")))
+ {
+ this.spawnorigin = gettaginfo(this, idx);
+ }
+ else
+ {
+ LOG_WARNINGF("weapon model %s does not support the 'shell' tag, will display casings wrong\n",
+ this.model);
+ this.spawnorigin = this.movedir;
+ }
+ }
+ if (v_shot_idx)
+ {
+ this.oldorigin = '0 0 0'; // use regular attachment
+ }
+ else
+ {
+ int idx;
+ if (this.weaponchild)
+ (idx = gettagindex(this, "weapon")) || (idx = gettagindex(this, "tag_weapon"));
+ else
+ (idx = gettagindex(this, "handle")) || (idx = gettagindex(this, "tag_handle"));
+ if (idx)
+ {
+ this.oldorigin = this.movedir - gettaginfo(this, idx);
+ }
+ else
+ {
+ LOG_WARNINGF(
+ "weapon model %s does not support the 'handle' tag "
+ "and neither does the v_ model support the 'shot' tag, "
+ "will display muzzle flashes TOTALLY wrong\n",
+ this.model);
+ this.oldorigin = '0 0 0'; // there is no way to recover from this
+ }
+ }
+
+#ifdef SVQC
+ this.viewmodelforclient = this.owner;
+#else
+ this.renderflags |= RF_VIEWMODEL;
+#endif
+ }
+
+ this.view_ofs = '0 0 0';
+
+ if (this.movedir.x >= 0)
+ {
+#ifdef SVQC
+ int algn = this.owner.cvar_cl_gunalign;
+#else
+ int algn = autocvar_cl_gunalign;
+#endif
+ vector v = this.movedir;
+ this.movedir = shotorg_adjust(v, false, false, algn);
+ this.view_ofs = shotorg_adjust(v, false, true, algn) - v;
+ }
+ int compressed_shotorg = compressShotOrigin(this.movedir);
+ // make them match perfectly
+#ifdef SVQC
+ this.movedir = decompressShotOrigin(this.owner.stat_shotorg = compressed_shotorg);
+#else
+ this.movedir = decompressShotOrigin(compressed_shotorg);
+#endif
+
+ this.spawnorigin += this.view_ofs; // offset the casings origin by the same amount
+
+ // check if an instant weapon switch occurred
+ setorigin(this, this.view_ofs);
+ // reset animstate now
+ this.wframe = WFRAME_IDLE;
+ setanim(this, this.anim_idle, true, false, true);
}
- REGISTER_NET_TEMP(wframe, bool isNew)
+#endif
+
+#ifndef MENUQC
+
- REGISTER_NET_TEMP(wglow, bool isNew)
++REGISTER_NET_TEMP(wframe)
+#ifdef CSQC
++NET_HANDLE(wframe, bool isNew)
+{
+ vector a;
+ a.x = ReadCoord();
+ a.y = ReadCoord();
+ a.z = ReadCoord();
+ bool restartanim = ReadByte();
+ setanim(viewmodel, a, restartanim == false, restartanim, restartanim);
+ viewmodel.state = ReadByte();
+ viewmodel.alpha = ReadByte() / 255;
++ return true;
+}
+#endif
+
+#ifdef SVQC
+void wframe_send(entity actor, entity weaponentity, vector a, bool restartanim)
+{
+ if (!IS_REAL_CLIENT(actor)) return;
+ int channel = MSG_ONE;
+ msg_entity = actor;
+ WriteHeader(channel, wframe);
+ WriteCoord(channel, a.x);
+ WriteCoord(channel, a.y);
+ WriteCoord(channel, a.z);
+ WriteByte(channel, restartanim);
+ WriteByte(channel, weaponentity.state);
+ WriteByte(channel, weaponentity.alpha * 255);
+}
+#endif
+
++REGISTER_NET_TEMP(wglow)
+#ifdef CSQC
++NET_HANDLE(wglow, bool isNew)
+{
+ vector g = '0 0 0';
+ g.x = ReadCoord();
+ g.y = ReadCoord();
+ g.z = ReadCoord();
+ viewmodel.glowmod = g;
++ return true;
+}
+#endif
+
+#ifdef SVQC
+void wglow_send(entity actor, vector g)
+{
+ if (!IS_REAL_CLIENT(actor)) return;
+ int channel = MSG_ONE;
+ msg_entity = actor;
+ WriteHeader(channel, wglow);
+ WriteCoord(channel, g.x);
+ WriteCoord(channel, g.y);
+ WriteCoord(channel, g.z);
+}
+#endif
+
+#endif
#endif
SELFPARAM();
this.nextthink = time;
if (intermission_running) this.frame = this.anim_idle.x;
- .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ .entity weaponentity = this.weaponentity_fld;
if (this.owner.(weaponentity) != this)
{
- if (this.(weaponentity)) remove(this.(weaponentity));
+ // owner has new gun; remove self
+ if (this.weaponchild) remove(this.weaponchild);
remove(this);
return;
}
}
// spawning weaponentity for client
-void CL_SpawnWeaponentity(entity e, .entity weaponentity)
+void CL_SpawnWeaponentity(entity actor, .entity weaponentity)
{
- entity view = e.(weaponentity) = new(weaponentity);
+ entity view = actor.(weaponentity) = new(weaponentity);
+ make_pure(view);
view.solid = SOLID_NOT;
- view.owner = e;
+ view.owner = actor;
setmodel(view, MDL_Null); // precision set when changed
setorigin(view, '0 0 0');
- view.angles = '0 0 0';
- view.viewmodelforclient = e;
- view.flags = 0;
+ view.weaponentity_fld = weaponentity;
view.think = CL_Weaponentity_Think;
- view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
view.nextthink = time;
+ view.viewmodelforclient = actor;
+ view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
if (weaponentity == weaponentities[0])
{
- entity exterior = e.exteriorweaponentity = new(exteriorweaponentity);
+ entity exterior = actor.exteriorweaponentity = new(exteriorweaponentity);
+ make_pure(exterior);
exterior.solid = SOLID_NOT;
- exterior.exteriorweaponentity = exterior;
- exterior.owner = e;
+ exterior.owner = actor;
setorigin(exterior, '0 0 0');
- exterior.angles = '0 0 0';
exterior.think = CL_ExteriorWeaponentity_Think;
exterior.nextthink = time;
t *= W_WeaponRateFactor();
// VorteX: haste can be added here
- if (actor.weapon_think == w_ready)
+ if (this.weapon_think == w_ready)
{
- actor.weapon_nextthink = time;
+ this.weapon_nextthink = time;
// dprint("started firing at ", ftos(time), "\n");
}
- if (actor.weapon_nextthink < time - actor.weapon_frametime * 1.5
- || actor.weapon_nextthink > time + actor.weapon_frametime * 1.5)
+ if (this.weapon_nextthink < time - actor.weapon_frametime * 1.5
+ || this.weapon_nextthink > time + actor.weapon_frametime * 1.5)
{
- actor.weapon_nextthink = time;
+ this.weapon_nextthink = time;
// dprint("reset weapon animation timer at ", ftos(time), "\n");
}
- actor.weapon_nextthink += t;
- actor.weapon_think = func;
- // dprint("next ", ftos(actor.weapon_nextthink), "\n");
- this.weapon_nextthink = this.weapon_nextthink + t;
++ this.weapon_nextthink += t;
++ if (weaponentity == weaponentities[0]) actor.weapon_nextthink = this.weapon_nextthink;
+ this.weapon_think = func;
+ // dprint("next ", ftos(this.weapon_nextthink), "\n");
if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
{