StarWeaver/MutPassLess
Maybe this is too much code; and it could probably use more other text, but I thought i'd put it up this way and see how it looks . . .
//----------------------------------------------------------- // Star Weaver; 2005 // A mutator to enable AI tweaks for bombing run that attempt // to keep the ball in the hands of the bots as much as possible, // for those of us who prefer to *support* rather than fling // ourselves into the maw of destruction. //----------------------------------------------------------- class MutPassLess extends Mutator; /* This was my first attempt at making this work . . . and it worked, but for some reason doing it this way disassociates the actors created from the return of the spawn() function that creates them, so the code that spawns the TeamAIs spewed a bunch of null references when I did it this way. And TeamAIs aren't sent through CheckReplacement! So... */ /* function bool CheckReplacement (Actor Other, out byte bSuperRelevant) { bSuperRelevant = 1; // Still don't know what this does if ( Other.IsA('BombingRunSquadAI') && ! Other.IsA('DA_PassLessSquadAI') ) { ReplaceWith(Other,"DA_MutPassLess.DA_PassLessSquadAI"); Log(" --- DA DEBUG --- Replacement Made"); return False; } return True; } */ function PreBeginPlay() { Super.PreBeginPlay(); /* This seems to be the right place to do this; the game object exists but the subobjects haven't been created yet as far as I can tell. I wish there was a bit more documentation on this . . . . */ TeamGame(Level.Game).TeamAIType[0] = Class'DA_MutPassLess.DA_PassLessTeamAI'; TeamGame(Level.Game).TeamAIType[1] = Class'DA_MutPassLess.DA_PassLessTeamAI'; } function bool MutatorIsAllowed() { if (Level.Game.IsA('xBombingRun')) { log(" --- DA DEBUG --- Is Bombingrun"); return True; } return False; } DefaultProperties { }
//----------------------------------------------------------- // Star Weaver; 2005 // A simple tweak to the default properties, really. //----------------------------------------------------------- class DA_PassLessTeamAI extends BombingRunTeamAI; Event PostBeginPlay() { log("--- DA DEBUG --- PassLessTeamAI Extant"); } DefaultProperties { SquadType=Class'DA_MutPassLess.DA_PassLessSquadAI' }
//----------------------------------------------------------- // Star Weaver; 2005 // Changes to bombing run squad AI to favor bots over humans // for passing and to require either grevious from the bot // or lots of extra health on the human. //----------------------------------------------------------- class DA_PassLessSquadAI extends BombingRunSquadAI; function Initialize(UnrealTeamInfo T, GameObjective O, Controller C) { Super.Initialize(T,O,C); Log(" --- DA Debug --- PassLessSquadAI Initialized"); } function bool TryPassTo(vector V, Bot CarrierBot, Pawn Recipient) { local Pawn Carrier; local float CarrierDurability; local float RecipientDurability; Carrier = CarrierBot.Pawn; CarrierDurability = Carrier.Health + Carrier.ShieldStrength; RecipientDurability = Recipient.Health + Recipient.ShieldStrength; if ( ( // Rules for passing to humans (Recipient.IsHumanControlled()) && ( ( (CarrierDurability <=20) || (RecipientDurability > 150 && RecipientDurability > CarrierDurability * 2.2)) ) && (Recipient.Health + Recipient.ShieldStrength >= 110) ) || ( // Rules for passing to bots ( (CarrierBot.Focus == CarrierBot.MoveTarget) || (Recipient.Controller.MoveTarget == CarrierBot.RouteCache[0]) || (Recipient.Controller.MoveTarget == CarrierBot.RouteCache[1]) || (Recipient.Controller.MoveTarget == CarrierBot.RouteCache[2])) && (Recipient.Health + Recipient.ShieldStrength >= FMin(60, Carrier.Health)) ) && ( // Rules that apply in both cases (VSize(Recipient.Location - Carrier.Location) < 3500) && (V Dot Normal(Recipient.Location - Carrier.Location) > 0.5) && (Carrier.LineOfSightTo(Recipient)) ) ) { PassTarget = Bot(Recipient.Controller); Carrier.Weapon.SetAITarget(Recipient); CarrierBot.bPlannedShot = true; CarrierBot.Target = Recipient; Carrier.Weapon.BotFire(false); return true; } return false; } /* OrdersForBombCarrier() Tell bot what to do if he's carrying the flag */ function bool OrdersForBombCarrier(Bot B) { local bot S; local vector V; local controller C; local PlayerController PC; local bool bPassing, bCanPass, bSelfPass; B.TryCombo("xGame.ComboSpeed"); if ( (B.Enemy != None) && (B.Pawn.Health < 60 )) B.SendMessage(None, 'OTHER', B.GetMessageIndex('NEEDBACKUP'), 25, 'TEAM'); B.bPlannedShot = false; SelfPasser = None; PassTarget = None; // decide whether to pass ball if ( (VSize(B.Pawn.Location - EnemyBase.Location) > 3000) && (VSize(B.Pawn.Location - HomeBase.Location) > 3000) && !B.Pawn.InCurrentCombo() ) bCanPass = true; bSelfPass = GameObject(B.PlayerReplicationInfo.HasFlag).CanBeThrownBy(B.Pawn) && bCanPass && (B.TranslocFreq < Level.TimeSeconds + 12) && (B.Skill + B.Tactics > 3 + 4*FRand()) && ((B.Enemy == None) || (VSize(B.Pawn.Location - HomeBase.Location) > 5000)); if ( bCanPass ) { // check for nearby teammate to pass to // first check for human player // -- Begin changed section of this function -- // -- Check for BOTS first -- Star V = vector(B.Pawn.Rotation); for ( S=SquadMembers; S!=None; S=S.NextSquadMember ) { if ( (S.Pawn != None) && ((S.Pawn.Physics != PHYS_Falling) || (S.Pawn.PhysicsVolume.Gravity.Z < -900)) && TryPassTo(V,B,S.Pawn) ) { bPassing = true; bCanPass = false; break; } } if ( bCanPass ) { for ( C=Level.ControllerList; C!=None; C=C.NextController ) { PC = PlayerController(C); if ( (PC != None) && B.SameTeamAs(PC) && (PC.Pawn != None) && TryPassTo(V,B,PC.Pawn) ) { bPassing = true; break; } else PC = None; } } // -- End changed section -- } if ( !bPassing && PreferShootScore(B) ) { if ( (ShootSpot(B.Pawn.Anchor) != None) && B.Pawn.ReachedDestination(B.Pawn.Anchor) ) { B.bPlannedShot = true; B.Pawn.Weapon.SetAITarget(EnemyBase); B.DoRangedAttackOn(EnemyBase); return true; } if ( AlternatePath == None ) { B.MoveTarget = B.FindPathTowardNearest(ShootSpotClass[Team.TeamIndex],false); if ( B.MoveTarget != None ) { if ( bSelfPass ) PassToSelf(B); B.GoalString = "Move to shoot spot "$B.RouteGoal; B.SetAttractionState(); return true; } if ( B.bSoaking ) B.SoakStop("NO PATH TO SHOOTSPOT"); } } if ( !FindPathToObjective(B,EnemyBase) ) { B.GoalString = "No path to enemy base for bomb carrier"; return false; } if ( B.MoveTarget == EnemyBase ) { B.GoalString = "Near enemy Base with bomb!"; if ( B.Pawn.ReachedDestination(EnemyBase) ) EnemyBase.Touch(B.Pawn); } else if ( bPassing ) B.bRecommendFastMove = true; else if ( bSelfPass ) PassToSelf(B); return true; } DefaultProperties { }
Question...
lizardman6:Are you using this code for UT2k4?
StarWeaver: Yeah, that's what I was building it with, but I kinda fell out of the game and never got around to testing it much so I don't even know how well it works. I seem to recall that it needed a gametype check because it was crashing if left in a DM game, but I don't remember if that's in the code I posted or if I even fixed it at all.
I'm getting back into editing again now (for real, I think), so I'll probably be taking another look at this sooner or later ^_^.