Skip to content

Commit 76271d8

Browse files
committed
Merge branch 'ca-engine/1.08-modifications' into ca-engine/1.08
2 parents 28c778b + 021aff1 commit 76271d8

File tree

25 files changed

+577
-26
lines changed

25 files changed

+577
-26
lines changed

OpenRA.Game/Map/MapPreview.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml, string[] mapComp
533533
newData.Players = new MapPlayers(MiniYaml.FromString(playersString,
534534
$"{yaml.NodeWithKey(nameof(r.players_block)).Location.Name}:{nameof(r.players_block)}"));
535535

536-
var rulesString = Encoding.UTF8.GetString(Convert.FromBase64String(r.rules));
536+
var rulesString = Encoding.UTF8.GetString(Convert.FromBase64String(r.rules ?? ""));
537537
var rulesYaml = new MiniYaml("", MiniYaml.FromString(rulesString,
538538
$"{yaml.NodeWithKey(nameof(r.rules)).Location.Name}:{nameof(r.rules)}")).ToDictionary();
539539
newData.SetCustomRules(modData, this, rulesYaml, null);

OpenRA.Game/Settings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ public class GameSettings
295295
[Desc("Allow mods to enable the Discord service that can interact with a local Discord client.")]
296296
public bool EnableDiscordService = true;
297297

298+
// added for CA
299+
public bool SelectionTooltip = true;
300+
298301
public TextNotificationPoolFilters TextNotificationPoolFilters = TextNotificationPoolFilters.Feedback | TextNotificationPoolFilters.Transients;
299302
}
300303

OpenRA.Mods.Cnc/Traits/Disguise.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public enum RevealDisguiseType
6565
}
6666

6767
[Desc("Provides access to the disguise command, which makes the actor appear to be another player's actor.")]
68-
sealed class DisguiseInfo : TraitInfo
68+
public class DisguiseInfo : TraitInfo
6969
{
7070
[VoiceReference]
7171
public readonly string Voice = "Action";
@@ -99,7 +99,7 @@ sealed class DisguiseInfo : TraitInfo
9999
public override object Create(ActorInitializer init) { return new Disguise(init.Self, this); }
100100
}
101101

102-
sealed class Disguise : IEffectiveOwner, IIssueOrder, IResolveOrder, IOrderVoice, INotifyAttack,
102+
public class Disguise : IEffectiveOwner, IIssueOrder, IResolveOrder, IOrderVoice, INotifyAttack,
103103
INotifyDamage, INotifyLoadCargo, INotifyUnloadCargo, INotifyDemolition, INotifyInfiltration, ITick
104104
{
105105
public ActorInfo AsActor { get; private set; }

OpenRA.Mods.Common/Activities/Air/FlyAttack.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,17 @@ public override bool Tick(Actor self)
127127
if (source == AttackSource.AttackMove)
128128
return true;
129129

130-
// AbortOnResupply cancels the current activity (after resupplying) plus any queued activities
131-
if (attackAircraft.Info.AbortOnResupply)
130+
var queuedMovement = NextActivity is Fly || NextActivity is AttackMoveActivity;
131+
132+
// AbortOnResupply cancels the current activity (after resupplying) plus any queued activities that aren't just moving
133+
if (attackAircraft.Info.AbortOnResupply && !queuedMovement)
132134
NextActivity?.Cancel(self);
133135

134-
QueueChild(new ReturnToBase(self));
136+
if (attackAircraft.Info.AbortOnResupply && queuedMovement)
137+
Queue(new ReturnToBase(self));
138+
else
139+
QueueChild(new ReturnToBase(self));
140+
135141
returnToBase = true;
136142
return attackAircraft.Info.AbortOnResupply;
137143
}

OpenRA.Mods.Common/Activities/Air/Land.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,13 @@ public override bool Tick(Actor self)
227227
return false;
228228
}
229229

230+
var sound = aircraft.Info.LandingSounds.RandomOrDefault(Game.CosmeticRandom);
231+
230232
if (aircraft.Info.LandingSounds.Length > 0)
231-
Game.Sound.Play(SoundType.World, aircraft.Info.LandingSounds, self.World, aircraft.CenterPosition);
233+
{
234+
var shouldStart = aircraft.Info.AudibleThroughFog || (!self.World.ShroudObscures(self.CenterPosition) && !self.World.FogObscures(self.CenterPosition));
235+
Game.Sound.Play(SoundType.World, sound, self.CenterPosition, shouldStart ? aircraft.Info.Volume : 0f);
236+
}
232237

233238
foreach (var notify in self.TraitsImplementing<INotifyLanding>())
234239
notify.Landing(self);

OpenRA.Mods.Common/Activities/Air/TakeOff.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ protected override void OnFirstRun(Actor self)
3636

3737
if (self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition).Length > aircraft.Info.MinAirborneAltitude)
3838
return;
39+
40+
var shouldStart = aircraft.Info.AudibleThroughFog || (!self.World.ShroudObscures(self.CenterPosition) && !self.World.FogObscures(self.CenterPosition));
41+
var sound = aircraft.Info.TakeoffSounds.RandomOrDefault(Game.CosmeticRandom);
3942

4043
if (aircraft.Info.TakeoffSounds.Length > 0)
41-
Game.Sound.Play(SoundType.World, aircraft.Info.TakeoffSounds, self.World, aircraft.CenterPosition);
44+
Game.Sound.Play(SoundType.World, sound, self.CenterPosition, shouldStart ? aircraft.Info.Volume : 0f);
4245

4346
foreach (var notify in self.TraitsImplementing<INotifyTakeOff>())
4447
notify.TakeOff(self);

OpenRA.Mods.Common/Activities/Move/Move.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,12 @@ public MoveFirstHalf(
538538
fromTerrainOrientation, toTerrainOrientation, terrainOrientationMargin, carryoverProgress, shouldArc, movingOnGroundLayer)
539539
{ }
540540

541+
protected override void OnFirstRun(Actor self)
542+
{
543+
var mobile = Move.mobile;
544+
mobile.EnteringCell(self);
545+
}
546+
541547
bool IsTurn(Actor self, Mobile mobile, CPos nextCell, Map map)
542548
{
543549
// Some actors with a limited number of sprite facings should never move along curved trajectories.

OpenRA.Mods.Common/Traits/Air/Aircraft.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ public class AircraftInfo : PausableConditionalTraitInfo, IPositionableInfo, IFa
147147
[Desc("Sounds to play when the actor is landing.")]
148148
public readonly string[] LandingSounds = Array.Empty<string>();
149149

150+
[Desc("Do the sounds play under shroud or fog.")]
151+
public readonly bool AudibleThroughFog = false;
152+
153+
[Desc("Volume the sounds played at.")]
154+
public readonly float Volume = 1f;
155+
150156
[Desc("The distance of the resupply base that the aircraft will wait for its turn.")]
151157
public readonly WDist WaitDistanceFromResupplyBase = new(3072);
152158

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#region Copyright & License Information
2+
/*
3+
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4+
* This file is part of OpenRA, which is free software. It is made
5+
* available to you under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation, either version 3 of
7+
* the License, or (at your option) any later version. For more
8+
* information, see COPYING.
9+
*/
10+
#endregion
11+
12+
using System;
13+
using System.Collections.Generic;
14+
using System.Linq;
15+
using OpenRA.Traits;
16+
17+
namespace OpenRA.Mods.Common.Traits
18+
{
19+
[Desc("Manages AI legacy bridge repair logic.")]
20+
public class BridgeRepairBotModuleInfo : ConditionalTraitInfo
21+
{
22+
[ActorReference]
23+
[Desc("Actor types that can repair bridges (via `RepairsBridges`).",
24+
"Leave this empty to disable the trait.")]
25+
public readonly HashSet<string> RepairActorTypes = new HashSet<string>();
26+
27+
[Desc("Avoid enemy actors nearby when searching for bridges in need of repair.",
28+
"Should be somewhere near the max weapon range.")]
29+
public readonly WDist EnemyAvoidanceRadius = WDist.FromCells(8);
30+
31+
[Desc("Minimum delay (in ticks) between trying to scan for repair targets.")]
32+
public readonly int MinimumWaitDelay = 300;
33+
34+
public override object Create(ActorInitializer init) { return new BridgeRepairBotModule(init.Self, this); }
35+
}
36+
37+
public class BridgeRepairBotModule : ConditionalTrait<BridgeRepairBotModuleInfo>, IBotTick
38+
{
39+
readonly World world;
40+
readonly Player player;
41+
readonly Predicate<Actor> unitCannotBeOrderedOrIsIdle;
42+
readonly ActorIndex.OwnerAndNamesAndTrait<RepairsBridgesInfo> repairersIndex;
43+
44+
// Units that the bot already knows about and has given a repair order. Any unit not on this list needs to be given a new order.
45+
readonly List<Actor> activeRepairers = new List<Actor>();
46+
47+
int waitDelayTicks;
48+
49+
IBotRequestUnitProduction[] requestUnitProduction;
50+
51+
public BridgeRepairBotModule(Actor self, BridgeRepairBotModuleInfo info)
52+
: base(info)
53+
{
54+
world = self.World;
55+
player = self.Owner;
56+
57+
if (world.Type == WorldType.Editor)
58+
return;
59+
60+
unitCannotBeOrderedOrIsIdle = a => a.Owner != player || a.IsDead || !a.IsInWorld || a.IsIdle;
61+
repairersIndex = new ActorIndex.OwnerAndNamesAndTrait<RepairsBridgesInfo>(world, info.RepairActorTypes, player);
62+
}
63+
64+
protected override void TraitEnabled(Actor self)
65+
{
66+
// Avoid all AIs reevaluating assignments on the same tick, randomize their initial evaluation delay.
67+
waitDelayTicks = world.LocalRandom.Next(Info.MinimumWaitDelay);
68+
69+
requestUnitProduction = player.PlayerActor.TraitsImplementing<IBotRequestUnitProduction>().ToArray();
70+
}
71+
72+
void IBotTick.BotTick(IBot bot)
73+
{
74+
if (--waitDelayTicks <= 0)
75+
{
76+
waitDelayTicks = Info.MinimumWaitDelay;
77+
QueueRepairOrders(bot);
78+
}
79+
}
80+
81+
void QueueRepairOrders(IBot bot)
82+
{
83+
if (!Info.RepairActorTypes.Any() || player.WinState != WinState.Undefined)
84+
return;
85+
86+
activeRepairers.RemoveAll(unitCannotBeOrderedOrIsIdle);
87+
88+
var targetOptions = world.ActorsWithTrait<LegacyBridgeHut>().Where(
89+
b => b.Trait.BridgeDamageState == DamageState.Dead);
90+
91+
if (!targetOptions.Any())
92+
return;
93+
94+
var newUnits = world.ActorsHavingTrait<IPositionable>()
95+
.Where(a => a.Owner == player && !activeRepairers.Contains(a));
96+
97+
var repairers = newUnits
98+
.Where(a => a.IsIdle && Info.RepairActorTypes.Contains(a.Info.Name) && a.Info.HasTraitInfo<RepairsBridgesInfo>())
99+
.Select(a => new TraitPair<RepairsBridges>(a, a.TraitOrDefault<RepairsBridges>()))
100+
.Where(tp => tp.Trait != null)
101+
.ToArray();
102+
103+
foreach (var repairer in repairers)
104+
{
105+
var nearestTargets = targetOptions.OrderBy(target => (target.Actor.CenterPosition - repairer.Actor.CenterPosition).LengthSquared);
106+
foreach (var nearestTarget in nearestTargets)
107+
{
108+
if (activeRepairers.Contains(repairer.Actor))
109+
continue;
110+
111+
var safeTarget = SafePath(repairer.Actor, nearestTarget.Actor);
112+
if (safeTarget.Type == TargetType.Invalid)
113+
continue;
114+
115+
bot.QueueOrder(new Order("RepairBridge", repairer.Actor, safeTarget, true));
116+
AIUtils.BotDebug("AI ({0}): Ordered {1} to repair {2}", player.ClientIndex, repairer.Actor, nearestTarget.Actor);
117+
activeRepairers.Add(repairer.Actor);
118+
}
119+
}
120+
121+
// Request a new repairer on demand.
122+
var unitBuilder = requestUnitProduction.FirstOrDefault(Exts.IsTraitEnabled);
123+
if (unitBuilder != null)
124+
{
125+
var engineers = AIUtils.CountActorByCommonName(repairersIndex);
126+
if (engineers == 0)
127+
unitBuilder.RequestUnitProduction(bot, Info.RepairActorTypes.Random(world.LocalRandom));
128+
}
129+
}
130+
131+
Target SafePath(Actor repairer, Actor target)
132+
{
133+
var mobile = repairer.Trait<Mobile>();
134+
var locomotor = mobile.Locomotor;
135+
136+
if (!mobile.PathFinder.PathExistsForLocomotor(locomotor, repairer.Location, target.Location))
137+
return Target.Invalid;
138+
139+
var path = mobile.PathFinder.FindPathToTargetCellByPredicate(
140+
repairer, new[] { repairer.Location }, loc => true, BlockedByActor.Stationary,
141+
loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius)
142+
.Where(u => !u.IsDead && repairer.Owner.RelationshipWith(u.Owner) == PlayerRelationship.Enemy && repairer.IsTargetableBy(u))
143+
.Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length)));
144+
145+
if (path.Count == 0)
146+
return Target.Invalid;
147+
148+
return Target.FromActor(target);
149+
}
150+
}
151+
}

OpenRA.Mods.Common/Traits/Cargo.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public class CargoInfo : ConditionalTraitInfo, Requires<IOccupySpaceInfo>
8989

9090
public class Cargo : ConditionalTrait<CargoInfo>, IIssueOrder, IResolveOrder, IOrderVoice,
9191
INotifyOwnerChanged, INotifySold, INotifyActorDisposing, IIssueDeployOrder,
92-
INotifyCreated, INotifyKilled, ITransformActorInitModifier
92+
INotifyCreated, INotifyKilled, ITransformActorInitModifier, INotifyPassengersDamage
9393
{
9494
readonly Actor self;
9595
readonly List<Actor> cargo = new();
@@ -483,6 +483,27 @@ void ITransformActorInitModifier.ModifyTransformActorInit(Actor self, TypeDictio
483483
{
484484
init.Add(new RuntimeCargoInit(Info, Passengers.ToArray()));
485485
}
486+
487+
int DamageVersus(Actor victim, Dictionary<string, int> versus)
488+
{
489+
// If no Versus values are defined, DamageVersus would return 100 anyway, so we might as well do that early.
490+
if (versus.Count == 0)
491+
return 100;
492+
var armor = victim.TraitsImplementing<Armor>()
493+
.Where(a => !a.IsTraitDisabled && a.Info.Type != null && versus.ContainsKey(a.Info.Type))
494+
.Select(a => versus[a.Info.Type]);
495+
return Util.ApplyPercentageModifiers(100, armor);
496+
}
497+
498+
void INotifyPassengersDamage.DamagePassengers(int damage, Actor attacker, int amount, Dictionary<string, int> versus, BitSet<DamageType> damageTypes, IEnumerable<int> damageModifiers)
499+
{
500+
var passengersToDamage = amount > 0 && amount < Passengers.Count() ? Passengers.Shuffle(self.World.SharedRandom).Take(amount) : Passengers;
501+
foreach (var passenger in passengersToDamage)
502+
{
503+
var d = Util.ApplyPercentageModifiers(damage, damageModifiers.Append(DamageVersus(passenger, versus)));
504+
passenger.InflictDamage(attacker, new Damage(d, damageTypes));
505+
}
506+
}
486507
}
487508

488509
public class RuntimeCargoInit : ValueActorInit<Actor[]>, ISuppressInitExport

0 commit comments

Comments
 (0)