*K5None }g7 /K55.{O_N/ggZlActorAllowAIToControlHullMGAllowAIToControlMainGunArrayProperty BoolProperty BoundriesbPlayerTransitioningCCSMeatPuppetCCSTankControllerCheckDifficultyLevelsInRangeCheckErrorVsDistRatiosCheckRangesInRangeCheckTargetSpeedsInRangeClass Collision Components ControllerCoreDefault__CCSTankControllerDefault__MutatorDefault__TankTweakDefault__TankTweakController!DifficultyInfantryMissPercentage*DifficultyInfantryMissPercentageBoundriesDifficultyLevelsDifficultyLevelsBoundries DifficultyMainGunVarianceFactor)DifficultyMainGunVarianceFactorBoundries DoTweakTankDriverEnteredVehicleDriverLeftVehicle EnemyDir EnemyPosEngineErrorVsDistanceRatiosErrorVsDistanceRatiosBoundriesFireTankWeaponFloatProperty FunctionGrip GroupNamesHasAtLeastOneHumanPlayerHero HorizontalIndex IntPropertyIsAIsHumanControlledl_tweakController l_vehicleLevelsLong!MainGunErrorVsDistRatioBoundriesMainGunErrorVsDistRatios"MainGunHorizontalErrorVsDistRatio MainGunVerticalErrorVsDistRatioMaximum MeatPuppet MeatPuppet0Medium MetaDataMinimumMinMax MovementMutator NavigationNoneObjectObjectProperty OrderIndexOrientSeatWeaponOrientSeatWeaponRelativeOwnerPPackageParentPawnPostBeginPlayRangeMissPercentageRangeMissPercentageBoundriesRangesRangesBoundriesRangeSettingsRangeSettingsBoundries rangeValuesratiosRecruit RelativeDirReplacementPrimitive ReturnValueROGame ROVehicleROVehicleTankRunning ScriptStruct ScriptTextSeatIdxSeatIdxHullMGSeatIdxMainGunner SeatPawnSeatsShortsoldierSpawnspeedsSpriteSpriteComponentStillStructPropertyTankControllerTankIsTweaked TankTweakTankTweakControllerTargetSpeedMissPercentage#TargetSpeedMissPercentageBoundries TargetSpeedsTargetSpeedsBoundries TextBuffer ToleranceUndoTankTweaksVValueVectorVehicle VehicleSeat VerticalVeteranWalking WeaponIndexNRqdYY':Z   % & - < C D ^ l u  @ L j {   [%6%7&.&M&g&&&/&$&F&G-a-b!@DHDDmDcJJ!J'JZ^z^|!ji*(K5~(s5f(5V(5J58,6=,6Jl6 ]([8 (8 k(8Js88,c9=,9Jt493(:;(;e(?;JPg;8,;=,#<JQ4O<}(=+(=J"t=8,G>=,s>J#>J(?J(?J5,?JN,@JR,C@Jq,o@J,@J,@J4,@JO,AJS,KAJr,wAJ,AJ,AJMbA01,]C00,C0I,C0x,CJ D41, F4I,7F4x,cFJwF7,H72,2HJ ^H:,HJ:h,tJJ jJ=, L=T,6LJ jbL@,M@U,MJ $NEY(:OE8(bOE=(OEy(OJ>OI,(]PIY(PIx,PJ)PoQJSRan(8Za(`Za(ZSY(ZSv(ZS,[S`(,[aFT[XY(\Xv(7\XW,_\X`(\aG\]Y(n]] ,]](]]`(]a$^_K,^a^awcpdaZFeb: e<<AeKi4iJ_4|(!ja_4       ! "#$%&'() *$#)"(!' &%,a-.+/,-aGL.12031GaL25 46A58 79 A8 ; :<A;> =?A>ABC@DCBAFGEHFG fIE@=:740+eKeJLMOPQNRMQPOTUVSWNMVUTYZ[X\NM[ZYJ]^$# " ! NML_NaMLg`_]XSbca%  *)('&%$#"! NMLBBBBBBBBBV%f%~%*%BBBBBB=lxV%f%~%*%B8lxV%f%~%*%BBBBB BB BB k%%]%BBB B B B =l\sk%%]%B8l\sk%%]%BBBBBBBBBe%;%3%BBBBBB=l\Pe%;%3%B8l\Pe%;%3%BBBBBBB+%}%BBBBBB=l@"+%}%B8l@"+%}%BBB@BB@BB@BB@BB @BB!@B B"@BB#@BB$BB%BB&BB'BB(BB)BB*z #6  $$ **6  ## ))6  "" ((6  !! ''6    &&6   %% SMBBB,BaB-BB.BB+/,./|, !\"w, *V,  !8,  !p- a,  JJJJJ, -G),.a,  L-L.a,  ||.a,  L-L.a,  w/. SBBB1BB2BB03O81.31 !\"w1 *11 -.G)1..a1  L-L.a1  w..a1  L-L.a1  32 SBBB5BB46O56A>5655555556A>5655555556A>5655555556A>56555555 S BBB8B B797+5 9A>5 95 585 585 9qA>5 95 95 585 9qA>5 95 95 58 S BBB;BB:< +5<A>5<55;55;5<qA>5<5<55;5<qA>5<5<55; S BBB>BB=?*#_5?A>5?55>55>5?A>5?55>55> S BBBABBBBBCBB@D)%lD-DCDCWWDBDBD:AS>BBBFBBGBBEH>#'CF%6F6H (w5FH *Tw5FH  *(5FH  J'F (:GS)fI 0 )I>E @ = : 740M+oieA?BKl8=lxV%Af%A~%A*%AB8lxV%Bf%B~%B*%BBBl8=lxV%>f%>~%>*%>B8lxV%@@f%@@~%@@*%@@BBrlt=l\sk%%]%B8l\sk%B%B]%BBBSlQ=l\Pe% A;% A3% AB8l\Pe%C;%C3%CBBOlQ=l\Pe%@;%@3%@B8l\Pe%B;%B3%BBB4l#=l@"+% ף;}% ף;B8l@"+%=}%=BB(TankAIMutatorseBBBBLBBMBBBBOBBPBBQBBNR(|R  N-N>R  M-MRQP(:OSFBBBTBBUBBVBBSWA|W  N-N>W  M-MWVU(:TSGBBBYBBZBB[BBX\X? |\  N-N>\  M-M\[Z(:YS$BBJB]^l6 % 5^ $$& 5^ $$, 5^ $$, 5^ $$% 5^ ##& 5^ ##, 5^ ##, 5^ ##% 5 ^ ""& 5 ^ "", 5 ^ ""% 5^ !!HB& 5^ !!HB, 5^ !!HB% 5^  & 5^  , 5^   5^  5^  N-N^ - M-M^ - L-L' SB_A               N-N a N-N M-M a M-M L-L( Swg` 0w`_$]GXFSBABb9DcBBB%E 16384000E 16384001E 16384002E 16384003E 16384004E 16384005 E 16384006 E 16384007 E 16384008E 16384009 E 16384010E 16384011E 16384012E 16384013E 16384014E 16384015E 16384016E 16384017E 16384018E 16384019*E 16384020)E 16384021(E 16384022'E 16384023&E 16384024%E 16384025$E 16384026#E 16384027"E 16384028!E 16384029 E 16384030E 16384031E 16384032E 16384033NE 16252928ME 16252929LE 16252930iXDBBd(/*! * @defgroup TnkTwk Tank Tweak Mutator * @brief Mutator to customize the behaviour of the AIs in a tank * @author Marlon van den Berg (Ducky) * @version Beta 7 * @Date Jan 25, 2012 * @{ */ /*! * @brief Main class to hook up with the game engine * @details This class does contain all the TankTweak custom settings */ class TankTweak extends Mutator config(TankTweak); /*! * @brief To hold game difficulty level related settings */ struct DifficultyLevels { var float Recruit; var float Soldier; var float Veteran; var float Hero; }; /*! * @brief Minimum and maximum boundries for the difficulty level related settings */ struct DifficultyLevelsBoundries { var DifficultyLevels Minimum; var DifficultyLevels Maximum; }; /*! * @brief To hold target speed related settings */ struct TargetSpeeds { var float Still; var float Walking; var float Running; }; /*! * @brief Minimum and maximum boundries for target speed related settings */ struct TargetSpeedsBoundries { var TargetSpeeds Minimum; var TargetSpeeds Maximum; }; /*! * @brief To hold distance related settings */ struct Ranges { var float Short; var float Medium; var float Long; }; /*! * @brief Minimum and maximum boudries for distance related settings */ struct RangesBoundries { var Ranges Minimum; var Ranges Maximum; }; /*! * @brief Error versus distance ratios * @note Looks like they aren't used at all in RO2 */ struct ErrorVsDistanceRatios { var float Horizontal; var float Vertical; }; /*! * @brief Minimum and maximum boudries for error versus distance ratios * @note Looks like they aren't used at all in RO2 */ struct ErrorVsDistanceRatiosBoundries { var ErrorVsDistanceRatios Minimum; var ErrorVsDistanceRatios Maximum; }; var DifficultyLevelsBoundries DifficultyInfantryMissPercentageBoundries; var DifficultyLevelsBoundries DifficultyMainGunVarianceFactorBoundries; var TargetSpeedsBoundries TargetSpeedMissPercentageBoundries; var RangesBoundries RangeSettingsBoundries; var RangesBoundries RangeMissPercentageBoundries; var ErrorVsDistanceRatiosBoundries MainGunErrorVsDistRatioBoundries; var config DifficultyLevels DifficultyInfantryMissPercentage; var config DifficultyLevels DifficultyMainGunVarianceFactor; var config TargetSpeeds TargetSpeedMissPercentage; var config Ranges RangeSettings; var config Ranges RangeMissPercentage; var config ErrorVsDistanceRatios MainGunErrorVsDistRatios; var config bool AllowAIToControlHullMG; var config bool AllowAIToControlMainGun; /*! * @brief Overload function for Mutator.PostBeginPlay * @par * Used to check if all values in the ini file are in range. If not, then adjust them */ function PostBeginPlay() { ; // Make sure that all values taken from the config file are within the boundries self.CheckDifficultyLevelsInRange(self.DifficultyInfantryMissPercentage, self.DifficultyInfantryMissPercentageBoundries); self.CheckDifficultyLevelsInRange(self.DifficultyMainGunVarianceFactor, self.DifficultyMainGunVarianceFactorBoundries); self.CheckTargetSpeedsInRange(self.TargetSpeedMissPercentage, self.TargetSpeedMissPercentageBoundries); self.CheckRangesInRange(self.RangeSettings, self.RangeSettingsBoundries); self.CheckRangesInRange(self.RangeMissPercentage, self.RangeMissPercentageBoundries); self.CheckErrorVsDistRatios(self.MainGunErrorVsDistRatios, self.MainGunErrorVsDistRatioBoundries); super.PostBeginPlay(); } /*! * @brief Overload function for Mutator.DriverEnteredVehicle * @par * Does the main part of the job. It replaces the CCSTankController instance with a TankTweakController * instance and calls the tweaking if required. */ function DriverEnteredVehicle(Vehicle V, Pawn P) { local TankTweakController l_tweakController; local ROVehicle l_vehicle; l_vehicle = ROVehicle(V); // Make sure that we are a tank and that we have a CCSTankController object candidate if (l_vehicle.IsA('ROVehicleTank') && (l_vehicle.TankController != None)) { // Make sure that we don't replace more than once for the same tank if (l_vehicle.TankController.IsA('CCSTankController') && !l_vehicle.TankController.IsA('TankTweakController')) { ; // Create the new tank overload class l_tweakController = Spawn(class'TankTweakController', l_vehicle.TankController.Owner); // Replace the CCSTankController with my new TankTweakController object // Hope this won't cause a memory leak l_vehicle.TankController = l_tweakController; } if (self.HasAtLeastOneHumanPlayer(l_vehicle)) { ; if (!TankTweakController(l_vehicle.TankController).TankIsTweaked) { TankTweakController(l_vehicle.TankController).DoTweakTank(self); } else { ; } } else { ; if (TankTweakController(l_vehicle.TankController).TankIsTweaked) { ; TankTweakController(l_vehicle.TankController).UndoTankTweaks(); } } } super.DriverEnteredVehicle(V, P); } /*! * @brief Overload function for Mutator.DriverLeftVehicle * @param[in] V The vehicle which the pawn did leave * @param[in] P The pawn that left the vehicle * @par * Will restore the defaults if there are no more human players in the tank */ function DriverLeftVehicle(Vehicle V, Pawn P) { local ROVehicle l_vehicle; l_vehicle = ROVehicle(V); // Make sure that we are a tank and that we have a CCSTankController object candidate if (l_vehicle.IsA('ROVehicleTank') && (l_vehicle.TankController != None)) { // Did the player really leave or is he just transfering seat? if (!l_vehicle.bPlayerTransitioning) { ; // If all humans are gone, then restore to TWI's defaults if (!self.HasAtLeastOneHumanPlayer(l_vehicle)) { ; if (TankTweakController(l_vehicle.TankController).TankIsTweaked) { TankTweakController(l_vehicle.TankController).UndoTankTweaks(); } else { ; } } } else { ; if (!TankTweakController(l_vehicle.TankController).TankIsTweaked) { ; TankTweakController(l_vehicle.TankController).DoTweakTank(self); } } } super.DriverLeftVehicle(V, P); } /*! * @brief Check if percentage settings are in range, corrects them if they are not * @param[in,out] levels The difficulty levels that needs to be checked * @param[in] boundries The difficulty levels must be within these boundries */ function CheckDifficultyLevelsInRange(DifficultyLevels levels, DifficultyLevelsBoundries boundries) { levels.Recruit = self.MinMax(levels.Recruit, boundries.Minimum.Recruit, boundries.Minimum.Recruit); levels.Soldier = self.MinMax(levels.Soldier, boundries.Minimum.Soldier, boundries.Minimum.Soldier); levels.Veteran = self.MinMax(levels.Veteran, boundries.Minimum.Recruit, boundries.Minimum.Veteran); levels.Hero = self.MinMax(levels.Hero, boundries.Minimum.Hero, boundries.Minimum.Hero); } /*! * @brief Check if target speed settings are in range, corrects them is they are not * @param[in,out] speeds The target speeds that needs to be checked * @param[in] boundries The target speeds must be within these boundries */ function CheckTargetSpeedsInRange(TargetSpeeds speeds, TargetSpeedsBoundries boundries) { speeds.Still = self.MinMax(speeds.Still, boundries.Minimum.Still, boundries.Minimum.Still); speeds.Walking = self.MinMax(speeds.Walking, speeds.Still, boundries.Minimum.Walking); speeds.Running = self.MinMax(speeds.Running, speeds.Walking, boundries.Minimum.Running); } /*! * @brief Check if range settings are in range, corrects them is they are not * @param[in,out] rangeValues The range settings that needs to be checked * @param[in] boundries The range settings must be within these boundries */ function CheckRangesInRange(Ranges rangeValues, RangesBoundries boundries) { rangeValues.Short = self.MinMax(rangeValues.Short, boundries.Minimum.Short, boundries.Maximum.Short); rangeValues.Medium = self.MinMax(rangeValues.Medium, rangeValues.Short, boundries.Maximum.Medium); rangeValues.Long = self.MinMax(rangeValues.Long, rangeValues.Medium, boundries.Maximum.Long); } /*! * @brief Check if ratio settings are in range, corrects them is they are not * @param[in,out] ratios The ratio settings that needs to be checked * @param[in] boundries The ratio settings must be within these boundries */ function CheckErrorVsDistRatios(ErrorVsDistanceRatios ratios, ErrorVsDistanceRatiosBoundries boundries) { ratios.Horizontal = self.MinMax(ratios.Horizontal, boundries.Minimum.Horizontal, boundries.Maximum.Horizontal); ratios.Vertical = self.MinMax(ratios.Vertical, boundries.Minimum.Vertical, boundries.Maximum.Vertical); } /*! * @brief Validates a value agains a minimim and maximum boundry and corrects the value in case it is out of range * @param[in] value The value that need to be checked * @param[in] minimum The minimum boundry * @param[in] maximum The maximum boundry * @return Value guaranteed within the boundries */ function float MinMax(float value, float minimum, float maximum) { if (value < minimum) { value = minimum; } else if (value > maximum) { value = maximum; } return value; } /*! * @brief Checks if there is at least one human player in the tank * @param[in] V The vehicle in which we need to search for humans * @returns True if there is, else false */ function bool HasAtLeastOneHumanPlayer(ROVehicle V) { local int index; for (index = 0; index < V.Seats.length; index++) { if ((V.Seats[index].SeatPawn != none) && (V.Seats[index].SeatPawn.Controller != None)) { if (V.Seats[index].SeatPawn.IsHumanControlled()) { return true; } } } return false; } /*! * @} */ B/*! * @addgroup TnkTwk * @{ */ /*! * @brief Tank controller class * @details The tweaking is done by this class */ class TankTweakController extends CCSTankController; /*! * @brief True if the AI may control the hull MG, else false */ var bool AllowAIToControlHullMG; /*! * @brief True if the AI may control the main gun, else false */ var bool AllowAIToControlMainGun; /*! * @brief True if the tank is tweaked, else false */ var bool TankIsTweaked; /*! * @brief Overload function for CCSTankController.OrientSeatWeapon * @param[in] SeatIdx The seat index on which the AI is stitting when he tries to fire his weapon * @param[in] EnemyDir Position of the enemy * @param[in] Tolerance Positioning tolerance * @return True if the AI may rotate the main gun. False means the gun is broken or disabled. * @par * Here we prevent that the turret can be rotated. Returning false will tell * the game that the turret is broken and can't be moved. We just fool the * game by pretending it's broken if the main gun is disabled in the ini file. */ event bool OrientSeatWeapon(int SeatIdx, Vector EnemyDir, int Tolerance ) { if (((SeatIdx == self.SeatIdxHullMG) && self.AllowAIToControlHullMG) || ((SeatIdx == self.SeatIdxMainGunner) && self.AllowAIToControlMainGun)) { return super.OrientSeatWeapon(SeatIdx, EnemyDir, Tolerance); } else { return false; } } /*! * @brief Overload function for CCSTankController.OrientSeatWeaponRelative * @param[in] SeatIdx The seat index on which the AI is stitting when he tries to fire his weapon * @param[in] RelativeDir The relative direction coordinates * @param[in] Tolerance Positioning tolerance * @return True if the AI may rotate the main gun. False means the gun is broken or disabled. * @par * Here we prevent that the turret can be rotated. Returning false will tell * the game that the turret is broken and can't be moved. We just fool the * game by pretending it's broken if the main gun is disabled in the ini file. * @note Same as @ref OrientSeatWeapon, but for relative rotations */ event bool OrientSeatWeaponRelative(int SeatIdx, Vector RelativeDir, int Tolerance ) { if (((SeatIdx == self.SeatIdxHullMG) && self.AllowAIToControlHullMG) || ((SeatIdx == self.SeatIdxMainGunner) && self.AllowAIToControlMainGun)) { return super.OrientSeatWeaponRelative(SeatIdx, RelativeDir, Tolerance); } else { return false; } } /*! * @brief Overload function for CCSTankController.FireTankWeapon * @param[in] SeatIdx The seat index on which the AI is stitting when he tries to fire his weapon * @param[in] WeaponIndex The index of the weapon position * @param[in] EnemyPos Position of the enemy at which the bot wants to fire * @return True if the AI may fire. False means the gun is broken or disabled. * @par * Here we prevent that the main gun and/or the hull mG are fired depending * on the settings in the ini file. */ event bool FireTankWeapon(int SeatIdx, int WeaponIndex, Vector EnemyPos) { if (((SeatIdx == self.SeatIdxHullMG) && self.AllowAIToControlHullMG) || ((SeatIdx == self.SeatIdxMainGunner) && self.AllowAIToControlMainGun)) { return super.FireTankWeapon(SeatIdx, WeaponIndex, EnemyPos); } else { return false; } } /*! * @brief Tweaks the tank by overriding the TWI settings with custom settings * parem[in] parent Reference to my parent object */ function DoTweakTank(TankTweak parent) { ; // In the next blocks the settings are updated self.DifficultyInfantryMissPercentage[0] = parent.DifficultyInfantryMissPercentage.Recruit; self.DifficultyInfantryMissPercentage[1] = parent.DifficultyInfantryMissPercentage.Soldier; self.DifficultyInfantryMissPercentage[2] = parent.DifficultyInfantryMissPercentage.Veteran; self.DifficultyInfantryMissPercentage[3] = parent.DifficultyInfantryMissPercentage.Hero; self.DifficultyMainGunVarianceFactor[0] = parent.DifficultyMainGunVarianceFactor.Recruit; self.DifficultyMainGunVarianceFactor[1] = parent.DifficultyMainGunVarianceFactor.Soldier; self.DifficultyMainGunVarianceFactor[2] = parent.DifficultyMainGunVarianceFactor.Veteran; self.DifficultyMainGunVarianceFactor[3] = parent.DifficultyMainGunVarianceFactor.Hero; self.TargetSpeedMissPercentage[0] = parent.TargetSpeedMissPercentage.Still; self.TargetSpeedMissPercentage[1] = parent.TargetSpeedMissPercentage.Walking; self.TargetSpeedMissPercentage[2] = parent.TargetSpeedMissPercentage.Running; self.RangeSettings[0] = (parent.RangeSettings.Short * 50.0); self.RangeSettings[1] = (parent.RangeSettings.Medium * 50.0); self.RangeSettings[2] = (parent.RangeSettings.Long * 50.0); self.RangeMissPercentage[0] = parent.RangeMissPercentage.Short; self.RangeMissPercentage[1] = parent.RangeMissPercentage.Medium; self.RangeMissPercentage[2] = parent.RangeMissPercentage.Long; self.MainGunHorizontalErrorVsDistRatio = parent.MainGunErrorVsDistRatios.Horizontal; self.MainGunVerticalErrorVsDistRatio = parent.MainGunErrorVsDistRatios.Vertical; self.AllowAIToControlHullMG = parent.AllowAIToControlHullMG; self.AllowAIToControlMainGun = parent.AllowAIToControlMainGun; // Mark the tank as tweaked self.TankIsTweaked = true; } /*! * @brief Undoes (new English word) the tweaks by restoring the TWI defaults */ function UndoTankTweaks() { ; // Restote the default settings self.DifficultyInfantryMissPercentage = class'CCSTankController'.default.DifficultyInfantryMissPercentage; self.DifficultyMainGunVarianceFactor = class'CCSTankController'.default.DifficultyMainGunVarianceFactor; self.TargetSpeedMissPercentage = class'CCSTankController'.default.TargetSpeedMissPercentage; self.RangeSettings = class'CCSTankController'.default.RangeSettings; self.RangeMissPercentage = class'CCSTankController'.default.RangeMissPercentage; self.MainGunHorizontalErrorVsDistRatio = class'CCSTankController'.default.MainGunHorizontalErrorVsDistRatio; self.MainGunVerticalErrorVsDistRatio = class'CCSTankController'.default.MainGunVerticalErrorVsDistRatio; self.AllowAIToControlHullMG = class'TankTweakController'.default.AllowAIToControlHullMG; self.AllowAIToControlMainGun = class'TankTweakController'.default.AllowAIToControlMainGun; // Mark the tank as normal (not tweaked) self.TankIsTweaked = false; } /*! * @} */