]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mapobjects/triggers.qc
triggers: set bmodel correctly in CSQC
[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_IS_LOOT(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         if (this.modelindex)
209                 setmodelindex(this, this.modelindex);
210         this.mins = ReadVector();
211         this.maxs = ReadVector();
212         this.scale = ReadByte() / 16;
213         setsize(this, this.mins, this.maxs);
214 }
215
216 void trigger_remove_generic(entity this)
217 {
218         strfree(this.target);
219         strfree(this.target2);
220         strfree(this.target3);
221         strfree(this.target4);
222         strfree(this.targetname);
223         strfree(this.killtarget);
224 }
225 #endif
226
227
228 /*
229 ==============================
230 SUB_UseTargets
231
232 the global "activator" should be set to the entity that initiated the firing.
233
234 If this.delay is set, a DelayedUse entity will be created that will actually
235 do the SUB_UseTargets after that many seconds have passed.
236
237 Centerprints any this.message to the activator.
238
239 Removes all entities with a targetname that match this.killtarget,
240 and removes them, so some events can remove other triggers.
241
242 Search for (string)targetname in all entities that
243 match (string)this.target and call their .use function
244
245 ==============================
246 */
247
248 void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse, int skiptargets)
249 {
250 //
251 // check for a delay
252 //
253         if (this.delay)
254         {
255                 // create a temp object to fire at a later time
256                 entity t = new_pure(DelayedUse);
257                 t.nextthink = time + this.delay;
258                 setthink(t, DelayThink);
259                 t.enemy = actor;
260                 t.message = this.message;
261                 t.killtarget = this.killtarget;
262                 if(!(skiptargets & BIT(1))) t.target = this.target;
263                 if(!(skiptargets & BIT(2))) t.target2 = this.target2;
264                 if(!(skiptargets & BIT(3))) t.target3 = this.target3;
265                 if(!(skiptargets & BIT(4))) t.target4 = this.target4;
266                 t.antiwall_flag = this.antiwall_flag;
267                 return;
268         }
269
270         string s;
271
272 //
273 // print the message
274 //
275 #ifdef SVQC
276         if(this)
277         if(IS_PLAYER(actor) && this.message != "")
278         if(IS_REAL_CLIENT(actor))
279         {
280                 centerprint(actor, this.message);
281                 if (this.noise == "")
282                         play2(actor, SND(TALK));
283         }
284
285 //
286 // kill the killtagets
287 //
288         s = this.killtarget;
289         if (s != "")
290         {
291                 for(entity t = NULL; (t = find(t, targetname, s)); )
292                         delete(t);
293         }
294 #endif
295
296 //
297 // fire targets
298 //
299
300         if(this.target_random)
301                 RandomSelection_Init();
302
303         for(int i = 0; i < 4; ++i)
304         {
305                 if(skiptargets & BIT(i + 1))
306                         continue;
307                 switch(i)
308                 {
309                         default:
310                         case 0: s = this.target; break;
311                         case 1: s = this.target2; break;
312                         case 2: s = this.target3; break;
313                         case 3: s = this.target4; break;
314                 }
315                 if (s != "")
316                 {
317                         for(entity t = NULL; (t = find(t, targetname, s)); )
318                         {
319                                 if(t != this && t.use && (t.sub_target_used != time || !preventReuse))
320                                 {
321                                         if(this.target_random)
322                                         {
323                                                 RandomSelection_AddEnt(t, 1, 0);
324                                         }
325                                         else
326                                         {
327                                                 t.use(t, actor, this);
328                                                 if(preventReuse)
329                                                         t.sub_target_used = time;
330                                         }
331                                 }
332                         }
333                 }
334         }
335
336         if(this.target_random && RandomSelection_chosen_ent)
337         {
338                 RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
339                 if(preventReuse)
340                         RandomSelection_chosen_ent.sub_target_used = time;
341         }
342 }
343
344 void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false, 0); }
345 void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true, 0); }
346 void SUB_UseTargets_SkipTargets(entity this, entity actor, entity trigger, int skiptargets) { SUB_UseTargets_Ex(this, actor, trigger, false, skiptargets); }