]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Implement whole new passing/throwing system which *actually* throws the flag to team...
authorSamual <samual@xonotic.org>
Sun, 1 Apr 2012 00:24:25 +0000 (20:24 -0400)
committerSamual <samual@xonotic.org>
Sun, 1 Apr 2012 00:24:25 +0000 (20:24 -0400)
defaultXonotic.cfg
qcsrc/server/autocvars.qh
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_ctf.qh

index f723560b4a31885a1c1cd2b10f822e14a71e827f..0b03ce261b8ddc5904bed5b9aa7a9688cb3b3f08 100644 (file)
@@ -609,10 +609,11 @@ set g_ctf_flag_pickup_verbosename 1 "show the name of the person who picked up t
 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 1 "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_pass_radius 300 "maximum radius that you can pass to a team mate in"
 set g_ctf_pass_wait 2 "delay in seconds between how often players can pass the flag (antispam, essentially)"
 set g_ctf_dropped_capture_radius 100 "allow dropped flags to be automatically captured by base flags if the dropped flag is within this radius of it"
 set g_ctf_pass_request 1 "allow players to request the flag carrier to pass the flag to them"
+set g_ctf_pass_turnrate 50 "how well the flag follows the best direction to its target while passing"
 
 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"
index fa8661673e20efca7f72b20d980ad217d3e6836d..8d70b4067bbbd91964565b520683951ef645a6d7 100644 (file)
@@ -765,6 +765,7 @@ float autocvar_g_ctf_allow_pass;
 float autocvar_g_ctf_pass_radius;
 float autocvar_g_ctf_pass_wait;
 float autocvar_g_ctf_pass_request;
+float autocvar_g_ctf_pass_turnrate;
 float autocvar_g_ctf_throw_velocity;
 float autocvar_g_ctf_captimerecord_always;
 float autocvar_g_ctf_dynamiclights;
index 1fa260ef8470db26e30238ec850f066e0c6dc2f8..6b731955bf2b10a31fba3a0432ea1600bb429cb5 100644 (file)
@@ -172,12 +172,12 @@ void ctf_CaptureShield_Spawn(entity flag)
 // Drop/Pass/Throw Code
 // ====================
 
-void ctf_Handle_Recieve(entity player, entity reciever)
+void ctf_Handle_Failed_Pass(entity flag)
 {
-       entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
-       entity flag = player.flagcarried;
+       /*entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
+       entity sender = flag.pass_sender;
        if(!flag) { return; }
-       if(time < flag.pass_antispam) { return; } // antispam of passing
+       if(time < flag.throw_antispam) { return; } // antispam of passing
        
        // reset player
        player.flagcarried = world;
@@ -187,7 +187,7 @@ void ctf_Handle_Recieve(entity player, entity reciever)
        // transfer flag to reciever
        flag.owner = reciever;
        flag.owner.flagcarried = flag;
-       flag.ctf_pickupper = reciever;
+       flag.ctf_carrier = reciever;
        setattachment(flag, reciever, "");
        setorigin(flag, FLAG_CARRY_OFFSET);
 
@@ -212,58 +212,65 @@ void ctf_Handle_Recieve(entity player, entity reciever)
        WaypointSprite_UpdateHealth(reciever.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(reciever.health, reciever.armorvalue, autocvar_g_balance_armor_blockpercent));
        WaypointSprite_UpdateTeamRadar(reciever.wps_flagcarrier, RADARICON_FLAGCARRIER, '1 1 0');
        
-       flag.pass_antispam = time + autocvar_g_ctf_pass_wait;
+       flag.throw_antispam = time + autocvar_g_ctf_pass_wait;*/
+       
+       print("ctf_Handle_Failed_Pass() called.\n");
+       ctf_RespawnFlag(flag);
 }
 
-void ctf_Handle_Pass(entity player, entity reciever)
+void ctf_Handle_Retrieve(entity flag, entity player)
 {
        entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
-       entity flag = player.flagcarried;
-       if(!flag) { return; }
-       if(time < flag.pass_antispam) { return; } // antispam of passing
+       entity sender = flag.pass_sender;
        
-       // reset player
-       player.flagcarried = world;
-       WaypointSprite_Ping(player.wps_flagcarrier);
-       WaypointSprite_Kill(player.wps_flagcarrier);
-       
-       // transfer flag to reciever
-       flag.owner = reciever;
+       // transfer flag to player
+       flag.ctf_carrier = player;
+       flag.owner = player;
        flag.owner.flagcarried = flag;
-       flag.ctf_pickupper = reciever;
-       setattachment(flag, reciever, "");
+       
+       // reset flag
+       setattachment(flag, player, "");
        setorigin(flag, FLAG_CARRY_OFFSET);
+       flag.movetype = MOVETYPE_NONE;
+       flag.takedamage = DAMAGE_NO;
+       flag.solid = SOLID_NOT;
+       flag.ctf_carrier = player;
+       flag.ctf_status = FLAG_CARRY;
 
        // messages and sounds
-       sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTN_NORM);
-       ctf_EventLog("pass", flag.team, player);
-       ctf_EventLog("recieve", flag.team, reciever);
+       sound(player, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTN_NORM);
+       ctf_EventLog("recieve", flag.team, player);
        FOR_EACH_PLAYER(tmp_player)
-               if(tmp_player == player)
-                       centerprint(tmp_player, strcat("You passed the ", flag.netname, " to ", reciever.netname));
-               else if(tmp_player == reciever)
-                       centerprint(tmp_player, strcat("You recieved the ", flag.netname, " from ", player.netname));
-               else if(tmp_player.team == player.team)
-                       centerprint(tmp_player, strcat(player.netname, " passed the ", flag.netname, " to ", reciever.netname));
-                       
-       // effects
-       te_lightning2(world, reciever.origin, player.origin);
+               if(tmp_player == sender)
+                       centerprint(tmp_player, strcat("You passed the ", flag.netname, " to ", player.netname));
+               else if(tmp_player == player)
+                       centerprint(tmp_player, strcat("You recieved the ", flag.netname, " from ", sender.netname));
+               else if(tmp_player.team == sender.team)
+                       centerprint(tmp_player, strcat(sender.netname, " passed the ", flag.netname, " to ", player.netname));
        
        // create new waypoint
-       WaypointSprite_Spawn("flagcarrier", 0, 0, reciever, '0 0 64', world, reciever.team, reciever, wps_flagcarrier, FALSE, RADARICON_FLAG, '1 1 0');
-       WaypointSprite_UpdateMaxHealth(reciever.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent) * 2);
-       WaypointSprite_UpdateHealth(reciever.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(reciever.health, reciever.armorvalue, autocvar_g_balance_armor_blockpercent));
-       WaypointSprite_UpdateTeamRadar(reciever.wps_flagcarrier, RADARICON_FLAGCARRIER, '1 1 0');
+       WaypointSprite_Spawn("flagcarrier", 0, 0, player, '0 0 64', world, player.team, player, wps_flagcarrier, FALSE, RADARICON_FLAG, '1 1 0');
+       WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent) * 2);
+       WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent));
+       WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, '1 1 0');
+       
+       sender.throw_antispam = time + autocvar_g_ctf_pass_wait;
+       player.throw_antispam = sender.throw_antispam;
+       
+       flag.pass_sender = world;
+       flag.pass_target = world;
        
-       flag.pass_antispam = time + autocvar_g_ctf_pass_wait;
+       print("ctf_Handle_Retrieve() called.\n");
 }
 
-void ctf_Handle_Drop(entity player, float droptype)
+void ctf_Handle_Throw(entity player, entity reciever, float droptype)
 {
        entity flag = player.flagcarried;
+       
        if(!flag) { return; }
+       if((droptype == DROPTYPE_PASS) && !reciever) { return; }
        
-       if(flag.speedrunning) { ctf_RespawnFlag(flag); return; }
+       //if(flag.speedrunning) { ctf_RespawnFlag(flag); return; }
        
        // reset the flag
        setattachment(flag, world, "");
@@ -272,12 +279,17 @@ void ctf_Handle_Drop(entity player, float droptype)
        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:
+               case DROPTYPE_PASS:
+               {
+                       vector targ_origin = (0.5 * (reciever.absmin + reciever.absmax));
+                       flag.velocity = W_CalculateProjectileVelocity(player.velocity, ('0 0 200' + (normalize(targ_origin - player.origin) * autocvar_g_ctf_throw_velocity)), FALSE);
+                       break;
+               }
+               
+               case DROPTYPE_THROW:
                {
                        makevectors((player.v_angle_y * '0 1 0') + (player.v_angle_x * '0.5 0 0'));
                        flag.velocity = W_CalculateProjectileVelocity(player.velocity, ('0 0 200' + (v_forward * autocvar_g_ctf_throw_velocity)), FALSE);
@@ -285,47 +297,69 @@ void ctf_Handle_Drop(entity player, float droptype)
                }
                
                default:
-               case DROPTYPE_NORMAL:
+               case DROPTYPE_DROP:
                {
                        flag.velocity = ('0 0 200' + ('0 100 0' * crandom()) + ('100 0 0' * crandom()));
                        break;
                }
        }
        
-       flag.ctf_droptime = time;
-       flag.ctf_dropper = player;
-       flag.ctf_status = FLAG_DROPPED;
+       switch(droptype)
+       {
+               case DROPTYPE_PASS:
+               {
+                       // main
+                       flag.takedamage = DAMAGE_NO;
+                       flag.pass_sender = player;
+                       flag.pass_target = reciever;
+                       flag.ctf_status = FLAG_PASSING;
+                       
+                       // other
+                       sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTN_NORM);
+                       ctf_EventLog("pass", flag.team, player);
+                       te_lightning2(world, reciever.origin, player.origin);
+                       break;
+               }
+               
+               default:
+               case DROPTYPE_THROW:
+               case DROPTYPE_DROP:
+               {
+                       // main
+                       flag.takedamage = DAMAGE_YES;
+                       flag.health = flag.max_flag_health;
+                       flag.ctf_droptime = time;
+                       flag.ctf_dropper = player;
+                       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)
 
-       // 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);
+                       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);
+                       }
+                       break;
+               }
+       }
 
-       // 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)
-       
+       // kill old waypointsprite
        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");
+       //ctf_CaptureShield_Update(player, 0); // shield only
 }
 
 
@@ -425,7 +459,7 @@ void ctf_Handle_Pickup_Base(entity flag, entity player)
        flag.solid = SOLID_NOT;
        flag.angles = '0 0 0';
        flag.ctf_pickuptime = time; // used for timing runs
-       flag.ctf_pickupper = player;
+       flag.ctf_carrier = player;
        flag.ctf_status = FLAG_CARRY;
        
        // messages and sounds
@@ -475,7 +509,7 @@ void ctf_Handle_Pickup_Dropped(entity flag, entity player)
        flag.solid = SOLID_NOT;
        flag.angles = '0 0 0';
        //flag.ctf_pickuptime = time; // don't update pickuptime since this isn't a real steal. 
-       flag.ctf_pickupper = player;
+       flag.ctf_carrier = player;
        flag.ctf_status = FLAG_CARRY;
 
        // messages and sounds
@@ -603,7 +637,23 @@ void ctf_FlagThink()
                
                case FLAG_PASSING:
                {
-                       dprint("Someone touched a flag even though it was being carried?\n");
+                       traceline(self.origin, self.pass_target.origin, MOVE_NOMONSTERS, self);
+                       
+                       vector targ_origin = (0.5 * (self.pass_target.absmin + self.pass_target.absmax));
+                       
+                       if((self.pass_target.deadflag != DEAD_NO)
+                               || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius)
+                               || ((trace_fraction < 1) && (trace_ent != self.pass_target)))
+                       {
+                               ctf_Handle_Failed_Pass(self);
+                       }
+                       else // still a viable target, go for it
+                       {
+                               vector desired_direction = normalize(targ_origin - self.origin);
+                               vector current_direction = normalize(self.velocity);
+                               
+                               self.velocity = (normalize(current_direction + (desired_direction * autocvar_g_ctf_pass_turnrate)) * autocvar_g_ctf_throw_velocity); 
+                       }
                        return;
                }
 
@@ -665,7 +715,13 @@ void ctf_FlagTouch()
                
                case FLAG_PASSING:
                {
-                       dprint("Someone touched a flag even though it was being carried?\n");
+                       if((other.classname == "player") && (other.deadflag == DEAD_NO) && (other != self.pass_sender))
+                       {
+                               if(IsDifferentTeam(other, self.pass_sender))
+                                       ctf_Handle_Return(self, other);
+                               else
+                                       ctf_Handle_Retrieve(self, other);
+                       }
                        break;
                }
                
@@ -696,23 +752,31 @@ void ctf_RespawnFlag(entity flag)
 
        // reset the flag
        setattachment(flag, world, "");
-       setorigin(flag, flag.ctf_spawnorigin); // replace with flag.ctf_spawnorigin
+       setorigin(flag, flag.ctf_spawnorigin);
+       
        flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS);
        flag.takedamage = DAMAGE_NO;
        flag.health = flag.max_flag_health;
        flag.solid = SOLID_TRIGGER;
        flag.velocity = '0 0 0';
        flag.angles = flag.mangle;
-       flag.ctf_status = FLAG_BASE;
        flag.flags = FL_ITEM | FL_NOTARGET;
+       
+       flag.ctf_status = FLAG_BASE;
        flag.owner = world;
+       flag.pass_sender = world;
+       flag.pass_target = world;
+       flag.ctf_carrier = world;
+       flag.ctf_dropper = world;
+       flag.ctf_pickuptime = 0;
+       flag.ctf_droptime = 0;
 }
 
 void ctf_Reset()
 {
        if(self.owner)
                if(self.owner.classname == "player")
-                       ctf_Handle_Drop(self.owner, DROPTYPE_NORMAL);
+                       ctf_Handle_Throw(self.owner, world, DROPTYPE_DROP);
                        
        ctf_RespawnFlag(self);
 }
@@ -834,7 +898,7 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag
 
 MUTATOR_HOOKFUNCTION(ctf_HookedDrop)
 {
-       if(self.flagcarried) { ctf_Handle_Drop(self, DROPTYPE_NORMAL); }
+       if(self.flagcarried) { ctf_Handle_Throw(self, world, DROPTYPE_DROP); }
        return 0;
 }
 
@@ -897,39 +961,43 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
 {
        entity player = self;
 
-       // pass the flag to a team mate
-       if(autocvar_g_ctf_allow_pass && (time > player.pass_antispam))
+       if(time > player.throw_antispam)
        {
-               entity head, closest_target;
-               head = findradius(player.origin, autocvar_g_ctf_pass_radius);
-               
-               while(head) // find the closest acceptable target to pass to
+               // pass the flag to a team mate
+               if(autocvar_g_ctf_allow_pass)
                {
-                       if(head.classname == "player" && head.deadflag == DEAD_NO)
-                       if(head != player && !IsDifferentTeam(head, player))
-                       if(!player.speedrunning && !head.speedrunning)
+                       entity head, closest_target;
+                       head = findradius(player.origin, autocvar_g_ctf_pass_radius);
+                       
+                       while(head) // find the closest acceptable target to pass to
                        {
-                               if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) 
-                               { 
-                                       centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname)); 
-                                       player.pass_antispam = time + autocvar_g_ctf_pass_wait; 
-                                       return 0; 
-                               }
-                               else if(player.flagcarried)
+                               if(head.classname == "player" && head.deadflag == DEAD_NO)
+                               if(head != player && !IsDifferentTeam(head, player))
+                               if(!player.speedrunning && !head.speedrunning)
                                {
-                                       if(closest_target) { if(vlen(player.origin - head.origin) < vlen(player.origin - closest_target.origin)) { closest_target = head; } }
-                                       else { closest_target = head; }
+                                       if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) 
+                                       { 
+                                               centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname)); 
+                                               centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
+                                               player.throw_antispam = time + autocvar_g_ctf_pass_wait; 
+                                               return 0; 
+                                       }
+                                       else if(player.flagcarried)
+                                       {
+                                               if(closest_target) { if(vlen(player.origin - head.origin) < vlen(player.origin - closest_target.origin)) { closest_target = head; } }
+                                               else { closest_target = head; }
+                                       }
                                }
+                               head = head.chain;
                        }
-                       head = head.chain;
+                       
+                       if(closest_target) { ctf_Handle_Throw(player, closest_target, DROPTYPE_PASS); return 0; }
                }
                
-               if(closest_target) { ctf_Handle_Pass(player, closest_target); return 0; }
+               // throw the flag in front of you
+               if(autocvar_g_ctf_allow_drop && player.flagcarried && !player.speedrunning)
+                       { ctf_Handle_Throw(player, world, DROPTYPE_THROW); }
        }
-       
-       // throw the flag in front of you
-       if(autocvar_g_ctf_allow_drop && player.flagcarried && !player.speedrunning)
-               { ctf_Handle_Drop(player, DROPTYPE_THROWN); }
                
        return 0;
 }
index ce39a04df1392871bb2faf7d1ae1333b12c0cc33..38f48b4e2bfef71c2abadf2c3f221ff472f6d5c9 100644 (file)
@@ -4,7 +4,7 @@
 void ctf_RespawnFlag(entity flag)
 
 // used in portals.qc
-void ctf_Handle_Drop(entity player, float droptype);
+void ctf_Handle_Throw(entity player, entity reciever, float droptype);
 
 // used in g_damage.qc
 float ctf_ReadScore(string parameter); // SOON WON'T BE NEEDED. // FIXCTF
@@ -58,8 +58,9 @@ entity ctf_worldflaglist;
 #define FLAG_CARRY 3
 #define FLAG_PASSING 4
 
-#define DROPTYPE_NORMAL 1
-#define DROPTYPE_THROWN 2
+#define DROPTYPE_DROP 1
+#define DROPTYPE_THROW 2
+#define DROPTYPE_PASS 3
 
 // flag properties
 #define ctf_spawnorigin dropped_origin
@@ -67,12 +68,14 @@ float ctf_captimerecord; // record time for capturing the flag
 .float ctf_pickuptime;
 .float ctf_droptime;
 .float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
-.float max_flag_health;
-.entity ctf_pickupper;
+.entity ctf_carrier;
 .entity ctf_dropper; // don't allow spam of dropping the flag
+.float max_flag_health;
 
-// other properties
-.float pass_antispam;
+// passing properties
+.entity pass_sender;
+.entity pass_target;
+.float throw_antispam;
 
 // 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