]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/triggers.qc
Add strfree to reduce explicit use of strunzone/string_null
[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                 strfree(this.target);
120                 strfree(this.target2);
121                 strfree(this.target3);
122                 strfree(this.target4);
123                 strfree(this.targetname);
124                 strfree(this.killtarget);
125
126                 int targbits = ReadByte();
127
128                 this.target = ((targbits & BIT(0)) ? strzone(ReadString()) : string_null);
129                 this.target2 = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null);
130                 this.target3 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null);
131                 this.target4 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null);
132                 this.targetname = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null);
133                 this.killtarget = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null);
134         }
135
136         if(f & 4)
137                 this.origin = ReadVector();
138         else
139                 this.origin = '0 0 0';
140         setorigin(this, this.origin);
141
142         if(f & 8)
143                 this.movedir = ReadVector();
144         else
145                 this.movedir = '0 0 0';
146
147         if(f & 16)
148                 this.angles = ReadVector();
149         else
150                 this.angles = '0 0 0';
151
152         this.modelindex = ReadShort();
153         this.mins = ReadVector();
154         this.maxs = ReadVector();
155         this.scale = ReadByte() / 16;
156         setsize(this, this.mins, this.maxs);
157 }
158
159 void trigger_remove_generic(entity this)
160 {
161         strfree(this.target);
162         strfree(this.target2);
163         strfree(this.target3);
164         strfree(this.target4);
165         strfree(this.targetname);
166         strfree(this.killtarget);
167 }
168 #endif
169
170
171 /*
172 ==============================
173 SUB_UseTargets
174
175 the global "activator" should be set to the entity that initiated the firing.
176
177 If this.delay is set, a DelayedUse entity will be created that will actually
178 do the SUB_UseTargets after that many seconds have passed.
179
180 Centerprints any this.message to the activator.
181
182 Removes all entities with a targetname that match this.killtarget,
183 and removes them, so some events can remove other triggers.
184
185 Search for (string)targetname in all entities that
186 match (string)this.target and call their .use function
187
188 ==============================
189 */
190
191 void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse)
192 {
193 //
194 // check for a delay
195 //
196         if (this.delay)
197         {
198         // create a temp object to fire at a later time
199                 entity t = new(DelayedUse);
200                 t.nextthink = time + this.delay;
201                 setthink(t, DelayThink);
202                 t.enemy = actor;
203                 t.message = this.message;
204                 t.killtarget = this.killtarget;
205                 t.target = this.target;
206                 t.target2 = this.target2;
207                 t.target3 = this.target3;
208                 t.target4 = this.target4;
209                 t.antiwall_flag = this.antiwall_flag;
210                 return;
211         }
212
213         string s;
214
215 //
216 // print the message
217 //
218 #ifdef SVQC
219         if(this)
220         if(IS_PLAYER(actor) && this.message != "")
221         if(IS_REAL_CLIENT(actor))
222         {
223                 centerprint(actor, this.message);
224                 if (this.noise == "")
225                         play2(actor, SND(TALK));
226         }
227
228 //
229 // kill the killtagets
230 //
231         s = this.killtarget;
232         if (s != "")
233         {
234                 for(entity t = NULL; (t = find(t, targetname, s)); )
235                         delete(t);
236         }
237 #endif
238
239 //
240 // fire targets
241 //
242
243         if(this.target_random)
244                 RandomSelection_Init();
245
246         for(int 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                         int aw_flag = this.antiwall_flag;
261                         for(entity t = NULL; (t = find(t, targetname, s)); )
262                         {
263                                 if(t.use && (t.sub_target_used != time || !preventReuse))
264                                 {
265                                         if(this.target_random)
266                                         {
267                                                 RandomSelection_AddEnt(t, 1, 0);
268                                         }
269                                         else
270                                         {
271                                                 if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary")
272                                                         t.antiwall_flag = aw_flag;
273
274                                                 t.use(t, actor, this);
275                                                 if(preventReuse)
276                                                         t.sub_target_used = time;
277                                         }
278                                 }
279                         }
280                 }
281         }
282
283         if(this.target_random && RandomSelection_chosen_ent)
284         {
285                 RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
286                 if(preventReuse)
287                         RandomSelection_chosen_ent.sub_target_used = time;
288         }
289 }
290
291 void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); }
292 void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); }