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