From 13f715345beb2f2a51647d845b9b57407371323d Mon Sep 17 00:00:00 2001 From: dnqbob Date: Sun, 7 May 2023 20:38:42 +0800 Subject: [PATCH 1/3] Add LoadCargoBotModule --- .../Traits/BotModules/LoadCargoBotModule.cs | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 OpenRA.Mods.CA/Traits/BotModules/LoadCargoBotModule.cs diff --git a/OpenRA.Mods.CA/Traits/BotModules/LoadCargoBotModule.cs b/OpenRA.Mods.CA/Traits/BotModules/LoadCargoBotModule.cs new file mode 100644 index 0000000000..17c1015f64 --- /dev/null +++ b/OpenRA.Mods.CA/Traits/BotModules/LoadCargoBotModule.cs @@ -0,0 +1,154 @@ +#region Copyright & License Information +/** + * Copyright (c) The OpenRA Combined Arms Developers (see CREDITS). + * This file is part of OpenRA Combined Arms, which is free software. + * It is made available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. For more information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Mods.Common.Activities; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.CA.Traits +{ + [Flags] + public enum LoadRequirement + { + All = 0, + IdleUnit = 1, + } + + [Flags] + public enum TransportOwner + { + Self = 0, + AlliedBot = 1, + Allies = 2 + } + + [TraitLocation(SystemActors.Player)] + [Desc("Manages AI load unit related with Cargo and Passenger traits. Better used with AI unload trait")] + public class LoadCargoBotModuleInfo : ConditionalTraitInfo + { + [FieldLoader.Require] + [Desc("Actor types that can be targeted for load, must have Cargo.", + "the boolean describe if this transport need the unit that is no-idle to get inside")] + public readonly Dictionary TransportTypesAndLoadRequirement = default; + + [FieldLoader.Require] + [Desc("Actor types that used for loading, must have Passenger.")] + public readonly HashSet PassengerTypes = default; + + [Desc("Actor relationship that can be targeted for load.")] + public readonly TransportOwner ValidTransportOwner = TransportOwner.Self; + + [Desc("Scan suitable actors and target in this interval.")] + public readonly int ScanTick = 317; + + [Desc("Don't load passengers to this actor if damage state is worse than this.")] + public readonly DamageState ValidDamageState = DamageState.Heavy; + + [Desc("Don't load passengers that are further than this distance to this actor.")] + public readonly WDist MaxDistance = WDist.FromCells(20); + + public override object Create(ActorInitializer init) { return new LoadCargoBotModule(init.Self, this); } + } + + public class LoadCargoBotModule : ConditionalTrait, IBotTick + { + readonly World world; + readonly Player player; + readonly Predicate unitCannotBeOrdered; + readonly Predicate unitCannotBeOrderedOrIsBusy; + readonly Predicate invalidTransport; + int minAssignRoleDelayTicks; + + public LoadCargoBotModule(Actor self, LoadCargoBotModuleInfo info) + : base(info) + { + world = self.World; + player = self.Owner; + switch (info.ValidTransportOwner) + { + case TransportOwner.Self: + invalidTransport = a => a == null || a.IsDead || !a.IsInWorld || a.Owner != player; + break; + case TransportOwner.AlliedBot: + invalidTransport = a => a == null || a.IsDead || !a.IsInWorld || !a.Owner.IsBot || a.Owner.RelationshipWith(player) != PlayerRelationship.Ally; + break; + case TransportOwner.Allies: + invalidTransport = a => a == null || a.IsDead || !a.IsInWorld || a.Owner.RelationshipWith(player) != PlayerRelationship.Ally; + break; + } + + unitCannotBeOrdered = a => a == null || a.IsDead || !a.IsInWorld || a.Owner != player; + unitCannotBeOrderedOrIsBusy = a => unitCannotBeOrdered(a) || (!a.IsIdle && !(a.CurrentActivity is FlyIdle)); + } + + protected override void TraitEnabled(Actor self) + { + // Avoid all AIs reevaluating assignments on the same tick, randomize their initial evaluation delay. + minAssignRoleDelayTicks = world.LocalRandom.Next(0, Info.ScanTick); + } + + void IBotTick.BotTick(IBot bot) + { + if (--minAssignRoleDelayTicks <= 0) + { + minAssignRoleDelayTicks = Info.ScanTick; + + var tcs = world.ActorsWithTrait().Where( + at => + { + var health = at.Actor.TraitOrDefault()?.DamageState; + return Info.TransportTypesAndLoadRequirement.ContainsKey(at.Actor.Info.Name) && !invalidTransport(at.Actor) + && at.Trait.HasSpace(1) && (health == null || health < Info.ValidDamageState); + }).ToList(); + + if (tcs.Count == 0) + return; + + var tc = tcs.Random(world.LocalRandom); + var cargo = tc.Trait; + var transport = tc.Actor; + var spaceTaken = 0; + + Predicate invalidPassenger; + if (Info.TransportTypesAndLoadRequirement[transport.Info.Name] == LoadRequirement.IdleUnit) + invalidPassenger = unitCannotBeOrderedOrIsBusy; + else + invalidPassenger = unitCannotBeOrdered; + + var passengers = world.ActorsWithTrait().Where(at => Info.PassengerTypes.Contains(at.Actor.Info.Name) && !invalidPassenger(at.Actor) && cargo.HasSpace(at.Trait.Info.Weight) && (at.Actor.CenterPosition - transport.CenterPosition).HorizontalLengthSquared <= Info.MaxDistance.LengthSquared) + .OrderBy(at => (at.Actor.CenterPosition - transport.CenterPosition).HorizontalLengthSquared); + + var orderedActors = new List(); + + foreach (var p in passengers) + { + var mobile = p.Actor.TraitOrDefault(); + if (mobile == null || !mobile.PathFinder.PathExistsForLocomotor(mobile.Locomotor, p.Actor.Location, transport.Location)) + continue; + + if (cargo.HasSpace(spaceTaken + p.Trait.Info.Weight)) + { + spaceTaken += p.Trait.Info.Weight; + orderedActors.Add(p.Actor); + } + + if (!cargo.HasSpace(spaceTaken + 1)) + break; + } + + if (orderedActors.Count > 0) + bot.QueueOrder(new Order("EnterTransport", null, Target.FromActor(transport), false, groupedActors: orderedActors.ToArray())); + } + } + } +} From b99071317a69a4c0b6b3ef034d1dc3daf2301852 Mon Sep 17 00:00:00 2001 From: dnqbob Date: Sun, 7 May 2023 22:54:07 +0800 Subject: [PATCH 2/3] Add AI yaml for LoadCargoBotModule --- mods/ca/rules/ai.yaml | 98 +++++++++++++++++++------------------ mods/ca/rules/defaults.yaml | 6 +++ mods/ca/rules/scrin.yaml | 2 +- mods/ca/rules/vehicles.yaml | 17 ++++--- 4 files changed, 67 insertions(+), 56 deletions(-) diff --git a/mods/ca/rules/ai.yaml b/mods/ca/rules/ai.yaml index b95941a92b..229614065a 100644 --- a/mods/ca/rules/ai.yaml +++ b/mods/ca/rules/ai.yaml @@ -1962,24 +1962,19 @@ Player: bori: 15 yuri: 15 mast: 15 - apc.ai: 20 - sapc.ai: 10 - sapc.ai2: 10 - intl.ai: 10 - intl.ai2: 10 + apc: 20 + sapc: 10 + intl: 10 jeep: 30 - apc2.nodai: 20 - apc2.gdiai: 20 - rapc.ai: 20 - vulc.ai: 10 + apc2: 20 + rapc: 20 + vulc: 10 hmmv: 30 gdrn: 30 mdrn: 20 xo: 20 btr: 20 - btr.ai: 10 btr.yuri: 20 - btr.yuri.ai: 10 gunw: 20 shrw: 20 bggy: 30 @@ -1996,7 +1991,7 @@ Player: ruin: 20 atmz: 20 1tnk: 70 - ifv.ai: 70 + ifv: 70 2tnk: 45 gtnk.squad: 20 tnkd: 45 @@ -2037,7 +2032,7 @@ Player: nhaw: 3 ctnk: 3 chpr: 3 - batf.ai: 10 + batf: 10 wtnk: 3 ttnk: 25 ttra: 25 @@ -2097,18 +2092,16 @@ Player: mrj: 1 cdrn: 1 nhaw: 1 - apc.ai: 2 - apc2.nodai: 2 - apc2.gdiai: 2 - rapc.ai: 2 - vulc.ai: 5 - btr.ai: 5 - btr.yuri.ai: 5 + apc: 2 + apc2: 2 + rapc: 2 + vulc: 5 + btr: 5 + btr.yuri: 5 + sapc: 1 hmmv: 2 bggy: 2 jeep: 2 - sapc.ai: 1 - sapc.ai2: 1 e6: 1 n6: 1 s6: 1 @@ -2245,23 +2238,18 @@ Player: bori: 15 yuri: 15 mast: 15 - apc.ai: 20 - sapc.ai: 10 - sapc.ai2: 10 - intl.ai: 10 - intl.ai2: 10 + apc: 20 + sapc: 10 + intl: 10 jeep: 30 - apc2.nodai: 20 - apc2.gdiai: 20 - vulc.ai: 10 + apc2: 20 + vulc: 10 hmmv: 30 gdrn: 30 mdrn: 20 xo: 20 btr: 20 - btr.ai: 10 btr.yuri: 20 - btr.yuri.ai: 10 gunw: 20 shrw: 20 bggy: 30 @@ -2278,7 +2266,7 @@ Player: ruin: 20 atmz: 20 1tnk: 70 - ifv.ai: 70 + ifv: 70 2tnk: 45 gtnk.squad: 20 tnkd: 45 @@ -2319,7 +2307,7 @@ Player: nhaw: 3 ctnk: 3 chpr: 3 - batf.ai: 10 + batf: 10 wtnk: 3 ttnk: 25 ttra: 25 @@ -2379,18 +2367,16 @@ Player: mrj: 1 cdrn: 1 nhaw: 1 - apc.ai: 2 - apc2.nodai: 2 - apc2.gdiai: 2 - rapc.ai: 2 - vulc.ai: 5 - btr.ai: 5 - btr.yuri.ai: 5 + apc: 2 + apc2: 2 + rapc: 2 + vulc: 5 + btr: 5 + btr.yuri: 5 + sapc: 1 hmmv: 2 bggy: 2 jeep: 2 - sapc.ai: 1 - sapc.ai2: 1 e6: 1 n6: 1 s6: 1 @@ -2526,7 +2512,7 @@ Player: ruin: 20 atmz: 20 1tnk: 70 - ifv.ai: 70 + ifv: 70 2tnk: 45 gtnk.squad: 20 tnkd: 45 @@ -2567,7 +2553,7 @@ Player: nhaw: 3 ctnk: 3 chpr: 3 - batf.ai: 10 + batf: 10 wtnk: 3 ttnk: 25 ttra: 25 @@ -2770,7 +2756,7 @@ Player: ruin: 20 atmz: 20 1tnk: 70 - ifv.ai: 70 + ifv: 70 2tnk: 45 gtnk.squad: 20 tnkd: 45 @@ -2811,7 +2797,7 @@ Player: nhaw: 3 ctnk: 3 chpr: 3 - batf.ai: 10 + batf: 10 wtnk: 3 ttnk: 25 ttra: 25 @@ -2994,3 +2980,21 @@ Player: deva: 3 pac: 3 mshp: 1 + LoadCargoBotModule: + RequiresCondition: enable-brutal-ai || enable-vhard-ai || enable-hard-ai || enable-normal-ai || enable-easy-ai || enable-naval-ai + TransportTypesAndLoadRequirement: + apc: IdleUnit + sapc: IdleUnit + intl: IdleUnit + apc2: IdleUnit + apc2.reinforce: IdleUnit + rapc: IdleUnit + vulc: IdleUnit + btr: IdleUnit + btr.yuri: IdleUnit + ifv: All + batf: All + reck: All + PassengerTypes: e1, e1r1, e2, e3, e3r1, e4, n1, n1r1, n2, n2r1, n3, n3r1, n4, n5, n5r1, n1c, n3c, s1, s2, s3, s4, feed, u3, rmbc, enli, mort, shok, e8, snip, bjet, acol, tplr, bh, ivan, rmbo, e7, seal, bori + ScanTick: 203 + ValidTransportOwner: AlliedBot diff --git a/mods/ca/rules/defaults.yaml b/mods/ca/rules/defaults.yaml index 3e0bb1aa98..8c12e2cf49 100644 --- a/mods/ca/rules/defaults.yaml +++ b/mods/ca/rules/defaults.yaml @@ -4097,6 +4097,12 @@ UnloadOnCondition@AIUNLOAD: RequiresCondition: damage || aiming +^AIEJECT: + GrantConditionOnDamageState@AIEJECT: + Condition: damage + UnloadOnCondition@AIEJECT: + RequiresCondition: damage + ^QueueUpdater: FreeActor@QUEUEUPDATER: Actor: QueueUpdaterDummy diff --git a/mods/ca/rules/scrin.yaml b/mods/ca/rules/scrin.yaml index da4945c91f..ecdf15f18f 100644 --- a/mods/ca/rules/scrin.yaml +++ b/mods/ca/rules/scrin.yaml @@ -1236,6 +1236,7 @@ INTL: Inherits@TANKBUSTERVULN: ^TankBusterVulnerability Inherits@SCRINVEHICLEVOICE: ^ScrinVehicleVoice Inherits@COMMANDOSKULL: ^CommandoSkull + Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: PlayerPalette: playerscrin Valued: @@ -1353,7 +1354,6 @@ INTL.AI: INTL.AI2: Inherits: INTL.AI - Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: Image: INTL Buildable: diff --git a/mods/ca/rules/vehicles.yaml b/mods/ca/rules/vehicles.yaml index ac42aeced5..78805cdd7b 100644 --- a/mods/ca/rules/vehicles.yaml +++ b/mods/ca/rules/vehicles.yaml @@ -1471,6 +1471,7 @@ SAPC: Inherits@TRANSPORT: ^Transport Inherits@NOUNLOADCHRONO: ^NoUnloadWhenChronoshifted Inherits@COMMANDOSKULL: ^CommandoSkull + Inherits@AIUNLOAD: ^AIUNLOAD Buildable: Queue: VehicleSQ, VehicleMQ BuildPaletteOrder: 255 @@ -1566,7 +1567,6 @@ SAPC: SAPC.AI: Inherits: SAPC - Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: Image: sapc Buildable: @@ -1578,7 +1578,6 @@ SAPC.AI: SAPC.AI2: Inherits: SAPC - Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: Image: sapc Buildable: @@ -1910,6 +1909,7 @@ APC: Inherits@SLOWCRUSH: ^SlowedByCrushing Inherits@UpgradeOverlay: ^UpgradeOverlay Inherits@COMMANDOSKULL: ^CommandoSkull + Inherits@AIUNLOAD: ^AIUNLOAD Buildable: Queue: VehicleSQ, VehicleMQ BuildPaletteOrder: 50 @@ -2020,7 +2020,6 @@ RAPC: APC.AI: Inherits: APC - Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: Image: APC Buildable: @@ -2035,7 +2034,6 @@ APC.AI: RAPC.AI: Inherits: RAPC - Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: Image: RAPC Buildable: @@ -3229,6 +3227,7 @@ APC2: Inherits@SLOWCRUSH: ^SlowedByCrushing Inherits@UpgradeOverlay: ^UpgradeOverlay Inherits@COMMANDOSKULL: ^CommandoSkull + Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: FactionImages: nod: apc2.nod @@ -3318,7 +3317,6 @@ APC2: APC2.NODAI: Inherits: APC2 - Inherits@AIUNLOAD: ^AIUNLOAD Buildable: Prerequisites: ~botplayer, ~vehicles.nod, ~!vulcan.upgrade, ~vehicles.apc2, ~techlevel.low RenderSprites: @@ -3331,7 +3329,6 @@ APC2.NODAI: APC2.GDIAI: Inherits: APC2 - Inherits@AIUNLOAD: ^AIUNLOAD Buildable: Prerequisites: ~botplayer, ~vehicles.gdi, ~!vulcan.upgrade, ~vehicles.apc2, ~techlevel.low RenderSprites: @@ -3368,6 +3365,7 @@ VULC: Inherits@SLOWCRUSH: ^SlowedByCrushing Inherits@A2GPROTECTION: ^AirToGroundProtection Inherits@COMMANDOSKULL: ^CommandoSkull + Inherits@AIUNLOAD: ^AIUNLOAD Buildable: Queue: VehicleSQ, VehicleMQ BuildPaletteOrder: 52 @@ -3535,7 +3533,6 @@ VULC: VULC.AI: Inherits: VULC - Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: Image: VULC Buildable: @@ -4863,6 +4860,7 @@ BTR: Inherits@A2GPROTECTION: ^AirToGroundProtection Inherits@UpgradeOverlay: ^UpgradeOverlay Inherits@COMMANDOSKULL: ^CommandoSkull + Inherits@AIUNLOAD: ^AIUNLOAD Buildable: Queue: VehicleSQ, VehicleMQ BuildPaletteOrder: 21 @@ -4958,7 +4956,6 @@ BTR: BTR.AI: Inherits: BTR - Inherits@AIUNLOAD: ^AIUNLOAD RenderSprites: Image: BTR Buildable: @@ -5117,6 +5114,7 @@ IFV: Inherits@SLOWCRUSH: ^SlowedByCrushing Inherits@COMMANDOSKULL: ^CommandoSkull Inherits@A2GPROTECTION: ^AirToGroundProtection + Inherits@AIEJECT: ^AIEJECT Buildable: Queue: VehicleSQ, VehicleMQ BuildPaletteOrder: 120 @@ -7052,6 +7050,7 @@ BATF: Inherits@SHRAPNEL: ^ThrowsShrapnel Inherits@COMMANDOSKULL: ^CommandoSkull Inherits@TANKBUSTERVULN: ^TankBusterVulnerability + Inherits@AIEJECT: ^AIEJECT Buildable: Queue: VehicleSQ, VehicleMQ BuildPaletteOrder: 340 @@ -7230,6 +7229,8 @@ BATF: Color: 00FF0080 Range: 4c512 RequiresCondition: loaded-medic + GrantConditionOnDamageState@AIEJECT: + ValidDamageStates: Critical BATF.AI: Inherits: BATF From 0ae74de50b63e3f973d78aac68b7cf4381a8ceca Mon Sep 17 00:00:00 2001 From: dnqbob Date: Sun, 10 Sep 2023 13:26:40 +0800 Subject: [PATCH 3/3] Ignore the squad member that is entering cargo --- OpenRA.Mods.CA/Traits/BotModules/SquadManagerBotModuleCA.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenRA.Mods.CA/Traits/BotModules/SquadManagerBotModuleCA.cs b/OpenRA.Mods.CA/Traits/BotModules/SquadManagerBotModuleCA.cs index aa62146ff9..2c23bf52cd 100644 --- a/OpenRA.Mods.CA/Traits/BotModules/SquadManagerBotModuleCA.cs +++ b/OpenRA.Mods.CA/Traits/BotModules/SquadManagerBotModuleCA.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Mods.CA.Traits.BotModules.Squads; +using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; using OpenRA.Traits; @@ -163,7 +164,7 @@ public SquadManagerBotModuleCA(Actor self, SquadManagerBotModuleCAInfo info) World = self.World; Player = self.Owner; - unitCannotBeOrdered = a => a == null || a.Owner != Player || a.IsDead || !a.IsInWorld; + unitCannotBeOrdered = a => a == null || a.Owner != Player || a.IsDead || !a.IsInWorld || a.CurrentActivity is Enter; } bool IsValidEnemyUnit(Actor a)