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