]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/item_key.qc
Merge branch 'TimePath/experiments/csqc_prediction' into Mario/qc_physics
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / item_key.qc
1 #if defined(CSQC)
2 #elif defined(MENUQC)
3 #elif defined(SVQC)
4         #include "../dpdefs/progsdefs.qh"
5     #include "../dpdefs/dpextensions.qh"
6     #include "../warpzonelib/util_server.qh"
7     #include "../common/util.qh"
8     #include "../common/monsters/monsters.qh"
9         #include "../common/triggers/subs.qh"
10     #include "defs.qh"
11     #include "../common/notifications.qh"
12     #include "item_key.qh"
13 #endif
14
15 /*
16 TODO:
17 - add an unlock sound (here to trigger_keylock and to func_door)
18 - display available keys on the HUD
19 - make more tests
20 - think about adding NOT_EASY/NOT_NORMAL/NOT_HARD for Q1 compatibility
21 - should keys have a trigger?
22 */
23
24 bool item_keys_usekey(entity l, entity p) {
25         float valid = l.itemkeys & p.itemkeys;
26
27         if (!valid) {
28                 // other has none of the needed keys
29                 return false;
30         } else if (l.itemkeys == valid) {
31                 // ALL needed keys were given
32                 l.itemkeys = 0;
33                 return true;
34         } else {
35                 // only some of the needed keys were given
36                 l.itemkeys &= ~valid;
37                 return true;
38         }
39 }
40
41 string item_keys_keylist(float keylist) {
42         float base, l;
43         string n;
44
45         // no keys
46         if (!keylist)
47                 return "";
48
49         // one key
50         if ((keylist & (keylist-1)) != 0)
51                 return strcat("the ", item_keys_names[lowestbit(keylist)]);
52
53         n = "";
54         base = 0;
55         while (keylist) {
56                 l = lowestbit(keylist);
57                 if (n)
58                         n = strcat(n, ", the ", item_keys_names[base + l]);
59                 else
60                         n = strcat("the ", item_keys_names[base + l]);
61
62                 keylist = bitshift(keylist,  -(l + 1));
63                 base+= l + 1;
64         }
65
66         return n;
67 }
68
69
70 /*
71 ================================
72 item_key
73 ================================
74 */
75
76 /**
77  * Key touch handler.
78  */
79 void item_key_touch(void) {
80         if (!IS_PLAYER(other))
81                 return;
82
83         // player already picked up this key
84         if (other.itemkeys & self.itemkeys)
85                 return;
86
87         other.itemkeys |= self.itemkeys;
88         play2(other, self.noise);
89
90         centerprint(other, self.message);
91 };
92
93 /**
94  * Spawn a key with given model, key code and color.
95  */
96 void spawn_item_key() {
97         precache_model(self.model);
98
99         if (self.spawnflags & 1) // FLOATING
100                 self.noalign = 1;
101
102         if (self.noalign)
103                 self.movetype = MOVETYPE_NONE;
104         else
105                 self.movetype = MOVETYPE_TOSS;
106
107         precache_sound(self.noise);
108
109         self.mdl = self.model;
110         self.effects = EF_LOWPRECISION;
111         setmodel(self, self.model);
112         //setsize(self, '-16 -16 -24', '16 16 32');
113         setorigin(self, self.origin + '0 0 32');
114         setsize(self, '-16 -16 -56', '16 16 0');
115         self.modelflags |= MF_ROTATE;
116         self.solid = SOLID_TRIGGER;
117
118         if (!self.noalign)
119         {
120                 // first nudge it off the floor a little bit to avoid math errors
121                 setorigin(self, self.origin + '0 0 1');
122                 // note droptofloor returns false if stuck/or would fall too far
123                 droptofloor();
124         }
125
126         self.touch = item_key_touch;
127 };
128
129
130 /*QUAKED item_key (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
131 A key entity.
132 The itemkeys should contain one of the following key IDs:
133 1 - GOLD key -
134 2 - SILVER key
135 4 - BRONZE key
136 8 - RED keycard
137 16 - BLUE keycard
138 32 - GREEN keycard
139 Custom keys:
140 ... - last key is 1<<23
141 Keys with bigger Id than 32 don't have a default netname and model, if you use one of them, you MUST provide those.
142 -----------KEYS------------
143 colormod: color of the key (default: '.9 .9 .9').
144 itemkeys: a key Id.
145 message: message to print when player picks up this key.
146 model: custom key model to use.
147 netname: the display name of the key.
148 noise: custom sound to play when player picks up the key.
149 -------- SPAWNFLAGS --------
150 FLOATING: the item will float in air, instead of aligning to the floor by falling
151 ---------NOTES----------
152 This is the only correct way to put keys on the map!
153
154 itemkeys MUST always have exactly one bit set.
155 */
156 void spawnfunc_item_key() {
157         string _netname;
158         vector _colormod;
159
160         // reject this entity if more than one key was set!
161         if (self.itemkeys>0 && (self.itemkeys & (self.itemkeys-1)) != 0) {
162                 objerror("item_key.itemkeys must contain only 1 bit set specifying the key it represents!");
163                 remove(self);
164                 return;
165         }
166
167         // find default netname and colormod
168         switch(self.itemkeys) {
169         case 1:
170                 _netname = "GOLD key";
171                 _colormod = '1 .9 0';
172                 break;
173
174         case 2:
175                 _netname = "SILVER key";
176                 _colormod = '.9 .9 .9';
177                 break;
178
179         case 4:
180                 _netname = "BRONZE key";
181                 _colormod = '.6 .25 0';
182                 break;
183
184         case 8:
185                 _netname = "RED keycard";
186                 _colormod = '.9 0 0';
187                 break;
188
189         case 16:
190                 _netname = "BLUE keycard";
191                 _colormod = '0 0 .9';
192                 break;
193
194         case 32:
195                 _netname = "GREEN keycard";
196                 _colormod = '0 .9 0';
197                 break;
198
199         default:
200                 _netname = "FLUFFY PINK keycard";
201                 _colormod = '1 1 1';
202
203                 if (self.netname == "") {
204                         objerror("item_key doesn't have a default name for this key and a custom one was not specified!");
205                         remove(self);
206                         return;
207                 }
208                 break;
209
210         }
211
212         // find default model
213         string _model = string_null;
214         if (self.itemkeys <= ITEM_KEY_BIT(2)) {
215                 _model = "models/keys/key.md3";
216         } else if (self.itemkeys >= ITEM_KEY_BIT(3) && self.itemkeys <= ITEM_KEY_BIT(5)) {
217                 _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model!
218         } else if (self.model == "") {
219                 objerror("item_key doesn't have a default model for this key and a custom one was not specified!");
220                 remove(self);
221                 return;
222         }
223
224         // set defailt netname
225         if (self.netname == "")
226                 self.netname = _netname;
227
228         // set default colormod
229         if (!self.colormod)
230                 self.colormod = _colormod;
231
232         // set default model
233         if (self.model == "")
234                 self.model = _model;
235
236         // set default pickup message
237         if (self.message == "")
238                 self.message = strzone(strcat("You've picked up the ", self.netname, "!"));
239
240         if (self.noise == "")
241                 self.noise = "misc/itempickup.wav";
242
243         // save the name for later
244         item_keys_names[lowestbit(self.itemkeys)] = self.netname;
245
246         // put the key on the map
247         spawn_item_key();
248 }
249
250 /*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
251 SILVER key.
252 -----------KEYS------------
253 colormod: color of the key (default: '.9 .9 .9').
254 message: message to print when player picks up this key.
255 model: custom model to use.
256 noise: custom sound to play when player picks up the key.
257 -------- SPAWNFLAGS --------
258 FLOATING: the item will float in air, instead of aligning to the floor by falling
259 ---------NOTES----------
260 Don't use this entity on new maps! Use item_key instead.
261 */
262 void spawnfunc_item_key1(void) {
263         self.classname = "item_key";
264         self.itemkeys = ITEM_KEY_BIT(1);
265         spawnfunc_item_key();
266 };
267
268 /*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
269 GOLD key.
270 -----------KEYS------------
271 colormod: color of the key (default: '1 .9 0').
272 message: message to print when player picks up this key.
273 model: custom model to use.
274 noise: custom sound to play when player picks up the key.
275 -------- SPAWNFLAGS --------
276 FLOATING: the item will float in air, instead of aligning to the floor by falling
277 ---------NOTES----------
278 Don't use this entity on new maps! Use item_key instead.
279 */
280 void spawnfunc_item_key2(void) {
281         self.classname = "item_key";
282         self.itemkeys = ITEM_KEY_BIT(0);
283         spawnfunc_item_key();
284 };
285
286
287 /*
288 ================================
289 trigger_keylock
290 ================================
291 */
292
293 /**
294  * trigger givent targets
295  */
296 void trigger_keylock_trigger(string s) {
297         entity stemp = self;
298         entity otemp = other;
299         entity atemp = activator;
300
301         entity t;
302         for(t = world; (t = find(t, targetname, s)); )
303                 if (t.use) {
304                         self = t;
305                         other = stemp;
306                         activator = atemp;
307                         self.use();
308                 }
309
310         self = stemp;
311         other = otemp;
312         activator = atemp;
313 };
314
315 /**
316  * kill killtarget of trigger keylock.
317  */
318 void trigger_keylock_kill(string s) {
319         entity t;
320         for(t = world; (t = find(t, targetname, s)); )
321                 remove(t);
322 };
323
324 void trigger_keylock_touch(void) {
325         bool key_used = false;
326         bool started_delay = false;
327
328         // only player may trigger the lock
329         if (!IS_PLAYER(other))
330                 return;
331
332
333         // check silver key
334         if (self.itemkeys)
335                 key_used = item_keys_usekey(self, other);
336
337         activator = other;
338
339         if (self.itemkeys) {
340                 // at least one of the keys is missing
341                 if (key_used) {
342                         // one or more keys were given, but others are still missing!
343                         play2(other, self.noise1);
344                         Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(self.itemkeys));
345                         other.key_door_messagetime = time + 2;
346                 } else if (other.key_door_messagetime <= time) {
347                         // no keys were given
348                         play2(other, self.noise2);
349                         Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(self.itemkeys));
350                         other.key_door_messagetime = time + 2;
351                 }
352
353                 // trigger target2
354                 if (self.delay <= time || started_delay == true)
355                 if (self.target2) {
356                         trigger_keylock_trigger(self.target2);
357                         started_delay = true;
358                         self.delay = time + self.wait;
359                 }
360         } else {
361                 // all keys were given!
362                 play2(other, self.noise);
363                 centerprint(other, self.message);
364
365                 if (self.target)
366                         trigger_keylock_trigger(self.target);
367
368                 if (self.killtarget)
369                         trigger_keylock_kill(self.killtarget);
370
371                 remove(self);
372         }
373
374 };
375
376 /*QUAKED trigger_keylock (.0 .5 .8) ?
377 Keylock trigger.  Must target other entities.
378 This trigger will trigger target entities when all required keys are provided.
379 -------- KEYS --------
380 itemkeys: A bit field with key IDs that are needed to open this lock.
381 sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default)
382 target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger
383 target2: trigger all entities with this targetname when triggered without giving it all the required keys.
384 killtarget: remove all entities with this targetname when triggered with all the needed keys.
385 message: print this message to the player who activated the trigger when all needed keys have been given.
386 message2: print this message to the player who activated the trigger when not all of the needed keys have been given.
387 noise: sound to play when lock gets unlocked (default: see sounds)
388 noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav)
389 noise2: sound to play when a key is missing (default: misc/talk.wav)
390 wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
391 ---------NOTES----------
392 If spawned without any key specified in itemkeys, this trigger will display an error and remove itself.
393 message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone.
394 */
395 void spawnfunc_trigger_keylock(void) {
396         if (!self.itemkeys) {
397                 remove(self);
398                 return;
399         }
400
401         // set unlocked message
402         if (self.message == "")
403                 self.message = "Unlocked!";
404
405         // set default unlock noise
406         if (self.noise == "") {
407                 if (self.sounds == 1)
408                         self.noise = "misc/secret.wav";
409                 else if (self.sounds == 2)
410                         self.noise = "misc/talk.wav";
411                 else //if (self.sounds == 3) {
412                         self.noise = "misc/trigger1.wav";
413         }
414
415         // set default use key sound
416         if (self.noise1 == "")
417                 self.noise1 = "misc/decreasevalue.wav";
418
419         // set closed sourd
420         if (self.noise2 == "")
421                 self.noise2 = "misc/talk.wav";
422
423         // delay between triggering message2 and trigger2
424         if (!self.wait)
425                 self.wait = 5;
426
427         // precache sounds
428         precache_sound(self.noise);
429         precache_sound(self.noise1);
430         precache_sound(self.noise2);
431
432         EXACTTRIGGER_INIT;
433
434         self.touch = trigger_keylock_touch;
435 }