prvm_leaktest_ignore_classnames "ctf_team dom_team tdm_team"
sv_allowdownloads_inarchive 1 // for csprogs.dat
+sv_allowdownloads 0 // download protocol is evil
set g_jump_grunt 0 "Do you make a grunting noise every time you jump? Is it the same grunting noise every time?"
WriteByte(MSG_ONE, SVC_SETVIEW);
WriteEntity(MSG_ONE, self);
}
+
+ // reset player keys
+ self.itemkeys = 0;
// player is dead and becomes observer
// FIXME fix LMS scoring for new system
.float checkfail;
void SV_ParseClientCommand(string s) {
- string cmd;
- float tokens;
float i;
entity e;
- tokens = tokenize_console(s);
-
- cmd = strtolower(argv(0));
- if(cmd != "reportcvar")
- if(cmd != "sentcvar")
- if(cmd != "pause")
- if(cmd != "prespawn")
- if(cmd != "spawn")
- if(cmd != "begin")
+ cmd_argc = tokenize_console(s);
+ cmd_string = s;
+ cmd_name = strtolower(argv(0));
+ if(cmd_name != "reportcvar")
+ if(cmd_name != "sentcvar")
+ if(cmd_name != "pause")
+ if(cmd_name != "prespawn")
+ if(cmd_name != "spawn")
+ if(cmd_name != "begin")
{
if(cmd_floodcheck())
return;
}
+ if(MUTATOR_CALLHOOK(SV_ParseClientCommand))
+ return; // already handled
+
if(GameCommand_Vote(s, self)) {
return;
} else if(GameCommand_MapVote(argv(0))) {
return;
- } else if(cmd == "checkfail") {
+ } else if(cmd_name == "checkfail") {
print(sprintf("CHECKFAIL: %s (%s) epically failed check %s\n", self.netname, self.netaddress, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))));
self.checkfail = 1;
- } else if(cmd == "autoswitch") {
+ } else if(cmd_name == "autoswitch") {
// be backwards compatible with older clients (enabled)
self.autoswitch = ("0" != argv(1));
string autoswitchmsg;
autoswitchmsg = "off";
}
sprint(self, strcat("^1autoswitch turned ", autoswitchmsg, "\n"));
- } else if(cmd == "clientversion") {
+ } else if(cmd_name == "clientversion") {
if not(self.flags & FL_CLIENT)
return;
if (argv(1) == "$gameversion") {
self.classname = "observer";
stuffcmd(self,"menu_showteamselect\n");
}
- } else if(cmd == "reportcvar") { // old system
+ } else if(cmd_name == "reportcvar") { // old system
if(substring(argv(2), 0, 1) == "$") // undefined cvar: use the default value on the server then
{
s = strcat(substring(s, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
- tokens = tokenize_console(s);
+ cmd_argc = tokenize_console(s);
}
GetCvars(1);
- } else if(cmd == "sentcvar") { // new system
- if(tokens == 2) // undefined cvar: use the default value on the server then
+ } else if(cmd_name == "sentcvar") { // new system
+ if(cmd_argc == 2) // undefined cvar: use the default value on the server then
{
s = strcat(substring(s, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
- tokens = tokenize_console(s);
+ cmd_argc = tokenize_console(s);
}
GetCvars(1);
- } else if(cmd == "spectate") {
+ } else if(cmd_name == "spectate") {
if(cmd_floodcheck())
return;
if not(self.flags & FL_CLIENT)
sprint(self, "WARNING: you will spectate in the next round.\n");
self.caplayer = 0;
}
- } else if(cmd == "join") {
+ } else if(cmd_name == "join") {
if not(self.flags & FL_CLIENT)
return;
if(!g_arena)
centerprint(self, PREVENT_JOIN_TEXT);
}
}
- } else if( cmd == "selectteam" ) {
+ } else if( cmd_name == "selectteam" ) {
if not(self.flags & FL_CLIENT)
return;
if( !teamplay ) {
} else {
sprint( self, strcat( "selectteam none/red/blue/yellow/pink/auto - \"", argv(1), "\" not recognised\n" ) );
}
- } else if(cmd == "ready") {
+ } else if(cmd_name == "ready") {
if not(self.flags & FL_CLIENT)
return;
sprint(self, "^1Game has already been restarted\n");
}
}
- } else if(cmd == "maplist") {
+ } else if(cmd_name == "maplist") {
sprint(self, maplist_reply);
- } else if(cmd == "lsmaps") {
+ } else if(cmd_name == "lsmaps") {
sprint(self, lsmaps_reply);
- } else if(cmd == "lsnewmaps") {
+ } else if(cmd_name == "lsnewmaps") {
sprint(self, lsnewmaps_reply);
- } else if(cmd == "records") {
+ } else if(cmd_name == "records") {
for(i = 0; i < 10; ++i)
sprint(self, records_reply[i]);
- } else if(cmd == "ladder") {
+ } else if(cmd_name == "ladder") {
sprint(self, ladder_reply);
- } else if(cmd == "rankings") {
+ } else if(cmd_name == "rankings") {
sprint(self, rankings_reply);
- } else if(cmd == "voice") {
- if(tokens >= 3)
+ } else if(cmd_name == "voice") {
+ if(cmd_argc >= 3)
VoiceMessage(argv(1), substring(s, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
else
VoiceMessage(argv(1), "");
- } else if(cmd == "say") {
- if(tokens >= 2)
+ } else if(cmd_name == "say") {
+ if(cmd_argc >= 2)
Say(self, FALSE, world, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
//clientcommand(self, formatmessage(s));
- } else if(cmd == "say_team") {
- if(tokens >= 2)
+ } else if(cmd_name == "say_team") {
+ if(cmd_argc >= 2)
Say(self, TRUE, world, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
//clientcommand(self, formatmessage(s));
- } else if(cmd == "tell") {
- e = GetCommandPlayerSlotTargetFromTokenizedCommand(tokens, 1);
- if(e && tokens > ParseCommandPlayerSlotTarget_firsttoken)
+ } else if(cmd_name == "tell") {
+ e = GetCommandPlayerSlotTargetFromTokenizedCommand(cmd_argc, 1);
+ if(e && cmd_argc > ParseCommandPlayerSlotTarget_firsttoken)
{
Say(self, FALSE, e, substring(s, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)), TRUE);
}
else
{
- if(tokens > ParseCommandPlayerSlotTarget_firsttoken)
+ if(cmd_argc > ParseCommandPlayerSlotTarget_firsttoken)
trigger_magicear_processmessage_forallears(self, -1, world, substring(s, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)));
sprint(self, "ERROR: usage: tell # playerid text...\n");
}
//clientcommand(self, formatmessage(s));
- } else if(cmd == "info") {
- cmd = cvar_string_builtin(strcat("sv_info_", argv(1))); // This needed fixed for the cvar check
- if(cmd == "")
+ } else if(cmd_name == "info") {
+ cmd_name = cvar_string_builtin(strcat("sv_info_", argv(1))); // This needed fixed for the cvar check
+ if(cmd_name == "")
sprint(self, "ERROR: unsupported info command\n");
else
- wordwrap_sprint(cmd, 1111);
- } else if(cmd == "suggestmap") {
+ wordwrap_sprint(cmd_name, 1111);
+ } else if(cmd_name == "suggestmap") {
sprint(self, strcat(MapVote_Suggest(argv(1)), "\n"));
- } else if(cmd == "timeout") {
+ } else if(cmd_name == "timeout") {
if not(self.flags & FL_CLIENT)
return;
if(autocvar_sv_timeout) {
else
sprint(self, "^7Error: only players can call a timeout!\n");
}
- } else if(cmd == "timein") {
+ } else if(cmd_name == "timein") {
if not(self.flags & FL_CLIENT)
return;
if(autocvar_sv_timeout) {
evaluateTimein();
}
- } else if(cmd == "teamstatus") {
+ } else if(cmd_name == "teamstatus") {
Score_NicePrint(self);
- } else if(cmd == "cvar_changes") {
+ } else if(cmd_name == "cvar_changes") {
sprint(self, cvar_changes);
- } else if(cmd == "cvar_purechanges") {
+ } else if(cmd_name == "cvar_purechanges") {
sprint(self, cvar_purechanges);
- } else if(CheatCommand(tokens)) {
+ } else if(CheatCommand(cmd_argc)) {
} else {
#if 0
//if(ctf_clientcommand())
// return;
// grep for Cmd_AddCommand_WithClientCommand to find them all
- if(cmd != "status")
- //if(cmd != "say") // handled above
- //if(cmd != "say_team") // handled above
- if(cmd != "kill")
- if(cmd != "pause")
- if(cmd != "ping")
- if(cmd != "name")
- if(cmd != "color")
- if(cmd != "rate")
- if(cmd != "pmodel")
- if(cmd != "playermodel")
- if(cmd != "playerskin")
- if(cmd != "prespawn")
- if(cmd != "spawn")
- if(cmd != "begin")
- if(cmd != "pings")
- if(cmd != "sv_startdownload")
- if(cmd != "download")
+ if(cmd_name != "status")
+ //if(cmd_name != "say") // handled above
+ //if(cmd_name != "say_team") // handled above
+ if(cmd_name != "kill")
+ if(cmd_name != "pause")
+ if(cmd_name != "ping")
+ if(cmd_name != "name")
+ if(cmd_name != "color")
+ if(cmd_name != "rate")
+ if(cmd_name != "pmodel")
+ if(cmd_name != "playermodel")
+ if(cmd_name != "playerskin")
+ if(cmd_name != "prespawn")
+ if(cmd_name != "spawn")
+ if(cmd_name != "begin")
+ if(cmd_name != "pings")
+ if(cmd_name != "sv_startdownload")
+ if(cmd_name != "download")
{
print("WARNING: Invalid clientcommand by ", self.netname, ": ", s, "\n");
return;
#endif
if(self.jointime > 0 && time > self.jointime + 10 && time > self.nickspamtime) // allow any changes in the first 10 seconds since joining
- if(cmd == "name" || cmd == "playermodel") // TODO also playerskin and color?
+ if(cmd_name == "name" || cmd_name == "playermodel") // TODO also playerskin and color?
{
if(self.nickspamtime == 0 || time > self.nickspamtime + autocvar_g_nick_flood_timeout)
// good, no serious flood
.float runes;
+// Keys player is holding
+.float itemkeys;
+// message delay for func_door locked by keys and key locks
+// this field is used on player entities
+.float key_door_messagetime;
+
.float version;
.float misc_bulletcounter; // replaces uzi & hlac bullet counter.
void PlayerUseKey();
+
--- /dev/null
+/*
+TODO:
+- add an unlock sound (here to trigger_keylock and to func_door)
+- display available keys on the HUD
+- make more tests
+- think about adding NOT_EASY/NOT_NORMAL/NOT_HARD for Q1 compatibility
+- 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_key
+================================
+*/
+
+/**
+ * Key touch handler.
+ */
+void item_key_touch(void) {
+ if (other.classname != "player")
+ return;
+
+ // player already picked up this key
+ if (other.itemkeys & self.itemkeys)
+ return;
+
+ other.itemkeys |= self.itemkeys;
+ play2(other, self.noise);
+
+ centerprint(other, self.message);
+};
+
+/**
+ * Spawn a key with given model, key code and color.
+ */
+void spawn_item_key() {
+ precache_model(self.model);
+
+ if (self.spawnflags & 1) // FLOATING
+ self.noalign = 1;
+
+ if (self.noalign)
+ self.movetype = MOVETYPE_NONE;
+ else
+ self.movetype = MOVETYPE_TOSS;
+
+ precache_sound(self.noise);
+
+ self.mdl = self.model;
+ self.effects = EF_LOWPRECISION;
+ setmodel(self, self.model);
+ //setsize(self, '-16 -16 -24', '16 16 32');
+ setorigin(self, self.origin + '0 0 32');
+ setsize(self, '-16 -16 -56', '16 16 0');
+ self.modelflags |= MF_ROTATE;
+ self.solid = SOLID_TRIGGER;
+
+ if (!self.noalign)
+ {
+ // first nudge it off the floor a little bit to avoid math errors
+ setorigin(self, self.origin + '0 0 1');
+ // note droptofloor returns FALSE if stuck/or would fall too far
+ droptofloor();
+ }
+
+ self.touch = item_key_touch;
+};
+
+
+/*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 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_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 = _colormod;
+
+ // set default model
+ if (!self.model)
+ self.model = _model;
+
+ // set default pickup message
+ if (!self.message)
+ 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
+GOLD key.
+-----------KEYS------------
+colormod: color of the key (default: '1 .9 0').
+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_key2(void) {
+ self.classname = "item_key";
+ self.itemkeys = ITEM_KEY_BIT(0);
+ spawnfunc_item_key();
+};
+
+
+/*
+================================
+trigger_keylock
+================================
+*/
+
+/**
+ * trigger givent targets
+ */
+void trigger_keylock_trigger(string s) {
+ local entity t, stemp, otemp, atemp;
+
+ stemp = self;
+ otemp = other;
+ atemp = activator;
+
+
+ for(t = world; (t = find(t, targetname, s)); )
+ if (t.use) {
+ self = t;
+ other = stemp;
+ activator = atemp;
+ self.use();
+ }
+
+ self = stemp;
+ other = otemp;
+ activator = atemp;
+};
+
+/**
+ * kill killtarget of trigger keylock.
+ */
+void trigger_keylock_kill(string s) {
+ local entity t;
+ for(t = world; (t = find(t, targetname, s)); )
+ remove(t);
+};
+
+void trigger_keylock_touch(void) {
+ local float key_used, started_delay;
+
+ key_used = FALSE;
+ started_delay = FALSE;
+
+ // only player may trigger the lock
+ if (other.classname != "player")
+ return;
+
+
+ // check silver key
+ if (self.itemkeys)
+ key_used = item_keys_usekey(self, other);
+
+ activator = other;
+
+ if (self.itemkeys) {
+ // at least one of the keys is missing
+ if (key_used) {
+ // one or more keys were given, but others are still missing!
+ play2(other, self.noise1);
+ 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;
+ }
+
+ // 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!
+ play2(other, self.noise);
+ centerprint(other, self.message);
+
+ if (self.target)
+ trigger_keylock_trigger(self.target);
+
+ if (self.killtarget)
+ trigger_keylock_kill(self.killtarget);
+
+ remove(self);
+ }
+
+};
+
+/*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 --------
+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.
+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 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)
+wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
+---------NOTES----------
+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.itemkeys) {
+ remove(self);
+ return;
+ }
+
+ // set unlocked message
+ if (!self.message)
+ self.message = "Unlocked!";
+
+ // set default unlock noise
+ if (!self.noise) {
+ if (self.sounds == 1)
+ self.noise = "misc/secret.wav";
+ else if (self.sounds == 2)
+ self.noise = "misc/talk.wav";
+ 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);
+
+ EXACTTRIGGER_INIT;
+
+ self.touch = trigger_keylock_touch;
+};
+
+
--- /dev/null
+/**
+ * Returns the bit ID of a key
+ */
+#define ITEM_KEY_BIT(n) ( bitshift(1, n) )
+
+#define ITEM_KEY_MAX 24
+
+/**
+ * list of key names.
+ */
+string item_keys_names[ITEM_KEY_MAX];
+
+/**
+ * Use keys from p on l.
+ * Returns TRUE if any new keys were given, FALSE otherwise.
+ */
+float item_keys_usekey(entity l, entity p);
+
+/**
+ * Returns a string with a comma separated list of key names, as specified in keylist.
+ */
+string item_keys_keylist(float keylist);
+
// called when the use key is pressed
// if MUTATOR_RETURNVALUE is 1, don't do anything
// return 1 if the use key actually did something
+
+MUTATOR_HOOKABLE(SV_ParseClientCommand);
+ // called when a client command is parsed
+ // NOTE: hooks MUST start with if(MUTATOR_RETURNVALUE) return 0;
+ // NOTE: return 1 if you handled the command, return 0 to continue handling
+ // NOTE: THESE HOOKS MUST NEVER EVER CALL tokenize()
+ // INPUT
+ string cmd_name; // command name
+ float cmd_argc; // also, argv() can be used
+ string cmd_string; // whole command, use only if you really have to
+ /*
+ // example:
+ MUTATOR_HOOKFUNCTION(foo_SV_ParseClientCommand)
+ {
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ return 0;
+ if(cmd_name == "echocvar" && cmd_argc >= 2)
+ {
+ print(cvar_string(argv(1)), "\n");
+ return 1;
+ }
+ if(cmd_name == "echostring" && cmd_argc >= 2)
+ {
+ print(substring(cmd_string, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), "\n");
+ return 1;
+ }
+ return 0;
+ }
+ */
playerdemo.qh
+item_key.qh
+
scores_rules.qc
miscfunctions.qc
t_items.qc
cl_weapons.qc
cl_impulse.qc
+item_key.qc
ent_cs.qc
}
+
/*
=============================================================================
=============================================================================
*/
+float door_check_keys(void) {
+ local entity door;
+
+
+ if (self.owner)
+ door = self.owner;
+ else
+ door = self;
+
+ // 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;
+ }
+ } 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;
+ } else
+ return FALSE;
+}
+
+
void door_fire()
{
entity oself;
entity oself;
//dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+
if (self.owner)
{
oself = self;
void door_trigger_touch()
{
if (other.health < 1)
- if not(other.iscreature && other.deadflag == DEAD_NO)
- return;
+ if not(other.iscreature && other.deadflag == DEAD_NO)
+ return;
if (time < self.attack_finished_single)
return;
+
+ // check if door is locked
+ if (!door_check_keys())
+ return;
+
self.attack_finished_single = time + 1;
activator = other;
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
self.health = self.health - damage;
+
+ if (self.itemkeys) {
+ // don't allow opening doors through damage if keys are required
+ return;
+ }
+
if (self.health <= 0)
{
oself = self;
}
-/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
+/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
if two doors touch, they are assumed to be connected and operate as a unit.
TOGGLE causes the door to wait in both the start and end states for a trigger event.
START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
+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.
+
"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
"angle" determines the opening direction
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
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()
{
+ // 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 ();
self.blocked = door_blocked;
self.use = door_use;
- if(self.spawnflags & 8)
- self.dmg = 10000;
+ // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
+ // if(self.spawnflags & 8)
+ // self.dmg = 10000;
if(self.dmg && (!self.message))
self.message = "was squished";