]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/triggers.qc
Limit what's sent for each trigger with a couple of bitflags
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / triggers.qc
1 #include "triggers.qh"
2 void SUB_DontUseTargets(entity this, entity actor, entity trigger) { }
3
4 void SUB_UseTargets(entity this, entity actor, entity trigger);
5
6 void DelayThink(entity this)
7 {
8         SUB_UseTargets (this, this.enemy, NULL);
9         delete(this);
10 }
11
12 void FixSize(entity e)
13 {
14         e.mins_x = rint(e.mins_x);
15         e.mins_y = rint(e.mins_y);
16         e.mins_z = rint(e.mins_z);
17
18         e.maxs_x = rint(e.maxs_x);
19         e.maxs_y = rint(e.maxs_y);
20         e.maxs_z = rint(e.maxs_z);
21 }
22
23 #ifdef SVQC
24
25 bool autocvar_g_triggers_debug = true;
26
27 void trigger_init(entity this)
28 {
29         string m = this.model;
30         EXACTTRIGGER_INIT;
31         if(autocvar_g_triggers_debug)
32         {
33                 if(m != "")
34                 {
35                         precache_model(m);
36                         _setmodel(this, m); // no precision needed
37                 }
38                 setorigin(this, this.origin);
39                 if(this.scale)
40                         setsize(this, this.mins * this.scale, this.maxs * this.scale);
41                 else
42                         setsize(this, this.mins, this.maxs);
43         }
44
45         if(autocvar_g_triggers_debug)
46                 BITSET_ASSIGN(this.effects, EF_NODEPTHTEST);
47 }
48
49 void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc)
50 {
51         setSendEntity(this, sendfunc);
52         this.SendFlags = 0xFFFFFF;
53 }
54
55 void trigger_common_write(entity this, bool withtarget)
56 {
57         int f = 0;
58         if(this.warpzone_isboxy)
59                 BITSET_ASSIGN(f, 1);
60         if(this.origin != '0 0 0')
61                 BITSET_ASSIGN(f, 4);
62         if(this.movedir != '0 0 0')
63                 BITSET_ASSIGN(f, 8);
64         if(this.angles != '0 0 0')
65                 BITSET_ASSIGN(f, 16);
66         WriteByte(MSG_ENTITY, f);
67
68         if(withtarget)
69         {
70                 // probably some way to clean this up...
71                 int targbits = 0;
72                 if(this.target && this.target != "") targbits |= BIT(0);
73                 if(this.target2 && this.target2 != "") targbits |= BIT(1);
74                 if(this.target3 && this.target3 != "") targbits |= BIT(2);
75                 if(this.target4 && this.target4 != "") targbits |= BIT(3);
76                 if(this.targetname && this.targetname != "") targbits |= BIT(4);
77                 if(this.killtarget && this.killtarget != "") targbits |= BIT(5);
78
79                 WriteByte(MSG_ENTITY, targbits);
80
81                 if(targbits & BIT(0))
82                         WriteString(MSG_ENTITY, this.target);
83                 if(targbits & BIT(1))
84                         WriteString(MSG_ENTITY, this.target2);
85                 if(targbits & BIT(2))
86                         WriteString(MSG_ENTITY, this.target3);
87                 if(targbits & BIT(3))
88                         WriteString(MSG_ENTITY, this.target4);
89                 if(targbits & BIT(4))
90                         WriteString(MSG_ENTITY, this.targetname);
91                 if(targbits & BIT(5))
92                         WriteString(MSG_ENTITY, this.killtarget);
93         }
94
95         if(f & 4)
96                 WriteVector(MSG_ENTITY, this.origin);
97
98         if(f & 8)
99                 WriteVector(MSG_ENTITY, this.movedir);
100
101         if(f & 16)
102                 WriteVector(MSG_ENTITY, this.angles);
103
104         WriteShort(MSG_ENTITY, this.modelindex);
105         WriteCoord(MSG_ENTITY, this.mins.x);
106         WriteCoord(MSG_ENTITY, this.mins.y);
107         WriteCoord(MSG_ENTITY, this.mins.z);
108         WriteCoord(MSG_ENTITY, this.maxs.x);
109         WriteCoord(MSG_ENTITY, this.maxs.y);
110         WriteCoord(MSG_ENTITY, this.maxs.z);
111         WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255));
112 }
113
114 #elif defined(CSQC)
115
116 void trigger_common_read(entity this, bool withtarget)
117 {
118         int f = ReadByte();
119         this.warpzone_isboxy = (f & 1);
120
121         if(withtarget)
122         {
123                 if(this.target) { strunzone(this.target); }
124                 if(this.target2) { strunzone(this.target2); }
125                 if(this.target3) { strunzone(this.target3); }
126                 if(this.target4) { strunzone(this.target4); }
127                 if(this.targetname) { strunzone(this.targetname); }
128                 if(this.killtarget) { strunzone(this.killtarget); }
129
130                 int targbits = ReadByte();
131
132                 #define X(xs,b) MACRO_BEGIN { \
133                         if(targbits & BIT(b)) \
134                                 xs = strzone(ReadString()); \
135                         else \
136                                 xs = string_null; \
137                 } MACRO_END
138
139                 X(this.target, 0);
140                 X(this.target2, 1);
141                 X(this.target3, 2);
142                 X(this.target4, 3);
143                 X(this.targetname, 4);
144                 X(this.killtarget, 5);
145                 #undef X
146         }
147
148         if(f & 4)
149                 this.origin = ReadVector();
150         else
151                 this.origin = '0 0 0';
152         setorigin(this, this.origin);
153
154         if(f & 8)
155                 this.movedir = ReadVector();
156         else
157                 this.movedir = '0 0 0';
158
159         if(f & 16)
160                 this.angles = ReadVector();
161         else
162                 this.angles = '0 0 0';
163
164         this.modelindex = ReadShort();
165         this.mins_x = ReadCoord();
166         this.mins_y = ReadCoord();
167         this.mins_z = ReadCoord();
168         this.maxs_x = ReadCoord();
169         this.maxs_y = ReadCoord();
170         this.maxs_z = ReadCoord();
171         this.scale = ReadByte() / 16;
172         setsize(this, this.mins, this.maxs);
173 }
174
175 void trigger_remove_generic(entity this)
176 {
177         if(this.target) { strunzone(this.target); }
178         this.target = string_null;
179
180         if(this.target2) { strunzone(this.target2); }
181         this.target2 = string_null;
182
183         if(this.target3) { strunzone(this.target3); }
184         this.target3 = string_null;
185
186         if(this.target4) { strunzone(this.target4); }
187         this.target4 = string_null;
188
189         if(this.targetname) { strunzone(this.targetname); }
190         this.target = string_null;
191
192         if(this.killtarget) { strunzone(this.killtarget); }
193         this.killtarget = string_null;
194 }
195 #endif
196
197
198 /*
199 ==============================
200 SUB_UseTargets
201
202 the global "activator" should be set to the entity that initiated the firing.
203
204 If this.delay is set, a DelayedUse entity will be created that will actually
205 do the SUB_UseTargets after that many seconds have passed.
206
207 Centerprints any this.message to the activator.
208
209 Removes all entities with a targetname that match this.killtarget,
210 and removes them, so some events can remove other triggers.
211
212 Search for (string)targetname in all entities that
213 match (string)this.target and call their .use function
214
215 ==============================
216 */
217
218 void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse)
219 {
220 //
221 // check for a delay
222 //
223         if (this.delay)
224         {
225         // create a temp object to fire at a later time
226                 entity t = new(DelayedUse);
227                 t.nextthink = time + this.delay;
228                 setthink(t, DelayThink);
229                 t.enemy = actor;
230                 t.message = this.message;
231                 t.killtarget = this.killtarget;
232                 t.target = this.target;
233                 t.target2 = this.target2;
234                 t.target3 = this.target3;
235                 t.target4 = this.target4;
236                 t.antiwall_flag = this.antiwall_flag;
237                 return;
238         }
239
240         string s;
241
242 //
243 // print the message
244 //
245 #ifdef SVQC
246         if(this)
247         if(IS_PLAYER(actor) && this.message != "")
248         if(IS_REAL_CLIENT(actor))
249         {
250                 centerprint(actor, this.message);
251                 if (this.noise == "")
252                         play2(actor, SND(TALK));
253         }
254
255 //
256 // kill the killtagets
257 //
258         s = this.killtarget;
259         if (s != "")
260         {
261                 for(entity t = NULL; (t = find(t, targetname, s)); )
262                         delete(t);
263         }
264 #endif
265
266 //
267 // fire targets
268 //
269
270         if(this.target_random)
271                 RandomSelection_Init();
272
273         for(int i = 0; i < 4; ++i)
274         {
275                 switch(i)
276                 {
277                         default:
278                         case 0: s = this.target; break;
279                         case 1: s = this.target2; break;
280                         case 2: s = this.target3; break;
281                         case 3: s = this.target4; break;
282                 }
283                 if (s != "")
284                 {
285                         // Flag to set func_clientwall state
286                         // 1 == deactivate, 2 == activate, 0 == do nothing
287                         int aw_flag = this.antiwall_flag;
288                         for(entity t = NULL; (t = find(t, targetname, s)); )
289                         {
290                                 if(t.use && (t.sub_target_used != time || !preventReuse))
291                                 {
292                                         if(this.target_random)
293                                         {
294                                                 RandomSelection_AddEnt(t, 1, 0);
295                                         }
296                                         else
297                                         {
298                                                 if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary")
299                                                         t.antiwall_flag = aw_flag;
300
301                                                 t.use(t, actor, this);
302                                                 if(preventReuse)
303                                                         t.sub_target_used = time;
304                                         }
305                                 }
306                         }
307                 }
308         }
309
310         if(this.target_random && RandomSelection_chosen_ent)
311         {
312                 RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
313                 if(preventReuse)
314                         RandomSelection_chosen_ent.sub_target_used = time;
315         }
316 }
317
318 void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); }
319 void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); }