r2412 - in trunk/data: . models models/keyhunt models/sprites qcsrc/server

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Mon Apr 30 10:11:12 EDT 2007


Author: div0
Date: 2007-04-30 10:11:11 -0400 (Mon, 30 Apr 2007)
New Revision: 2412

Added:
   trunk/data/models/keyhunt/
   trunk/data/models/keyhunt/key.md3
   trunk/data/models/keyhunt/key.tga
   trunk/data/models/keyhunt/key2.md3
   trunk/data/models/keyhunt/key3.md3
   trunk/data/models/keyhunt/key_gloss.tga
   trunk/data/models/keyhunt/key_glow.tga
   trunk/data/models/keyhunt/key_norm.tga
   trunk/data/models/sprites/key-dropped.sp2
   trunk/data/models/sprites/key-dropped.tga
   trunk/data/models/sprites/keycarrier-blue.sp2
   trunk/data/models/sprites/keycarrier-blue.tga
   trunk/data/models/sprites/keycarrier-finish.sp2
   trunk/data/models/sprites/keycarrier-finish.tga
   trunk/data/models/sprites/keycarrier-friend.sp2
   trunk/data/models/sprites/keycarrier-friend.tga
   trunk/data/models/sprites/keycarrier-pink.sp2
   trunk/data/models/sprites/keycarrier-pink.tga
   trunk/data/models/sprites/keycarrier-red.sp2
   trunk/data/models/sprites/keycarrier-red.tga
   trunk/data/models/sprites/keycarrier-yellow.sp2
   trunk/data/models/sprites/keycarrier-yellow.tga
   trunk/data/qcsrc/server/keyhunt.qc
   trunk/data/qcsrc/server/keyhunt.qh
Modified:
   trunk/data/default.cfg
   trunk/data/game_reset.cfg
   trunk/data/qcsrc/server/cl_client.qc
   trunk/data/qcsrc/server/cl_player.qc
   trunk/data/qcsrc/server/clientcommands.qc
   trunk/data/qcsrc/server/g_damage.qc
   trunk/data/qcsrc/server/g_world.qc
   trunk/data/qcsrc/server/havocbot_roles.qc
   trunk/data/qcsrc/server/miscfunctions.qc
   trunk/data/qcsrc/server/progs.src
   trunk/data/qcsrc/server/teamplay.qc
   trunk/data/qcsrc/server/waypointsprites.qc
Log:
game mode "keyhunt", still in testing


Modified: trunk/data/default.cfg
===================================================================
--- trunk/data/default.cfg	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/default.cfg	2007-04-30 14:11:11 UTC (rev 2412)
@@ -713,3 +713,21 @@
 alias g_maplist_remove     "qc_cmd rpn /g_maplist g_maplist /$1 difference def"
 alias g_maplist_putfirst   "qc_cmd rpn /maps/$1.mapcfg fexists_assert /g_maplist /$1 g_maplist union def"
 alias g_maplist_shufflenow "qc_cmd rpn /g_maplist g_maplist shuffle def"
+
+// key hunt
+set g_keyhunt 0
+set g_balance_keyhunt_delay_return 30
+set g_balance_keyhunt_delay_round 5
+set g_balance_keyhunt_delay_tracking 10
+set g_balance_keyhunt_delay_fadeout 2
+set g_balance_keyhunt_delay_collect 1.5
+set g_balance_keyhunt_delay_drop 0.4
+set g_balance_keyhunt_maxdist 150
+set g_balance_keyhunt_score_collect 3
+set g_balance_keyhunt_score_carrierfrag 2
+set g_balance_keyhunt_score_dropkey 0
+set g_balance_keyhunt_score_capture 100
+set g_balance_keyhunt_score_destroyed 50
+set g_balance_keyhunt_score_destroyed_ownfactor 1
+set g_keyhunt_teams_override 0
+set g_keyhunt_teams 0

Modified: trunk/data/game_reset.cfg
===================================================================
--- trunk/data/game_reset.cfg	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/game_reset.cfg	2007-04-30 14:11:11 UTC (rev 2412)
@@ -14,6 +14,7 @@
 set g_lms 0
 set g_arena 0
 set g_campaign 0
+set g_keyhunt 0
 set teamplay 0
 set gamecfg 0
 

Added: trunk/data/models/keyhunt/key.md3
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/keyhunt/key.md3
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/keyhunt/key.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/keyhunt/key.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/keyhunt/key2.md3
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/keyhunt/key2.md3
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/keyhunt/key3.md3
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/keyhunt/key3.md3
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/keyhunt/key_gloss.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/keyhunt/key_gloss.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/keyhunt/key_glow.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/keyhunt/key_glow.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/keyhunt/key_norm.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/keyhunt/key_norm.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/key-dropped.sp2
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/key-dropped.sp2
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/key-dropped.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/key-dropped.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-blue.sp2
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-blue.sp2
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-blue.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-blue.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-finish.sp2
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-finish.sp2
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-finish.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-finish.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-friend.sp2
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-friend.sp2
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-friend.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-friend.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-pink.sp2
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-pink.sp2
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-pink.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-pink.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-red.sp2
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-red.sp2
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-red.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-red.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-yellow.sp2
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-yellow.sp2
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/models/sprites/keycarrier-yellow.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/sprites/keycarrier-yellow.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: trunk/data/qcsrc/server/cl_client.qc
===================================================================
--- trunk/data/qcsrc/server/cl_client.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/cl_client.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -287,13 +287,14 @@
 		WriteEntity(MSG_ONE, self);
 	}
 
-	WaypointSprite_PlayerDead();
-
 	DropAllRunes(self);
+	kh_Key_DropAll(self);
 
 	if(self.flagcarried)
 		DropFlag(self.flagcarried);
 
+	WaypointSprite_PlayerDead();
+
 	DistributeFragsAmongTeam(self, self.team, 1);
 
 	if(self.frags <= 0 && self.frags > -666 && cvar("g_lms") && self.killcount != -666)
@@ -831,6 +832,7 @@
 	WaypointSprite_PlayerGone();
 
 	DropAllRunes(self);
+	kh_Key_DropAll(self);
 
 	if(self.flagcarried)
 		DropFlag(self.flagcarried);
@@ -1508,6 +1510,7 @@
 			minstagib_ammocheck();
 
 		ctf_setstatus();
+		kh_setstatus();
 
 		//self.angles_y=self.v_angle_y + 90;   // temp
 

Modified: trunk/data/qcsrc/server/cl_player.qc
===================================================================
--- trunk/data/qcsrc/server/cl_player.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/cl_player.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -285,8 +285,6 @@
 	{
 		self.deaths += 1;
 
-		// clear waypoints
-		WaypointSprite_PlayerDead();
 		// become fully visible
 		self.alpha = 1;
 		// clear selected player display
@@ -296,8 +294,11 @@
 		// print an obituary message
 		Obituary (attacker, self, deathtype);
 		DropAllRunes(self);
+		kh_Key_DropAll(self);
 		if(self.flagcarried)
 			DropFlag(self.flagcarried);
+		// clear waypoints
+		WaypointSprite_PlayerDead();
 		// make the corpse upright (not tilted)
 		self.angles_x = 0;
 		self.angles_z = 0;

Modified: trunk/data/qcsrc/server/clientcommands.qc
===================================================================
--- trunk/data/qcsrc/server/clientcommands.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/clientcommands.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -319,6 +319,8 @@
 		if(self.classname == "player" && cvar("sv_spectate") == 1) {
 			if(self.flagcarried)
 				DropFlag(self.flagcarried);
+			kh_Key_DropAll(self);
+			WaypointSprite_PlayerDead();
 			DistributeFragsAmongTeam(self, self.team, 1.0);
 			self.classname = "observer";
 			PutClientInServer();

Modified: trunk/data/qcsrc/server/g_damage.qc
===================================================================
--- trunk/data/qcsrc/server/g_damage.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/g_damage.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -26,6 +26,11 @@
 		if(f > 0)
 			f = RunematchHandleFrags(attacker, targ, f);
 	}
+	else if(cvar("g_keyhunt"))
+	{
+		if(f > 0)
+			f = kh_HandleFrags(attacker, targ, f);
+	}
 	else if(cvar("g_lms"))
 	{
 		// count remaining lives, not frags in lms

Modified: trunk/data/qcsrc/server/g_world.qc
===================================================================
--- trunk/data/qcsrc/server/g_world.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/g_world.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -1559,7 +1559,7 @@
 	{
 		if(teams_matter)
 		{
-			if(cvar("g_tdm") || cvar("g_runematch") || cvar("g_ctf") || cvar("g_domination"))
+			if(cvar("g_tdm") || cvar("g_runematch") || cvar("g_ctf") || cvar("g_domination") || cvar("g_keyhunt"))
 				status = WinningCondition_MaxTeamSum(fraglimit);
 			//else if()
 			//	status = WinningCondition_MaxTeamMax(fraglimit);

Modified: trunk/data/qcsrc/server/havocbot_roles.qc
===================================================================
--- trunk/data/qcsrc/server/havocbot_roles.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/havocbot_roles.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -508,6 +508,183 @@
 	self.havocbot_role = havocbot_role_dom;
 };
 
+
+
+
+
+
+void(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy) havocbot_goalrating_kh =
+{
+	local entity head;
+	for(head = world; (head = find(head, classname, "item_kh_key")); )
+	{
+		if(head.owner == self)
+			continue;
+		if(!head.owner)
+			navigation_routerating(head, ratingscale_dropped);
+		else if(head.team == self.team)
+			navigation_routerating(head, ratingscale_team);
+		else
+			navigation_routerating(head, ratingscale_enemy);
+	}
+};
+
+void() havocbot_role_kh_carrier;
+void() havocbot_role_kh_defense;
+void() havocbot_role_kh_offense;
+void() havocbot_role_kh_freelancer;
+void() havocbot_role_kh_carrier =
+{
+	local entity head;
+	if (!(self.items & IT_KEY1))
+	{
+		dprint("changing role to freelancer\n");
+		self.havocbot_role = havocbot_role_kh_freelancer;
+		self.havocbot_role_timeout = 0;
+		return;
+	}
+
+	if (self.bot_strategytime < time)
+	{
+		float enemies_have_keys;
+
+		self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
+		navigation_goalrating_start();
+
+		enemies_have_keys = FALSE;
+		for(head = world; (head = find(head, classname, "item_kh_key")); )
+		{
+			if(head.owner)
+			if(head.team != self.team)
+			{
+				enemies_have_keys = TRUE;
+				break;
+			}
+		}
+
+		if(enemies_have_keys)
+			havocbot_goalrating_kh(4000, 4000, 100);
+		else
+			havocbot_goalrating_kh(10000, 10000, 1);
+		havocbot_goalrating_items(10000, self.origin, 10000);
+		navigation_goalrating_end();
+	}
+}
+
+void() havocbot_role_kh_defense =
+{
+	if (self.items & IT_KEY1)
+	{
+		dprint("changing role to carrier\n");
+		self.havocbot_role = havocbot_role_kh_carrier;
+		self.havocbot_role_timeout = 0;
+		return;
+	}
+
+	if (!self.havocbot_role_timeout)
+		self.havocbot_role_timeout = time + random() * 10 + 20;
+	if (time > self.havocbot_role_timeout)
+	{
+		dprint("changing role to freelancer\n");
+		self.havocbot_role = havocbot_role_kh_freelancer;
+		self.havocbot_role_timeout = 0;
+		return;
+	}
+
+	if (self.bot_strategytime < time)
+	{
+		self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
+		navigation_goalrating_start();
+		havocbot_goalrating_kh(4000, 1000, 1);
+		havocbot_goalrating_items(10000, self.origin, 10000);
+		navigation_goalrating_end();
+	}
+};
+
+void() havocbot_role_kh_offense =
+{
+	if (self.items & IT_KEY1)
+	{
+		dprint("changing role to carrier\n");
+		self.havocbot_role = havocbot_role_kh_carrier;
+		self.havocbot_role_timeout = 0;
+		return;
+	}
+
+	if (!self.havocbot_role_timeout)
+		self.havocbot_role_timeout = time + random() * 10 + 20;
+	if (time > self.havocbot_role_timeout)
+	{
+		dprint("changing role to freelancer\n");
+		self.havocbot_role = havocbot_role_kh_freelancer;
+		self.havocbot_role_timeout = 0;
+		return;
+	}
+
+	if (self.bot_strategytime < time)
+	{
+		self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
+		navigation_goalrating_start();
+		havocbot_goalrating_kh(1, 1000, 4000);
+		havocbot_goalrating_items(10000, self.origin, 10000);
+		navigation_goalrating_end();
+	}
+};
+
+void() havocbot_role_kh_freelancer =
+{
+	if (self.items & IT_KEY1)
+	{
+		dprint("changing role to carrier\n");
+		self.havocbot_role = havocbot_role_kh_carrier;
+		self.havocbot_role_timeout = 0;
+		return;
+	}
+
+	if (!self.havocbot_role_timeout)
+		self.havocbot_role_timeout = time + random() * 10 + 10;
+	if (time > self.havocbot_role_timeout)
+	{
+		if (random() < 0.5)
+		{
+			dprint("changing role to offense\n");
+			self.havocbot_role = havocbot_role_kh_offense;
+		}
+		else
+		{
+			dprint("changing role to defense\n");
+			self.havocbot_role = havocbot_role_kh_defense;
+		}
+		self.havocbot_role_timeout = 0;
+		return;
+	}
+
+	if (self.bot_strategytime < time)
+	{
+		self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
+		navigation_goalrating_start();
+		havocbot_goalrating_kh(1000, 4000, 1000);
+		havocbot_goalrating_items(10000, self.origin, 10000);
+		navigation_goalrating_end();
+	}
+};
+
+
+
+
+
+void() havocbot_chooserole_kh =
+{
+	local float r;
+	r = random() * 3;
+	if (r < 1)
+		self.havocbot_role = havocbot_role_kh_offense;
+	else if (r < 2)
+		self.havocbot_role = havocbot_role_kh_defense;
+	else
+		self.havocbot_role = havocbot_role_kh_freelancer;
+};
+
 void() havocbot_chooserole =
 {
 	dprint("choose a role...\n");
@@ -517,6 +694,8 @@
 		havocbot_chooserole_ctf();
 	else if (cvar("g_domination"))
 		havocbot_chooserole_dom();
+	else if (cvar("g_keyhunt"))
+		havocbot_chooserole_kh();
 	else // assume anything else is deathmatch
 		havocbot_chooserole_dm();
 };

Added: trunk/data/qcsrc/server/keyhunt.qc
===================================================================
--- trunk/data/qcsrc/server/keyhunt.qc	                        (rev 0)
+++ trunk/data/qcsrc/server/keyhunt.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -0,0 +1,593 @@
+string STR_ITEM_KH_KEY = "item_kh_key";
+#define FOR_EACH_KH_KEY(v) for(v = world; (v = find(v, classname, STR_ITEM_KH_KEY)); )
+
+typedef void(void) kh_Think_t;
+var kh_Think_t kh_Controller_Thinkfunc;
+string kh_Controller_Waitmsg;
+
+float kh_Team_ByID(float t)
+{
+	if(t == 0) return COLOR_TEAM1;
+	if(t == 1) return COLOR_TEAM2;
+	if(t == 2) return COLOR_TEAM3;
+	if(t == 3) return COLOR_TEAM4;
+	return 0;
+}
+
+entity kh_controller;
+float kh_tracking_enabled;
+float kh_teams;
+.entity kh_next, kh_prev; // linked list
+.float kh_droptime;
+
+float kh_sprite_dropped, kh_sprite_finish, kh_sprite_red, kh_sprite_blue, kh_sprite_pink, kh_sprite_yellow, kh_sprite_friend;
+
+float kh_GetCarrierSprite(float t)
+{
+	if(t == COLOR_TEAM1) return kh_sprite_red;
+	if(t == COLOR_TEAM2) return kh_sprite_blue;
+	if(t == COLOR_TEAM3) return kh_sprite_pink;
+	if(t == COLOR_TEAM4) return kh_sprite_yellow;
+	return 0;
+}
+
+void kh_Controller_SetThink(float t, string msg, kh_Think_t func)
+{
+	kh_Controller_Thinkfunc = func;
+	kh_controller.cnt = t;
+	if(kh_Controller_Waitmsg != "")
+		strunzone(kh_Controller_Waitmsg);
+	if(msg == "")
+		kh_Controller_Waitmsg = "";
+	else
+		kh_Controller_Waitmsg = strzone(msg);
+	if(t == 0)
+		kh_controller.nextthink = time; // force
+}
+
+void kh_Controller_Think()
+{
+	entity e;
+	if(self.cnt > 0)
+	{
+		if(kh_Controller_Waitmsg != "")
+		{
+			string s;
+			if(substring(kh_Controller_Waitmsg, strlen(kh_Controller_Waitmsg)-1, 1) == " ")
+				s = strcat(kh_Controller_Waitmsg, ftos(self.cnt));
+			else
+				s = kh_Controller_Waitmsg;
+
+			dprint(s, "\n");
+
+			FOR_EACH_PLAYER(e)
+				if(clienttype(e) == CLIENTTYPE_REAL)
+					centerprint_atprio(e, CENTERPRIO_SPAM, s);
+		}
+		self.cnt -= 1;
+	}
+	else if(self.cnt == 0)
+	{
+		FOR_EACH_PLAYER(e)
+			if(clienttype(e) == CLIENTTYPE_REAL)
+				centerprint_expire(e, CENTERPRIO_SPAM);
+		self.cnt -= 1;
+		kh_Controller_Thinkfunc();
+	}
+	self.nextthink = time + 1;
+}
+
+void kh_Log()
+{
+	string s;
+	if(!cvar("sv_eventlog"))
+		return;
+	// TODO ...
+	GameLogEcho(s, FALSE);
+}
+
+// frags f: take from cvar * f
+// frags 0: no frags
+void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner)
+{
+	float basefrags;
+	basefrags = cvar(strcat("g_balance_keyhunt_score_", what));
+
+	if(frags_player)
+		player.frags = player.frags + floor(0.5 + basefrags * frags_player);
+	if(frags_owner)
+		key.owner.frags = key.owner.frags + floor(0.5 + basefrags * frags_owner);
+}
+
+void kh_Key_Attach(entity key, float wpchange)
+{
+	setattachment(key, key.owner, "");
+	setorigin(key, '0 0 0'); // fixed later in think
+	key.angles = '0 0 0';
+	key.flags = 0;
+	key.solid = SOLID_NOT;
+	key.movetype = MOVETYPE_NONE;
+	key.team = key.owner.team;
+	key.nextthink = time;
+}
+
+void kh_Key_Detach(entity key, float wpchange)
+{
+	setattachment(key, world, "");
+	makevectors(key.owner.angles);
+	setorigin(key, key.owner.origin + key.origin_x * v_forward - key.origin_y * v_right + key.origin_z * v_up);
+	key.angles_y = key.owner.angles_y;
+	key.aiment = world;
+	key.flags = FL_ITEM;
+	key.solid = SOLID_TRIGGER;
+	key.movetype = MOVETYPE_TOSS;
+	key.pain_finished = time + cvar("g_balance_keyhunt_delay_return");
+	// let key.team stay
+}
+
+void kh_Key_AssignTo(entity key, entity player, float wpchange)
+{
+	if(key.owner == player)
+		return;
+
+	if(key.owner)
+	{
+		kh_Key_Detach(key, wpchange);
+
+		// remove from linked list
+		if(key.kh_next)
+			key.kh_next.kh_prev = key.kh_prev;
+		key.kh_prev.kh_next = key.kh_next;
+		key.kh_next = world;
+		key.kh_prev = world;
+
+		if(key.owner.kh_next == world)
+		{
+			// No longer a key carrier
+			if(wpchange)
+				WaypointSprite_Kill(key.owner.waypointsprite_attachedforcarrier);
+			else
+				WaypointSprite_DetachCarrier(key.owner);
+		}
+	}
+
+	key.owner = player;
+
+	if(player)
+	{
+		// insert into linked list
+		key.kh_next = player.kh_next;
+		key.kh_prev = player;
+		player.kh_next = key;
+		if(key.kh_next)
+			key.kh_next.kh_prev = key;
+
+		kh_Key_Attach(key, wpchange);
+
+		if(key.kh_next == world)
+		{
+			// player is now a key carrier
+			WaypointSprite_AttachCarrier("", player);
+			player.waypointsprite_attachedforcarrier.waypointsprite_for_player = kh_KeyCarrier_waypointsprite_for_player;
+			player.waypointsprite_attachedforcarrier.team = player.team;
+		}
+	}
+
+	key.pusher = world;
+}
+
+void kh_Key_Spawn(entity initial_owner, float angle)
+{
+	entity key;
+	key = spawn();
+	key.classname = STR_ITEM_KH_KEY;
+	key.touch = kh_Key_Touch;
+	key.think = kh_Key_Think;
+	key.nextthink = time;
+	key.items = IT_KEY1 | IT_KEY2;
+	key.cnt = angle;
+	setmodel(key, "models/keyhunt/key3.md3");
+	setsize(key, '0 0 -24', '0 0 25');
+
+	switch(initial_owner.team)
+	{
+		case COLOR_TEAM1:
+			key.netname = "^1red key";
+			key.colormod = '1.73 0.10 0.10';
+			break;
+		case COLOR_TEAM2:
+			key.netname = "^4blue key";
+			key.colormod = '0.10 0.10 1.73';
+			break;
+		case COLOR_TEAM3:
+			key.netname = "^6pink key";
+			key.colormod = '1.22 0.10 1.22';
+			break;
+		case COLOR_TEAM4:
+			key.netname = "^3yellow key";
+			key.colormod = '1.22 1.22 0.10';
+			break;
+		default:
+			key.netname = "NETGIER key";
+			key.colormod = '1.00 1.00 1.00';
+			break;
+	}
+
+	sprint(initial_owner, strcat("You got the ^2", key.netname, "\n"));
+
+	WaypointSprite_AttachCarrier("", key);
+	key.waypointsprite_attachedforcarrier.waypointsprite_for_player = kh_Key_waypointsprite_for_player;
+
+	kh_Key_AssignTo(key, initial_owner, TRUE);
+}
+
+void kh_Key_Remove(entity key)
+{
+	entity o;
+	o = key.owner;
+	kh_Key_AssignTo(key, world, FALSE);
+	if(o) // it was attached
+		WaypointSprite_Kill(key.waypointsprite_attachedforcarrier);
+	else // it was dropped
+		WaypointSprite_DetachCarrier(key);
+
+	remove(key);
+}
+
+void kh_Key_Collect(entity key, entity player)
+{
+	entity head;
+
+	kh_Scores_Event(player, key, "collect", 1, 0);
+	bprint(player.netname, "^7 collected the ", key.netname, "\n");
+	kh_Key_AssignTo(key, player, TRUE);
+
+	FOR_EACH_KH_KEY(key)
+		if(!key.owner || key.team != player.team)
+			goto notallowned;
+	FOR_EACH_PLAYER(head)
+	{
+		if(head.team == player.team)
+			if(head.kh_next)
+				centerprint(head, "All keys are in your team's hands!\n\nMeet the other key carriers ^1NOW^7!\n");
+			else
+				centerprint(head, "All keys are in your team's hands!\n\nHelp the key carriers to meet!\n");
+		else
+			centerprint(head, "All keys are in the enemy's hands!\n\nInterfere ^1NOW^7!\n");
+	}
+:notallowned
+}
+
+void kh_Key_DropAll(entity player)
+{
+	entity key;
+	entity mypusher;
+	mypusher = world;
+	if(player.pusher)
+		if(time < player.pushltime)
+			mypusher = player.pusher;
+	while((key = player.kh_next))
+	{
+		kh_Scores_Event(player, key, "losekey", 0, 0);
+		bprint(player.netname, "^7 lost the ", key.netname, "\n");
+		kh_Key_AssignTo(key, world, TRUE);
+		key.pusher = player.pusher;
+		key.pushltime = player.pushltime;
+	}
+}
+
+void kh_Key_Touch()
+{
+	if(self.owner) // already carried
+		return;
+	if(other.classname != "player")
+		return;
+	if(other.deadflag != DEAD_NO)
+		return;
+	if(other == self.enemy)
+		if(time < self.kh_droptime + cvar("g_balance_keyhunt_delay_collect"))
+			return; // you just dropped it!
+	kh_Key_Collect(self, other);
+}
+
+void kh_Key_Think()
+{
+	if(self.owner)
+	{
+		makevectors('0 1 0' * (self.cnt + math_mod(time, 360) * 45));
+		setorigin(self, v_forward * 16);
+
+		if(self.owner.buttonuse)
+		if(time >= self.owner.kh_droptime + cvar("g_balance_keyhunt_delay_drop"))
+		{
+			self.owner.kh_droptime = time;
+			self.kh_droptime = time; // prevent collecting this one for some time
+			self.enemy = self.owner;
+			self.pusher = world;
+			kh_Scores_Event(self.owner, self, "dropkey", 0, 0);
+			bprint(self.owner.netname, "^7 dropped the ", self.netname, "\n");
+			kh_Key_AssignTo(self, world, TRUE);
+		}
+	}
+
+	// if in nodrop or time over, end the round
+	if(!self.owner)
+		if(time > self.pain_finished)
+			kh_LoserTeam(self.team, self);
+	
+	if(self.owner)
+	{
+		entity key;
+		vector p;
+		float teem;
+		teem = self.team;
+		p = self.owner.origin;
+		FOR_EACH_KH_KEY(key)
+		{
+			if(key.owner == self.owner)
+				continue;
+			if(key.owner)
+			if(key.team == teem)
+			if(vlen(key.owner.origin - p) < cvar("g_balance_keyhunt_maxdist"))
+				continue;
+			goto not_winning;
+		}
+		kh_WinnerTeam(teem);
+:not_winning
+	}
+
+	self.nextthink = time + 0.1;
+}
+
+void kh_WinnerTeam(float teem)
+{
+	// all key carriers get some points
+	entity key;
+	float first;
+	float score;
+	score = 1.0 / kh_teams;
+	first = TRUE;
+	FOR_EACH_KH_KEY(key)
+	{
+		kh_Scores_Event(key.owner, key, "capture", score, 0);
+		if(key.owner.kh_next == key)
+		{
+			if(!first)
+				bprint("^7, ");
+			bprint(key.owner.netname);
+			first = FALSE;
+		}
+	}
+	bprint("^7 captured the keys for the ", ColoredTeamName(teem), "\n");
+	kh_FinishRound();
+}
+
+void kh_LoserTeam(float teem, entity lostkey)
+{
+	entity player, key, attacker;
+	float players;
+	float keys;
+	float score;
+
+	attacker = world;
+	if(lostkey.pusher)
+		if(player.pusher.team != player.team)
+			if(player.pusher.classname == "player")
+				attacker = lostkey.pusher;
+
+	players = keys = 0;
+
+	if(attacker)
+	{
+		kh_Scores_Event(attacker, world, "push", 1, 0);
+		centerprint(attacker, "Your push is the best!\n\n\n");
+		bprint("The ", ColoredTeamName(teem), "^7 could not take care of the ", lostkey.netname, "\n");
+	}
+	else
+	{
+		FOR_EACH_PLAYER(player)
+			if(player.team != teem)
+				++players;
+		
+		FOR_EACH_KH_KEY(key)
+			if(key.owner && key.team != teem)
+				++keys;
+
+		score = 1.0 / (keys * cvar("g_balance_keyhunt_score_destroyed_ownfactor") + players);
+
+		FOR_EACH_PLAYER(player)
+			if(player.team != teem)
+				kh_Scores_Event(player, world, "destroyed", score, 0);
+
+		FOR_EACH_KH_KEY(key)
+			if(key.owner && key.team != teem)
+				kh_Scores_Event(key.owner, world, "destroyed", score * cvar("g_balance_keyhunt_score_destroyed_ownfactor"), 0);
+
+		bprint("The ", ColoredTeamName(teem), "^7 could not take care of the ", lostkey.netname, "\n");
+	}
+
+	kh_FinishRound();
+}
+
+void kh_FinishRound()
+{
+	// prepare next round
+	entity key;
+	FOR_EACH_KH_KEY(key)
+		kh_Key_Remove(key);
+
+	kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_round"), "Round starts in ", kh_StartRound);
+}
+
+float kh_EnoughPlayers()
+{
+	float i, players, teem;
+	entity player;
+	
+	// find a random player per team
+	for(i = 0; i < kh_teams; ++i)
+	{
+		teem = kh_Team_ByID(i);
+		players = 0;
+		FOR_EACH_PLAYER(player)
+			if(player.deadflag == DEAD_NO)
+				if(player.team == teem)
+					++players;
+		if(players == 0)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+void kh_WaitForPlayers()
+{
+	if(kh_EnoughPlayers())
+		kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_round"), "Round starts in ", kh_StartRound);
+	else
+		kh_Controller_SetThink(1, "Waiting for players to join...", kh_WaitForPlayers);
+}
+
+void kh_StartRound()
+{
+	float i, players, teem;
+	entity player;
+
+	if(!kh_EnoughPlayers())
+	{
+		kh_Controller_SetThink(1, "Waiting for players to join...", kh_WaitForPlayers);
+		return;
+	}
+
+	for(i = 0; i < kh_teams; ++i)
+	{
+		teem = kh_Team_ByID(i);
+		players = 0;
+		entity my_player;
+		FOR_EACH_PLAYER(player)
+			if(player.deadflag == DEAD_NO)
+				if(player.team == teem)
+				{
+					++players;
+					if(random() * players <= 1)
+						my_player = player;
+				}
+		kh_Key_Spawn(my_player, 360 * i / kh_teams);
+	}
+
+	kh_tracking_enabled = FALSE;
+	kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_tracking"), "Scanning frequency range...", kh_EnableTrackingDevice);
+}
+
+void kh_setstatus()
+{
+	if(kh_teams)
+	{
+		float kh_KEY;
+		kh_KEY = (IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST); // the one impossible combination
+		if(self.kh_next)
+			self.items = self.items | kh_KEY;
+		else
+			self.items = self.items - (self.items & kh_KEY);
+	}
+}
+
+void kh_EnableTrackingDevice()
+{
+	kh_tracking_enabled = TRUE;
+}
+
+float kh_Key_waypointsprite_for_player(entity e)
+{
+	if(!kh_tracking_enabled)
+		return 0;
+	if(!self.owner)
+		return kh_sprite_dropped;
+	if(!self.owner.owner)
+		return kh_sprite_dropped;
+	return 0; // draw only when key is not owned
+}
+
+float kh_KeyCarrier_waypointsprite_for_player(entity e)
+{
+	entity key;
+	
+	if(e.classname != "player" || self.team != e.team)
+		if(!kh_tracking_enabled)
+			return 0;
+
+	FOR_EACH_KH_KEY(key)
+		if(!key.owner || key.team != e.team)
+		{
+			if(self.team == e.team)
+				return kh_sprite_friend;
+			else
+				return kh_GetCarrierSprite(self.team);
+		}
+
+	return kh_sprite_finish;
+}
+
+float kh_HandleFrags(entity attacker, entity targ, float f)
+{
+	float newfrags;
+
+	if(f <= 0)
+		return f;
+	if(attacker == targ)
+		return f;
+
+	if(targ.kh_next)
+		f = f - 1 + cvar("g_balance_keyhunt_score_carrierfrag");
+
+	if(newfrags)
+		f = f - 1 + newfrags;
+	return f;
+}
+
+void kh_init()
+{
+	precache_model("models/sprites/key-dropped.sp2");
+	precache_model("models/sprites/keycarrier-finish.sp2");
+	precache_model("models/sprites/keycarrier-friend.sp2");
+	precache_model("models/sprites/keycarrier-red.sp2");
+	precache_model("models/sprites/keycarrier-blue.sp2");
+	precache_model("models/sprites/keycarrier-pink.sp2");
+	precache_model("models/sprites/keycarrier-yellow.sp2");
+	precache_model("models/keyhunt/key3.md3");
+
+	// setup variables
+	kh_teams = cvar("g_keyhunt_teams_override");
+	if(kh_teams < 2)
+		kh_teams = cvar("g_keyhunt_teams");
+	if(kh_teams < 2)
+		kh_teams = 2;
+
+	// make a KH entity for controlling the game
+	kh_controller = spawn();
+	kh_controller.think = kh_Controller_Think;
+	kh_Controller_SetThink(0, "", kh_WaitForPlayers);
+
+	setmodel(kh_controller, "models/sprites/key-dropped.sp2");
+	kh_sprite_dropped = kh_controller.modelindex;
+	setmodel(kh_controller, "models/sprites/keycarrier-finish.sp2");
+	kh_sprite_finish = kh_controller.modelindex;
+	setmodel(kh_controller, "models/sprites/keycarrier-friend.sp2");
+	kh_sprite_friend = kh_controller.modelindex;
+	setmodel(kh_controller, "models/sprites/keycarrier-red.sp2");
+	kh_sprite_red = kh_controller.modelindex;
+	setmodel(kh_controller, "models/sprites/keycarrier-blue.sp2");
+	kh_sprite_blue = kh_controller.modelindex;
+	setmodel(kh_controller, "models/sprites/keycarrier-pink.sp2");
+	kh_sprite_pink = kh_controller.modelindex;
+	setmodel(kh_controller, "models/sprites/keycarrier-yellow.sp2");
+	kh_sprite_yellow = kh_controller.modelindex;
+	setmodel(kh_controller, "");
+}
+
+void kh_finalize()
+{
+	// to be called before intermission
+	kh_FinishRound();
+	remove(kh_controller);
+	kh_controller = world;
+}

Added: trunk/data/qcsrc/server/keyhunt.qh
===================================================================
--- trunk/data/qcsrc/server/keyhunt.qh	                        (rev 0)
+++ trunk/data/qcsrc/server/keyhunt.qh	2007-04-30 14:11:11 UTC (rev 2412)
@@ -0,0 +1,24 @@
+float kh_teams;
+
+void kh_Log();
+void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner);
+void kh_Key_Attach(entity key, float wpchange);
+void kh_Key_Detach(entity key, float wpchange);
+void kh_Key_AssignTo(entity key, entity player, float wpchange);
+void kh_Key_Spawn(entity initial_owner, float angle);
+void kh_Key_Remove(entity key);
+void kh_Key_Collect(entity key, entity player);
+void kh_Key_DropAll(entity player);
+void kh_Key_Touch();
+void kh_Key_Think();
+void kh_WinnerTeam(float teem);
+void kh_LoserTeam(float teem, entity lostkey);
+void kh_FinishRound();
+void kh_StartRound();
+void kh_EnableTrackingDevice();
+void kh_init();
+void kh_finalize();
+float kh_KeyCarrier_waypointsprite_for_player(entity e);
+float kh_Key_waypointsprite_for_player(entity e);
+void kh_setstatus();
+float kh_HandleFrags(entity attacker, entity targ, float f);

Modified: trunk/data/qcsrc/server/miscfunctions.qc
===================================================================
--- trunk/data/qcsrc/server/miscfunctions.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/miscfunctions.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -610,6 +610,7 @@
 */
 
 #define CENTERPRIO_POINT 1
+#define CENTERPRIO_SPAM 2
 #define CENTERPRIO_REBALANCE 2
 #define CENTERPRIO_VOTE 4
 #define CENTERPRIO_NORMAL 5

Modified: trunk/data/qcsrc/server/progs.src
===================================================================
--- trunk/data/qcsrc/server/progs.src	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/progs.src	2007-04-30 14:11:11 UTC (rev 2412)
@@ -12,6 +12,8 @@
 ../common/util.qh
 ../common/util.qc
 
+keyhunt.qh
+
 miscfunctions.qc
 
 waypointsprites.qc
@@ -84,3 +86,5 @@
 
 ../common/gamecommand.qc
 gamecommand.qc
+
+keyhunt.qc

Modified: trunk/data/qcsrc/server/teamplay.qc
===================================================================
--- trunk/data/qcsrc/server/teamplay.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/teamplay.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -5,13 +5,14 @@
 float GAME_RUNEMATCH		= 5;
 float GAME_LMS			= 6;
 float GAME_ARENA		= 7;
+float GAME_KEYHUNT		= 8;
 
 // client counts for each team
 float c1, c2, c3, c4;
 // # of bots on those teams
 float cb1, cb2, cb3, cb4;
 
-float g_domination, g_ctf, g_tdm;
+float g_domination, g_ctf, g_tdm, g_keyhunt;
 
 float audit_teams_time;
 
@@ -101,6 +102,7 @@
 	cvar_set("g_runematch", "0");
 	cvar_set("g_lms", "0");
 	cvar_set("g_arena", "0");
+	cvar_set("g_keyhunt", "0");
 	cvar_set("teamplay", "0");
 }
 
@@ -224,6 +226,16 @@
 		gamemode_name = "Arena";
 		teams_matter = 0;
 	}
+	else if(game == GAME_KEYHUNT || cvar("g_keyhunt"))
+	{
+		ResetGameCvars();
+		game = GAME_KEYHUNT;
+		cvar_set("g_keyhunt", "1");
+		fraglimit_override = cvar("g_keyhunt_point_limit");
+		ActivateTeamplay();
+		gamemode_name = "Key Hunt";
+		teams_matter = 1;
+	}
 	else
 	{
 		// we can only assume...
@@ -260,6 +272,8 @@
 		runematch_init();
 	else if (game == GAME_TEAM_DEATHMATCH)//cvar("g_runematch"))
 		tdm_init();
+	else if (game == GAME_KEYHUNT)//cvar("g_keyhunt"))
+		kh_init();
 
 	// those mutators rule each other out
 	if(cvar("g_minstagib"))
@@ -286,6 +300,7 @@
 	g_domination = cvar("g_domination");
 	g_ctf = cvar("g_ctf");
 	g_tdm = cvar("g_tdm");
+	g_keyhunt = cvar("g_keyhunt");
 }
 
 string GetClientVersionMessage(float v) {
@@ -503,7 +518,10 @@
 	else
 	{
 		// cover anything else by treating it like tdm with no teams spawned
-		dm = cvar("g_tdm_teams");
+		if(g_keyhunt)
+			dm = kh_teams;
+		else
+			dm = cvar("g_tdm_teams");
 		if(dm < 2)
 			error("g_tdm_teams < 2, not enough teams to play team deathmatch\n");
 
@@ -634,6 +652,8 @@
 			error("Too few teams available for domination\n");
 		else if(g_ctf)
 			error("Too few teams available for ctf\n");
+		else if(g_keyhunt)
+			error("Too few teams available for key hunt\n");
 		else
 			error("Too few teams available for team deathmatch\n");
 	}
@@ -719,7 +739,7 @@
 	float smallest, selectedteam;
 
 	// don't join a team if we're not playing a team game
-	if(!cvar("teamplay") && !g_domination && !g_ctf)
+	if(!cvar("teamplay") && !g_domination && !g_ctf && !g_keyhunt)
 		return 0;
 
 	// find out what teams are available

Modified: trunk/data/qcsrc/server/waypointsprites.qc
===================================================================
--- trunk/data/qcsrc/server/waypointsprites.qc	2007-04-30 14:04:11 UTC (rev 2411)
+++ trunk/data/qcsrc/server/waypointsprites.qc	2007-04-30 14:11:11 UTC (rev 2412)
@@ -9,7 +9,32 @@
 float waypointsprite_limitedrange;
 
 ..entity owned_by_field;
+.float(entity) waypointsprite_for_player; // returns a model index or 0 for hide
+float waypointsprite_for_player_default(entity e)
+{
+	// personal waypoints
+	if(self.enemy)
+		if(self.enemy != other)
+			return FALSE;
 
+	// team waypoints
+	if(self.team)
+	{
+		if(self.team != other.team)
+			return FALSE;
+		if(other.classname != "player")
+			return FALSE;
+	}
+
+	// fixed waypoints
+	if(self.currentammo) // hidable?
+		if(other.cvar_cl_hidewaypoints) // wants to hide;
+			return FALSE;
+
+	// otherwise, accept the model
+	return self.modelindex;
+}
+
 void WaypointSprite_Init()
 {
 	waypointsprite_fadedistance = vlen(world.maxs - world.mins);
@@ -104,6 +129,7 @@
 	vector realorigin, porigin;
 	float distancealpha, timealpha;
 	float distance;
+	float newmodel;
 
 	if(self.health)
 	{
@@ -115,21 +141,12 @@
 	else
 		timealpha = 1;
 
-	if(self.enemy)
-		if(self.enemy != other)
-			return FALSE;
-	if(self.team)
-	{
-		if(self.team != other.team)
-			return FALSE;
-		if(other.classname != "player")
-			return FALSE;
-	}
+	// customize WP
+	newmodel = self.waypointsprite_for_player(other);
+	if(newmodel == 0)
+		return FALSE;
+	self.modelindex = newmodel;
 
-	if(self.currentammo) // hidable?
-		if(other.cvar_cl_hidewaypoints) // wants to hide;
-			return FALSE;
-
 	porigin = other.origin + other.view_ofs_z * '0 0 1';
 
 #ifdef ATTACHMENT_WORKS_WITH_EF_NODEPTHTEST
@@ -228,7 +245,12 @@
 	wp.think = WaypointSprite_Think;
 	wp.nextthink = time;
 	wp.effects = EF_NODEPTHTEST | EF_LOWPRECISION;
-	setmodel(wp, strcat("models/sprites/", spr, ".sp2")); // precision set above
+	if(spr != "")
+		setmodel(wp, strcat("models/sprites/", spr, ".sp2")); // precision set above
+	else
+		wp.model = "waypoint";
+	setsize(wp, '0 0 0', '0 0 0');
+	wp.waypointsprite_for_player = waypointsprite_for_player_default;
 	return wp;
 }
 




More information about the nexuiz-commits mailing list