1#include "item_key.qh" 2 3#include "../common/triggers/subs.qh" 4#include "../common/monsters/_mod.qh" 5#include "../common/notifications/all.qh" 6#include "../common/util.qh" 7#include "../lib/warpzone/util_server.qh" 8 9/* 10TODO: 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 18bool 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 36string 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================================ 64item_key 65================================ 66*/ 67 68/** 69 * Key touch handler. 70 */ 71void 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 */ 94void 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 130A key entity. 131The itemkeys should contain one of the following key IDs: 1321 - GOLD key - 1332 - SILVER key 1344 - BRONZE key 1358 - RED keycard 13616 - BLUE keycard 13732 - GREEN keycard 138Custom keys: 139... - last key is 1<<23 140Keys 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------------ 142colormod: color of the key (default: '.9 .9 .9'). 143itemkeys: a key Id. 144message: message to print when player picks up this key. 145model: custom key model to use. 146netname: the display name of the key. 147noise: custom sound to play when player picks up the key. 148-------- SPAWNFLAGS -------- 149FLOATING: the item will float in air, instead of aligning to the floor by falling 150---------NOTES---------- 151This is the only correct way to put keys on the map! 152 153itemkeys MUST always have exactly one bit set. 154*/ 155spawnfunc(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 delete(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 delete(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 delete(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 251SILVER key. 252-----------KEYS------------ 253colormod: color of the key (default: '.9 .9 .9'). 254message: message to print when player picks up this key. 255model: custom model to use. 256noise: custom sound to play when player picks up the key. 257-------- SPAWNFLAGS -------- 258FLOATING: the item will float in air, instead of aligning to the floor by falling 259---------NOTES---------- 260Don't use this entity on new maps! Use item_key instead. 261*/ 262spawnfunc(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 270GOLD key. 271-----------KEYS------------ 272colormod: color of the key (default: '1 .9 0'). 273message: message to print when player picks up this key. 274model: custom model to use. 275noise: custom sound to play when player picks up the key. 276-------- SPAWNFLAGS -------- 277FLOATING: the item will float in air, instead of aligning to the floor by falling 278---------NOTES---------- 279Don't use this entity on new maps! Use item_key instead. 280*/ 281spawnfunc(item_key2) 282{ 283 this.classname = "item_key"; 284 this.itemkeys = ITEM_KEY_BIT(0); 285 spawnfunc_item_key(this); 286}; 287