Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
triggers.qc
Go to the documentation of this file.
1#include "triggers.qh"
2
4{
5 if(e.pushable)
6 return true;
7#ifdef SVQC
8 if(IS_VEHICLE(e))
9 return false;
10 if(e.iscreature)
11 return true;
12 if (ITEM_IS_LOOT(e))
13 {
14 return true;
15 }
16 switch(e.classname)
17 {
18 case "body":
19 return true;
20 case "bullet": // antilagged bullets can't hit this either
21 return false;
22 }
23 if (e.projectiledeathtype)
24 return true;
25#endif
26#ifdef CSQC
27 if(e.flags & FL_PROJECTILE)
28 return true;
29 if(e.isplayermodel)
30 return true;
31#endif
32 return false;
33}
34
35void SUB_UseTargets(entity this, entity actor, entity trigger);
36
38{
39 SUB_UseTargets (this, this.enemy, NULL);
40 delete(this);
41}
42
43#ifdef SVQC
44void generic_setactive(entity this, int act)
45{
46 if(act == ACTIVE_TOGGLE)
47 {
48 if(this.active == ACTIVE_ACTIVE)
49 {
50 this.active = ACTIVE_NOT;
51 }
52 else
53 {
54 this.active = ACTIVE_ACTIVE;
55 }
56 }
57 else
58 {
59 this.active = act;
60 }
61}
62
64{
65 int old_status = this.active;
66 generic_setactive(this, act);
67
68 if (this.active != old_status)
69 {
71 }
72}
73
75{
76 if(this.targetname && this.targetname != "")
77 {
78 if(this.spawnflags & START_ENABLED)
79 {
80 this.active = ACTIVE_ACTIVE;
81 }
82 else
83 {
84 this.active = ACTIVE_NOT;
85 }
86 }
87 else
88 {
89 this.active = ACTIVE_ACTIVE;
90 }
91
93}
94
95// Compatibility with old maps
97{
98 //LOG_WARNF("Entity %s was (de)activated by a trigger, please update map to use relays", this.targetname);
99 this.setactive(this, ACTIVE_TOGGLE);
100}
101
102void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc)
103{
104 setSendEntity(this, sendfunc);
105 this.SendFlags = 0xFFFFFF;
106}
107
108void trigger_common_write(entity this, bool withtarget)
109{
110 int f = 0;
111 if(this.warpzone_isboxy)
112 BITSET_ASSIGN(f, 1);
113 if(this.origin != '0 0 0')
114 BITSET_ASSIGN(f, 4);
115 if(this.movedir != '0 0 0')
116 BITSET_ASSIGN(f, 8);
117 if(this.angles != '0 0 0')
118 BITSET_ASSIGN(f, 16);
120
121 if(withtarget)
122 {
123 // probably some way to clean this up...
124 int targbits = 0;
125 if(this.target && this.target != "") targbits |= BIT(0);
126 if(this.target2 && this.target2 != "") targbits |= BIT(1);
127 if(this.target3 && this.target3 != "") targbits |= BIT(2);
128 if(this.target4 && this.target4 != "") targbits |= BIT(3);
129 if(this.targetname && this.targetname != "") targbits |= BIT(4);
130 if(this.killtarget && this.killtarget != "") targbits |= BIT(5);
131
132 WriteByte(MSG_ENTITY, targbits);
133
134 if(targbits & BIT(0))
136 if(targbits & BIT(1))
138 if(targbits & BIT(2))
140 if(targbits & BIT(3))
142 if(targbits & BIT(4))
144 if(targbits & BIT(5))
146 }
147
148 if(f & 4)
149 WriteVector(MSG_ENTITY, this.origin);
150
151 if(f & 8)
152 WriteVector(MSG_ENTITY, this.movedir);
153
154 if(f & 16)
155 WriteVector(MSG_ENTITY, this.angles);
156
158 WriteVector(MSG_ENTITY, this.mins);
159 WriteVector(MSG_ENTITY, this.maxs);
160 WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255));
161}
162
163#elif defined(CSQC)
164
165void trigger_common_read(entity this, bool withtarget)
166{
167 int f = ReadByte();
168 this.warpzone_isboxy = (f & 1);
169
170 if(withtarget)
171 {
172 strfree(this.target);
173 strfree(this.target2);
174 strfree(this.target3);
175 strfree(this.target4);
176 strfree(this.targetname);
177 strfree(this.killtarget);
178
179 int targbits = ReadByte();
180
181 this.target = ((targbits & BIT(0)) ? strzone(ReadString()) : string_null);
182 this.target2 = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null);
183 this.target3 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null);
184 this.target4 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null);
185 this.targetname = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null);
186 this.killtarget = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null);
187 }
188
189 if(f & 4)
190 this.origin = ReadVector();
191 else
192 this.origin = '0 0 0';
193 setorigin(this, this.origin);
194
195 if(f & 8)
196 this.movedir = ReadVector();
197 else
198 this.movedir = '0 0 0';
199
200 if(f & 16)
201 this.angles = ReadVector();
202 else
203 this.angles = '0 0 0';
204
205 this.modelindex = ReadShort();
206 if (this.modelindex)
207 setmodelindex(this, this.modelindex);
208 this.mins = ReadVector();
209 this.maxs = ReadVector();
210 this.scale = ReadByte() / 16;
211 setsize(this, this.mins, this.maxs);
212}
213
215{
216 strfree(this.target);
217 strfree(this.target2);
218 strfree(this.target3);
219 strfree(this.target4);
220 strfree(this.targetname);
221 strfree(this.killtarget);
222}
223#endif
224
225
226/*
227==============================
228SUB_UseTargets
229
230the global "activator" should be set to the entity that initiated the firing.
231
232If this.delay is set, a DelayedUse entity will be created that will actually
233do the SUB_UseTargets after that many seconds have passed.
234
235Centerprints any this.message to the activator.
236
237Removes all entities with a targetname that match this.killtarget,
238and removes them, so some events can remove other triggers.
239
240Search for (string)targetname in all entities that
241match (string)this.target and call their .use function
242
243==============================
244*/
245
246void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse, int skiptargets)
247{
248//
249// check for a delay
250//
251 if (this.delay)
252 {
253 // create a temp object to fire at a later time
254 entity t = new_pure(DelayedUse);
255 t.nextthink = time + this.delay;
257 t.enemy = actor;
258 t.message = this.message;
259 t.killtarget = this.killtarget;
260 if(!(skiptargets & BIT(1))) t.target = this.target;
261 if(!(skiptargets & BIT(2))) t.target2 = this.target2;
262 if(!(skiptargets & BIT(3))) t.target3 = this.target3;
263 if(!(skiptargets & BIT(4))) t.target4 = this.target4;
264 t.antiwall_flag = this.antiwall_flag;
265 return;
266 }
267
268//
269// print the message
270//
271#ifdef SVQC
272 if(this)
273 if(IS_PLAYER(actor) && this.message != "")
274 if(IS_REAL_CLIENT(actor))
275 {
276 centerprint(actor, this.message);
277 if (this.noise == "")
278 play2(actor, SND(TALK));
279 }
280
281//
282// kill the killtargets
283//
284 string s = this.killtarget;
285 if (s != "")
286 {
287 for(entity t = NULL; (t = find(t, targetname, s)); )
288 delete(t);
289 }
290#endif
291
292//
293// fire targets
294//
295
296 if(this.target_random)
298
299 .string tfield;
300 for(int i = 0; i < 4; ++i)
301 {
302 if(skiptargets & BIT(i + 1))
303 continue;
304 switch(i)
305 {
306 default:
307 case 0: tfield = target; break;
308 case 1: tfield = target2; break;
309 case 2: tfield = target3; break;
310 case 3: tfield = target4; break;
311 }
312 if (this.(tfield) != "")
313 {
314 for(entity t = NULL; (t = find(t, targetname, this.(tfield))); )
315 {
316 if (t.loopdetector & 1) // t is already using its targets!
317 {
318 #ifdef SVQC
320 LOG_WARNF("MAP BUG: target field of %s \"%s\" loops back to %s \"%s\"\n", this.classname, this.targetname, t.classname, t.targetname);
321 #endif
322 this.(tfield) = string_null; // break loop to shallower point so map works as intended without spamming every time
323 }
324 else if(t != this && t.use && (t.sub_target_used != time || !preventReuse))
325 {
326 if(this.target_random)
327 {
328 RandomSelection_AddEnt(t, 1, 0);
329 }
330 else
331 {
332 this.loopdetector |= 1;
333 t.use(t, actor, this);
334 this.loopdetector &= ~1;
335 if(preventReuse)
336 t.sub_target_used = time;
337 }
338 }
339 }
340 }
341 }
342
344 {
345 this.loopdetector |= 1;
347 this.loopdetector &= ~1;
348 if(preventReuse)
349 RandomSelection_chosen_ent.sub_target_used = time;
350 }
351}
352
353void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false, 0); }
354void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true, 0); }
355void SUB_UseTargets_SkipTargets(entity this, entity actor, entity trigger, int skiptargets) { SUB_UseTargets_Ex(this, actor, trigger, false, skiptargets); }
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define ReadString
string message
Definition powerups.qc:19
float delay
Definition items.qc:17
int spawnflags
Definition ammo.qh:15
#define IS_PLAYER(s)
Definition player.qh:242
const int FL_PROJECTILE
Definition constants.qh:77
string classname
float modelindex
vector mins
float time
vector maxs
vector origin
const int ACTIVE_TOGGLE
Definition defs.qh:40
const int ACTIVE_NOT
Definition defs.qh:36
int active
Definition defs.qh:34
const int ACTIVE_ACTIVE
Definition defs.qh:37
const int START_ENABLED
Definition defs.qh:6
const int SF_TRIGGER_UPDATE
Definition defs.qh:23
WriteString(chan, ent.netname)
ent angles
Definition ent_cs.qc:146
WriteByte(chan, ent.angles.y/DEC_FACTOR)
#define ITEM_IS_LOOT(item)
Returns whether the item is loot.
Definition spawning.qh:39
int SendFlags
Definition net.qh:159
const int MSG_ENTITY
Definition net.qh:156
#define ReadVector()
Definition net.qh:349
int ReadByte()
#define BITSET_ASSIGN(a, b)
Definition common.qh:122
float warpzone_isboxy
Definition common.qh:15
#define LOG_WARNF(...)
Definition log.qh:59
vector movedir
Definition viewloc.qh:18
float bound(float min, float value, float max)
entity find(entity start,.string field, string match)
void WriteShort(float data, float dest, float desto)
void centerprint(string text,...)
string strzone(string s)
string string_null
Definition nil.qh:9
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:66
#define NULL
Definition post.qh:14
float scale
Definition projectile.qc:14
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 setSendEntity(e, f)
#define setthink(e, f)
void play2(entity e, string filename)
Definition all.qc:116
#define SND(id)
Definition all.qh:35
#define strfree(this)
Definition string.qh:57
string noise
Definition subs.qh:83
string killtarget
Definition subs.qh:48
string target4
Definition subs.qh:55
string target3
Definition subs.qh:54
float target_random
Definition subs.qh:57
entity enemy
Definition sv_ctf.qh:152
string target2
void generic_netlinked_reset(entity this)
Definition triggers.qc:74
void DelayThink(entity this)
Definition triggers.qc:37
void generic_netlinked_legacy_use(entity this, entity actor, entity trigger)
Definition triggers.qc:96
void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc)
Definition triggers.qc:102
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition triggers.qc:353
void generic_setactive(entity this, int act)
Definition triggers.qc:44
bool isPushable(entity e)
Definition triggers.qc:3
void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse, int skiptargets)
Definition triggers.qc:246
void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger)
Definition triggers.qc:354
void trigger_common_write(entity this, bool withtarget)
Definition triggers.qc:108
void generic_netlinked_setactive(entity this, int act)
Definition triggers.qc:63
void SUB_UseTargets_SkipTargets(entity this, entity actor, entity trigger, int skiptargets)
allow excluding certain .target* fields without needing to nullify them use BIT(1) through BIT(4)
Definition triggers.qc:355
void trigger_remove_generic(entity this)
int loopdetector
bitfield for recursive functions to avoid crashing
Definition triggers.qh:29
float antiwall_flag
Definition triggers.qh:6
string targetname
Definition triggers.qh:56
void trigger_common_read(entity this, bool withtarget)
string target
Definition triggers.qh:55
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define IS_VEHICLE(v)
Definition utils.qh:24
bool autocvar_sv_dedicated
Definition world.qh:41