]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mapobjects/triggers.qc
Merge branch 'Spike29/translators_credits_update' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mapobjects / triggers.qc
1 #include "triggers.qh"
2
3 bool isPushable(entity e)
4 {
5         if(e.pushable)
6                 return true;
7 #ifdef SVQC
8         if(IS_VEHICLE(e))
9                 return false;
10         if(e.iscreature)
11                 return true;
12         if (Item_IsLoot(e))
13         {
14                 return true;
15         }
16         switch(e.classname)
17         {
18                 case "body":
19                         return true;
20                 case "bullet": // antilagged bullets can't hit this either
21                         return false;
22         }
23         if (e.projectiledeathtype)
24                 return true;
25 #endif
26 #ifdef CSQC
27         if(e.flags & FL_PROJECTILE)
28                 return true;
29         if(e.isplayermodel)
30                 return true;
31 #endif
32         return false;
33 }
34
35 void SUB_DontUseTargets(entity this, entity actor, entity trigger) { }
36
37 void SUB_UseTargets(entity this, entity actor, entity trigger);
38
39 void DelayThink(entity this)
40 {
41         SUB_UseTargets (this, this.enemy, NULL);
42         delete(this);
43 }
44
45 #ifdef SVQC
46 void generic_setactive(entity this, int act)
47 {
48         if(act == ACTIVE_TOGGLE)
49         {
50                 if(this.active == ACTIVE_ACTIVE)
51                 {
52                         this.active = ACTIVE_NOT;
53                 }
54                 else
55                 {
56                         this.active = ACTIVE_ACTIVE;
57                 }
58         }
59         else
60         {
61                 this.active = act;
62         }
63 }
64
65 void generic_netlinked_setactive(entity this, int act)
66 {
67         int old_status = this.active;
68         generic_setactive(this, act);
69
70         if (this.active != old_status)
71         {
72                 this.SendFlags |= SF_TRIGGER_UPDATE;
73         }
74 }
75
76 void generic_netlinked_reset(entity this)
77 {
78         if(this.targetname && this.targetname != "")
79         {
80                 if(this.spawnflags & START_ENABLED)
81                 {
82                         this.active = ACTIVE_ACTIVE;
83                 }
84                 else
85                 {
86                         this.active = ACTIVE_NOT;
87                 }
88         }
89         else
90         {
91                 this.active = ACTIVE_ACTIVE;
92         }
93
94         this.SendFlags |= SF_TRIGGER_UPDATE;
95 }
96
97 // Compatibility with old maps
98 void generic_netlinked_legacy_use(entity this, entity actor, entity trigger)
99 {
100         //LOG_WARNF("Entity %s was (de)activated by a trigger, please update map to use relays", this.targetname);
101         this.setactive(this, ACTIVE_TOGGLE);
102 }
103
104 void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc)
105 {
106         setSendEntity(this, sendfunc);
107         this.SendFlags = 0xFFFFFF;
108 }
109
110 void trigger_common_write(entity this, bool withtarget)
111 {
112         int f = 0;
113         if(this.warpzone_isboxy)
114                 BITSET_ASSIGN(f, 1);
115         if(this.origin != '0 0 0')
116                 BITSET_ASSIGN(f, 4);
117         if(this.movedir != '0 0 0')
118                 BITSET_ASSIGN(f, 8);
119         if(this.angles != '0 0 0')
120                 BITSET_ASSIGN(f, 16);
121         WriteByte(MSG_ENTITY, f);
122
123         if(withtarget)
124         {
125                 // probably some way to clean this up...
126                 int targbits = 0;
127                 if(this.target && this.target != "") targbits |= BIT(0);
128                 if(this.target2 && this.target2 != "") targbits |= BIT(1);
129                 if(this.target3 && this.target3 != "") targbits |= BIT(2);
130                 if(this.target4 && this.target4 != "") targbits |= BIT(3);
131                 if(this.targetname && this.targetname != "") targbits |= BIT(4);
132                 if(this.killtarget && this.killtarget != "") targbits |= BIT(5);
133
134                 WriteByte(MSG_ENTITY, targbits);
135
136                 if(targbits & BIT(0))
137                         WriteString(MSG_ENTITY, this.target);
138                 if(targbits & BIT(1))
139                         WriteString(MSG_ENTITY, this.target2);
140                 if(targbits & BIT(2))
141                         WriteString(MSG_ENTITY, this.target3);
142                 if(targbits & BIT(3))
143                         WriteString(MSG_ENTITY, this.target4);
144                 if(targbits & BIT(4))
145                         WriteString(MSG_ENTITY, this.targetname);
146                 if(targbits & BIT(5))
147                         WriteString(MSG_ENTITY, this.killtarget);
148         }
149
150         if(f & 4)
151                 WriteVector(MSG_ENTITY, this.origin);
152
153         if(f & 8)
154                 WriteVector(MSG_ENTITY, this.movedir);
155
156         if(f & 16)
157                 WriteVector(MSG_ENTITY, this.angles);
158
159         WriteShort(MSG_ENTITY, this.modelindex);
160         WriteVector(MSG_ENTITY, this.mins);
161         WriteVector(MSG_ENTITY, this.maxs);
162         WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255));
163 }
164
165 #elif defined(CSQC)
166
167 void trigger_common_read(entity this, bool withtarget)
168 {
169         int f = ReadByte();
170         this.warpzone_isboxy = (f & 1);
171
172         if(withtarget)
173         {
174                 strfree(this.target);
175                 strfree(this.target2);
176                 strfree(this.target3);
177                 strfree(this.target4);
178                 strfree(this.targetname);
179                 strfree(this.killtarget);
180
181                 int targbits = ReadByte();
182
183                 this.target = ((targbits & BIT(0)) ? strzone(ReadString()) : string_null);
184                 this.target2 = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null);
185                 this.target3 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null);
186                 this.target4 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null);
187                 this.targetname = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null);
188                 this.killtarget = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null);
189         }
190
191         if(f & 4)
192                 this.origin = ReadVector();
193         else
194                 this.origin = '0 0 0';
195         setorigin(this, this.origin);
196
197         if(f & 8)
198                 this.movedir = ReadVector();
199         else
200                 this.movedir = '0 0 0';
201
202         if(f & 16)
203                 this.angles = ReadVector();
204         else
205                 this.angles = '0 0 0';
206
207         this.modelindex = ReadShort();
208         this.mins = ReadVector();
209         this.maxs = ReadVector();
210         this.scale = ReadByte() / 16;
211         setsize(this, this.mins, this.maxs);
212 }
213
214 void trigger_remove_generic(entity this)
215 {
216         strfree(this.target);
217         strfree(this.target2);
218         strfree(this.target3);
219         strfree(this.target4);
220         strfree(this.targetname);
221         strfree(this.killtarget);
222 }
223 #endif
224
225
226 /*
227 ==============================
228 SUB_UseTargets
229
230 the global "activator" should be set to the entity that initiated the firing.
231
232 If this.delay is set, a DelayedUse entity will be created that will actually
233 do the SUB_UseTargets after that many seconds have passed.
234
235 Centerprints any this.message to the activator.
236
237 Removes all entities with a targetname that match this.killtarget,
238 and removes them, so some events can remove other triggers.
239
240 Search for (string)targetname in all entities that
241 match (string)this.target and call their .use function
242
243 ==============================
244 */
245
246 void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse, int skiptargets)
247 {
248 //
249 // check for a delay
250 //
251         if (this.delay)
252         {
253                 // create a temp object to fire at a later time
254                 entity t = new_pure(DelayedUse);
255                 t.nextthink = time + this.delay;
256                 setthink(t, DelayThink);
257                 t.enemy = actor;
258                 t.message = this.message;
259                 t.killtarget = this.killtarget;
260                 if(!(skiptargets & BIT(1))) t.target = this.target;
261                 if(!(skiptargets & BIT(2))) t.target2 = this.target2;
262                 if(!(skiptargets & BIT(3))) t.target3 = this.target3;
263                 if(!(skiptargets & BIT(4))) t.target4 = this.target4;
264                 t.antiwall_flag = this.antiwall_flag;
265                 return;
266         }
267
268         string s;
269
270 //
271 // print the message
272 //
273 #ifdef SVQC
274         if(this)
275         if(IS_PLAYER(actor) && this.message != "")
276         if(IS_REAL_CLIENT(actor))
277         {
278                 centerprint(actor, this.message);
279                 if (this.noise == "")
280                         play2(actor, SND(TALK));
281         }
282
283 //
284 // kill the killtagets
285 //
286         s = this.killtarget;
287         if (s != "")
288         {
289                 for(entity t = NULL; (t = find(t, targetname, s)); )
290                         delete(t);
291         }
292 #endif
293
294 //
295 // fire targets
296 //
297
298         if(this.target_random)
299                 RandomSelection_Init();
300
301         for(int i = 0; i < 4; ++i)
302         {
303                 if(skiptargets & BIT(i + 1))
304                         continue;
305                 switch(i)
306                 {
307                         default:
308                         case 0: s = this.target; break;
309                         case 1: s = this.target2; break;
310                         case 2: s = this.target3; break;
311                         case 3: s = this.target4; break;
312                 }
313                 if (s != "")
314                 {
315                         for(entity t = NULL; (t = find(t, targetname, s)); )
316                         {
317                                 if(t != this && t.use && (t.sub_target_used != time || !preventReuse))
318                                 {
319                                         if(this.target_random)
320                                         {
321                                                 RandomSelection_AddEnt(t, 1, 0);
322                                         }
323                                         else
324                                         {
325                                                 t.use(t, actor, this);
326                                                 if(preventReuse)
327                                                         t.sub_target_used = time;
328                                         }
329                                 }
330                         }
331                 }
332         }
333
334         if(this.target_random && RandomSelection_chosen_ent)
335         {
336                 RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
337                 if(preventReuse)
338                         RandomSelection_chosen_ent.sub_target_used = time;
339         }
340 }
341
342 void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false, 0); }
343 void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true, 0); }
344 void SUB_UseTargets_SkipTargets(entity this, entity actor, entity trigger, int skiptargets) { SUB_UseTargets_Ex(this, actor, trigger, false, skiptargets); }