Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
quake3.qc
Go to the documentation of this file.
1#include "quake3.qh"
2
12#include <common/stats.qh>
15#include <server/client.qh>
16#include <server/items/items.qh>
18#include <server/world.qh>
19
20/***********************
21 * QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons
22 ***********************
23
24 * Map entities NOT handled in this file:
25 holdable_invulnerability Q3TA buffs/all.inc
26 holdable_kamikaze Q3TA buffs/all.inc
27 holdable_teleporter Q3A buffs/all.inc
28 item_ammoregen Q3TA buffs/all.inc
29 item_doubler Q3TA buffs/all.inc
30 item_guard Q3TA buffs/all.inc
31 item_scout Q3TA buffs/all.inc
32 item_armor_jacket CPMA quake2.qc
33 item_flight Q3A buffs/all.inc
34 item_health Q3A quake.qc
35 item_health_large Q3A items/spawning.qc
36 item_health_small Q3A health.qh
37 item_health_mega Q3A health.qh
38 item_regen Q3A buffs/all.inc
39 item_key_gold QL keys.qc
40 item_key_silver QL keys.qc
41 item_key_master QL keys.qc
42 weapon_machinegun Q3A machinegun.qh
43 weapon_grenadelauncher Q3A mortar.qh
44 weapon_rocketlauncher Q3A devastator.qh
45 * CTF spawnfuncs in sv_ctf.qc
46
47 NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG
48 Guns that are very different may need .count scaling beyond that of GetAmmoConsumption().
49*/
50
51// Shotgun <-> Machine Gun
52// We don't want the swap on defrag maps (some of which have a valid .arena file as well as the .defi).
53// In Q3 defaults SG has 10 shells and MG has 100 bullets, Xonotic's GetAmmoConsumption() will return 1 for both
54// so .count will be out by 10x, however Xonotic has a stronger MG than Q3 so using 8x for SG->MG.
55SPAWNFUNC_Q3(weapon_shotgun, ammo_shells,
56 q3compat == Q3COMPAT_ARENA ? WEP_MACHINEGUN : WEP_SHOTGUN,
57 q3compat == Q3COMPAT_ARENA ? 8 : 1)
58SPAWNFUNC_Q3(weapon_machinegun, ammo_bullets,
59 q3compat == Q3COMPAT_ARENA ? WEP_SHOTGUN : WEP_MACHINEGUN,
60 q3compat == Q3COMPAT_ARENA ? 0.1 : 1)
61
62// Grenade Launcher -> Mortar
63SPAWNFUNC_Q3(weapon_grenadelauncher, ammo_grenades, WEP_MORTAR)
64
65// Team Arena Proximity Launcher -> Mine Layer
66SPAWNFUNC_Q3(weapon_prox_launcher, ammo_mines, WEP_MINE_LAYER)
67
68// Team Arena Chain Gun -> HLAC
69SPAWNFUNC_Q3(weapon_chaingun, ammo_belt, WEP_HLAC)
70
71// Quake Live Heavy MachineGun -> HLAC
72SPAWNFUNC_Q3(weapon_hmg, ammo_hmg, WEP_HLAC)
73
74// Team Arena Nail Gun -> Crylink || Quake Nail Gun -> Electro
75SPAWNFUNC_Q3(weapon_nailgun, ammo_nails, autocvar_sv_mapformat_is_quake3 ? WEP_CRYLINK : WEP_ELECTRO)
76
77// Lightning Gun -> Electro
78// Damage potential of 100 LG bolts (varies between Q3 derivatives) is roughly half that of 100 electro cells,
79// Xonotic's GetAmmoConsumption() will return 4 so we need a 1/8 .count scale here.
80SPAWNFUNC_Q3(weapon_lightning, ammo_lightning, WEP_ELECTRO, 0.125)
81
82// Plasma Gun -> Hagar
83SPAWNFUNC_Q3(weapon_plasmagun, ammo_cells, WEP_HAGAR)
84
85// Rail Gun -> Vortex
86SPAWNFUNC_Q3(weapon_railgun, ammo_slugs, WEP_VORTEX)
87
88// BFG -> Crylink || Fireball
89SPAWNFUNC_Q3(weapon_bfg, ammo_bfg, cvar_string("g_mod_balance") == "XDF" ? WEP_CRYLINK : WEP_FIREBALL)
90 // NB: WEP_FIREBALL has no ammo_type field so ammo_bfg is deleted by SPAWNFUNC_BODY
91
92// Grappling Hook
93SPAWNFUNC_WEAPON(weapon_grapplinghook, WEP_HOOK)
94 // NB: in Q3 this doesn't use ammo
95
96// Rocket Launcher -> Devastator
97SPAWNFUNC_Q3(weapon_rocketlauncher, ammo_rockets, WEP_DEVASTATOR)
98
99// Gauntlet -> Tuba
100SPAWNFUNC_ITEM(weapon_gauntlet, WEP_TUBA)
101
102// Armor
103SPAWNFUNC_ITEM(item_armor_body, ITEM_ArmorMega)
104SPAWNFUNC_ITEM(item_armor_combat, ITEM_ArmorBig)
105SPAWNFUNC_ITEM(item_armor_shard, ITEM_ArmorSmall)
106SPAWNFUNC_ITEM(item_armor_green, ITEM_ArmorMedium) // CCTF
107
108// Powerups
109SPAWNFUNC_ITEM(item_quad, ITEM_Strength)
110SPAWNFUNC_ITEM(item_enviro, ITEM_Shield)
111SPAWNFUNC_ITEM(item_haste, ITEM_Speed)
112SPAWNFUNC_ITEM(item_invis, ITEM_Invisibility)
113
114// medkit -> armor (we have no holdables)
115SPAWNFUNC_ITEM(holdable_medkit, ITEM_ArmorBig)
116
117
118// weapon remove ent from df
119void target_init_use(entity this, entity actor, entity trigger)
120{
121 if (!(this.spawnflags & 1))
122 {
124 actor.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot;
125 }
126
127 if (!(this.spawnflags & 2))
128 {
129 SetResource(actor, RES_HEALTH, start_health);
130 actor.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot;
131 actor.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
132 }
133
134 if (!(this.spawnflags & 4))
135 {
136 if(this.spawnflags & 32) // spawn with only melee
137 {
138 SetResource(actor, RES_SHELLS, 0);
139 SetResource(actor, RES_BULLETS, 0);
140 SetResource(actor, RES_ROCKETS, 0);
141 SetResource(actor, RES_CELLS, 0);
142 SetResource(actor, RES_FUEL, 0);
143
144 STAT(WEAPONS, actor) = WEPSET(SHOTGUN);
145 }
146 else
147 {
148 SetResource(actor, RES_SHELLS, start_ammo_shells);
149 SetResource(actor, RES_BULLETS, start_ammo_nails);
150 SetResource(actor, RES_ROCKETS, start_ammo_rockets);
151 SetResource(actor, RES_CELLS, start_ammo_cells);
152 SetResource(actor, RES_FUEL, start_ammo_fuel);
153
154 STAT(WEAPONS, actor) = start_weapons;
155 }
156 }
157
158 if (!(this.spawnflags & 8))
159 {
160 FOREACH(StatusEffects, it.instanceOfPowerupStatusEffect,
161 {
162 it.m_remove(it, actor, STATUSEFFECT_REMOVE_NORMAL);
163 });
164 entity heldbuff = buff_FirstFromFlags(actor);
165 if(heldbuff) // TODO: make a dropbuffs function to handle this
166 {
167 int buffid = heldbuff.m_id;
168 Send_Notification(NOTIF_ONE, actor, MSG_MULTI, ITEM_BUFF_DROP, buffid);
169 sound(actor, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
170 if(!IS_INDEPENDENT_PLAYER(actor))
171 Send_Notification(NOTIF_ALL_EXCEPT, actor, MSG_INFO, INFO_ITEM_BUFF_LOST, actor.netname, buffid);
173 }
174 }
175
176 if (!(this.spawnflags & 16))
177 {
178 // We don't have holdables.
179 }
180
181 SUB_UseTargets(this, actor, trigger);
182}
183
184spawnfunc(target_init)
185{
186 this.use = target_init_use;
187}
188
189
190void score_use(entity this, entity actor, entity trigger)
191{
192 if(!IS_PLAYER(actor))
193 return;
194 actor.fragsfilter_cnt += this.count;
195}
196spawnfunc(target_score)
197{
198 if(!g_cts) { delete(this); return; }
199
200 if(!this.count)
201 this.count = 1;
202 this.use = score_use;
203}
204
205#define FRAGSFILTER_REMOVER BIT(0)
206#define FRAGSFILTER_RUNONCE BIT(1) // introduced but now unused by q3df
207#define FRAGSFILTER_SILENT BIT(2)
208#define FRAGSFILTER_RESET BIT(3)
209
210void fragsfilter_use(entity this, entity actor, entity trigger)
211{
212 if(!IS_PLAYER(actor))
213 return;
214 if(actor.fragsfilter_cnt >= this.frags)
215 {
217 actor.fragsfilter_cnt = 0;
218 else if(this.spawnflags & FRAGSFILTER_REMOVER)
219 actor.fragsfilter_cnt -= this.frags;
220 SUB_UseTargets(this, actor, trigger);
221 }
222 else if(!(this.spawnflags & FRAGSFILTER_SILENT))
223 {
224 int req_frags = this.frags - actor.fragsfilter_cnt;
225 centerprint(actor, sprintf("%d more frag%s needed", req_frags, req_frags > 1 ? "s" : ""));
226 play2(actor, SND(TALK));
227 }
228}
229spawnfunc(target_fragsFilter)
230{
231 if(!g_cts) { delete(this); return; }
232
233 if(!this.frags)
234 this.frags = 1;
235 this.use = fragsfilter_use;
236}
237
238#define PRINT_REDTEAM BIT(0) // Q3 only, not used in Q3DF
239#define PRINT_BLUETEAM BIT(1) // Q3 only, not used in Q3DF
240#define PRINT_PRIVATE BIT(2) // Q3 only, not used in Q3DF
241#define PRINT_BROADCAST BIT(3) // Q3DF only, default behavior in Q3
242
243void target_print_message(entity this, entity actor)
244{
245 centerprint(actor, this.message);
246 play2(actor, SND(TALK));
247}
248
249void target_print_use(entity this, entity actor, entity trigger)
250{
251 if(!IS_PLAYER(actor))
252 return;
253
254 if(this.message == "")
255 return;
256
257 bool priv, red, blue;
258 if(!(q3compat & Q3COMPAT_DEFI)) // Q3 spawnflags
259 {
260 priv = boolean(this.spawnflags & PRINT_PRIVATE);
261 red = boolean(this.spawnflags & PRINT_REDTEAM);
262 blue = boolean(this.spawnflags & PRINT_BLUETEAM);
263 }
264 else // Q3DF spawnflags
265 {
266 priv = !boolean(this.spawnflags & PRINT_BROADCAST);
267 red = blue = false;
268 }
269
270 if(priv)
271 {
272 target_print_message(this, actor);
273 }
274 else
275 {
276 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && ((!red && !blue) || (red && it.team == NUM_TEAM_1) || (blue && it.team == NUM_TEAM_2)), target_print_message(this, it));
277 }
278}
279
280/*
281 * ENTITY PARAMETERS:
282 *
283 * message: text string to print on screen.
284 * targetname: the activating trigger points to this.
285 */
286spawnfunc(target_print)
287{
288 this.use = target_print_use;
289}
290
291// target_smallprint, Q3DF only
292spawnfunc(target_smallprint)
293{
294 spawnfunc_target_print(this);
295}
296
297.string gametype;
300{
301 // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY)
302
303 if (!this.classname)
304 return true;
305
306 // DeFRaG mappers use "notcpm" or "notvq3" to disable an entity in CPM or VQ3 physics
307 // Xonotic is usually played with a CPM-based physics so we default to CPM mode
308 if(cvar_string("g_mod_physics") == "Q3")
309 {
310 if(stof(GetField_fullspawndata(this, "notvq3", false)))
311 return true;
312 }
313 else if(stof(GetField_fullspawndata(this, "notcpm", false)))
314 return true;
315
316 // Q3 mappers use "notq3a" or "notta" to disable an entity in Q3A or Q3TA
317 // Xonotic has ~equivalent features to Team Arena
318 if(stof(GetField_fullspawndata(this, "notta", false)))
319 return true;
320
321 // FIXME: singleplayer does not use maxclients 1 as that would prevent bots,
322 // this is the case in Q3 also, it uses another method to block clients.
323 // Only accessible in VQ3, via the `spmap` command.
324 if(stof(GetField_fullspawndata(this, "notsingle", false)))
325 if(maxclients == 1 && IS_GAMETYPE(DEATHMATCH))
326 return true;
327
328 if(stof(GetField_fullspawndata(this, "notteam", false)))
329 if(teamplay)
330 return true;
331
332 if(stof(GetField_fullspawndata(this, "notfree", false)))
333 if(!teamplay)
334 return true;
335
336 if(this.gametype || this.not_gametype)
337 {
338 // Q3 checks these with strstr(): case-sensitive, no gametype can be a substring of another,
339 // any separator is allowed (conventions are: spaces, commas, or commas with spaces).
340 // QL's entities.def says they're space delineated.
341
342 // Q3 gametype entity fields: ffa tournament single team ctf oneflag obelisk harvester (game/g_spawn.c)
343 // Q3 arena file 'type' key: ffa tourney ctf oneflag overload harvester (ui/ui_gameinfo.c)
344
345 // QL gametype entity fields: ffa duel tdm ca ft rr ctf ad dom har 1f race ob
346 // QL arena file 'type' key: ffa duel tdm ca ft rr ctf ad dom har oneflag race
347
348 string gametypename_q3, gametypename_ql;
349
350 // One of these will apply if our gametype has no Q3/QL equivalent
351 if(teamplay)
352 {
353 gametypename_q3 = "team";
354 gametypename_ql = "tdm";
355 }
356 else
357 gametypename_q3 = gametypename_ql = "ffa";
358
359 if(g_ctf)
360 {
361 if (cvar("g_ctf_oneflag"))
362 {
363 gametypename_q3 = "oneflag";
364 gametypename_ql = "1f";
365 }
366 else
367 gametypename_q3 = gametypename_ql = "ctf";
368 }
369 else if(g_duel)
370 {
371 gametypename_q3 = "tournament";
372 gametypename_ql = "duel";
373 }
374 else if(IS_GAMETYPE(DEATHMATCH) && maxclients == 1)
375 gametypename_q3 = "single";
376 else if(g_ca)
377 gametypename_ql = "ql";
378 else if(IS_GAMETYPE(FREEZETAG))
379 gametypename_ql = "ft";
380 else if(IS_GAMETYPE(DOMINATION))
381 gametypename_ql = "dom";
382 else if(g_race || g_cts)
383 gametypename_ql = "race";
384
385 if(this.gametype)
386 if(strstrofs(this.gametype, gametypename_q3, 0) < 0
387 && strstrofs(this.gametype, gametypename_ql, 0) < 0)
388 return true;
389
390 // Only supported by QL
391 if(strstrofs(this.not_gametype, gametypename_ql, 0) >= 0)
392 return true;
393 }
394
395 return false;
396}
#define boolean(value)
Definition bool.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
void SetResource(entity e, Resource res_type, float amount)
Sets the current amount of resource the given entity will have.
#define g_ca
Definition clanarena.qh:51
string message
Definition powerups.qc:19
float count
Definition powerups.qc:22
entity gametype
Definition main.qh:43
#define g_race
Definition race.qh:48
int spawnflags
Definition ammo.qh:15
#define SPAWNFUNC_ITEM(name, item)
Definition item.qh:106
#define IS_PLAYER(s)
Definition player.qh:242
string classname
float maxclients
float time
const float ATTN_NORM
#define use
#define g_ctf
Definition ctf.qh:39
#define g_cts
Definition cts.qh:36
#define strstrofs
#define g_duel
Definition duel.qh:32
RES_ARMOR
Definition ent_cs.qc:130
int frags
Definition ent_cs.qh:71
Header file that describes the functions related to game items.
#define FOREACH(list, cond, body)
Definition iter.qh:19
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition triggers.qc:344
#define STAT(...)
Definition stats.qh:82
float stof(string val,...)
float cvar(string name)
const string cvar_string(string name)
void centerprint(string text,...)
@ STATUSEFFECT_REMOVE_NORMAL
Effect is being removed by a function, calls regular removal mechanics.
Definition all.qh:28
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
#define PRINT_BLUETEAM
ammo_bullets
Definition quake3.qc:58
bool DoesQ3ARemoveThisEntity(entity this)
Definition quake3.qc:299
#define PRINT_PRIVATE
#define FRAGSFILTER_SILENT
#define PRINT_BROADCAST
#define FRAGSFILTER_RESET
q3compat
Definition quake3.qc:59
#define FRAGSFILTER_REMOVER
#define PRINT_REDTEAM
string not_gametype
Definition quake3.qc:298
#define Q3COMPAT_ARENA
Definition quake3.qh:4
#define Q3COMPAT_DEFI
Definition quake3.qh:5
#define SPAWNFUNC_Q3(weapon_classname, ammo_classname, xonwep,...)
Definition quake3.qh:36
int ammo_nails
Definition resources.qh:16
int ammo_cells
Definition resources.qh:18
int ammo_shells
Definition resources.qh:15
int ammo_rockets
Definition resources.qh:17
entity this
Definition self.qh:72
#define IS_INDEPENDENT_PLAYER(e)
Definition client.qh:312
string GetField_fullspawndata(entity e, string fieldname, bool vfspath)
Retrieves the value of a map entity field from fullspawndata.
Definition main.qc:451
const int CH_TRIGGER
Definition sound.qh:12
const float VOL_BASE
Definition sound.qh:36
#define sound(e, c, s, v, a)
Definition sound.qh:52
void play2(entity e, string filename)
Definition all.qc:116
#define SND(id)
Definition all.qh:35
#define spawnfunc(id)
Definition spawnfunc.qh:96
entity buff_FirstFromFlags(entity actor)
Definition sv_buffs.qc:291
void buff_RemoveAll(entity actor, int removal_type)
Definition sv_buffs.qc:282
Header file that describes the resource system.
float autocvar_g_balance_pause_armor_rot
float autocvar_g_balance_pause_health_rot
float autocvar_g_balance_pause_health_regen
const int NUM_TEAM_2
Definition teams.qh:14
bool teamplay
Definition teams.qh:59
const int NUM_TEAM_1
Definition teams.qh:13
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
#define WEPSET(id)
Definition all.qh:45
#define SPAWNFUNC_WEAPON(name, weapon)
Definition weapon.qh:150
WepSet start_weapons
Definition world.qh:80
float start_ammo_shells
Definition world.qh:84
float start_ammo_fuel
Definition world.qh:88
bool autocvar_sv_mapformat_is_quake3
Definition world.qh:32
float start_ammo_cells
Definition world.qh:87
float start_ammo_rockets
Definition world.qh:86
float start_armorvalue
Definition world.qh:97
float start_health
Definition world.qh:96
float start_ammo_nails
Definition world.qh:85