- should keys have a trigger?
*/
+float item_keys_usekey(entity l, entity p) {
+ float valid = l.itemkeys & p.itemkeys;
+
+ if not(valid) {
+ // other has none of the needed keys
+ return FALSE;
+ } else if (l.itemkeys == valid) {
+ // ALL needed keys were given
+ l.itemkeys = 0;
+ return TRUE;
+ } else {
+ // only some of the needed keys were given
+ l.itemkeys &~= valid;
+ return TRUE;
+ }
+}
+
+string item_keys_keylist(float keylist) {
+ float base, l;
+ string n;
+
+ // no keys
+ if not(keylist)
+ return "";
+
+ // one key
+ if ((keylist & (keylist-1)) != 0)
+ return strcat("the ", item_keys_names[lowestbit(keylist)]);
+
+ while (keylist) {
+ l = lowestbit(keylist);
+ if (n)
+ n = strcat(n, ", the ", item_keys_names[base + l]);
+ else
+ n = strcat("the ", item_keys_names[base + l]);
+
+ keylist = bitshift(keylist, -(l + 1));
+ base+= l + 1;
+ }
+
+ return n;
+}
+
+
/*
================================
-item_key1 / item_key2
+item_key
================================
*/
-/*
-Key touch handler.
-*/
+/**
+ * Key touch handler.
+ */
void item_key_touch(void) {
if (other.classname != "player")
return;
other.itemkeys |= self.itemkeys;
play2(other, self.noise);
- if (self.message) {
- centerprint(other, self.message);
- }
+ centerprint(other, self.message);
};
-/*
-Spawn a key with given model, key code and color.
-*/
-void spawn_item_key(float key_code) {
- self.itemkeys = key_code;
+/**
+ * Spawn a key with given model, key code and color.
+ */
+void spawn_item_key() {
precache_model(self.model);
if (self.spawnflags & 1) // FLOATING
else
self.movetype = MOVETYPE_TOSS;
- if (!self.noise)
- self.noise = "misc/itempickup.wav";
-
precache_sound(self.noise);
self.mdl = self.model;
};
-/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
-SILVER key.
+/*QUAKED item_key (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
+A key entity.
+The itemkeys should contain one of the following key IDs:
+1 - GOLD key -
+2 - SILVER key
+4 - BRONZE key
+8 - RED keycard
+16 - BLUE keycard
+32 - GREEN keycard
+Custom keys:
+... - last key is 1<<23
+Keys with bigger Id than 32 don't have a default netname and model, if you use one of them, you MUST provide those.
-----------KEYS------------
colormod: color of the key (default: '.9 .9 .9').
+itemkeys: a key Id.
message: message to print when player picks up this key.
-model: custom model to use.
+model: custom key model to use.
+netname: the display name of the key.
noise: custom sound to play when player picks up the key.
-------- SPAWNFLAGS --------
FLOATING: the item will float in air, instead of aligning to the floor by falling
---------NOTES----------
+This is the only correct way to put keys on the map!
+
+itemkeys MUST always have exactly one bit set.
*/
-void spawnfunc_item_key1(void) {
- if (!self.model)
- self.model = "models/keys/key.md3";
+void spawnfunc_item_key() {
+ local string _model, _netname;
+ local vector _colormod;
+ // reject this entity if more than one key was set!
+ if (self.itemkeys>0 && (self.itemkeys & (self.itemkeys-1)) != 0) {
+ objerror("item_key.itemkeys must contain only 1 bit set specifying the key it represents!");
+ remove(self);
+ return;
+ }
+
+ // find default netname and colormod
+ switch(self.itemkeys) {
+ case 1:
+ _netname = "GOLD key";
+ _colormod = '1 .9 0';
+ break;
+
+ case 2:
+ _netname = "SILVER key";
+ _colormod = '.9 .9 .9';
+ break;
+
+ case 4:
+ _netname = "BRONZE key";
+ _colormod = '.6 .25 0';
+ break;
+
+ case 8:
+ _netname = "RED keycard";
+ _colormod = '.9 0 0';
+ break;
+
+ case 16:
+ _netname = "BLUE keycard";
+ _colormod = '0 0 .9';
+ break;
+
+ case 32:
+ _netname = "GREEN keycard";
+ _colormod = '0 .9 0';
+ break;
+
+ default:
+ if (!self.netname) {
+ objerror("item_key doesn't have a default name for this key and a custom one was not specified!");
+ remove(self);
+ return;
+ } else if (!self.colormod) {
+ _colormod = '1 1 1';
+ }
+ break;
+
+ }
+
+ // find default model
+ if (self.itemkeys <= ITEM_KEY_BIT(2)) {
+ _model = "models/keys/key.md3";
+ } else if (self.itemkeys >= ITEM_KEY_BIT(3) && self.itemkeys <= ITEM_KEY_BIT(5)) {
+ _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model!
+ } else if (!self.model) {
+ objerror("item_key doesn't have a default model for this key and a custom one was not specified!");
+ remove(self);
+ return;
+ }
+
+ // set defailt netname
+ if (!self.netname)
+ self.netname = _netname;
+
+ // set default colormod
if (!self.colormod)
- self.colormod = '.9 .9 .9';
+ self.colormod = _colormod;
+ // set default model
+ if (!self.model)
+ self.model = _model;
+
+ // set default pickup message
if (!self.message)
- self.message = "You've picked up the silver key!";
-
- spawn_item_key(KEYS_SILVER_KEY);
+ self.message = strzone(strcat("You've picked up the ", self.netname, "!"));
+
+ if (!self.noise)
+ self.noise = "misc/itempickup.wav";
+
+ // save the name for later
+ item_keys_names[lowestbit(self.itemkeys)] = self.netname;
+
+ // put the key on the map
+ spawn_item_key();
+}
+
+/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
+SILVER key.
+-----------KEYS------------
+colormod: color of the key (default: '.9 .9 .9').
+message: message to print when player picks up this key.
+model: custom model to use.
+noise: custom sound to play when player picks up the key.
+-------- SPAWNFLAGS --------
+FLOATING: the item will float in air, instead of aligning to the floor by falling
+---------NOTES----------
+Don't use this entity on new maps! Use item_key instead.
+*/
+void spawnfunc_item_key1(void) {
+ self.classname = "item_key";
+ self.itemkeys = ITEM_KEY_BIT(1);
+ spawnfunc_item_key();
};
/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
-------- SPAWNFLAGS --------
FLOATING: the item will float in air, instead of aligning to the floor by falling
---------NOTES----------
+Don't use this entity on new maps! Use item_key instead.
*/
void spawnfunc_item_key2(void) {
- if (!self.model)
- self.model = "models/keys/key.md3";
-
- if (!self.colormod)
- self.colormod = '1 .9 0';
-
- if (!self.message)
- self.message = "You've picked up the gold key!";
-
- spawn_item_key(KEYS_GOLD_KEY);
+ self.classname = "item_key";
+ self.itemkeys = ITEM_KEY_BIT(0);
+ spawnfunc_item_key();
};
================================
*/
-/*
-trigger givent targets
-*/
+/**
+ * trigger givent targets
+ */
void trigger_keylock_trigger(string s) {
local entity t, stemp, otemp, atemp;
activator = atemp;
};
-/*
-kill killtarget of trigger keylock.
-*/
+/**
+ * kill killtarget of trigger keylock.
+ */
void trigger_keylock_kill(string s) {
- local entity t, stemp, otemp, atemp;
-
- stemp = self;
- otemp = other;
- atemp = activator;
-
+ local entity t;
for(t = world; (t = find(t, targetname, s)); )
- if (t.use) {
- remove(t);
- }
-
- self = stemp;
- other = otemp;
- activator = atemp;
+ remove(t);
};
void trigger_keylock_touch(void) {
- local float key_used, silver_key_missing, gold_key_missing, started_delay;
+ local float key_used, started_delay;
key_used = FALSE;
- silver_key_missing = FALSE;
- gold_key_missing = FALSE;
started_delay = FALSE;
// only player may trigger the lock
// check silver key
- if (self.itemkeys & KEYS_SILVER_KEY) {
- // lock still requires the SILVER key
- if (other.itemkeys & KEYS_SILVER_KEY) {
- self.itemkeys &~= KEYS_SILVER_KEY;
- key_used = TRUE;
- } else {
- silver_key_missing = TRUE;
- }
- }
-
- // check gold key
- if (self.itemkeys & KEYS_GOLD_KEY) {
- // lock still requires the GOLD key
- if (other.itemkeys & KEYS_GOLD_KEY) {
- self.itemkeys &~= KEYS_GOLD_KEY;
- key_used = TRUE;
- } else {
- gold_key_missing = TRUE;
- }
- }
-
+ if (self.itemkeys)
+ key_used = item_keys_usekey(self, other);
activator = other;
- if (silver_key_missing) {
- // silver key is missing
- if (self.delay <= time) {
- if (self.target4) {
- trigger_keylock_trigger(self.target4);
- started_delay = TRUE;
- self.delay = time + self.wait;
- }
- }
- }
-
- if (gold_key_missing) {
- // gold key is missing
- if (self.delay <= time || started_delay) {
- if (self.target3) {
- trigger_keylock_trigger(self.target3);
- started_delay = TRUE;
- self.delay = time + self.wait;
- }
- }
-
- }
-
- if (silver_key_missing || gold_key_missing) {
+ if (self.itemkeys) {
// at least one of the keys is missing
-
if (key_used) {
- // one key was given, but an other one is missing!
+ // one or more keys were given, but others are still missing!
play2(other, self.noise1);
- if (silver_key_missing)
- centerprint(other, "You also need the silver key!");
- else if (gold_key_missing)
- centerprint(other, "You also need the gold key!");
+ centerprint(other, strcat("You also need ", item_keys_keylist(self.itemkeys), "!"));
+ other.key_door_messagetime = time + 2;
+ } else if (other.key_door_messagetime <= time) {
+ // no keys were given
+ play2(other, self.noise2);
+ centerprint(other, strcat("You need ", item_keys_keylist(self.itemkeys), "!"));
other.key_door_messagetime = time + 2;
- } else {
- if (other.key_door_messagetime <= time) {
- play2(other, self.noise2);
- centerprint(other, self.message2);
- other.key_door_messagetime = time + 2;
- }
}
- if (self.delay <= time || started_delay == TRUE) {
- if (self.target2) {
- trigger_keylock_trigger(self.target2);
- started_delay = TRUE;
- self.delay = time + self.wait;
- }
-
+ // trigger target2
+ if (self.delay <= time || started_delay == TRUE)
+ if (self.target2) {
+ trigger_keylock_trigger(self.target2);
+ started_delay = TRUE;
+ self.delay = time + self.wait;
}
} else {
// all keys were given!
};
-/*QUAKED trigger_keylock (.0 .5 .8) ? - - - GOLD_KEY SILVER_KEY
+/*QUAKED trigger_keylock (.0 .5 .8) ?
Keylock trigger. Must target other entities.
This trigger will trigger target entities when all required keys are provided.
-------- KEYS --------
-wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
-sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav
+itemkeys: A bit field with key IDs that are needed to open this lock.
+sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default)
target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger
target2: trigger all entities with this targetname when triggered without giving it all the required keys.
-target3: trigger all entities with this targetname when triggered with GOLD_KEY missing (requires GOLD_KEY spawnflag)
-target4: trigger all entities with this targetname when triggered with SILVER_KEY missing (requires SILVER_KEY spawnflag)
+killtarget: remove all entities with this targetname when triggered with all the needed keys.
message: print this message to the player who activated the trigger when all needed keys have been given.
message2: print this message to the player who activated the trigger when not all of the needed keys have been given.
noise: sound to play when lock gets unlocked (default: see sounds)
-noise1: sound to play when only one of the needed key was used (default: misc/decreasevalue.wav)
+noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav)
noise2: sound to play when a key is missing (default: misc/talk.wav)
-killtarget: remove all entities with this targetname when triggered with all the needed keys.
--------- SPAWNFLAGS --------
-GOLD_KEY: causes the door to open only if the activator holds a gold key.
-SILVER_KEY: causes the door to open only if the activator holds a silver key.
+wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
---------NOTES----------
-If spawned without any key specified, this trigger will remove itself.
+If spawned without any key specified in itemkeys, this trigger will display an error and remove itself.
message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone.
*/
void spawnfunc_trigger_keylock(void) {
- if (!(self.spawnflags & (SPAWNFLAGS_SILVER_KEY | SPAWNFLAGS_GOLD_KEY))) {
+ if (!self.itemkeys) {
remove(self);
return;
}
- // give the trigger the silver key
- if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
- self.itemkeys |= KEYS_SILVER_KEY;
-
- // give the trigger the gold key
- if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
- self.itemkeys |= KEYS_GOLD_KEY;
-
- if (!self.message2) {
- // generate default missing key message
- if (self.itemkeys & (KEYS_GOLD_KEY | KEYS_SILVER_KEY) == KEYS_GOLD_KEY | KEYS_SILVER_KEY) {
- self.message2 = "Silver key and gold key required!";
- } else if (self.itemkeys & KEYS_GOLD_KEY) {
- self.message2 = "Gold key required!";
- } else if (self.itemkeys & KEYS_SILVER_KEY) {
- self.message2 = "Silver key required!";
- }
- }
-
- if (!self.message) {
+ // set unlocked message
+ if (!self.message)
self.message = "Unlocked!";
- }
+ // set default unlock noise
if (!self.noise) {
- if (self.sounds == 1) {
+ if (self.sounds == 1)
self.noise = "misc/secret.wav";
- } else if (self.sounds == 2) {
+ else if (self.sounds == 2)
self.noise = "misc/talk.wav";
- } else { //if (self.sounds == 3) {
+ else //if (self.sounds == 3) {
self.noise = "misc/trigger1.wav";
- }
}
-
+
+ // set default use key sound
if (!self.noise1)
self.noise1 = "misc/decreasevalue.wav";
-
+
+ // set closed sourd
if (!self.noise2)
self.noise2 = "misc/talk.wav";
+ // delay between triggering message2 and trigger2
if (!self.wait)
self.wait = 5;
+ // precache sounds
precache_sound(self.noise);
precache_sound(self.noise1);
precache_sound(self.noise2);
self.touch = trigger_keylock_touch;
};
+
else
door = self;
- if (door.spawnflags & (SPAWNFLAGS_GOLD_KEY | SPAWNFLAGS_SILVER_KEY)) {
- // this door require a key
- // only a player can have a key
- if (other.classname != "player")
- return FALSE;
-
- // check gold key
- if (self.owner.spawnflags & SPAWNFLAGS_GOLD_KEY) {
- if (!(other.itemkeys & KEYS_GOLD_KEY)) {
- if (other.key_door_messagetime <= time) {
- play2(other, "misc/talk.wav");
- centerprint(other, "You don't have the gold key!");
- other.key_door_messagetime = time + 2;
- }
- return FALSE;
- } else {
- self.owner.spawnflags &~= SPAWNFLAGS_GOLD_KEY;
- }
+ // no key needed
+ if not(door.itemkeys)
+ return TRUE;
+
+ // this door require a key
+ // only a player can have a key
+ if (other.classname != "player")
+ return FALSE;
+
+ if (item_keys_usekey(door, other)) {
+ // some keys were used
+ if (other.key_door_messagetime <= time) {
+ play2(other, "misc/talk.wav");
+ centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
+ other.key_door_messagetime = time + 2;
}
-
- // check silver key
- if (self.owner.spawnflags & SPAWNFLAGS_SILVER_KEY) {
- if (!(other.itemkeys & KEYS_SILVER_KEY)) {
- if (other.key_door_messagetime <= time) {
- play2(other, "misc/talk.wav");
- centerprint(other, "You don't have the silver key!");
- other.key_door_messagetime = time + 2;
- }
- return FALSE;
- } else {
- self.owner.spawnflags &~= SPAWNFLAGS_SILVER_KEY;
- }
+ } else {
+ // no keys were used
+ if (other.key_door_messagetime <= time) {
+ play2(other, "misc/talk.wav");
+ centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
+ other.key_door_messagetime = time + 2;
}
-
+ }
+
+ if (door.itemkeys) {
// door is now unlocked
play2(other, "misc/talk.wav");
centerprint(other, "Door unlocked!");
- }
-
- return TRUE;
+ return TRUE;
+ } else
+ return FALSE;
}
return;
self.health = self.health - damage;
- if (self.spawnflags & SPAWNFLAGS_GOLD_KEY || self.spawnflags & SPAWNFLAGS_SILVER_KEY) {
+ if (self.itemkeys) {
// don't allow opening doors through damage if keys are required
return;
}
self.think = SUB_Null;
}
+// spawnflags require key (for now only func_door)
+#define SPAWNFLAGS_GOLD_KEY 8
+#define SPAWNFLAGS_SILVER_KEY 16
void spawnfunc_func_door()
{
- //dprint("spawnfunc_func_door() spawnflags=", ftos(self.spawnflags));
- //dprint(", gold_key=", ftos(self.spawnflags & SPAWNFLAGS_GOLD_KEY));
- //dprint(", silver_key=", ftos(self.spawnflags & SPAWNFLAGS_SILVER_KEY), "\n");
-
+ // Quake 1 keys compatibility
+ if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
+ self.itemkeys |= ITEM_KEY_BIT(0);
+ if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
+ self.itemkeys |= ITEM_KEY_BIT(1);
+
//if (!self.deathtype) // map makers can override this
// self.deathtype = " got in the way";
SetMovedir ();