]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/triggers.qc
improve descriptions, add missing cvar to config
[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         WriteVector(MSG_ENTITY, this.mins);
106         WriteVector(MSG_ENTITY, this.maxs);
107         WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255));
108 }
109
110 #elif defined(CSQC)
111
112 void trigger_common_read(entity this, bool withtarget)
113 {
114         int f = ReadByte();
115         this.warpzone_isboxy = (f & 1);
116
117         if(withtarget)
118         {
119                 if(this.target) { strunzone(this.target); }
120                 if(this.target2) { strunzone(this.target2); }
121                 if(this.target3) { strunzone(this.target3); }
122                 if(this.target4) { strunzone(this.target4); }
123                 if(this.targetname) { strunzone(this.targetname); }
124                 if(this.killtarget) { strunzone(this.killtarget); }
125
126                 int targbits = ReadByte();
127
128                 #define X(xs,b) MACRO_BEGIN { \
129                         if(targbits & BIT(b)) \
130                                 xs = strzone(ReadString()); \
131                         else \
132                                 xs = string_null; \
133                 } MACRO_END
134
135                 X(this.target, 0);
136                 X(this.target2, 1);
137                 X(this.target3, 2);
138                 X(this.target4, 3);
139                 X(this.targetname, 4);
140                 X(this.killtarget, 5);
141                 #undef X
142         }
143
144         if(f & 4)
145                 this.origin = ReadVector();
146         else
147                 this.origin = '0 0 0';
148         setorigin(this, this.origin);
149
150         if(f & 8)
151                 this.movedir = ReadVector();
152         else
153                 this.movedir = '0 0 0';
154
155         if(f & 16)
156                 this.angles = ReadVector();
157         else
158                 this.angles = '0 0 0';
159
160         this.modelindex = ReadShort();
161         this.mins = ReadVector();
162         this.maxs = ReadVector();
163         this.scale = ReadByte() / 16;
164         setsize(this, this.mins, this.maxs);
165 }
166
167 void trigger_remove_generic(entity this)
168 {
169         if(this.target) { strunzone(this.target); }
170         this.target = string_null;
171
172         if(this.target2) { strunzone(this.target2); }
173         this.target2 = string_null;
174
175         if(this.target3) { strunzone(this.target3); }
176         this.target3 = string_null;
177
178         if(this.target4) { strunzone(this.target4); }
179         this.target4 = string_null;
180
181         if(this.targetname) { strunzone(this.targetname); }
182         this.target = string_null;
183
184         if(this.killtarget) { strunzone(this.killtarget); }
185         this.killtarget = string_null;
186 }
187 #endif
188
189
190 /*
191 ==============================
192 SUB_UseTargets
193
194 the global "activator" should be set to the entity that initiated the firing.
195
196 If this.delay is set, a DelayedUse entity will be created that will actually
197 do the SUB_UseTargets after that many seconds have passed.
198
199 Centerprints any this.message to the activator.
200
201 Removes all entities with a targetname that match this.killtarget,
202 and removes them, so some events can remove other triggers.
203
204 Search for (string)targetname in all entities that
205 match (string)this.target and call their .use function
206
207 ==============================
208 */
209
210 void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse)
211 {
212 //
213 // check for a delay
214 //
215         if (this.delay)
216         {
217         // create a temp object to fire at a later time
218                 entity t = new(DelayedUse);
219                 t.nextthink = time + this.delay;
220                 setthink(t, DelayThink);
221                 t.enemy = actor;
222                 t.message = this.message;
223                 t.killtarget = this.killtarget;
224                 t.target = this.target;
225                 t.target2 = this.target2;
226                 t.target3 = this.target3;
227                 t.target4 = this.target4;
228                 t.antiwall_flag = this.antiwall_flag;
229                 return;
230         }
231
232         string s;
233
234 //
235 // print the message
236 //
237 #ifdef SVQC
238         if(this)
239         if(IS_PLAYER(actor) && this.message != "")
240         if(IS_REAL_CLIENT(actor))
241         {
242                 centerprint(actor, this.message);
243                 if (this.noise == "")
244                         play2(actor, SND(TALK));
245         }
246
247 //
248 // kill the killtagets
249 //
250         s = this.killtarget;
251         if (s != "")
252         {
253                 for(entity t = NULL; (t = find(t, targetname, s)); )
254                         delete(t);
255         }
256 #endif
257
258 //
259 // fire targets
260 //
261
262         if(this.target_random)
263                 RandomSelection_Init();
264
265         for(int i = 0; i < 4; ++i)
266         {
267                 switch(i)
268                 {
269                         default:
270                         case 0: s = this.target; break;
271                         case 1: s = this.target2; break;
272                         case 2: s = this.target3; break;
273                         case 3: s = this.target4; break;
274                 }
275                 if (s != "")
276                 {
277                         // Flag to set func_clientwall state
278                         // 1 == deactivate, 2 == activate, 0 == do nothing
279                         int aw_flag = this.antiwall_flag;
280                         for(entity t = NULL; (t = find(t, targetname, s)); )
281                         {
282                                 if(t.use && (t.sub_target_used != time || !preventReuse))
283                                 {
284                                         if(this.target_random)
285                                         {
286                                                 RandomSelection_AddEnt(t, 1, 0);
287                                         }
288                                         else
289                                         {
290                                                 if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary")
291                                                         t.antiwall_flag = aw_flag;
292
293                                                 t.use(t, actor, this);
294                                                 if(preventReuse)
295                                                         t.sub_target_used = time;
296                                         }
297                                 }
298                         }
299                 }
300         }
301
302         if(this.target_random && RandomSelection_chosen_ent)
303         {
304                 RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
305                 if(preventReuse)
306                         RandomSelection_chosen_ent.sub_target_used = time;
307         }
308 }
309
310 void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); }
311 void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); }