]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/item_key.qc
Teamplay: Made team balance entities destroy themselves on the next frame.
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / item_key.qc
1 #include "item_key.qh"
2
3 #include "../common/mapobjects/subs.qh"
4 #include <common/mapobjects/triggers.qh>
5 #include "../common/monsters/_mod.qh"
6 #include "../common/notifications/all.qh"
7 #include "../common/util.qh"
8 #include "../lib/warpzone/util_server.qh"
9
10 /*
11 TODO:
12 - add an unlock sound (here to trigger_keylock and to func_door)
13 - display available keys on the HUD
14 - make more tests
15 - think about adding NOT_EASY/NOT_NORMAL/NOT_HARD for Q1 compatibility
16 - should keys have a trigger?
17 */
18
19 bool item_keys_usekey(entity l, entity p)
20 {
21         int valid = l.itemkeys & PS(p).itemkeys;
22
23         if (!valid) {
24                 // player has none of the needed keys
25                 return false;
26         } else if (l.itemkeys == valid) {
27                 // ALL needed keys were given
28                 l.itemkeys = 0;
29                 return true;
30         } else {
31                 // only some of the needed keys were given
32                 l.itemkeys &= ~valid;
33                 return true;
34         }
35 }
36
37 string item_keys_keylist(float keylist) {
38         // no keys
39         if (!keylist)
40                 return "";
41
42         // one key
43         if ((keylist & (keylist-1)) == 0)
44                 return strcat("the ", item_keys_names[lowestbit(keylist)]);
45
46         string n = "";
47         int base = 0;
48         while (keylist) {
49                 int l = lowestbit(keylist);
50                 if (n)
51                         n = strcat(n, ", the ", item_keys_names[base + l]);
52                 else
53                         n = strcat("the ", item_keys_names[base + l]);
54
55                 keylist = bitshift(keylist,  -(l + 1));
56                 base+= l + 1;
57         }
58
59         return n;
60 }
61
62
63 /*
64 ================================
65 item_key
66 ================================
67 */
68
69 /**
70  * Key touch handler.
71  */
72 void item_key_touch(entity this, entity toucher)
73 {
74         if (!IS_PLAYER(toucher))
75                 return;
76
77         // player already picked up this key
78         if (PS(toucher).itemkeys & this.itemkeys)
79                 return;
80
81         PS(toucher).itemkeys |= this.itemkeys;
82         play2(toucher, this.noise);
83
84         centerprint(toucher, this.message);
85
86         string oldmsg = this.message;
87         this.message = "";
88         SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for the trigger here?
89         this.message = oldmsg;
90 }
91
92 /**
93  * Spawn a key with given model, key code and color.
94  */
95 void spawn_item_key(entity this)
96 {
97         precache_model(this.model);
98
99         if (this.spawnflags & 1) // FLOATING
100                 this.noalign = 1;
101
102         if (this.noalign)
103                 set_movetype(this, MOVETYPE_NONE);
104         else
105                 set_movetype(this, MOVETYPE_TOSS);
106
107         precache_sound(this.noise);
108
109         this.mdl = this.model;
110         this.effects = EF_LOWPRECISION;
111         _setmodel(this, this.model);
112         //setsize(this, '-16 -16 -24', '16 16 32');
113         setorigin(this, this.origin + '0 0 32');
114         setsize(this, '-16 -16 -56', '16 16 0');
115         this.modelflags |= MF_ROTATE;
116         this.solid = SOLID_TRIGGER;
117
118         if (!this.noalign)
119         {
120                 // first nudge it off the floor a little bit to avoid math errors
121                 setorigin(this, this.origin + '0 0 1');
122                 // note droptofloor returns false if stuck/or would fall too far
123                 droptofloor(this);
124         }
125
126         settouch(this, 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 spawnfunc(item_key)
157 {
158         string _netname;
159         vector _colormod;
160
161         // reject this entity if more than one key was set!
162         if (this.itemkeys>0 && (this.itemkeys & (this.itemkeys-1)) != 0) {
163                 objerror(this, "item_key.itemkeys must contain only 1 bit set specifying the key it represents!");
164                 delete(this);
165                 return;
166         }
167
168         // find default netname and colormod
169         switch(this.itemkeys) {
170         case BIT(0):
171                 _netname = "GOLD key";
172                 _colormod = '1 .9 0';
173                 break;
174
175         case BIT(1):
176                 _netname = "SILVER key";
177                 _colormod = '.9 .9 .9';
178                 break;
179
180         case BIT(2):
181                 _netname = "BRONZE key";
182                 _colormod = '.6 .25 0';
183                 break;
184
185         case BIT(3):
186                 _netname = "RED keycard";
187                 _colormod = '.9 0 0';
188                 break;
189
190         case BIT(4):
191                 _netname = "BLUE keycard";
192                 _colormod = '0 0 .9';
193                 break;
194
195         case BIT(5):
196                 _netname = "GREEN keycard";
197                 _colormod = '0 .9 0';
198                 break;
199
200         default:
201                 _netname = "FLUFFY PINK keycard";
202                 _colormod = '1 1 1';
203
204                 if (this.netname == "") {
205                         objerror(this, "item_key doesn't have a default name for this key and a custom one was not specified!");
206                         delete(this);
207                         return;
208                 }
209                 break;
210
211         }
212
213         // find default model
214         string _model = string_null;
215         if (this.itemkeys <= ITEM_KEY_BIT(2)) {
216                 _model = "models/keys/key.md3";
217         } else if (this.itemkeys >= ITEM_KEY_BIT(3) && this.itemkeys <= ITEM_KEY_BIT(5)) {
218                 _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model!
219         } else if (this.model == "") {
220                 objerror(this, "item_key doesn't have a default model for this key and a custom one was not specified!");
221                 delete(this);
222                 return;
223         }
224
225         // set defailt netname
226         if (this.netname == "")
227                 this.netname = _netname;
228
229         // set default colormod
230         if (!this.colormod)
231                 this.colormod = _colormod;
232
233         // set default model
234         if (this.model == "")
235                 this.model = _model;
236
237         // set default pickup message
238         if (this.message == "")
239                 this.message = strzone(strcat("You've picked up the ", this.netname, "!"));
240
241         if (this.noise == "")
242                 this.noise = strzone(SND(ITEMPICKUP));
243
244         // save the name for later
245         item_keys_names[lowestbit(this.itemkeys)] = this.netname;
246
247         // put the key on the map
248         spawn_item_key(this);
249 }
250
251 /*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
252 SILVER key.
253 -----------KEYS------------
254 colormod: color of the key (default: '.9 .9 .9').
255 message: message to print when player picks up this key.
256 model: custom model to use.
257 noise: custom sound to play when player picks up the key.
258 -------- SPAWNFLAGS --------
259 FLOATING: the item will float in air, instead of aligning to the floor by falling
260 ---------NOTES----------
261 Don't use this entity on new maps! Use item_key instead.
262 */
263 spawnfunc(item_key1)
264 {
265         this.classname = "item_key";
266         this.itemkeys = ITEM_KEY_BIT(1);
267         spawnfunc_item_key(this);
268 }
269
270 /*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
271 GOLD key.
272 -----------KEYS------------
273 colormod: color of the key (default: '1 .9 0').
274 message: message to print when player picks up this key.
275 model: custom model to use.
276 noise: custom sound to play when player picks up the key.
277 -------- SPAWNFLAGS --------
278 FLOATING: the item will float in air, instead of aligning to the floor by falling
279 ---------NOTES----------
280 Don't use this entity on new maps! Use item_key instead.
281 */
282 spawnfunc(item_key2)
283 {
284         this.classname = "item_key";
285         this.itemkeys = ITEM_KEY_BIT(0);
286         spawnfunc_item_key(this);
287 }