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