From 024fd341ba58e87929d285425361a2b67eb3d523 Mon Sep 17 00:00:00 2001
From: Samual <samual@xonotic.org>
Date: Mon, 28 Mar 2011 18:49:44 -0400
Subject: [PATCH] IT WORKS NOW!!!!!! sorta. The touch system and other such
 stuff isn't complete yet, BUT the game mode at least starts now.

---
 qcsrc/server/mutators/gamemode_ctf.qc | 246 +++++++++++++++++++++-----
 1 file changed, 202 insertions(+), 44 deletions(-)

diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc
index fa4dfec0bf..ddaeb05697 100644
--- a/qcsrc/server/mutators/gamemode_ctf.qc
+++ b/qcsrc/server/mutators/gamemode_ctf.qc
@@ -8,7 +8,7 @@
 #define FLAG_MAX (PL_MAX + '0 0 -13')
 #define FLAG_CARRY_POS '-15 0 7'
 
-.entity bot_basewaypoint; // Flag waypointsprite
+.entity bot_basewaypoint; // flag waypointsprite
 .entity wps_flagbase; 
 .entity wps_flagcarrier;
 .entity wps_flagdropped;
@@ -16,22 +16,19 @@
 entity ctf_worldflaglist; // CTF flags in the map
 .entity ctf_worldflagnext;
 
-.float ctf_dropperid; // Don't allow spam of dropping the flag
+float ctf_captimerecord; // record time for capturing the flag
+.float ctf_pickuptime;
+.float ctf_dropperid; // don't allow spam of dropping the flag
 .float ctf_droptime;
 .float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
 
-// Delay between when the person can pick up a flag // replace with .wait? 
-.float next_take_time;
-
-// Record time for capturing the flag
-float flagcaptimerecord;
-.float flagpickuptime;
+.float next_take_time; // Delay between when the person can pick up a flag // is this obsolete from the stuff above?
 
 // CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag.
 .float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
-float captureshield_min_negscore; // punish at -20 points
-float captureshield_max_ratio; // punish at most 30% of each team
-float captureshield_force; // push force of the shield
+float ctf_captureshield_min_negscore; // punish at -20 points
+float ctf_captureshield_max_ratio; // punish at most 30% of each team
+float ctf_captureshield_force; // push force of the shield
 
 // declare functions so they can be used in any order in the file
 void ctf_FlagTouch(void);
@@ -80,11 +77,6 @@ void ctf_EventLog(string mode, float flagteam, entity actor)
 	GameLogEcho(s);
 }
 
-void ctf_CaptureShockwave(vector org)
-{
-	shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1);
-}
-
 void ctf_CreateBaseWaypoints(entity flag, float teamnumber)
 {
 	// for bots
@@ -156,7 +148,10 @@ void ctf_SetStatus() // re-write this in some less shitty way
 
 void ctf_Reset()
 {
-	ctf_Handle_Drop(self);
+	if(self.owner)
+		if(self.owner.classname == "player")
+			ctf_Handle_Drop(self.owner);
+			
 	ctf_RespawnFlag(self);
 }
 
@@ -202,6 +197,7 @@ void ctf_Handle_Drop(entity player) // make sure this works
 	WaypointSprite_Ping(player.wps_flagcarrier);
 	WaypointSprite_Kill(player.wps_flagcarrier);
 
+	// 
 	ctf_CaptureShield_Update(player, 0); // shield only
 
 	// eh? 
@@ -211,26 +207,186 @@ void ctf_Handle_Drop(entity player) // make sure this works
 		dprint("FLAG FALLTHROUGH will happen SOON\n");
 }
 
-// finish these
-
-void ctf_Handle_Capture(entity flag, entity player)
+void ctf_Handle_Capture(entity flag, entity player) // this too
 {
-	// blah blah blah
+	// declarations
+	float cap_time, cap_record, success;
+	string cap_message, refername;
+
+	// records
+	if((autocvar_g_ctf_captimerecord_always) || (player_count - currentbots)) {
+		cap_record = ctf_captimerecord;
+		cap_time = (time - flag.ctf_pickuptime);
+
+		refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
+		refername = ((refername == player.netname) ? "their" : strcat(refername, "^7's"));
+
+		if(ctf_captimerecord) 
+			{ cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds"); success = TRUE; }
+		else if(cap_time < cap_record) 
+			{ cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, breaking ", refername, " previous record of ", ftos_decimals(cap_record, 2), " seconds"); success = TRUE; }
+		else
+			{ cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, failing to break ", refername, " record of ", ftos_decimals(cap_record, 2), " seconds"); success = FALSE; }
+
+		if(success) {
+			ctf_captimerecord = cap_time;
+			db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
+			db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
+			write_recordmarker(player, (time - cap_time), cap_time); } }
+	
+	// messages and sounds
+	Send_KillNotification(player.netname, player.flagcarried.netname, cap_message, INFO_CAPTUREFLAG, MSG_INFO);
+	sound(player, CHAN_AUTO, flag.noise2, VOL_BASE, ATTN_NONE);
+	
+	// scoring
+	PlayerTeamScore_Add(player, SP_CTF_CAPS, ST_CTF_CAPS, 1);
+	ctf_EventLog("capture", player.flagcarried.team, player);
+	UpdateFrags(player, ctf_ReadScore("score_capture"));
+
+	// effects
+	if (autocvar_g_ctf_flag_capture_effects) 
+	{
+		pointparticles(particleeffectnum((player.team == COLOR_TEAM1) ? "red_ground_quake" : "blue_ground_quake"), flag.origin, '0 0 0', 1);
+		shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1);
+	}
+
+	// waypointsprites // todo: improve this vvvv
+	WaypointSprite_DetachCarrier(player);
+
+	// reset the flag
+	if(flag.speedrunning)
+		ctf_FakeTimeLimit(player, -1);
+	
+	ctf_RespawnFlag(player.flagcarried);
+	//player.flagcarried = world;
+	//player.next_take_time = time + 1;
 }
 
-void ctf_Handle_Return(entity flag, entity player)
+void ctf_Handle_Return(entity flag, entity player) // todo: re-write this
 {
-	// blah blah blah
+	/*
+	// return flag
+	Send_KillNotification (player.netname, flag.netname, "", INFO_RETURNFLAG, MSG_INFO);
+	//bprint(player.netname, "^7 returned the ", flag.netname, "\n");
+
+	// punish the player who last had it
+	FOR_EACH_PLAYER(player)
+		if(player.playerid == flag.ctf_dropperid)
+		{
+			PlayerScore_Add(player, SP_SCORE, -ctf_ReadScore("penalty_returned"));
+			ctf_captureshield_update(player, 0); // shield only
+		}
+
+	// punish the team who was last carrying it
+	TeamScore_AddToTeam(((flag.team == COLOR_TEAM1) ? COLOR_TEAM2 : COLOR_TEAM1), ST_SCORE, -ctf_ReadScore("penalty_returned"));
+
+	// reward the player who returned it
+	if(player.playerid == flag.playerid) // is this the guy who killed the FC last?
+	{
+		if (player.team == COLOR_TEAM1 || player.team == COLOR_TEAM2)
+			UpdateFrags(player, ctf_ReadScore("score_return_by_killer"));
+		else
+			UpdateFrags(player, ctf_ReadScore("score_return_rogue_by_killer"));
+	}
+	else
+	{
+		if (player.team == COLOR_TEAM1 || player.team == COLOR_TEAM2)
+			UpdateFrags(player, ctf_ReadScore("score_return"));
+		else
+			UpdateFrags(player, ctf_ReadScore("score_return_rogue"));
+	}
+	PlayerScore_Add(player, SP_CTF_RETURNS, 1);
+	ctf_EventLog("return", flag.team, player);
+	sound(player, CHAN_AUTO, flag.noise1, VOL_BASE, ATTN_NONE);
+	ReturnFlag(flag);
+	*/
 }
 
-void ctf_Handle_Pickup_Base(entity flag, entity player)
+void ctf_Handle_Pickup_Base(entity flag, entity player) // todo: re-write this
 {
-	// blah blah blah
-}
+	/*
+	if (player.next_take_time > time)
+		return;
+			
+	if (autocvar_g_ctf_flag_pickup_effects) // pickup effect
+		pointparticles(particleeffectnum("smoke_ring"), 0.5 * (flag.absmin + flag.absmax), '0 0 0', 1);
+			
+	// pick up
+	flag.ctf_pickuptime = time; // used for timing runs
+	flag.speedrunning = player.speedrunning; // if speedrunning, flag will flag-return and teleport the owner back after the record
+	if(player.speedrunning)
+	if(ctf_captimerecord)
+		FakeTimeLimit(player, time + ctf_captimerecord);
+	flag.solid = SOLID_NOT;
+	setorigin(flag, flag.origin); // relink
+	flag.owner = player;
+	player.flagcarried = flag;
+	flag.cnt = FLAG_CARRY;
+	flag.angles = '0 0 0';
+	Send_KillNotification (player.netname, flag.netname, "", INFO_GOTFLAG, MSG_INFO);
+	UpdateFrags(player, ctf_ReadScore("score_pickup_base"));
+	flag.dropperid = player.playerid;
+	PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
+	LogCTF("steal", flag.team, player);
+	sound (player, CHAN_AUTO, flag.noise, VOL_BASE, ATTN_NONE);
+
+	FOR_EACH_PLAYER(player)
+		if(player.team == flag.team)
+			centerprint(player, "The enemy got your flag! Retrieve it!");
 
-void ctf_Handle_Pickup_Dropped(entity flag, entity player)
+	flag.movetype = MOVETYPE_NONE;
+	setorigin(flag, FLAG_CARRY_POS);
+	setattachment(flag, player, "");
+	WaypointSprite_AttachCarrier("flagcarrier", player);
+	WaypointSprite_UpdateTeamRadar(player.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '1 1 0');
+	WaypointSprite_Ping(flag.sprite);
+	*/
+}
+ 
+void ctf_Handle_Pickup_Dropped(entity flag, entity player) // todo: re-write this
 {
-	// blah blah blah
+	/*
+	if(flag.waypointsprite_attachedforcarrier)
+		WaypointSprite_DetachCarrier(flag);
+
+	if (autocvar_g_ctf_flag_pickup_effects) // field pickup effect
+		pointparticles(particleeffectnum("smoke_ring"), 0.5 * (flag.absmin + flag.absmax), '0 0 0', 1);
+			
+	// pick up
+	flag.solid = SOLID_NOT;
+	setorigin(flag, flag.origin); // relink
+	flag.owner = player;
+	player.flagcarried = flag;
+	flag.cnt = FLAG_CARRY;
+	Send_KillNotification (player.netname, flag.netname, "", INFO_PICKUPFLAG, MSG_INFO);
+	//bprint(player.netname, "^7 picked up the ", flag.netname, "\n");
+
+	float f;
+	f = bound(0, (flag.pain_finished - time) / autocvar_g_ctf_flag_returntime, 1);
+	//print("factor is ", ftos(f), "\n");
+	f = ctf_ReadScore("score_pickup_dropped_late") * (1-f)
+	  + ctf_ReadScore("score_pickup_dropped_early") * f;
+	f = floor(f + 0.5);
+	flag.dropperid = player.playerid;
+	//print("score is ", ftos(f), "\n");
+
+	UpdateFrags(player, f);
+	PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
+	LogCTF("pickup", flag.team, player);
+	sound (player, CHAN_AUTO, flag.noise, VOL_BASE, ATTN_NONE);
+
+	FOR_EACH_PLAYER(player)
+		if(player.team == flag.team)
+			centerprint(player, "The enemy got your flag! Retrieve it!");
+
+	flag.movetype = MOVETYPE_NONE;	// flag must have MOVETYPE_NONE here, playerwise it will drop through the floor...
+	setorigin(flag, FLAG_CARRY_POS);
+	setattachment(flag, player, "");
+	flag.damageforcescale = 0;
+	flag.takedamage = DAMAGE_NO;
+	WaypointSprite_AttachCarrier("flagcarrier", player);
+	WaypointSprite_UpdateTeamRadar(player.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '1 1 0');
+	*/
 }
 
 
@@ -276,8 +432,8 @@ void ctf_SetupFlag(float teamnumber, entity flag) // called when spawning a flag
 	
 	flag.reset = ctf_Reset;
 	flag.touch = ctf_FlagTouch;
-	flag.think = ctf_RespawnFlag;
-	flag.nextthink = time + 0.2; // start after doors etc // Samual: 0.2 though? Why? 
+	//flag.think = ctf_RespawnFlag;
+	//flag.nextthink = time + 0.2; // start after doors etc // Samual: 0.2 though? Why? 
 
 	// appearence
 	if(!flag.model) { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); }
@@ -320,11 +476,13 @@ void ctf_SetupFlag(float teamnumber, entity flag) // called when spawning a flag
 	// other initialization stuff
 	ctf_CreateBaseWaypoints(flag, teamnumber);
 	ctf_CaptureShield_Spawn(flag);
+	//InitializeEntity(flag, ctf_RespawnFlag, INITPRIO_SETLOCATION);
 	//InitializeEntity(self, ctf_CaptureShield_Spawn, INITPRIO_SETLOCATION);
 }
 
-void ctf_RespawnFlag(entity flag) // re-write this
+void ctf_RespawnFlag(entity flag) // todo: re-write this
 {
+	//if((self) && (!flag) { flag = self } 
 	if(flag.classname != "item_flag_team") { backtrace("ctf_RespawnFlag was called incorrectly."); return; }
 
 	if(flag.owner)
@@ -357,7 +515,7 @@ void ctf_RespawnFlag(entity flag) // re-write this
 	flag.flags = FL_ITEM; // clear FL_ONGROUND and any other junk // there shouldn't be any "junk" set on this... look into it and make sure it's kept clean. 
 }
 
-void ctf_FlagThink() // re-write this
+void ctf_FlagThink() // todo: re-write this
 {
 	local entity e;
 
@@ -382,10 +540,10 @@ void ctf_FlagThink() // re-write this
 	if(self.cnt == FLAG_CARRY)
 	{
 		if(self.owner)
-		if(flagcaptimerecord)
-		if(time >= self.flagpickuptime + flagcaptimerecord)
+		if(ctf_captimerecord)
+		if(time >= self.ctf_pickuptime + ctf_captimerecord)
 		{
-			bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n");
+			bprint("The ", self.netname, " became impatient after ", ftos_decimals(ctf_captimerecord, 2), " seconds and returned itself\n");
 
 			sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
 			self.owner.impulse = 141; // returning!
@@ -486,11 +644,11 @@ float ctf_CaptureShield_CheckStatus(entity p) // check to see
 	entity e;
 	float players_worseeq, players_total;
 
-	if(captureshield_max_ratio <= 0)
+	if(ctf_captureshield_max_ratio <= 0)
 		return FALSE;
 
 	s = PlayerScore_Add(p, SP_SCORE, 0);
-	if(s >= -captureshield_min_negscore)
+	if(s >= -ctf_captureshield_min_negscore)
 		return FALSE;
 
 	players_total = players_worseeq = 0;
@@ -507,7 +665,7 @@ float ctf_CaptureShield_CheckStatus(entity p) // check to see
 	// player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
 	// use this rule here
 	
-	if(players_worseeq >= players_total * captureshield_max_ratio)
+	if(players_worseeq >= players_total * ctf_captureshield_max_ratio)
 		return FALSE;
 
 	return TRUE;
@@ -550,7 +708,7 @@ void ctf_CaptureShield_Touch()
 	vector othermid;
 	mymid = (self.absmin + self.absmax) * 0.5;
 	othermid = (other.absmin + other.absmax) * 0.5;
-	Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force);
+	Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
 	centerprint_atprio(other, CENTERPRIO_SHIELDING, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.");
 }
 
@@ -714,11 +872,11 @@ void ctf_DelayedInit()
 
 void ctf_Initialize()
 {
-	flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
+	ctf_captimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
 
-	captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
-	captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
-	captureshield_force = autocvar_g_ctf_shield_force;
+	ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
+	ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
+	ctf_captureshield_force = autocvar_g_ctf_shield_force;
 
 	g_ctf_win_mode = cvar("g_ctf_win_mode");
 	
@@ -752,5 +910,5 @@ MUTATOR_DEFINITION(gamemode_ctf)
 		error("This is a game type and it cannot be removed at runtime.");
 	}
 
-	return TRUE;
+	return 0;
 }
-- 
2.39.5