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