Properly support team field on trigger_multiple
[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         WriteByte(MSG_ENTITY, f);
63
64         if(withtarget)
65         {
66                 WriteString(MSG_ENTITY, this.target);
67                 WriteString(MSG_ENTITY, this.target2);
68                 WriteString(MSG_ENTITY, this.target3);
69                 WriteString(MSG_ENTITY, this.target4);
70                 WriteString(MSG_ENTITY, this.targetname);
71                 WriteString(MSG_ENTITY, this.killtarget);
72         }
73
74         if(f & 4)
75         {
76                 WriteCoord(MSG_ENTITY, this.origin.x);
77                 WriteCoord(MSG_ENTITY, this.origin.y);
78                 WriteCoord(MSG_ENTITY, this.origin.z);
79         }
80
81         WriteShort(MSG_ENTITY, this.modelindex);
82         WriteCoord(MSG_ENTITY, this.mins.x);
83         WriteCoord(MSG_ENTITY, this.mins.y);
84         WriteCoord(MSG_ENTITY, this.mins.z);
85         WriteCoord(MSG_ENTITY, this.maxs.x);
86         WriteCoord(MSG_ENTITY, this.maxs.y);
87         WriteCoord(MSG_ENTITY, this.maxs.z);
88         WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255));
89
90         WriteCoord(MSG_ENTITY, this.movedir_x);
91         WriteCoord(MSG_ENTITY, this.movedir_y);
92         WriteCoord(MSG_ENTITY, this.movedir_z);
93
94         WriteCoord(MSG_ENTITY, this.angles_x);
95         WriteCoord(MSG_ENTITY, this.angles_y);
96         WriteCoord(MSG_ENTITY, this.angles_z);
97 }
98
99 #elif defined(CSQC)
100
101 void trigger_common_read(entity this, bool withtarget)
102 {
103         int f = ReadByte();
104         this.warpzone_isboxy = (f & 1);
105
106         if(withtarget)
107         {
108                 if(this.target) { strunzone(this.target); }
109                 this.target = strzone(ReadString());
110                 if(this.target2) { strunzone(this.target2); }
111                 this.target2 = strzone(ReadString());
112                 if(this.target3) { strunzone(this.target3); }
113                 this.target3 = strzone(ReadString());
114                 if(this.target4) { strunzone(this.target4); }
115                 this.target4 = strzone(ReadString());
116                 if(this.targetname) { strunzone(this.targetname); }
117                 this.targetname = strzone(ReadString());
118                 if(this.killtarget) { strunzone(this.killtarget); }
119                 this.killtarget = strzone(ReadString());
120         }
121
122         if(f & 4)
123         {
124                 this.origin_x = ReadCoord();
125                 this.origin_y = ReadCoord();
126                 this.origin_z = ReadCoord();
127         }
128         else
129                 this.origin = '0 0 0';
130         setorigin(this, this.origin);
131
132         this.modelindex = ReadShort();
133         this.mins_x = ReadCoord();
134         this.mins_y = ReadCoord();
135         this.mins_z = ReadCoord();
136         this.maxs_x = ReadCoord();
137         this.maxs_y = ReadCoord();
138         this.maxs_z = ReadCoord();
139         this.scale = ReadByte() / 16;
140         setsize(this, this.mins, this.maxs);
141
142         this.movedir_x = ReadCoord();
143         this.movedir_y = ReadCoord();
144         this.movedir_z = ReadCoord();
145
146         this.angles_x = ReadCoord();
147         this.angles_y = ReadCoord();
148         this.angles_z = ReadCoord();
149 }
150
151 void trigger_remove_generic(entity this)
152 {
153         if(this.target) { strunzone(this.target); }
154         this.target = string_null;
155
156         if(this.target2) { strunzone(this.target2); }
157         this.target2 = string_null;
158
159         if(this.target3) { strunzone(this.target3); }
160         this.target3 = string_null;
161
162         if(this.target4) { strunzone(this.target4); }
163         this.target4 = string_null;
164
165         if(this.targetname) { strunzone(this.targetname); }
166         this.target = string_null;
167
168         if(this.killtarget) { strunzone(this.killtarget); }
169         this.killtarget = string_null;
170 }
171 #endif
172
173
174 /*
175 ==============================
176 SUB_UseTargets
177
178 the global "activator" should be set to the entity that initiated the firing.
179
180 If this.delay is set, a DelayedUse entity will be created that will actually
181 do the SUB_UseTargets after that many seconds have passed.
182
183 Centerprints any this.message to the activator.
184
185 Removes all entities with a targetname that match this.killtarget,
186 and removes them, so some events can remove other triggers.
187
188 Search for (string)targetname in all entities that
189 match (string)this.target and call their .use function
190
191 ==============================
192 */
193
194 void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse)
195 {
196 //
197 // check for a delay
198 //
199         if (this.delay)
200         {
201         // create a temp object to fire at a later time
202                 entity t = new(DelayedUse);
203                 t.nextthink = time + this.delay;
204                 setthink(t, DelayThink);
205                 t.enemy = actor;
206                 t.message = this.message;
207                 t.killtarget = this.killtarget;
208                 t.target = this.target;
209                 t.target2 = this.target2;
210                 t.target3 = this.target3;
211                 t.target4 = this.target4;
212                 t.antiwall_flag = this.antiwall_flag;
213                 return;
214         }
215
216         string s;
217
218 //
219 // print the message
220 //
221 #ifdef SVQC
222         if(this)
223         if(IS_PLAYER(actor) && this.message != "")
224         if(IS_REAL_CLIENT(actor))
225         {
226                 centerprint(actor, this.message);
227                 if (this.noise == "")
228                         play2(actor, SND(TALK));
229         }
230
231 //
232 // kill the killtagets
233 //
234         s = this.killtarget;
235         if (s != "")
236         {
237                 for(entity t = NULL; (t = find(t, targetname, s)); )
238                         delete(t);
239         }
240 #endif
241
242 //
243 // fire targets
244 //
245
246         if(this.target_random)
247                 RandomSelection_Init();
248
249         for(int i = 0; i < 4; ++i)
250         {
251                 switch(i)
252                 {
253                         default:
254                         case 0: s = this.target; break;
255                         case 1: s = this.target2; break;
256                         case 2: s = this.target3; break;
257                         case 3: s = this.target4; break;
258                 }
259                 if (s != "")
260                 {
261                         // Flag to set func_clientwall state
262                         // 1 == deactivate, 2 == activate, 0 == do nothing
263                         int aw_flag = this.antiwall_flag;
264                         for(entity t = NULL; (t = find(t, targetname, s)); )
265                         {
266                                 if(t.use && (t.sub_target_used != time || !preventReuse))
267                                 {
268                                         if(this.target_random)
269                                         {
270                                                 RandomSelection_AddEnt(t, 1, 0);
271                                         }
272                                         else
273                                         {
274                                                 if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary")
275                                                         t.antiwall_flag = aw_flag;
276
277                                                 t.use(t, actor, this);
278                                                 if(preventReuse)
279                                                         t.sub_target_used = time;
280                                         }
281                                 }
282                         }
283                 }
284         }
285
286         if(this.target_random && RandomSelection_chosen_ent)
287         {
288                 RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
289                 if(preventReuse)
290                         RandomSelection_chosen_ent.sub_target_used = time;
291         }
292 }
293
294 void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); }
295 void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); }
296
297 void SUB_UseTargets_self(entity this)
298 {
299         SUB_UseTargets(this, NULL, NULL);
300 }