From 028b16b922b977480a9b7b9cdf09309b71114ed5 Mon Sep 17 00:00:00 2001 From: Samual Date: Fri, 30 Mar 2012 20:07:19 -0400 Subject: [PATCH] New passing/dropping system, flags are now "thrown" instead of dropped, and some other fixes/clean ups for centerprint messages. Also, make the "dropped flag" waypoint be colored slightly like the flag color --- defaultXonotic.cfg | 4 + qcsrc/client/announcer.qc | 42 --------- qcsrc/server/autocvars.qh | 3 + qcsrc/server/cl_client.qc | 5 -- qcsrc/server/cl_player.qc | 10 --- qcsrc/server/defs.qh | 3 - qcsrc/server/mutators/gamemode_ctf.qc | 119 +++++++++++++++++++++++--- qcsrc/server/mutators/gamemode_ctf.qh | 25 ++++-- qcsrc/server/portals.qc | 2 +- 9 files changed, 135 insertions(+), 78 deletions(-) diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index 14701d62f..52138f06a 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -605,6 +605,10 @@ set g_ctf_flag_take_damage 0 set g_ctf_flag_dropped_waypoint 2 "show dropped flag waypointsprite when a flag is lost. 1 = team only, 2 = for all players" set g_ctf_flag_pickup_verbosename 1 "show the name of the person who picked up the flag too" set g_ctf_flag_return_when_unreachable 1 "automatically return the flag if it falls into lava/slime/trigger hurt" +set g_ctf_throw_velocity 500 "how far a player can throw the flag" +set g_ctf_allow_pass 0 "allow passing of flags to nearby team mates" +set g_ctf_pass_radius 200 "maximum radius that you can pass to a team mate in" + set g_ctf_shield_max_ratio 0 "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)" set g_ctf_shield_min_negscore 20 "shield the player from the flag if he's got this negative amount of points or less" diff --git a/qcsrc/client/announcer.qc b/qcsrc/client/announcer.qc index 3e109e780..db1ae4133 100644 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@ -134,52 +134,10 @@ void Announcer_Time() } } -float redflag_prev; -float blueflag_prev; -void carrierAnnouncer() { - float stat_items, redflag, blueflag; - float pickup; - string item; - - if not(autocvar_cl_notify_carried_items) - return; - - stat_items = getstati(STAT_ITEMS); - - redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3; - blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3; - - if (redflag == 3 && redflag != redflag_prev) { - item = _("^1RED^7 flag"); - pickup = (redflag_prev == 2); - } - - if (blueflag == 3 && blueflag != blueflag_prev) { - item = _("^4BLUE^7 flag"); - pickup = (blueflag_prev == 2); - } - - if (item) - { - if (pickup) { - if (autocvar_cl_notify_carried_items & 2) - centerprint_hud(sprintf(_("You picked up the %s!"), item)); - } - else { - if (autocvar_cl_notify_carried_items & 1) - centerprint_hud(sprintf(_("You got the %s!"), item)); - } - } - - blueflag_prev = blueflag; - redflag_prev = redflag; -} - void Announcer() { Announcer_Gamestart(); Announcer_Time(); - carrierAnnouncer(); } void Announcer_Precache () diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index c80857725..79a81a6b7 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -761,6 +761,9 @@ float autocvar_g_chat_flood_spl_tell; float autocvar_g_chat_nospectators; float autocvar_g_chat_teamcolors; float autocvar_g_ctf_allow_drop; +float autocvar_g_ctf_allow_pass; +float autocvar_g_ctf_pass_radius; +float autocvar_g_ctf_throw_velocity; float autocvar_g_ctf_captimerecord_always; float autocvar_g_ctf_dynamiclights; string autocvar_g_ctf_flag_blue_model; diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 5d630057e..d80770f97 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -417,9 +417,6 @@ void PutObserverInServer (void) if(self.vehicle) vehicles_exit(VHEF_RELESE); - if(self.flagcarried) - ctf_Handle_Drop(self); // FIXCTF - WaypointSprite_PlayerDead(); if not(g_ca) // don't reset teams when moving a ca player to the spectators @@ -1652,8 +1649,6 @@ void ClientDisconnect (void) Portal_ClearAll(self); RemoveGrapplingHook(self); - if(self.flagcarried) - ctf_Handle_Drop(self); // FIXCTF // Here, everything has been done that requires this player to be a client. diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 54468b34f..475171161 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -693,16 +693,6 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht RemoveGrapplingHook(self); - if(self.flagcarried) - { - // FIXCTF - if(attacker.classname != "player") - ctf_Handle_Drop(self); // penalty for flag loss by suicide - else if(attacker.team == self.team) - ctf_Handle_Drop(self); // penalty for flag loss by suicide/teamkill - else - ctf_Handle_Drop(self); - } Portal_ClearAllLater(self); if(clienttype(self) == CLIENTTYPE_REAL) diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 097f213b6..c5cfa3936 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -16,8 +16,6 @@ noref float require_spawnfunc_prefix; // if this float exists, only functions wi // Globals -float ctf_ReadScore(string parameter); // SOON WON'T BE NEEDED. // FIXCTF - float g_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, g_midair, g_minstagib, g_pinata, g_norecoil, g_minstagib_invis_alpha, g_bloodloss; float g_warmup_limit; float g_warmup_allguns; @@ -342,7 +340,6 @@ string gamemode_name; float startitem_failed; -void ctf_Handle_Drop(entity player); // FIXCTF void DropAllRunes(entity pl); diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc index d57c565c9..e2174480b 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@ -125,7 +125,7 @@ void ctf_CaptureShield_Spawn(entity flag) // Event Handlers // ============== -void ctf_Handle_Drop(entity player) +void ctf_Handle_Pass(entity player) { entity flag = player.flagcarried; @@ -179,6 +179,77 @@ void ctf_Handle_Drop(entity player) dprint("FLAG FALLTHROUGH will happen SOON\n"); } +void ctf_Handle_Drop(entity player, float droptype) +{ + entity flag = player.flagcarried; + + if(!flag) { return; } + if(flag.speedrunning) { ctf_RespawnFlag(flag); return; } + + makevectors((player.v_angle_y * '0 1 0') + (player.v_angle_x * '0.5 0 0')); + + // reset the flag + setattachment(flag, world, ""); + setorigin(flag, player.origin + FLAG_DROP_OFFSET); + flag.owner.flagcarried = world; + flag.owner = world; + flag.movetype = MOVETYPE_TOSS; + flag.solid = SOLID_TRIGGER; + flag.takedamage = DAMAGE_YES; + flag.health = flag.max_flag_health; + + switch(droptype) + { + case DROPTYPE_THROWN: + { + flag.velocity = W_CalculateProjectileVelocity(player.velocity, ('0 0 200' + (v_forward * autocvar_g_ctf_throw_velocity)), FALSE); + break; + } + + default: + case DROPTYPE_NORMAL: + { + flag.velocity = ('0 0 200' + ('0 100 0' * crandom()) + ('100 0 0' * crandom())); + break; + } + } + + flag.ctf_droptime = time; + flag.ctf_dropperid = player.playerid; + flag.ctf_status = FLAG_DROPPED; + + // messages and sounds + Send_KillNotification(player.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO); + sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTN_NONE); + ctf_EventLog("dropped", player.team, player); + + // scoring + PlayerTeamScore_AddScore(player, -ctf_ReadScore("penalty_drop")); + PlayerScore_Add(player, SP_CTF_DROPS, 1); + + // waypoints + if(autocvar_g_ctf_flag_dropped_waypoint) + WaypointSprite_Spawn("flagdropped", 0, 0, flag, '0 0 64', world, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, FALSE, RADARICON_FLAG, '0 0.5 0' + ((flag.team == COLOR_TEAM1) ? '0.75 0 0' : '0 0 0.75')); // (COLOR_TEAM1 + COLOR_TEAM2 - flag.team) + + WaypointSprite_Ping(player.wps_flagcarrier); + WaypointSprite_Kill(player.wps_flagcarrier); + + if(autocvar_g_ctf_flag_returntime || (autocvar_g_ctf_flag_take_damage && autocvar_g_ctf_flag_health)) + { + WaypointSprite_UpdateMaxHealth(flag.wps_flagdropped, flag.max_flag_health); + WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); + } + + // captureshield + ctf_CaptureShield_Update(player, 0); // shield only + + // check if the flag will fall off the map + trace_startsolid = FALSE; + tracebox(flag.origin, flag.mins, flag.maxs, flag.origin, TRUE, flag); + if(trace_startsolid) + dprint("FLAG FALLTHROUGH will happen SOON\n"); +} + void ctf_Handle_Capture(entity flag, entity player) { // declarations @@ -234,6 +305,7 @@ void ctf_Handle_Capture(entity flag, entity player) void ctf_Handle_Return(entity flag, entity player) { // messages and sounds + //centerprint(player, strcat("You returned ", flag.netname)); Send_KillNotification (player.netname, flag.netname, "", INFO_RETURNFLAG, MSG_INFO); sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTN_NONE); ctf_EventLog("return", flag.team, player); @@ -279,10 +351,12 @@ void ctf_Handle_Pickup_Base(entity flag, entity player) ctf_EventLog("steal", flag.team, player); verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat(Team_ColorCode(player.team), "(^7", player.netname, Team_ColorCode(player.team), ") ") : ""); FOR_EACH_PLAYER(tmp_player) - if(tmp_player.team == flag.team) - centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!")); - else if((tmp_player.team == player.team) && (tmp_player != player)) + if(tmp_player == player) + centerprint(tmp_player, strcat("You got the ", flag.netname, "!")); + else if(tmp_player.team == player.team) centerprint(tmp_player, strcat("Your ", Team_ColorCode(player.team), "team mate ", verbosename, "^7got the flag! Protect them!")); + else if(tmp_player.team == flag.team) + centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!")); // scoring PlayerTeamScore_AddScore(player, ctf_ReadScore("score_pickup_base")); @@ -336,10 +410,12 @@ void ctf_Handle_Pickup_Dropped(entity flag, entity player) // make sure this wor ctf_EventLog("pickup", flag.team, player); verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat(Team_ColorCode(player.team), "(^7", player.netname, Team_ColorCode(player.team), ") ") : ""); FOR_EACH_PLAYER(tmp_player) - if(tmp_player.team == flag.team) - centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!")); - else if((tmp_player.team == player.team) && (tmp_player != player)) + if(tmp_player == player) + centerprint(tmp_player, strcat("You got the ", flag.netname, "!")); + else if(tmp_player.team == player.team) centerprint(tmp_player, strcat("Your ", Team_ColorCode(player.team), "team mate ", verbosename, "^7got the flag! Protect them!")); + else if(tmp_player.team == flag.team) + centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!")); // scoring returnscore = floor((ctf_ReadScore("score_pickup_dropped_late") * (1-returnscore) + ctf_ReadScore("score_pickup_dropped_early") * returnscore) + 0.5); @@ -523,7 +599,7 @@ void ctf_Reset() { if(self.owner) if(self.owner.classname == "player") - ctf_Handle_Drop(self.owner); + ctf_Handle_Drop(self.owner, DROPTYPE_NORMAL); ctf_RespawnFlag(self); } @@ -644,7 +720,7 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag MUTATOR_HOOKFUNCTION(ctf_RemovePlayer) { - if(self.flagcarried) { ctf_Handle_Drop(self); } + if(self.flagcarried) { ctf_Handle_Drop(self, DROPTYPE_NORMAL); } return 0; } @@ -703,8 +779,29 @@ MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKill) MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey) { - if(autocvar_g_ctf_allow_drop) - ctf_Handle_Drop(self); + entity player = self; + + if(player.flagcarried) + { + if(autocvar_g_ctf_allow_pass) + { + entity head; + head = findradius(player.origin, autocvar_g_ctf_pass_radius); + + while(head) + { + if(head.classname == "player" && head.deadflag == DEAD_NO) + if(head != player && !IsDifferentTeam(head, player)) + { + ctf_Handle_Pass(player); + return 0; + } + head = head.chain; + } + } + + if(autocvar_g_ctf_allow_drop) { ctf_Handle_Drop(player, DROPTYPE_THROWN); } + } return 0; } diff --git a/qcsrc/server/mutators/gamemode_ctf.qh b/qcsrc/server/mutators/gamemode_ctf.qh index f171d9635..63ac7a0cf 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qh +++ b/qcsrc/server/mutators/gamemode_ctf.qh @@ -3,6 +3,12 @@ // used in cheats.qc void ctf_RespawnFlag(entity flag) +// used in portals.qc +void ctf_Handle_Drop(entity player, float droptype); + +// used in g_damage.qc +float ctf_ReadScore(string parameter); // SOON WON'T BE NEEDED. // FIXCTF + // used in t_quake3.qc void spawnfunc_info_player_team1(); void spawnfunc_info_player_team2(); @@ -15,15 +21,14 @@ void spawnfunc_ctf_team(); // flag constants #define FLAG_MIN (PL_MIN + '0 0 -13') #define FLAG_MAX (PL_MAX + '0 0 -13') -#define FLAG_CARRY_OFFSET ('-15 0 7') -#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13)) -#define FLAG_WAYPOINT_OFFSET ('0 0 64') #define FLAG_SCALE 0.6 + #define FLAG_THINKRATE 0.2 -#define FLAG_BASE 1 -#define FLAG_CARRY 2 -#define FLAG_DROPPED 3 +#define FLAG_DROP_OFFSET ('0 0 32') +#define FLAG_CARRY_OFFSET ('-15 0 7') +#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13)) +#define FLAG_WAYPOINT_OFFSET ('0 0 64') // sounds .string noise4; @@ -46,6 +51,14 @@ entity ctf_worldflaglist; .entity wps_flagcarrier; .entity wps_flagdropped; +// statuses +#define FLAG_BASE 1 +#define FLAG_CARRY 2 +#define FLAG_DROPPED 3 + +#define DROPTYPE_NORMAL 1 +#define DROPTYPE_THROWN 2 + // flag properties #define ctf_spawnorigin dropped_origin float ctf_captimerecord; // record time for capturing the flag diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index 1c6cf17eb..1c09b8b0c 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -155,7 +155,7 @@ float Portal_TeleportPlayer(entity teleporter, entity player) player.right_vector = -1 * AnglesTransform_Apply(transform, player.right_vector); if(player.flagcarried) - ctf_Handle_Drop(player); // FIXCTF + ctf_Handle_Drop(player, DROPTYPE_NORMAL); // FIXCTF if not(teleporter.enemy) { -- 2.39.2