]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' of git://de.git.xonotic.org/xonotic/xonotic-data.pk3dir
authorRudolf Polzer <divVerent@xonotic.org>
Tue, 25 Oct 2011 11:58:21 +0000 (13:58 +0200)
committerRudolf Polzer <divVerent@xonotic.org>
Tue, 25 Oct 2011 11:58:21 +0000 (13:58 +0200)
20 files changed:
defaultXonotic.cfg
models/keys/key.md3 [new file with mode: 0644]
models/keys/key.tga [new file with mode: 0644]
qcsrc/server/antilag.qc
qcsrc/server/antilag.qh
qcsrc/server/cl_client.qc
qcsrc/server/clientcommands.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_world.qc
qcsrc/server/item_key.qc [new file with mode: 0644]
qcsrc/server/item_key.qh [new file with mode: 0644]
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/gamemode_keepaway.qc
qcsrc/server/progs.src
qcsrc/server/t_plats.qc
qcsrc/server/tturrets/units/unit_tessla.qc
qcsrc/server/w_common.qc

index f92882f4424da8b0ca0f2693e7be3a64e0916e95..26ac83fd068ab88e24c7041d41d62f9d9e4a446f 100644 (file)
@@ -53,8 +53,6 @@ alias asay_drop "say_team (%l) dropped %w ; impulse 17"
 alias +hook +button6
 alias -hook -button6
 alias use "impulse 21"
-alias +use use // always send that impulse AND press the key (+use is engine internal command and executes anyway)
-set cl_newusekeysupported 1 // indicates that we always send the use impulse too, so they do not need to be synthesized
 alias ready "cmd ready"
 alias lockteams "sv_cmd lockteams"
 alias unlockteams "sv_cmd unlockteams"
@@ -1040,7 +1038,7 @@ bind MWHEELDOWN weapprev
 bind r reload
 bind BACKSPACE dropweapon
 bind g dropweapon
-bind f use
+bind f +use
 
 // misc
 bind e +hook
@@ -1712,6 +1710,7 @@ seta hud_width 560
 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?"
 
@@ -1756,6 +1755,8 @@ set cl_handicap 1 "the higher, the more damage you will receive (client setting)
 
 seta cl_clippedspectating 1 "movement collision for spectators so that you can't pass through walls and such. (client setting) NOTE: reconnect or use sendcvar command to update the choice." 
 
+seta cl_autoscreenshot 0 "client option to automatically take a screenshot once the map has ended (see also sv_autoscreenshot)"
+
 // must be at the bottom of this file:
 // alias for switching the teamselect menu
 alias menu_showteamselect "menu_cmd directmenu TeamSelect"
diff --git a/models/keys/key.md3 b/models/keys/key.md3
new file mode 100644 (file)
index 0000000..097c90c
Binary files /dev/null and b/models/keys/key.md3 differ
diff --git a/models/keys/key.tga b/models/keys/key.tga
new file mode 100644 (file)
index 0000000..5e70fa3
Binary files /dev/null and b/models/keys/key.tga differ
index 8a628a1bea91e1c5f9e5d5158b176b3eeedfad91..69d291a4ed69018848e0ebccb7c897b250b922d1 100644 (file)
@@ -14,6 +14,9 @@ void antilag_dummy()
 
 void antilag_record(entity e, float t)
 {
+    if(e.vehicle)
+        antilag_record(e.vehicle, t);
+
        if(time < e.(antilag_times[e.antilag_index]))
                return;
        e.antilag_index = e.antilag_index + 1;
@@ -24,6 +27,7 @@ void antilag_record(entity e, float t)
 
        if(e.antilag_debug)
                te_spark(antilag_takebackorigin(e, t - e.antilag_debug), '0 0 0', 32);
+
 }
 
 // finds the index BEFORE t
@@ -75,6 +79,7 @@ vector antilag_takebackorigin(entity e, float t)
 vector antilag_takebackavgvelocity(entity e, float t0, float t1)
 {
        vector o0, o1;
+
        if(t0 >= t1)
                return '0 0 0';
        o0 = antilag_takebackorigin(e, t0);
@@ -84,11 +89,17 @@ vector antilag_takebackavgvelocity(entity e, float t0, float t1)
 
 void antilag_takeback(entity e, float t)
 {
+    if(e.vehicle)
+        antilag_takeback(e.vehicle, t);
+
        e.antilag_saved_origin = e.origin;
        setorigin(e, antilag_takebackorigin(e, t));
 }
 
 void antilag_restore(entity e)
 {
+    if(e.vehicle)
+        antilag_restore(e.vehicle);
+
        setorigin(e, e.antilag_saved_origin);
 }
index 2a7343243dd6e4dc7cfd80d2cd2bd6394928197e..08e33e2e58c07c9ed8f18f703342826f0ddd7949 100644 (file)
@@ -1,5 +1,4 @@
 void antilag_record(entity e, float t);
-float antilag_find(entity e, float t);
 vector antilag_takebackorigin(entity e, float t);
 vector antilag_takebackavgvelocity(entity e, float t0, float t1);
 void antilag_takeback(entity e, float t);
index f41503ab09c790131d48727a08a8af085aeca642..de4c7431d5aafad3db68fe68f1da5e7ad5208070 100644 (file)
@@ -861,6 +861,9 @@ void PutClientInServer (void)
                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
@@ -2777,7 +2780,7 @@ void PlayerPreThink (void)
 
        MUTATOR_CALLHOOK(PlayerPreThink);
 
-       if(!self.cvar_cl_newusekeysupported)
+       if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
        {
                if(self.BUTTON_USE && !self.usekeypressed)
                        PlayerUseKey();
index 2ffa4afd9007578ba97f864e7f41621d02f328db..cfbc5ff18427f68e033e26bb70e5fc53afa447ad 100644 (file)
@@ -145,33 +145,34 @@ float cmd_floodcheck()
 
 .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;
@@ -181,7 +182,7 @@ void SV_ParseClientCommand(string s) {
                        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") {
@@ -201,21 +202,21 @@ void SV_ParseClientCommand(string s) {
                        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)
@@ -244,7 +245,7 @@ void SV_ParseClientCommand(string s) {
                        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)
@@ -265,7 +266,7 @@ void SV_ParseClientCommand(string s) {
                                centerprint(self, PREVENT_JOIN_TEXT);
                        }
                }
-       } else if( cmd == "selectteam" ) {
+       } else if( cmd_name == "selectteam" ) {
                if not(self.flags & FL_CLIENT)
                        return;
                if( !teamplay ) {
@@ -301,7 +302,7 @@ void SV_ParseClientCommand(string s) {
                } 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;
 
@@ -328,54 +329,54 @@ void SV_ParseClientCommand(string s) {
                                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) {
@@ -388,42 +389,42 @@ void SV_ParseClientCommand(string s) {
                        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;
@@ -431,7 +432,7 @@ void SV_ParseClientCommand(string s) {
 #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
index 6c37ac92d8b3f1f83436a1264731716c50908dd5..b84d03164d312d8c2b3b410bb8c86a602bb1fca0 100644 (file)
@@ -246,6 +246,12 @@ float alreadychangedlevel;
 
 .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;
 
@@ -315,6 +321,7 @@ float default_weapon_alpha;
 .float cvar_cl_handicap;
 .float cvar_cl_playerdetailreduction;
 .float cvar_cl_clippedspectating;
+.float cvar_cl_autoscreenshot;
 .float cvar_cl_movement_track_canjump;
 .float cvar_cl_newusekeysupported;
 
@@ -662,3 +669,4 @@ float serverflags;
 .float misc_bulletcounter;     // replaces uzi & hlac bullet counter.
 
 void PlayerUseKey();
+
index 5a583fc15ad101f07180877866f28641c5247c07..d7d2287ed6b093a23b63092caccfb496258af825 100644 (file)
@@ -329,7 +329,7 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
                }
                else if (attacker.classname == "player")
                {
-                       if(teamplay && attacker.team == targ.team)
+                       if(!IsDifferentTeam(attacker, targ))
                        {
                                if(attacker.team == COLOR_TEAM1)
                                        type = KILL_TEAM_RED;
@@ -561,7 +561,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
                                damage = 0;
                                force = '0 0 0';
                        }
-                       else if(teamplay && attacker.team == targ.team)
+                       else if(!IsDifferentTeam(attacker, targ))
                        {
                                if(autocvar_teamplay_mode == 1)
                                        damage = 0;
index e9f9b3454e18ef0151812464e7ce7aabecabf6b9..6a62d61b5d423e43debf69ab9461e031c427142c 100644 (file)
@@ -389,12 +389,9 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
        if (lag)
        {
                // take players back into the past
-               player = player_list;
-               while (player)
-               {
-                       antilag_takeback(player, time - lag);
-                       player = player.nextplayer;
-               }
+               FOR_EACH_PLAYER(player)
+                       if(player != forent)
+                               antilag_takeback(player, time - lag);
        }
 
        // do the trace
@@ -406,12 +403,9 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
        // restore players to current positions
        if (lag)
        {
-               player = player_list;
-               while (player)
-               {
-                       antilag_restore(player);
-                       player = player.nextplayer;
-               }
+               FOR_EACH_PLAYER(player)
+                       if(player != forent)
+                               antilag_restore(player);
        }
 
        // restore shooter solid type
index 5b28212583fa117487b4c9340f541b2c8dc2d7c0..6f318e27e623ab88338d7017f228dbe65e3b89a2 100644 (file)
@@ -1345,9 +1345,8 @@ void IntermissionThink()
 {
        FixIntermissionClient(self);
 
-       if(autocvar_sv_autoscreenshot)
-       if(self.autoscreenshot > 0)
-       if(time > self.autoscreenshot)
+       if( (autocvar_sv_autoscreenshot || self.cvar_cl_autoscreenshot)
+               && ((self.autoscreenshot > 0) && (time > self.autoscreenshot)) )
        {
                self.autoscreenshot = -1;
                if(clienttype(self) == CLIENTTYPE_REAL)
diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc
new file mode 100644 (file)
index 0000000..e39f281
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+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;
+};
+
+
diff --git a/qcsrc/server/item_key.qh b/qcsrc/server/item_key.qh
new file mode 100644 (file)
index 0000000..24ef1e9
--- /dev/null
@@ -0,0 +1,23 @@
+/**
+ * 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);
+
index 27c04f8a4d02499a2549a81c9aedec74ec53958e..7e675922833eb1107ffacfdcfedeb3c4269be048 100644 (file)
@@ -583,6 +583,7 @@ void GetCvars(float f)
        get_cvars_s = s;
        MUTATOR_CALLHOOK(GetCvars);
        GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
+       GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
        GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
        GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
        GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
index e26280e61a695cf0fedcff92ff3fd487c803a830..73b23bbd3f224201ecffa53725fe544a3b225573 100644 (file)
@@ -159,3 +159,32 @@ MUTATOR_HOOKABLE(PlayerUseKey);
        // 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;
+               }
+       */
index 8d3511eceda0750e74266d5649133b69f9c15e84..9134353e015574860dc4478938633ba80e3a56ec 100644 (file)
@@ -3,6 +3,7 @@ void ka_TouchEvent(void);
 void ka_RespawnBall(void);
 void ka_DropEvent(entity);
 void ka_TimeScoring(void);
+void ka_EventLog(string, entity);
 
 entity ka_ball;
 
@@ -125,6 +126,7 @@ void ka_TouchEvent() // runs any time that the ball comes in contact with someth
        other.effects |= autocvar_g_keepaway_ballcarrier_effects;
        
        // messages and sounds
+       ka_EventLog("pickup", other);
        Send_KillNotification(other.netname, "", "", KA_PICKUPBALL, MSG_KA);
        WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
        WriteString(MSG_BROADCAST, strcat(other.netname, "^7 has picked up the ball!"));
@@ -167,6 +169,7 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los
        plyr.effects &~= autocvar_g_keepaway_ballcarrier_effects;
 
        // messages and sounds
+       ka_EventLog("dropped", plyr);
        Send_KillNotification(plyr.netname, "", "", KA_DROPBALL, MSG_KA);
        WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
        WriteString(MSG_BROADCAST, strcat(plyr.netname, "^7 has dropped the ball!"));
@@ -205,6 +208,12 @@ void ka_TimeScoring()
        }
 }
 
+void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later
+{
+       if(autocvar_sv_eventlog)
+               GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
+}
+
 MUTATOR_HOOKFUNCTION(ka_Scoring)
 {
        if((frag_attacker != frag_target) && (frag_attacker.classname == "player"))
index 3d1e3970a1f34a31fdafda89a5367a8d803c832c..b5307d42893dda6cbb1a8715ac92c6209a20c50e 100644 (file)
@@ -64,6 +64,8 @@ vote.qh
 
 playerdemo.qh
 
+item_key.qh
+
 scores_rules.qc
 
 miscfunctions.qc
@@ -110,6 +112,7 @@ w_all.qc
 t_items.qc
 cl_weapons.qc
 cl_impulse.qc
+item_key.qc
 
 ent_cs.qc
 
index 9ac9df289243bd36ba3028cec515d4f5394117cb..f44dcb7b9476be220390010163dd175108282428 100644 (file)
@@ -885,6 +885,7 @@ void door_go_up()
 }
 
 
+
 /*
 =============================================================================
 
@@ -893,6 +894,50 @@ ACTIVATION FUNCTIONS
 =============================================================================
 */
 
+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;
@@ -958,6 +1003,7 @@ void door_use()
        entity oself;
 
        //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+       
        if (self.owner)
        {
                oself = self;
@@ -971,11 +1017,16 @@ void door_use()
 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;
@@ -992,6 +1043,12 @@ void door_damage(entity inflictor, entity attacker, float damage, float deathtyp
                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;
@@ -1271,13 +1328,17 @@ void LinkDoors()
 }
 
 
-/*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.
@@ -1311,8 +1372,17 @@ void door_reset()
        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 ();
@@ -1326,8 +1396,9 @@ void spawnfunc_func_door()
        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";
index 454557010d11f8b3328aa8a5381e5ad11cdc634b..6c2cce6a73341ad4d3b80fcd25a19233b269b688 100644 (file)
@@ -46,24 +46,45 @@ entity toast(entity from, float range, float damage)
 
 float turret_tesla_firecheck()
 {
-    if not (turret_stdproc_firecheck())
-        return 0;
+    // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
+    float do_target_scan;
+    
+    if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
+        do_target_scan = 1;
+
+    // Old target (if any) invalid?
+    if(self.target_validate_time < time)
+    if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
+    {
+        self.enemy = world;
+        self.target_validate_time = time + 0.5;
+        do_target_scan = 1;
+    }
 
-    self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
+    // But never more often then g_turrets_targetscan_mindelay!
+    if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
+        do_target_scan = 0;
 
-    self.enemy = turret_select_target();
+    if(do_target_scan)
+    {
+        self.enemy = turret_select_target();
+        self.target_select_time = time;
+    }
+
+    if not (turret_stdproc_firecheck())
+        return 0;
 
     if(self.enemy)
         return 1;
 
     return 0;
-
 }
 
+
 void turret_tesla_fire()
 {
-    entity e,t;
-    float d,r,i;
+    entity e, t;
+    float d, r, i;
 
     //w_deathtypestring = "discoverd how a tesla coil works";
 
index 4b465bd77aac8b6822925c202b0f00b541163b70..1a995191c2f81025e9c9235d145649f0b9f82c42 100644 (file)
@@ -418,7 +418,8 @@ void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, f
 
                if(lag)
                        FOR_EACH_PLAYER(pl)
-                               antilag_takeback(pl, time - lag);
+                               if(pl != self)
+                                       antilag_takeback(pl, time - lag);
 
                oldself = self;
                self = proj;
@@ -496,7 +497,8 @@ void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, f
 
                if(lag)
                        FOR_EACH_PLAYER(pl)
-                               antilag_restore(pl);
+                               if(pl != self)
+                                       antilag_restore(pl);
 
                remove(proj);