Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
all.qc
Go to the documentation of this file.
1#include "all.qh"
2#ifndef WEAPONS_ALL_C
3#define WEAPONS_ALL_C
4
5#if defined(CSQC)
6 #include <client/main.qh>
7 #include <common/constants.qh>
10 #include <common/stats.qh>
11 #include <common/util.qh>
17 #include <lib/warpzone/client.qh>
18 #include <lib/warpzone/common.qh>
19#elif defined(MENUQC)
21#elif defined(SVQC)
22 #include <common/constants.qh>
24 #include <common/items/_mod.qh>
25 #include <common/mapinfo.qh>
28 #include <common/stats.qh>
29 #include <common/teams.qh>
30 #include <common/util.qh>
37 #include <lib/warpzone/common.qh>
38 #include <lib/warpzone/server.qh>
40 #include <server/command/_mod.qh>
41 #include <server/hook.qh>
44 #include <server/portals.qh>
48#endif
49
50
51// WEAPON PLUGIN SYSTEM
52
54{
55 a -= WEP_FIRST;
56 if (REGISTRY_MAX(Weapons) > 24 && a >= 24)
57 {
58 a -= 24;
59 if (REGISTRY_MAX(Weapons) > 48 && a >= 24)
60 {
61 a -= 24;
62 return '0 0 1' * BIT(a);
63 }
64 return '0 1 0' * BIT(a);
65 }
66 return '1 0 0' * BIT(a);
67}
68#ifdef SVQC
69void WriteWepSet(float dst, WepSet w)
70{
71 if (REGISTRY_MAX(Weapons) > 48)
72 WriteInt72_t(dst, w);
73 else if (REGISTRY_MAX(Weapons) > 24)
74 WriteInt48_t(dst, w);
75 else
76 WriteInt24_t(dst, w.x);
77}
78#endif
79#ifdef CSQC
81{
82 return STAT(WEAPONS);
83}
85{
86 return STAT(WEAPONSINMAP);
87}
89{
90 if (REGISTRY_MAX(Weapons) > 48)
91 return ReadInt72_t();
92 if (REGISTRY_MAX(Weapons) > 24)
93 return ReadInt48_t();
94 return
95 ReadInt24_t() * '1 0 0';
96}
97#endif
98
99string W_FixWeaponOrder(string order, float complete)
100{
101 return fixPriorityList(order, WEP_FIRST, WEP_LAST, WEP_IMPULSE_BEGIN - WEP_FIRST, complete);
102}
104{
105 int i = stof(s);
106 if (s == "0" || i)
107 {
108 entity wi = REGISTRY_GET(Weapons, i);
109 if (wi != WEP_Null)
110 return wi.netname;
111 }
112 return s;
113}
114
115string W_NameWeaponOrder(string order)
116{
118}
120{
121 if (s == "0" || stof(s))
122 return s;
123 FOREACH(Weapons, it != WEP_Null && (it.netname == s || it.m_deprecated_netname == s), return ftos(i));
124 return s;
125}
126string W_NumberWeaponOrder(string order)
127{
129}
130
140{
142 Weapon e1 = REGISTRY_GET(Weapons, si);
144 Weapon e2 = REGISTRY_GET(Weapons, sj);
145 int d = (e1.impulse + 9) % 10 - (e2.impulse + 9) % 10;
146 if (d)
147 return -d; // high impulse first!
149 return strstrofs(s, sprintf(" %d ", si), 0)
150 - strstrofs(s, sprintf(" %d ", sj), 0); // low char index first!
151}
165
166string W_FixWeaponOrder_AllowIncomplete(entity this, string order)
167{
168 return W_FixWeaponOrder(order, 0);
169}
170
172{
173 if (order == "")
174 order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
175 return W_FixWeaponOrder(order, 1);
176}
177
179{
180 WepSet result = '0 0 0';
181 for (int j = 0; j < n; ++j)
182 {
184 FOREACH(Weapons, it != WEP_Null,
185 {
186 if (remaining & (it.m_wepset))
187 RandomSelection_AddEnt(it, 1, 1);
188 });
191 remaining &= ~WepSet_FromWeapon(w);
192 }
193 return result;
194}
195
197{
198 FOREACH(Items, it.netname == ammotype.netname, return it);
199 LOG_WARNF("Invalid ammo type %d ", ammotype.m_id);
200 return NULL;
201}
202
204{
205 FOREACH(Resources, it.netname == ammotype.netname, return it);
206 LOG_WARNF("Invalid ammo type %d ", ammotype.m_id);
207 return NULL;
208}
209
210#ifdef CSQC
212{
213 // TODO: handle networking via resources
214 switch (ammotype)
215 {
216 case RES_SHELLS: return STAT_SHELLS;
217 case RES_BULLETS: return STAT_NAILS;
218 case RES_ROCKETS: return STAT_ROCKETS;
219 case RES_CELLS: return STAT_CELLS;
220 case RES_FUEL: return STAT_FUEL.m_id;
221 default: return -1;
222 }
223}
224#endif
225
226string W_Sound(string w_snd)
227{
228 string output = strcat("weapons/", w_snd);
229 MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
230 return M_ARGV(1, string);
231}
232
233string W_Model(string w_mdl)
234{
235 string output = strcat("models/weapons/", w_mdl);
236 MUTATOR_CALLHOOK(WeaponModel, w_mdl, output);
237 return M_ARGV(1, string);
238}
239
240#ifdef GAMEQC
241vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
242{
243 switch (algn)
244 {
245 default:
246 case 3: // right alignment
247 break;
248 case 4: // left
249 vecs.y = -vecs.y;
250 break;
251 case 1: // center
252 case 2: // center
253 vecs.y = 0;
254 vecs.z -= 2;
255 break;
256 }
257 return vecs;
258}
259
260vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn)
261{
262 string s;
263 if (visual)
264 vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
265 else if (STAT(SHOOTFROMEYE))
266 vecs.y = vecs.z = 0;
267 else if (STAT(SHOOTFROMCENTER))
268 {
269 vecs.y = 0;
270 vecs.z -= 2;
271 }
272 else if ((s = G_SHOOTFROMFIXEDORIGIN) != "")
273 {
274 vector v = stov(s);
275 if (y_is_right)
276 v.y = -v.y;
277 if (v.x != 0)
278 vecs.x = v.x;
279 vecs.y = v.y;
280 vecs.z = v.z;
281 }
282 else // just do the same as top
283 vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
284
285 return vecs;
286}
287
288
341void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
342{
343 if (name == "")
344 {
345 vector oldmin = this.mins;
346 vector oldmax = this.maxs;
347 setmodel(this, MDL_Null);
348 setsize(this, oldmin, oldmax);
349 if (this.weaponchild)
350 delete(this.weaponchild);
351 this.weaponchild = NULL;
352 this.movedir = '0 0 0';
353#ifdef SVQC
354 this.spawnorigin = '0 0 0';
355#endif
356 this.oldorigin = '0 0 0';
357 this.anim_fire1 = '0 1 0.01';
358 this.anim_fire2 = '0 1 0.01';
359 this.anim_idle = '0 1 0.01';
360 this.anim_reload = '0 1 0.01';
361 }
362 else
363 {
364 // if there is a child entity, hide it until we're sure we use it
365 if (this.weaponchild)
366 this.weaponchild.model = "";
367 _setmodel(this, W_Model(strcat("v_", name, ".md3")));
368 int v_shot_idx;
369 (v_shot_idx = gettagindex(this, "shot")) || (v_shot_idx = gettagindex(this, "tag_shot"));
370
371 _setmodel(this, W_Model(strcat("h_", name, ".iqm")));
372 // preset some defaults that work great for renamed zym files (which don't need an animinfo)
373 this.anim_fire1 = animfixfps(this, '0 1 0.01', '0 0 0');
374 this.anim_fire2 = animfixfps(this, '1 1 0.01', '0 0 0');
375 this.anim_idle = animfixfps(this, '2 1 0.01', '0 0 0');
376 this.anim_reload = animfixfps(this, '3 1 0.01', '0 0 0');
377
378 // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
379 // if we don't, this is a "real" animated model
380 string t;
381 if ((t = "weapon", gettagindex(this, t)) || (t = "tag_weapon", gettagindex(this, t)))
382 {
383 if (!this.weaponchild)
384 {
385 this.weaponchild = new(weaponchild);
386#ifdef CSQC
387 this.weaponchild.drawmask = MASK_NORMAL;
388 this.weaponchild.renderflags |= RF_VIEWMODEL;
389#endif
390 }
391 _setmodel(this.weaponchild, W_Model(strcat("v_", name, ".md3")));
392 setsize(this.weaponchild, '0 0 0', '0 0 0');
393 setattachment(this.weaponchild, this, t);
394 }
395 else
396 {
397 if (this.weaponchild)
398 delete(this.weaponchild);
399 this.weaponchild = NULL;
400 }
401
402 setsize(this, '0 0 0', '0 0 0');
403 setorigin(this, '0 0 0');
404 this.angles = '0 0 0';
405 this.frame = 0;
406#ifdef SVQC
408#else
410#endif
411 if (v_shot_idx) // v_ model attached to invisible h_ model
412 this.movedir = gettaginfo(this.weaponchild, v_shot_idx);
413 else
414 {
415 int idx;
416 if ((idx = gettagindex(this, "shot")) || (idx = gettagindex(this, "tag_shot")))
417 this.movedir = gettaginfo(this, idx);
418 else
419 {
420 LOG_WARNF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong",
421 this.model);
422 this.movedir = '0 0 0';
423 }
424 }
425#ifdef SVQC
426 {
427 int idx = 0;
428 // v_ model attached to invisible h_ model
429 if (this.weaponchild
430 && ((idx = gettagindex(this.weaponchild, "shell")) || (idx = gettagindex(this.weaponchild, "tag_shell"))))
431 this.spawnorigin = gettaginfo(this.weaponchild, idx);
432 else if ((idx = gettagindex(this, "shell")) || (idx = gettagindex(this, "tag_shell")))
433 this.spawnorigin = gettaginfo(this, idx);
434 else
435 {
436 LOG_WARNF("weapon model %s does not support the 'shell' tag, will display casings wrong",
437 this.model);
438 this.spawnorigin = this.movedir;
439 }
440 }
441#endif
442 if (v_shot_idx)
443 this.oldorigin = '0 0 0'; // use regular attachment
444 else
445 {
446 int idx;
447 if (this.weaponchild)
448 (idx = gettagindex(this, "weapon")) || (idx = gettagindex(this, "tag_weapon"));
449 else
450 (idx = gettagindex(this, "handle")) || (idx = gettagindex(this, "tag_handle"));
451 if (idx)
452 this.oldorigin = this.movedir - gettaginfo(this, idx);
453 else
454 {
455 LOG_WARNF(
456 "weapon model %s does not support the 'handle' tag "
457 "and neither does the v_ model support the 'shot' tag, "
458 "will display muzzle flashes TOTALLY wrong\n",
459 this.model);
460 this.oldorigin = '0 0 0'; // there is no way to recover from this
461 }
462 }
463
464#ifdef SVQC
465 this.viewmodelforclient = this.owner;
466#else
468#endif
469 }
470
471 this.view_ofs = '0 0 0';
472 this.movedir_aligned = this.movedir;
473
474 if (this.movedir.x >= 0)
475 {
476 //int algn = STAT(GUNALIGN, this.owner);
477 int algn = W_GunAlign(this, STAT(GUNALIGN, this.owner));
478 #ifdef SVQC
479 this.m_gunalign = algn;
480 #endif
481 vector v = this.movedir;
482 this.movedir = shotorg_adjust(v, false, false, algn);
483 this.movedir_aligned = shotorg_adjust(v, false, true, algn);
484 this.view_ofs = this.movedir_aligned - v;
485 }
486
487 // shotorg_adjust can give negative .x from shootfromfixedorigin
488 // recheck .x here due to it
489 if (this.movedir.x >= 0)
490 {
491 int compressed_shotorg = compressShotOrigin(this.movedir);
492 // make them match perfectly
493 #ifdef SVQC
494 // null during init
495 if (this.owner)
496 STAT(SHOTORG, this.owner) = compressed_shotorg;
497 #endif
498 this.movedir = decompressShotOrigin(compressed_shotorg);
499 }
500 else
501 {
502 // don't support negative x shotorgs
503 this.movedir = '0 0 0';
504
505 // reset _aligned here too even though as a side effect
506 // g_shootfromfixedorigin can make it reset
507 // it'd be only slightly better if it was checked individually
508 this.movedir_aligned = '0 0 0';
509
510 #ifdef SVQC
511 // null during init
512 if (this.owner)
513 STAT(SHOTORG, this.owner) = 0;
514 #endif
515 }
516
517#ifdef SVQC
518 this.spawnorigin += this.view_ofs; // offset the casings origin by the same amount
519#endif
520
521 // check if an instant weapon switch occurred
522 setorigin(this, this.view_ofs);
523 if (!_anim)
524 return;
525 // reset animstate now
526 this.wframe = WFRAME_IDLE;
527 setanim(this, this.anim_idle, true, false, true);
528}
529#endif
530
531#ifdef GAMEQC
532
534#ifdef CSQC
535NET_HANDLE(wframe, bool isNew)
536{
537 WFRAME fr = ReadByte();
538 float t = ReadFloat();
539 int slot = ReadByte();
540 bool restartanim = ReadByte();
541 entity wepent = viewmodels[slot];
542 if (fr == WFRAME_IDLE)
543 wepent.animstate_looping = false; // we don't need to enforce idle animation
544 else
545 {
546 vector a = '0 0 0';
547 switch (fr)
548 {
549 default:
550 case WFRAME_IDLE: a = wepent.anim_idle; break;
551 case WFRAME_FIRE1: a = wepent.anim_fire1; break;
552 case WFRAME_FIRE2: a = wepent.anim_fire2; break;
553 case WFRAME_RELOAD: a = wepent.anim_reload; break;
554 }
555 a.z *= t;
556 anim_set(wepent, a, !restartanim, restartanim, restartanim);
557 }
558 wepent.state = ReadByte();
559 wepent.weapon_nextthink = ReadFloat();
560 switch (wepent.state)
561 {
562 case WS_RAISE:
563 wepent.weapon_switchdelay = wepent.switchweapon.switchdelay_raise;
564 break;
565 case WS_DROP:
566 wepent.weapon_switchdelay = wepent.activeweapon.switchdelay_drop;
567 break;
568 default:
569 wepent.weapon_switchdelay = 0;
570 break;
571 }
572 return true;
573}
574#endif
575
576#ifdef SVQC
577void wframe_send(entity actor, entity weaponentity, int wepframe, float attackrate, bool restartanim)
578{
579 if (!IS_REAL_CLIENT(actor))
580 return;
581 int channel = MSG_ONE;
582 msg_entity = actor;
583 WriteHeader(channel, wframe);
584 WriteByte(channel, wepframe);
585 WriteFloat(channel, attackrate);
586 WriteByte(channel, weaponslot(weaponentity.weaponentity_fld));
587 WriteByte(channel, restartanim);
588 WriteByte(channel, weaponentity.state);
589 WriteFloat(channel, weaponentity.weapon_nextthink);
590}
591#endif
592
593REGISTER_NET_C2S(w_whereis)
594#ifdef SVQC
595void Weapon_whereis(Weapon this, entity cl);
596NET_HANDLE(w_whereis, bool)
597{
599 if (wpn != WEP_Null)
600 Weapon_whereis(wpn, sender);
601 return true;
602}
603#else
604void w_whereis(Weapon this)
605{
606 int channel = MSG_C2S;
607 WriteHeader(channel, w_whereis);
608 WriteRegistered(Weapons, channel, this);
609}
610CLIENT_COMMAND(weapon_find, "Show spawn locations of a weapon")
611{
612 switch (request)
613 {
615 {
616 string s = argv(1);
617 if (s == "all")
618 {
619 FOREACH(Weapons, it != WEP_Null, w_whereis(it));
620 return;
621 }
622 if (s == "unowned")
623 {
624 FOREACH(Weapons, it != WEP_Null && !(STAT(WEAPONS) & it.m_wepset), w_whereis(it));
625 return;
626 }
627 FOREACH(Weapons, it != WEP_Null && it.netname == s,
628 {
629 w_whereis(it);
630 return;
631 });
632 }
633 default:
634 LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0));
636 LOG_HELP("Usage:^3 cl_cmd weapon_find <weapon>");
637 LOG_HELP(" Where <weapon> is the lowercase weapon name, 'all' or 'unowned'.");
638 return;
639 }
640}
641#endif
642
643#ifdef SVQC
644void W_MuzzleFlash_Model_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset)
645{
646 flash.owner = actor;
647 flash.angles.z = random() * 360;
648
649 entity view = actor.(weaponentity);
650 entity exterior = actor.exteriorweaponentity;
651
652 if (view.oldorigin.x > 0)
653 {
654 setattachment(flash, exterior, "");
655 setorigin(flash, view.oldorigin + offset);
656 }
657 else
658 {
659 setattachment(flash, exterior, (gettagindex(exterior, "shot") ? "shot" : "tag_shot"));
660 setorigin(flash, offset);
661 }
662}
663#elif defined(CSQC)
665{
666 flash.owner = wepent;
667 flash.angles.z = random() * 360;
668
669 setattachment(flash, wepent, (gettagindex(wepent, "shot")) ? "shot" : "tag_shot");
670 setorigin(flash, offset);
671}
672#endif
673
675{
676 this.frame += 2;
677 this.scale *= 0.5;
678 this.alpha -= 0.25;
679 this.nextthink = time + 0.05;
680
681 if (this.alpha <= 0)
682 {
683 setthink(this, SUB_Remove);
684 this.nextthink = time;
685 this.realowner.muzzle_flash = NULL;
686 return;
687 }
688}
689
690void W_MuzzleFlash_Model(entity wepent, entity muzzlemodel)
691{
692 if (wepent.muzzle_flash == NULL)
693 wepent.muzzle_flash = spawn();
694
695 entity flash = wepent.muzzle_flash;
696 setmodel(flash, muzzlemodel); // precision set below
697
698 flash.scale = 0.75;
700 flash.nextthink = time + 0.02;
701 flash.frame = 2;
702 flash.alpha = 0.75;
703 flash.angles.z = random() * 180;
704 flash.effects = EF_ADDITIVE | EF_FULLBRIGHT;
705 flash.owner = flash.realowner = wepent;
706
707#ifdef CSQC
708 flash.drawmask = MASK_NORMAL;
709#endif
710}
711
712REGISTER_NET_TEMP(w_muzzleflash)
713
714#ifdef SVQC
715void W_MuzzleFlash(Weapon thiswep, entity actor, .entity weaponentity, vector shotorg, vector shotdir)
716{
717 // don't show an exterior muzzle effect for the off-hand
718 if (weaponslot(weaponentity) == 0)
719 {
720 Send_Effect_Except(thiswep.m_muzzleeffect, shotorg, shotdir * 1000, 1, '0 0 0', '0 0 0', actor);
721
722 if (thiswep.m_muzzlemodel != MDL_Null)
723 {
724 W_MuzzleFlash_Model(actor.exteriorweaponentity, thiswep.m_muzzlemodel);
725 W_MuzzleFlash_Model_AttachToShotorg(actor, weaponentity, actor.exteriorweaponentity.muzzle_flash, '5 0 0');
726 }
727 }
728
729 FOREACH_CLIENT(it == actor || (IS_SPEC(it) && it.enemy == actor),
730 {
731 if (!IS_REAL_CLIENT(it))
732 continue;
733 int channel = MSG_ONE;
734 msg_entity = it;
735 WriteHeader(channel, w_muzzleflash);
736 WriteRegistered(Weapons, channel, thiswep);
737 WriteByte(channel, weaponslot(weaponentity));
738 WriteVector(channel, shotorg);
739 });
740}
741#elif defined(CSQC)
742NET_HANDLE(w_muzzleflash, bool isNew)
743{
744 return = true;
745 int weapon_id = ReadRegistered(Weapons).m_id;
746 int slot = ReadByte();
747 vector sv_shotorg = ReadVector();
748
749 Weapon thiswep = REGISTRY_GET(Weapons, weapon_id);
750 vector viewangles = getpropertyvec(VF_CL_VIEWANGLES);
751 vector forward, right, up;
752 MAKE_VECTORS(viewangles, forward, right, up);
753
755 {
756 // in third person mode, show the muzzle flash from the server side weapon position
757 // we don't have a view model to reference in this case
758 pointparticles(thiswep.m_muzzleeffect, sv_shotorg, forward * 1000, 1);
759 return;
760 }
762 return;
763
764 entity wepent = viewmodels[slot];
765 // get the local player entity to calculate shot origin
767 if (!rlplayer)
768 rlplayer = csqcplayer; // fall back to the global
769
770 vector md = wepent.movedir_aligned;
771 vector dv = forward * md.x + right * -md.y + up * md.z;
772 vector org = rlplayer.origin + rlplayer.view_ofs + dv;
773
774 pointparticles(thiswep.m_muzzleeffect, org, forward * 1000, 1);
775
776 if (thiswep.m_muzzlemodel != MDL_Null)
777 {
778 W_MuzzleFlash_Model(wepent, thiswep.m_muzzlemodel);
779 W_MuzzleFlash_Model_AttachToShotorg(wepent, wepent.muzzle_flash, '5 0 0');
780 }
781}
782#endif
783
784
785#endif
786
787#endif
788
789#ifdef SVQC
796#endif
797
798#ifdef CSQC
799REPLICATE(cvar_cl_accuracy_data_share, bool, "cl_accuracy_data_share");
800REPLICATE(cvar_cl_accuracy_data_receive, bool, "cl_accuracy_data_receive");
801#endif
802
803#ifdef GAMEQC
804REPLICATE(cvar_cl_gunalign, int, "cl_gunalign");
805REPLICATE(cvar_cl_weapon_switch_reload, bool, "cl_weapon_switch_reload");
806REPLICATE(cvar_cl_weapon_switch_fallback_to_impulse, bool, "cl_weapon_switch_fallback_to_impulse");
807REPLICATE(cvar_cl_weaponimpulsemode, int, "cl_weaponimpulsemode");
808REPLICATE(cvar_cl_weaponpriority, string, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
809REPLICATE(cvar_cl_weaponpriorities[0], string, "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
810REPLICATE(cvar_cl_weaponpriorities[1], string, "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
811REPLICATE(cvar_cl_weaponpriorities[2], string, "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
812REPLICATE(cvar_cl_weaponpriorities[3], string, "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
813REPLICATE(cvar_cl_weaponpriorities[4], string, "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
814REPLICATE(cvar_cl_weaponpriorities[5], string, "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
815REPLICATE(cvar_cl_weaponpriorities[6], string, "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
816REPLICATE(cvar_cl_weaponpriorities[7], string, "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
817REPLICATE(cvar_cl_weaponpriorities[8], string, "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
818REPLICATE(cvar_cl_weaponpriorities[9], string, "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
819#endif
820
821
822#ifdef MENUQC
823// Finds keybinds for a weapon
825{
826 string command, key, keys = "";
827 int n, j;
828 float k;
829 const bool joy_active = cvar("joy_active");
830
831 for (int cmd_type = 0; cmd_type < 2; ++cmd_type)
832 {
833 command = cmd_type
834 ? strcat("weapon_", wep.netname)
835 : strcat("weapon_group_", itos(wep.impulse));
836 n = tokenize(findkeysforcommand(command, 0)); // uses '...' strings
837
838 for (j = 0; j < n; ++j)
839 {
840 k = stof(argv(j));
841 if (k == -1)
842 continue;
843 key = keynumtostring(k);
844 if (!joy_active && startsWith(key, "JOY"))
845 continue;
846
847 key = translate_key(key);
848 if (keys == "")
849 keys = strcat("^3", key);
850 else
851 keys = strcat(keys, "^7, ^3", key);
852 }
853 }
854
855 if (keys == "")
856 return _("This weapon has not been bound directly."); // code may miss some weird bind configs, so say "directly"
857 return sprintf(_("You have bound this weapon to: %s."), strcat(keys, "^7"));
858}
859
860
861#define COLORED_DAMAGE(dmg) strcat("^9", dmg, "^7")
862
863#define DPS_ONE(mode) sprintf(_("(DPS: %.2f)"), mode##rft <= 0 ? 1337 : stof(mode##dmg_s) / mode##rft)
864#define DPS_MUL(mode) sprintf(_("(DPS: %.2f)"), mode##rft <= 0 ? 1337 : stof(mode##dmg_s) / mode##rft * stof(mode##shots_s))
865
866#define pri_str _("primary")
867#define sec_str _("secondary")
868#define _IT_DOES_MSG() strcat("\n* ", sprintf(_("It does %s damage"), COLORED_DAMAGE(dmg_s)), " ", DPS_ONE())
869#define _IT_SHOOTS_MSG() strcat("\n* ", sprintf(_("It shoots %s shots doing %s damage each"), shots_s, COLORED_DAMAGE(dmg_s)), " ", DPS_MUL())
870#define _DOES_MSG(mode) strcat("\n* ", sprintf(_("The %s does %s damage"), mode##str, COLORED_DAMAGE(mode##dmg_s)), " ", DPS_ONE(mode))
871#define _DOES_PERSEC_MSG(mode) strcat("\n* ", sprintf(_("The %s does %s damage per second"), mode##str, COLORED_DAMAGE(mode##dmg_s)))
872#define _SHOOTS_MSG(mode) strcat("\n* ", sprintf(_("The %s shoots %s shots doing %s damage each"), mode##str, mode##shots_s, COLORED_DAMAGE(mode##dmg_s)), " ", DPS_MUL(mode))
873#define _SHOOTS_UPTO_MSG(mode) strcat("\n* ", sprintf(_("The %s shoots up to %s shots doing %s damage each"), mode##str, mode##shots_s, COLORED_DAMAGE(mode##dmg_s)), " ", DPS_MUL(mode))
874
875// We could use the *_MSG macros directly but we use these because they are more intuitive and readable
876#define IT_DOES() _IT_DOES_MSG()
877#define IT_SHOOTS() _IT_SHOOTS_MSG()
878#define THE_PRIMARY_DOES() _DOES_MSG(pri_)
879#define THE_PRIMARY_SHOOTS() _SHOOTS_MSG(pri_)
880#define THE_PRIMARY_SHOOTS_UPTO() _SHOOTS_UPTO_MSG(pri_)
881#define THE_PRIMARY_DOES_PERSEC() _DOES_PERSEC_MSG(pri_)
882#define THE_SECONDARY_DOES() _DOES_MSG(sec_)
883#define THE_SECONDARY_SHOOTS() _SHOOTS_MSG(sec_)
884#define THE_SECONDARY_SHOOTS_UPTO() _SHOOTS_UPTO_MSG(sec_)
885#define THE_SECONDARY_DOES_PERSEC() _DOES_PERSEC_MSG(sec_)
886
887// One function for each translatable string
888
890{
891 const string wepbal_s = strcat("g_balance_", name);
892 const string dmg_s = cvar_defstring(strcat(wepbal_s, "_damage"));
893 const float rft = stof(cvar_defstring(strcat(wepbal_s, "_refire")));
894 const string dmg2_s = cvar_defstring(strcat(wepbal_s, "_damage2"));
895 const string dir_dps = sprintf(_("(DPS: %.2f)"), rft <= 0 ? 1337 : stof(dmg2_s) / rft);
896 return strcat(_("Default damage stats:"),
897 IT_DOES(),
898 strcat("\n* ", sprintf(_("A direct hit does %s damage"), COLORED_DAMAGE(dmg2_s)), " ", dir_dps)
899 );
900}
902{
903 const string wepbal_s = strcat("g_balance_", name);
904 const string dmg_s = cvar_defstring(strcat(wepbal_s, "_damage"));
905 const float rft = stof(cvar_defstring(strcat(wepbal_s, "_refire")));
906 return strcat(_("Default damage stats:"),
907 IT_DOES()
908 );
909}
910string W_Guide_DPS_onlyOne_andDirectHit(string name, string fire)
911{
913}
914string W_Guide_DPS_onlyOne(string name, string fire)
915{
916 return W_Guide_DPS_onlyOne_unnamed(strcat(name, "_", fire));
917}
918string W_Guide_DPS(string name, string pri, string sec)
919{
920 // TODO: rename all wep primary cvars to g_balance_*_primary_*, vice versa for secondary.
921 // Example exception: g_balance_arc_beam_* (primary)
922 const string wepbal_s = strcat("g_balance_", name, "_");
923 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
924 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
925 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
926 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
927 return strcat(_("Default damage stats:"),
930 );
931}
932string W_Guide_DPS_bothMultishot(string name, string pri, string sec)
933{
934 const string wepbal_s = strcat("g_balance_", name, "_");
935 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
936 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
937 const string pri_shots_s = cvar_defstring(strcat(wepbal_s, pri, "_shots"));
938 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
939 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
940 const string sec_shots_s = cvar_defstring(strcat(wepbal_s, sec, "_shots"));
941 return strcat(_("Default damage stats:"),
944 );
945}
946string W_Guide_DPS_onlySecondary(string name, string sec)
947{
948 const string wepbal_s = strcat("g_balance_", name, "_");
949 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
950 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
951 return strcat(_("Default damage stats:"),
953 );
954}
955string W_Guide_DPS_secondaryMultishot(string name, string pri, string sec, string shots, string refire2, bool sec_variable)
956{
957 const string wepbal_s = strcat("g_balance_", name, "_");
958 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
959 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
960 string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
961 float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
962 const string sec_shots_s = cvar_defstring(strcat(wepbal_s, shots));
963 if (sec_dmg_s == "")
964 sec_dmg_s = pri_dmg_s;
965 const float num_shots = stof(sec_shots_s);
966 if (refire2 != "")
967 sec_rft = (num_shots - 1) * sec_rft + stof(cvar_defstring(strcat(wepbal_s, refire2)));
968 return strcat(_("Default damage stats:"),
971 );
972}
973string W_Guide_DPS_primaryMultishot(string name, string pri, string sec, string shots, string refire2)
974{
975 const string wepbal_s = strcat("g_balance_", name, "_");
976 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
977 float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
978 const string pri_shots_s = cvar_defstring(strcat(wepbal_s, shots));
979 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
980 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
981 const float num_shots = stof(pri_shots_s);
982 if (refire2 != "")
983 pri_rft = (num_shots - 1) * pri_rft + stof(cvar_defstring(strcat(wepbal_s, refire2)));
984 return strcat(_("Default damage stats:"),
987 );
988}
989string W_Guide_DPS_onlyOneMultishot(string name, string fire, string shots, string refire2)
990{
991 const string wepbal_s = strcat("g_balance_", name, "_");
992 const string dmg_s = cvar_defstring(strcat(wepbal_s, fire, "_damage"));
993 float rft = stof(cvar_defstring(strcat(wepbal_s, fire, "_refire")));
994 const string shots_s = cvar_defstring(strcat(wepbal_s, shots));
995 const float num_shots = stof(shots_s);
996 if (refire2 != "")
997 rft = (num_shots - 1) * rft + stof(cvar_defstring(strcat(wepbal_s, refire2)));
998 return strcat(_("Default damage stats:"),
999 IT_SHOOTS()
1000 );
1001}
1002string W_Guide_DPS_secondaryMultishotWithCombo(string name, string pri, string sec, string shots, string refire2, string combo, bool sec_variable)
1003{
1004 const string wepbal_s = strcat("g_balance_", name, "_");
1005 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
1006 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
1007 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
1008 float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
1009 const string sec_shots_s = cvar_defstring(strcat(wepbal_s, shots));
1010 const string cmb_dmg_s = cvar_defstring(strcat(wepbal_s, combo, "_damage"));
1011 const float num_shots = stof(sec_shots_s);
1012 float combo_t = max(pri_rft, sec_rft);
1013 if (refire2 != "")
1014 {
1015 const float sec_rft2 = sec_rft;
1016 sec_rft = stof(cvar_defstring(strcat(wepbal_s, refire2)));
1017 combo_t = max(combo_t, num_shots * sec_rft + sec_rft2);
1018 sec_rft = (num_shots - 1) * sec_rft + sec_rft2;
1019 }
1020 const string DPS_combo = sprintf(_("(total DPS: %.2f)"),
1021 combo_t <= 0 ? 1337 : (stof(pri_dmg_s) + stof(sec_dmg_s) * num_shots + stof(cmb_dmg_s)) / combo_t);
1022 return strcat(_("Default damage stats:"),
1024 (sec_variable ? THE_SECONDARY_SHOOTS_UPTO() : THE_SECONDARY_SHOOTS()),
1025 strcat("\n* ", sprintf(_("The combo adds an extra %s damage"), COLORED_DAMAGE(cmb_dmg_s)), " ", DPS_combo)
1026 );
1027}
1028string W_Guide_DPS_primaryDPS(string name, string pri, string sec)
1029{
1030 const string wepbal_s = strcat("g_balance_", name, "_");
1031 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage")); // actually damage per second
1032 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
1033 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
1034 return strcat(_("Default damage stats:"),
1037 );
1038}
1039#endif
vector shotorg
Definition aim.qh:10
vector shotdir
Definition aim.qh:11
void anim_set(entity e, vector anim, bool looping, bool override, bool restart)
Definition anim.qc:6
#define setanim(...)
Definition anim.qh:45
float frame
primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4)
Definition anim.qh:6
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
int W_GunAlign(entity this, int preferred_align)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define CLIENT_COMMAND(id, description)
Definition cl_cmd.qh:16
entity csqcplayer
Definition cl_player.qh:26
Definition ammo.qh:24
int m_id
Definition item.qh:127
string netname
Definition resources.qh:27
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:42
int impulse
M: impulse : weapon impulse.
Definition weapon.qh:58
string netname
M: refname : reference name name.
Definition weapon.qh:84
entity m_muzzlemodel
M: flash model MDL_id_MUZZLEFLASH.
Definition weapon.qh:69
float alpha
Definition items.qc:13
entity owner
Definition main.qh:87
float renderflags
Definition main.qh:111
const int CMD_REQUEST_COMMAND
Definition command.qh:3
const int CMD_REQUEST_USAGE
Definition command.qh:4
#define setmodel(this, m)
Definition model.qh:26
#define M_ARGV(x, type)
Definition events.qh:17
string fixPriorityList(string order, float from, float to, float subtract, float complete)
Definition util.qc:598
float compressShotOrigin(vector v)
Definition util.qc:1348
string translate_key(string key)
Definition util.qc:1610
string mapPriorityList(string order, string(string) mapfunc)
Definition util.qc:640
vector decompressShotOrigin(int f)
Definition util.qc:1375
const float VF_CL_VIEWANGLES
const float STAT_CELLS
const float STAT_ROCKETS
const float MASK_NORMAL
vector mins
const float STAT_SHELLS
const float EF_ADDITIVE
const float EF_FULLBRIGHT
const float RF_VIEWMODEL
float time
const float STAT_NAILS
vector maxs
float nextthink
float player_localentnum
vector oldorigin
#define spawn
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:12
#define MAKE_VECTORS(angles, forward, right, up)
Same as the makevectors builtin but uses the provided locals instead of the v_* globals.
entity viewmodelforclient
#define strstrofs
#define gettagindex
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:7
#define pass(name, colormin, colormax)
void Send_Effect_Except(entity eff, vector eff_loc, vector eff_vel, int eff_cnt, vector eff_col_min, vector eff_col_max, entity ignore)
Definition all.qc:101
ent angles
Definition ent_cs.qc:146
model
Definition ent_cs.qc:164
WriteByte(chan, ent.angles.y/DEC_FACTOR)
entity CSQCModel_server2csqc(int i)
Definition cl_model.qc:314
Weapons
Definition guide.qh:113
#define itos(i)
Definition int.qh:6
Header file that describes the functions related to game items.
#define FOREACH(list, cond, body)
Definition iter.qh:19
#define ReadFloat()
Definition net.qh:348
#define NET_HANDLE(id, param)
Definition net.qh:15
#define ReadVector()
Definition net.qh:349
int ReadInt24_t()
Definition net.qh:337
#define ReadRegistered(r)
Definition net.qh:290
#define ReadInt48_t()
Definition net.qh:343
#define ReadInt72_t()
Definition net.qh:344
#define WriteHeader(to, id)
Definition net.qh:265
#define REGISTER_NET_C2S(id)
Definition net.qh:128
int ReadByte()
#define WriteRegistered(r, to, it)
Definition net.qh:292
#define REGISTER_NET_TEMP(id)
Definition net.qh:31
#define REPLICATE(...)
Replicates a client cvar into a server field.
Definition replicate.qh:23
#define STAT(...)
Definition stats.qh:94
#define LOG_WARNF(...)
Definition log.qh:59
#define LOG_HELP(...)
Definition log.qh:83
#define LOG_INFOF(...)
Definition log.qh:63
vector movedir
Definition viewloc.qh:18
string name
Definition menu.qh:30
float MSG_ONE
Definition menudefs.qc:56
float stof(string val,...)
string substring(string s, float start, float length)
float cvar(string name)
string keynumtostring(float keynum)
vector stov(string s)
float random(void)
string ftos(float f)
const string cvar_defstring(string name)
float tokenize(string s)
string argv(float n)
float max(float f,...)
vector animfixfps(entity e, vector a, vector b)
Definition util.qc:1905
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
#define gettaginfo
Definition post.qh:32
entity msg_entity
Definition progsdefs.qc:63
vector view_ofs
Definition progsdefs.qc:151
float scale
Definition projectile.qc:14
entity result
Definition promise.qc:45
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
#define RandomSelection_AddEnt(e, weight, priority)
Definition random.qh:14
entity RandomSelection_chosen_ent
Definition random.qh:5
#define REGISTRY_GET(id, i)
Definition registry.qh:62
#define REGISTRY_MAX(id)
Definition registry.qh:17
#define setthink(e, f)
vector
Definition self.qh:96
vector org
Definition self.qh:96
string weaponorder_byimpulse
Definition client.qh:62
ERASEABLE void heapsort(int n, swapfunc_t swap, comparefunc_t cmp, entity pass)
Definition sort.qh:9
#define CS_CVAR(this)
Definition state.qh:51
#define startsWith(haystack, needle)
Definition string.qh:234
#define strcpy(this, s)
Definition string.qh:51
entity realowner
#define IS_SPEC(v)
Definition utils.qh:10
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
int autocvar_chase_active
Definition view.qh:17
bool autocvar_r_drawviewmodel
Definition view.qh:97
entity viewmodels[MAX_WEAPONSLOTS]
Definition view.qh:108
string W_FixWeaponOrder_ForceComplete(string order)
Definition all.qc:171
string W_FixWeaponOrder_AllowIncomplete(entity this, string order)
Definition all.qc:166
vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
Definition all.qc:241
void wframe_send(entity actor, entity weaponentity, int wepframe, float attackrate, bool restartanim)
Definition all.qc:577
string W_Model(string w_mdl)
Definition all.qc:233
string W_Guide_Keybinds(Weapon wep)
Definition all.qc:824
#define COLORED_DAMAGE(dmg)
Definition all.qc:861
string W_Guide_DPS_primaryMultishot(string name, string pri, string sec, string shots, string refire2)
Definition all.qc:973
void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
supported formats:
Definition all.qc:341
WepSet WepSet_GetFromStat_InMap()
Definition all.qc:84
string W_Guide_DPS_secondaryMultishot(string name, string pri, string sec, string shots, string refire2, bool sec_variable)
Definition all.qc:955
void WriteWepSet(float dst, WepSet w)
Definition all.qc:69
int GetAmmoStat(Resource ammotype)
Definition all.qc:211
void W_FixWeaponOrder_BuildImpulseList_swap(int i, int j, entity pass)
Definition all.qc:133
entity GetAmmoResource(Ammo ammotype)
Definition all.qc:203
float W_FixWeaponOrder_BuildImpulseList_buf[REGISTRY_MAX(Weapons)]
Definition all.qc:131
void W_MuzzleFlash_Model_Think(entity this)
Definition all.qc:674
float W_FixWeaponOrder_BuildImpulseList_cmp(int i, int j, entity pass)
Definition all.qc:139
string W_NumberWeaponOrder(string order)
Definition all.qc:126
string W_FixWeaponOrder_BuildImpulseList(string o)
Definition all.qc:152
string W_Guide_DPS(string name, string pri, string sec)
Definition all.qc:918
#define THE_SECONDARY_DOES()
Definition all.qc:882
string W_NumberWeaponOrder_MapFunc(string s)
Definition all.qc:119
string W_Sound(string w_snd)
Definition all.qc:226
string W_Guide_DPS_bothMultishot(string name, string pri, string sec)
Definition all.qc:932
string W_NameWeaponOrder(string order)
Definition all.qc:115
string W_Guide_DPS_onlySecondary(string name, string sec)
Definition all.qc:946
void Weapon_whereis(Weapon this, entity cl)
Definition selection.qc:26
void W_MuzzleFlash_Model(entity wepent, entity muzzlemodel)
Definition all.qc:690
string W_Guide_DPS_onlyOne_andDirectHit(string name, string fire)
Definition all.qc:910
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:715
#define THE_SECONDARY_SHOOTS_UPTO()
Definition all.qc:884
string W_NameWeaponOrder_MapFunc(string s)
Definition all.qc:103
string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(entity this, string wo)
Definition all.qc:790
string W_Guide_DPS_secondaryMultishotWithCombo(string name, string pri, string sec, string shots, string refire2, string combo, bool sec_variable)
Definition all.qc:1002
#define THE_PRIMARY_SHOOTS()
Definition all.qc:879
#define IT_DOES()
Definition all.qc:876
WepSet WepSet_GetFromStat()
Definition all.qc:80
string W_Guide_DPS_onlyOneMultishot(string name, string fire, string shots, string refire2)
Definition all.qc:989
WepSet _WepSet_FromWeapon(int a)
Definition all.qc:53
#define THE_PRIMARY_DOES_PERSEC()
Definition all.qc:881
string W_Guide_DPS_onlyOne_unnamed(string name)
Definition all.qc:901
void W_MuzzleFlash_Model_AttachToShotorg(entity actor,.entity weaponentity, entity flash, vector offset)
Definition all.qc:644
string W_FixWeaponOrder(string order, float complete)
Definition all.qc:99
string W_Guide_DPS_onlyOne(string name, string fire)
Definition all.qc:914
string W_Guide_DPS_primaryDPS(string name, string pri, string sec)
Definition all.qc:1028
string W_FixWeaponOrder_BuildImpulseList_order
Definition all.qc:132
#define IT_SHOOTS()
Definition all.qc:877
entity GetAmmoItem(Resource ammotype)
Definition all.qc:196
#define THE_SECONDARY_SHOOTS()
Definition all.qc:883
WepSet ReadWepSet()
Definition all.qc:88
vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn)
Definition all.qc:260
string W_Guide_DPS_onlyOne_unnamed_andDirectHit(string name)
Definition all.qc:889
WepSet W_RandomWeapons(entity e, WepSet remaining, int n)
Definition all.qc:178
#define THE_PRIMARY_DOES()
Definition all.qc:878
int m_gunalign
Definition all.qh:410
#define G_SHOOTFROMFIXEDORIGIN
Definition all.qh:443
vector movedir_aligned
Definition all.qh:419
vector anim_reload
Definition all.qh:425
#define WEP_LAST
Definition all.qh:343
vector anim_fire2
Definition all.qh:423
vector anim_idle
Definition all.qh:424
const int WEP_FIRST
Definition all.qh:342
entity weaponchild
Definition all.qh:400
vector anim_fire1
Definition all.qh:422
WFRAME wframe
Definition all.qh:439
#define WEP_IMPULSE_BEGIN
Definition all.qh:350
vector spawnorigin
Definition all.qh:416
#define WepSet_FromWeapon(it)
Definition all.qh:48
const int WS_RAISE
raise frame
Definition weapon.qh:32
int weaponslot(.entity weaponentity)
Definition weapon.qh:19
const int WS_DROP
deselecting frame
Definition weapon.qh:34
vector WepSet
Definition weapon.qh:14