]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/all.qh
Weapons: Introduce concept of offhand weapons
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / all.qh
1 #ifndef WEAPONS_ALL_H
2 #define WEAPONS_ALL_H
3
4 #ifndef MENUQC
5 #include "calculations.qh"
6 #include "../models/models.qh"
7 #endif
8
9 #include "../util.qh"
10
11 #ifdef SVQC
12 #include "../../server/bot/aim.qh"
13 #endif
14
15 const int MAX_SHOT_DISTANCE = 32768;
16
17 // weapon pickup ratings for bot logic
18 const int BOT_PICKUP_RATING_LOW  =  2500;
19 const int BOT_PICKUP_RATING_MID  =  5000;
20 const int BOT_PICKUP_RATING_HIGH = 10000;
21
22 // weapon flags
23 const int WEP_TYPE_OTHER          =  0x00; // not for damaging people
24 const int WEP_TYPE_SPLASH         =  0x01; // splash damage
25 const int WEP_TYPE_HITSCAN        =  0x02; // hitscan
26 const int WEP_TYPEMASK            =  0x0F;
27 const int WEP_FLAG_CANCLIMB       =  0x10; // can be used for movement
28 const int WEP_FLAG_NORMAL         =  0x20; // in "most weapons" set
29 const int WEP_FLAG_HIDDEN         =  0x40; // hides from menu
30 const int WEP_FLAG_RELOADABLE     =  0x80; // can has reload
31 const int WEP_FLAG_SUPERWEAPON    = 0x100; // powerup timer
32 const int WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
33
34 // weapon requests
35 const int WR_SETUP          =  1; // (SERVER) setup weapon data
36 .bool(entity this) wr_setup;
37 /** (SERVER) logic to run every frame */
38 .bool(entity this, bool fire1, bool fire2) wr_think;
39 const int WR_CHECKAMMO1     =  3; // (SERVER) checks ammo for weapon primary
40 .bool(entity this) wr_checkammo1;
41 const int WR_CHECKAMMO2     =  4; // (SERVER) checks ammo for weapon second
42 .bool(entity this) wr_checkammo2;
43 const int WR_AIM            =  5; // (SERVER) runs bot aiming code for this weapon
44 .bool(entity this) wr_aim;
45 const int WR_INIT           =  6; // (BOTH)   precaches models/sounds used by this weapon, also sets up weapon properties
46 .bool(entity this) wr_init;
47 const int WR_SUICIDEMESSAGE =  7; // (SERVER) notification number for suicide message (may inspect w_deathtype for details)
48 .bool(entity this) wr_suicidemessage;
49 const int WR_KILLMESSAGE    =  8; // (SERVER) notification number for kill message (may inspect w_deathtype for details)
50 .bool(entity this) wr_killmessage;
51 const int WR_RELOAD         =  9; // (SERVER) handles reloading for weapon
52 .bool(entity this) wr_reload;
53 const int WR_RESETPLAYER    = 10; // (SERVER) clears fields that the weapon may use
54 .bool(entity this) wr_resetplayer;
55 const int WR_IMPACTEFFECT   = 11; // (CLIENT) impact effect for weapon explosion
56 .bool(entity this) wr_impacteffect;
57 const int WR_PLAYERDEATH    = 12; // (SERVER) called whenever a player dies
58 .bool(entity this) wr_playerdeath;
59 const int WR_GONETHINK      = 13; // (SERVER) logic to run when weapon is lost
60 .bool(entity this) wr_gonethink;
61 const int WR_CONFIG         = 14; // (ALL)    dump weapon cvars to config in data directory (see: sv_cmd dumpweapons)
62 .bool(entity this) wr_config;
63 const int WR_ZOOMRETICLE    = 15; // (CLIENT) weapon specific zoom reticle
64 .bool(entity this) wr_zoomreticle;
65 const int WR_DROP           = 16; // (SERVER) the weapon is dropped
66 .bool(entity this) wr_drop;
67 const int WR_PICKUP         = 17; // (SERVER) a weapon is picked up
68 .bool(entity this) wr_pickup;
69
70 bool w_new(entity this, int req) {
71     if (req == WR_SETUP) return this.wr_setup ? this.wr_setup(this) : false;
72     if (req == WR_CHECKAMMO1) return this.wr_checkammo1 ? this.wr_checkammo1(this) : false;
73     if (req == WR_CHECKAMMO2) return this.wr_checkammo2 ? this.wr_checkammo2(this) : false;
74     if (req == WR_AIM) return this.wr_aim ? this.wr_aim(this) : false;
75     if (req == WR_INIT) return this.wr_init ? this.wr_init(this) : false;
76     if (req == WR_SUICIDEMESSAGE) return this.wr_suicidemessage ? this.wr_suicidemessage(this) : false;
77     if (req == WR_KILLMESSAGE) return this.wr_killmessage ? this.wr_killmessage(this) : false;
78     if (req == WR_RELOAD) return this.wr_reload ? this.wr_reload(this) : false;
79     if (req == WR_RESETPLAYER) return this.wr_resetplayer ? this.wr_resetplayer(this) : false;
80     if (req == WR_IMPACTEFFECT) return this.wr_impacteffect ? this.wr_impacteffect(this) : false;
81     if (req == WR_PLAYERDEATH) return this.wr_playerdeath ? this.wr_playerdeath(this) : false;
82     if (req == WR_GONETHINK) return this.wr_gonethink ? this.wr_gonethink(this) : false;
83     if (req == WR_CONFIG) return this.wr_config ? this.wr_config(this) : false;
84     if (req == WR_ZOOMRETICLE) return this.wr_zoomreticle ? this.wr_zoomreticle(this) : false;
85     if (req == WR_DROP) return this.wr_drop ? this.wr_drop(this) : false;
86     if (req == WR_PICKUP) return this.wr_pickup ? this.wr_pickup(this) : false;
87     return false;
88 }
89
90 // variables:
91 string weaponorder_byid;
92
93 // weapon sets
94 typedef vector WepSet;
95 WepSet WepSet_FromWeapon(int a);
96 #ifdef SVQC
97 void WepSet_AddStat();
98 void WepSet_AddStat_InMap();
99 void WriteWepSet(float dest, WepSet w);
100 #endif
101
102 #ifdef CSQC
103 WepSet WepSet_GetFromStat();
104 WepSet WepSet_GetFromStat_InMap();
105 WepSet ReadWepSet();
106 #endif
107
108 // weapon name macros
109 const int WEP_FIRST = 1;
110 #define WEP_MAXCOUNT 32 // Increase as needed. Can be up to 72.
111 int WEP_COUNT;
112 #define WEP_LAST (WEP_FIRST + WEP_COUNT - 1)
113 WepSet WEPSET_ALL;
114 WepSet WEPSET_SUPERWEAPONS;
115
116 // functions:
117 entity get_weaponinfo(int id);
118 string W_FixWeaponOrder(string order, float complete);
119 string W_UndeprecateName(string s);
120 string W_NameWeaponOrder(string order);
121 string W_NumberWeaponOrder(string order);
122 string W_FixWeaponOrder_BuildImpulseList(string o);
123 string W_FixWeaponOrder_AllowIncomplete(string order);
124 string W_FixWeaponOrder_ForceComplete(string order);
125 void W_RandomWeapons(entity e, float n);
126
127 string GetAmmoPicture(.int ammotype);
128
129 #ifdef CSQC
130 .int GetAmmoFieldFromNum(int i);
131 int GetAmmoStat(.int ammotype);
132 #endif
133
134 string W_Sound(string w_snd);
135 string W_Model(string w_mdl);
136
137 // ammo types
138 .int ammo_shells;
139 .int ammo_nails;
140 .int ammo_rockets;
141 .int ammo_cells;
142 .int ammo_plasma;
143 .int ammo_fuel;
144 .int ammo_none;
145
146 // other useful macros
147 #define WEP_ACTION(wpn,wrequest) wpn.weapon_func(wpn, wrequest)
148 #define _WEP_ACTION(wpn,wrequest) WEP_ACTION(get_weaponinfo(wpn), wrequest)
149 #define WEP_AMMO(wpn) (WEP_##wpn.ammo_field) // only used inside weapon files/with direct name, don't duplicate prefix
150 #define WEP_NAME(wpn) ((get_weaponinfo(wpn)).message)
151
152
153 // ======================
154 //  Configuration Macros
155 // ======================
156
157 // create cvars for weapon settings
158 #define WEP_ADD_CVAR_NONE(wepname,name) [[last]] float autocvar_g_balance_##wepname##_##name;
159
160 #define WEP_ADD_CVAR_PRI(wepname,name) WEP_ADD_CVAR_NONE(wepname, primary_##name)
161 #define WEP_ADD_CVAR_SEC(wepname,name) WEP_ADD_CVAR_NONE(wepname, secondary_##name)
162 #define WEP_ADD_CVAR_BOTH(wepname,name) \
163         WEP_ADD_CVAR_PRI(wepname, name) \
164         WEP_ADD_CVAR_SEC(wepname, name)
165
166 #define WEP_ADD_CVAR(wepid,wepname,mode,name) WEP_ADD_CVAR_##mode(wepname, name)
167
168 // create properties for weapon settings
169 #define WEP_ADD_PROP(wepid,wepname,type,prop,name) \
170         .type prop; \
171         [[last]] type autocvar_g_balance_##wepname##_##name;
172
173 // read cvars from weapon settings
174 #define WEP_CVAR(wepname,name) autocvar_g_balance_##wepname##_##name
175 #define WEP_CVAR_PRI(wepname,name) WEP_CVAR(wepname, primary_##name)
176 #define WEP_CVAR_SEC(wepname,name) WEP_CVAR(wepname, secondary_##name)
177 #define WEP_CVAR_BOTH(wepname,isprimary,name) ((isprimary) ? WEP_CVAR_PRI(wepname, name) : WEP_CVAR_SEC(wepname, name))
178
179 // set initialization values for weapon settings
180 #define WEP_SKIP_CVAR(unuseda,unusedb,unusedc,unusedd) /* skip cvars */
181 #define WEP_SET_PROP(wepid,wepname,type,prop,name) WEP_##wepid.prop = autocvar_g_balance_##wepname##_##name;
182
183
184 // =====================
185 //  Weapon Registration
186 // =====================
187
188 /** fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A" */
189 CLASS(Weapon, Object)
190         ATTRIB(Weapon, m_id, int, 0)
191     /**
192      * M: WEP_id    : WEP_...
193      * you can recognize dummies when this == 0
194      */
195     ATTRIB(Weapon, weapon, int, 0);
196     /** A: WEPSET_id : WEPSET_... */
197     ATTRIB(Weapon, weapons, WepSet, '0 0 0');
198     /** M: function  : w_... */
199     METHOD(Weapon, weapon_func, bool(entity this, int req)) { return w_new(this, req); }
200     /** M: ammotype  : main ammo field */
201     ATTRIB(Weapon, ammo_field, .int, ammo_none);
202     /** M: impulse   : weapon impulse */
203     ATTRIB(Weapon, impulse, int, -1);
204     /** M: flags     : WEPSPAWNFLAG_... combined */
205     ATTRIB(Weapon, spawnflags, int, 0);
206     /** M: rating    : bot weapon priority */
207     ATTRIB(Weapon, bot_pickupbasevalue, float, 0);
208     /** M: color     : waypointsprite color */
209     ATTRIB(Weapon, wpcolor, vector, '0 0 0');
210     /** A: wpn-id    : wpn- sprite name */
211     ATTRIB(Weapon, wpmodel, string, "");
212     /** M: modelname : name of model (without g_ v_ or h_ prefixes) */
213     ATTRIB(Weapon, mdl, string, "");
214     /** M: model MDL_id_ITEM */
215     ATTRIB(Weapon, m_model, entity, NULL);
216     /** M: crosshair : per-weapon crosshair: "CrosshairImage Size" */
217     ATTRIB(Weapon, w_crosshair, string, "gfx/crosshair1");
218     /** A: crosshair : per-weapon crosshair size (argument two of "crosshair" field) */
219     ATTRIB(Weapon, w_crosshair_size, float, 1);
220     /** M: wepimg    : "weaponfoobar" side view image file of weapon. WEAPONTODO: Move out of skin files, move to common files */
221     ATTRIB(Weapon, model2, string, "");
222     /** M: refname   : reference name name */
223     ATTRIB(Weapon, netname, string, "");
224     /** M: wepname   : human readable name */
225     ATTRIB(Weapon, message, string, "AOL CD Thrower");
226
227         METHOD(Weapon, display, void(entity this, void(string name, string icon) returns)) {
228                 returns(this.message, this.model2 ? sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.model2) : string_null);
229         }
230
231         CONSTRUCTOR(Weapon,
232                 bool(entity this, int req) function,
233                 .int ammotype,
234                 int i,
235                 int weapontype,
236                 float pickupbasevalue,
237                 vector clr,
238                 string modelname,
239                 entity m,
240                 string crosshair,
241                 string wepimg,
242                 string refname,
243                 string wepname
244         ) {
245                 CONSTRUCT(Weapon);
246                 this.weapon_func = function;
247                 this.ammo_field = ammotype;
248                 this.impulse = i;
249                 this.spawnflags = weapontype;
250                 this.bot_pickupbasevalue = pickupbasevalue;
251                 this.wpcolor = clr;
252                 this.mdl = modelname;
253                 this.m_model = m;
254                 this.w_crosshair = strzone(car(crosshair));
255                 string s = cdr(crosshair);
256                 this.w_crosshair_size = ((s != "") ? stof(s) : 1); // so that we can scale the crosshair from code (for compat)
257                 this.model2 = strzone(wepimg);
258                 this.netname = refname;
259                 this.message = wepname;
260         }
261         void register_weapon(entity this, int id, WepSet bit)
262         {
263                 this.weapon = id;
264                 this.weapons = bit;
265                 this.wpmodel = strzone(strcat("wpn-", ftos(id)));
266                 #ifdef CSQC
267                 this.weapon_func(this, WR_INIT);
268                 #endif
269         }
270 ENDCLASS(Weapon)
271
272 CLASS(OffhandWeapon, Object)
273     METHOD(OffhandWeapon, offhand_think, void(OffhandWeapon this, entity player, bool key_pressed)) {}
274 ENDCLASS(OffhandWeapon)
275
276 #ifdef SVQC
277 .OffhandWeapon offhand;
278 #endif
279
280 void RegisterWeapons();
281 REGISTER_REGISTRY(RegisterWeapons)
282 entity weapon_info[WEP_MAXCOUNT], weapon_info_first, weapon_info_last;
283 entity dummy_weapon_info;
284
285 #define REGISTER_WEAPON(...) EVAL(OVERLOAD(REGISTER_WEAPON, __VA_ARGS__))
286
287 #define REGISTER_WEAPON_2(id, inst) \
288         WepSet WEPSET_##id; \
289         REGISTER(RegisterWeapons, WEP, weapon_info, WEP_COUNT, id, m_id, inst) { \
290                 this.m_id++; \
291                 WEPSET_ALL |= (WEPSET_##id = WepSet_FromWeapon(this.m_id)); \
292                 if ((this.spawnflags) & WEP_FLAG_SUPERWEAPON) WEPSET_SUPERWEAPONS |= WEPSET_##id; \
293                 register_weapon(this, this.m_id, WEPSET_##id); \
294                 localcmd(sprintf("alias weapon_%s \"impulse %d\"\n", this.netname, 230 + this.m_id - 1)); \
295         } \
296         REGISTER_INIT(WEP, id)
297
298 #define _REGISTER_WEAPON(id, function, ammotype, impulse, flags, rating, color, modelname, mdl, crosshair, wepimg, refname, wepname) \
299         REGISTER_WEAPON_2(id, NEW(Weapon, function, ammotype, impulse, flags, rating, color, modelname, mdl, crosshair, wepimg, refname, wepname))
300
301 #ifndef MENUQC
302         #define REGISTER_WEAPON_13(id, function, ammotype, impulse, flags, rating, color, modelname, mdl, crosshair, wepimg, refname, wepname) \
303         bool function(entity this, int); \
304         _REGISTER_WEAPON(id, function, ammotype, impulse, flags, rating, color, modelname, mdl, crosshair, wepimg, refname, wepname)
305 #else
306         #define REGISTER_WEAPON_13(id, function, ammotype, impulse, flags, rating, color, modelname, mdl, crosshair, wepimg, refname, wepname) \
307                 _REGISTER_WEAPON(id, w_new,   ammotype, impulse, flags, rating, color, modelname, NULL, crosshair, wepimg, refname, wepname)
308 #endif
309
310 #include "all.inc"
311
312 #undef WEP_ADD_CVAR_MO_PRI
313 #undef WEP_ADD_CVAR_MO_SEC
314 #undef WEP_ADD_CVAR_MO_BOTH
315 #undef WEP_ADD_CVAR_MO_NONE
316 #undef WEP_ADD_CVAR
317 #undef WEP_ADD_PROP
318 void register_weapons_done();
319 ACCUMULATE_FUNCTION(RegisterWeapons, register_weapons_done)
320 #endif