From: TimePath Date: Fri, 6 Nov 2015 08:55:40 +0000 (+1100) Subject: Merge branch 'master' into TimePath/csqc_viewmodels X-Git-Tag: xonotic-v0.8.2~1601^2~14 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=43eba8ca70f00458db385630f86009f6d7fa849a;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into TimePath/csqc_viewmodels # Conflicts: # qcsrc/common/weapons/all.qc # qcsrc/common/weapons/weapon/rifle.qc # qcsrc/server/weapons/weaponsystem.qc --- 43eba8ca70f00458db385630f86009f6d7fa849a diff --cc qcsrc/client/view.qc index 0e7ec4aa4,6f37f39e3..f1e107de2 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@@ -10,8 -9,8 +9,9 @@@ #include "mutators/events.qh" +#include "../common/anim.qh" #include "../common/constants.qh" + #include "../common/debug.qh" #include "../common/mapinfo.qh" #include "../common/gamemodes/all.qh" #include "../common/nades/all.qh" diff --cc qcsrc/common/weapons/all.qc index b408ee157,b33812213..177d12c2d --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@@ -277,23 -291,12 +277,12 @@@ string GetAmmoPicture(.int ammotype 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 } @@@ -301,341 -304,11 +290,345 @@@ string W_Model(string w_mdl { 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); } +#endif + +#ifndef MENUQC + - REGISTER_NET_TEMP(wframe, 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, bool isNew) ++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 diff --cc qcsrc/common/weapons/weapon/rifle.qc index a48765364,e7cd2606c..be9788fcc --- a/qcsrc/common/weapons/weapon/rifle.qc +++ b/qcsrc/common/weapons/weapon/rifle.qc @@@ -86,8 -86,8 +86,8 @@@ void W_Rifle_Attack2( W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND(CAMPINGRIFLE_FIRE2)); } - .void(void) rifle_bullethail_attackfunc; + .void() rifle_bullethail_attackfunc; -.float rifle_bullethail_frame; +.WFRAME rifle_bullethail_frame; .float rifle_bullethail_animtime; .float rifle_bullethail_refire; void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponentity, int fire) @@@ -115,8 -113,7 +113,7 @@@ } } - void W_Rifle_BulletHail(.entity weaponentity, float mode, void(void) AttackFunc, WFRAME fr, float animtime, float refire) -void W_Rifle_BulletHail(.entity weaponentity, float mode, void() AttackFunc, float fr, float animtime, float refire) ++void W_Rifle_BulletHail(.entity weaponentity, float mode, void() AttackFunc, WFRAME fr, float animtime, float refire) {SELFPARAM(); // if we get here, we have at least one bullet to fire AttackFunc(); diff --cc qcsrc/server/cl_client.qc index fdfdedfe7,4994c1936..9cd8da807 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@@ -2138,35 -2143,7 +2144,9 @@@ void PlayerUseKey( MUTATOR_CALLHOOK(PlayerUseKey); } - float isInvisibleString(string s) - { - float i, n, c; - s = strdecolorize(s); - for((i = 0), (n = strlen(s)); i < n; ++i) - { - c = str2chr(s, i); - switch(c) - { - case 0: - case 32: // space - break; - case 192: // charmap space - if (!autocvar_utf8_enable) - break; - return false; - case 160: // space in unicode fonts - case 0xE000 + 192: // utf8 charmap space - if (autocvar_utf8_enable) - break; - default: - return false; - } - } - return true; - } +void wglow_send(entity actor, vector g); + /* ============= PlayerPreThink diff --cc qcsrc/server/weapons/weaponsystem.qc index 9c0902507,3ea45ad46..930bb9065 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@@ -63,11 -327,10 +65,11 @@@ void CL_Weaponentity_Think( 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; } @@@ -150,24 -443,31 +152,27 @@@ void CL_ExteriorWeaponentity_Think( } // 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; @@@ -354,20 -654,20 +363,21 @@@ void weapon_thinkf(entity actor, .entit 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) {