Skip to content

Commit 4891d47

Browse files
committed
Merge remote-tracking branch 'darkademic/dev'
2 parents 80c57ec + db4698e commit 4891d47

80 files changed

Lines changed: 1953 additions & 1019 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

OpenRA.Mods.CA/Activities/SpawnActor.cs

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ namespace OpenRA.Mods.CA.Activities
2121
{
2222
public class SpawnActor : Activity
2323
{
24-
readonly WPos targetPos;
2524
readonly CPos targetCell;
25+
readonly WAngle? initFacing;
2626
readonly bool skipMakeAnims;
27-
readonly string type;
27+
readonly string[]types;
2828
readonly string[] spawnSounds;
2929
readonly AmmoPool ammoPool;
3030
readonly int range;
@@ -34,14 +34,14 @@ public class SpawnActor : Activity
3434
readonly HashSet<string> allowedTerrainTypes;
3535
readonly Action<Actor, Actor> onActorSpawned;
3636

37-
public SpawnActor(Actor self, CPos targetCell, WPos targetPos, string type, bool skipMakeAnims, string[] spawnSounds,
37+
public SpawnActor(string[] types, CPos targetCell, WAngle? initFacing, bool skipMakeAnims, string[] spawnSounds,
3838
AmmoPool ammoPool, int range, bool avoidActors, WDist maxRange, bool spawnInShroud, HashSet<string> allowedTerrainTypes,
3939
Action<Actor, Actor> onActorSpawned = null)
4040
{
41-
this.targetPos = targetPos;
4241
this.targetCell = targetCell;
42+
this.initFacing = initFacing;
4343
this.skipMakeAnims = skipMakeAnims;
44-
this.type = type;
44+
this.types = types;
4545
this.spawnSounds = spawnSounds;
4646
this.ammoPool = ammoPool;
4747
this.range = range;
@@ -50,6 +50,8 @@ public SpawnActor(Actor self, CPos targetCell, WPos targetPos, string type, bool
5050
this.spawnInShroud = spawnInShroud;
5151
this.allowedTerrainTypes = allowedTerrainTypes;
5252
this.onActorSpawned = onActorSpawned;
53+
54+
IsInterruptible = false;
5355
}
5456

5557
public override bool Tick(Actor self)
@@ -66,94 +68,102 @@ void Spawn(Actor self)
6668
var map = self.World.Map;
6769
var targetCells = map.FindTilesInCircle(targetCell, range);
6870
var cell = targetCells.GetEnumerator();
69-
var ai = map.Rules.Actors[type.ToLowerInvariant()];
70-
var td = CreateTypeDictionary(self, targetCell);
71-
var placed = false;
71+
var soundPlayed = false;
7272

73-
self.World.AddFrameEndTask(w =>
73+
foreach (var type in types)
7474
{
75-
var unit = self.World.CreateActor(false, type, td);
76-
var positionable = unit.TraitOrDefault<IPositionable>();
77-
78-
cell = targetCells.GetEnumerator();
75+
var actorType = type.ToLowerInvariant();
76+
var ai = map.Rules.Actors[actorType];
77+
var td = CreateTypeDictionary(self, targetCell);
78+
var placed = false;
7979

80-
if (positionable == null)
80+
self.World.AddFrameEndTask(w =>
8181
{
82-
unit.Dispose();
82+
Actor unit = null;
83+
cell = targetCells.GetEnumerator();
8384

84-
if (avoidActors)
85+
if (!ai.HasTraitInfo<IPositionableInfo>())
8586
{
86-
while (cell.MoveNext() && !placed)
87+
if (avoidActors)
8788
{
88-
if (!IsValidTargetCell(cell.Current, self))
89-
continue;
89+
while (cell.MoveNext() && !placed)
90+
{
91+
if (!IsValidTargetCell(cell.Current, self))
92+
continue;
9093

91-
var actorsInCell = self.World.ActorMap.GetActorsAt(cell.Current);
94+
var actorsInCell = self.World.ActorMap.GetActorsAt(cell.Current);
9295

93-
if (actorsInCell.Any())
94-
continue;
96+
if (actorsInCell.Any())
97+
continue;
9598

96-
placed = true;
97-
td = CreateTypeDictionary(self, cell.Current);
99+
placed = true;
100+
td = CreateTypeDictionary(self, cell.Current);
101+
}
98102
}
103+
else
104+
placed = true;
105+
106+
if (placed)
107+
unit = self.World.CreateActor(type, td);
99108
}
100109
else
101-
placed = true;
102-
103-
if (placed)
104-
unit = self.World.CreateActor(type, td);
105-
}
106-
else
107-
{
108-
while (cell.MoveNext() && !placed)
109110
{
110-
var subCell = positionable.GetAvailableSubCell(cell.Current);
111+
unit = self.World.CreateActor(false, actorType, td);
112+
var positionable = unit.TraitOrDefault<IPositionable>();
113+
114+
while (cell.MoveNext() && !placed)
115+
{
116+
var subCell = positionable.GetAvailableSubCell(cell.Current);
111117

112-
if (ai.HasTraitInfo<AircraftInfo>()
113-
&& ai.TraitInfo<AircraftInfo>().CanEnterCell(self.World, unit, cell.Current, SubCell.FullCell, null, BlockedByActor.None))
114-
subCell = SubCell.FullCell;
118+
if (ai.HasTraitInfo<AircraftInfo>()
119+
&& ai.TraitInfo<AircraftInfo>().CanEnterCell(self.World, unit, cell.Current, SubCell.FullCell, null, BlockedByActor.None))
120+
subCell = SubCell.FullCell;
115121

116-
if (!IsValidTargetCell(cell.Current, self))
117-
continue;
122+
if (!IsValidTargetCell(cell.Current, self))
123+
continue;
118124

119-
if (subCell != SubCell.Invalid)
120-
{
121-
positionable.SetPosition(unit, cell.Current, subCell);
125+
if (subCell != SubCell.Invalid)
126+
{
127+
positionable.SetPosition(unit, cell.Current, subCell);
122128

123-
var pos = unit.CenterPosition;
129+
var pos = unit.CenterPosition;
124130

125-
positionable.SetCenterPosition(unit, pos);
126-
w.Add(unit);
131+
positionable.SetCenterPosition(unit, pos);
132+
w.Add(unit);
127133

128-
unit.QueueActivity(new FallDown(unit, pos, 130));
134+
unit.QueueActivity(new FallDown(unit, pos, 130));
129135

130-
placed = true;
136+
placed = true;
137+
}
131138
}
132-
}
133139

134-
if (!placed)
135-
unit.Dispose();
136-
}
140+
if (!placed)
141+
unit.Dispose();
142+
}
137143

138-
if (placed)
139-
{
140-
if (ammoPool != null)
141-
ammoPool.TakeAmmo(self, 1);
144+
if (placed && unit != null)
145+
{
146+
if (ammoPool != null)
147+
ammoPool.TakeAmmo(self, 1);
142148

143-
if (spawnSounds.Length > 0)
144-
Game.Sound.Play(SoundType.World, spawnSounds, self.World, unit.CenterPosition);
149+
if (!soundPlayed && spawnSounds.Length > 0)
150+
{
151+
Game.Sound.Play(SoundType.World, spawnSounds, self.World, unit.CenterPosition);
152+
soundPlayed = true;
153+
}
145154

146-
onActorSpawned?.Invoke(self, unit);
147-
}
148-
});
155+
onActorSpawned?.Invoke(self, unit);
156+
}
157+
});
158+
}
149159
}
150160

151161
bool IsValidTargetCell(CPos cell, Actor self)
152162
{
153163
var targetPos = self.World.Map.CenterOfCell(cell);
154164
var sourcePos = self.CenterPosition;
155165

156-
return ((targetPos - sourcePos).Length <= maxRange.Length
166+
return ((maxRange == WDist.Zero || (targetPos - sourcePos).Length <= maxRange.Length)
157167
&& (spawnInShroud || self.Owner.Shroud.IsExplored(cell))
158168
&& (allowedTerrainTypes.Count == 0 || allowedTerrainTypes.Contains(self.World.Map.GetTerrainInfo(cell).Type)));
159169
}
@@ -167,6 +177,9 @@ TypeDictionary CreateTypeDictionary(Actor self, CPos targetCell)
167177
new LocationInit(targetCell)
168178
};
169179

180+
if (initFacing.HasValue)
181+
td.Add(new FacingInit(initFacing.Value));
182+
170183
if (skipMakeAnims)
171184
td.Add(new SkipMakeAnimsInit());
172185

OpenRA.Mods.CA/Traits/Attack/AttackFrontalCharged.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public class AttackFrontalChargedInfo : AttackFrontalInfo, Requires<IFacingInfo>
4545
[Desc("Armaments that count towards shots per charge. Leave empty for all.")]
4646
public readonly string[] ChargeConsumingArmaments = default;
4747

48+
[Desc("If true, charge will be lost while turning.")]
49+
public readonly bool LosesChargeWhileTurning = false;
50+
4851
public readonly bool ShowSelectionBar = false;
4952
public readonly Color SelectionBarColor = Color.FromArgb(128, 200, 255);
5053

@@ -118,6 +121,9 @@ protected override void Tick(Actor self)
118121
if (IsTraitDisabled || IsTraitPaused)
119122
return;
120123

124+
if (!IsAiming && ChargeLevel == 0)
125+
return;
126+
121127
var reloading = false;
122128
foreach (var armament in Armaments)
123129
{
@@ -129,7 +135,13 @@ protected override void Tick(Actor self)
129135
}
130136

131137
// Stop charging when we lose our target
132-
charging = (self.CurrentActivity is AttackCharged || self.CurrentActivity is AttackMoveActivity) && !reloading && IsAiming && (ChargeLevel > 0 || !IsTurning);
138+
bool isTurning = IsTurning;
139+
140+
charging =
141+
(self.CurrentActivity is AttackCharged || self.CurrentActivity is AttackMoveActivity)
142+
&& !reloading
143+
&& IsAiming
144+
&& (!isTurning || (isTurning && ChargeLevel > 0 && !Info.LosesChargeWhileTurning));
133145

134146
var delta = charging ? Info.ChargeRate : -Info.DischargeRate;
135147
ChargeLevel = (ChargeLevel + delta).Clamp(0, requiredChargeLevel);

OpenRA.Mods.CA/Traits/BotModules/SquadManagerBotModuleCA.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public class SquadManagerBotModuleCAInfo : ConditionalTraitInfo
9898
public readonly int AirToAirPriority = 85;
9999

100100
[Desc("Limit target types for specific air unit squads.")]
101-
public readonly Dictionary<string, BitSet<TargetableType>> AirSquadTargetTypes = null;
101+
public readonly Dictionary<string, BitSet<TargetableType>> AirSquadTargetArmorTypes = null;
102102

103103
[Desc("Enemy building types around which to scan for targets for naval squads.")]
104104
public readonly HashSet<string> StaticAntiAirTypes = new HashSet<string>();
@@ -196,18 +196,16 @@ public bool IsHighValueTarget(Actor a)
196196
return IsValidEnemyUnit(a) && Info.HighValueTargetTypes.Contains(a.Info.Name);
197197
}
198198

199-
public bool IsAirSquadTargetType(Actor a, SquadCA owner)
199+
public bool IsAirSquadTargetArmorType(Actor a, SquadCA owner)
200200
{
201201
if (a == null || a.IsDead)
202202
return false;
203203

204204
var airSquadUnitType = owner.Units.First().Info.Name;
205-
if (owner.SquadManager.Info.AirSquadTargetTypes.ContainsKey(airSquadUnitType))
205+
if (owner.SquadManager.Info.AirSquadTargetArmorTypes.ContainsKey(airSquadUnitType))
206206
{
207-
var targetTypes = a.GetEnabledTargetTypes();
208-
209-
if (targetTypes.IsEmpty || !targetTypes.Overlaps(owner.SquadManager.Info.AirSquadTargetTypes[airSquadUnitType]))
210-
return false;
207+
var desiredArmorTypes = owner.SquadManager.Info.AirSquadTargetArmorTypes[airSquadUnitType];
208+
return a.Info.TraitInfos<ArmorInfo>().Any(ai => desiredArmorTypes.Contains(ai.Type.ToString()));
211209
}
212210

213211
return true;

OpenRA.Mods.CA/Traits/BotModules/Squads/States/AirStatesCA.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ protected static bool NearToPosSafely(SquadCA owner, WPos loc, out Actor detecte
138138
if (!unitsAroundPos.Any())
139139
return true;
140140

141-
var possibleTargets = unitsAroundPos.Where(a => owner.SquadManager.IsAirSquadTargetType(a, owner)).ToList();
141+
var possibleTargets = unitsAroundPos.Where(a => owner.SquadManager.IsAirSquadTargetArmorType(a, owner)).ToList();
142142
var possibleAntiAir = unitsAroundPos.ToList();
143143

144144
if (CountStaticAntiAir(possibleAntiAir, owner) < owner.Units.Count)
@@ -204,8 +204,8 @@ public void Tick(SquadCA owner)
204204
{
205205
var a = owner.Units.Random(owner.Random);
206206

207-
// copied from FindClosestEnemy() so that non-air squads don't check IsAirSquadTargetType unnecessarily
208-
var units = a.World.Actors.Where(t => owner.SquadManager.IsPreferredEnemyUnit(t) && owner.SquadManager.IsAirSquadTargetType(t, owner));
207+
// copied from FindClosestEnemy() so that non-air squads don't check IsAirSquadTargetArmorType unnecessarily
208+
var units = a.World.Actors.Where(t => owner.SquadManager.IsPreferredEnemyUnit(t) && owner.SquadManager.IsAirSquadTargetArmorType(t, owner));
209209
closestEnemy = units.Where(owner.SquadManager.IsNotHiddenUnit).ClosestTo(a.CenterPosition) ?? units.ClosestTo(a.CenterPosition);
210210
}
211211

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#region Copyright & License Information
2+
/*
3+
* Copyright (c) The OpenRA Developers and Contributors
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.Collections.Generic;
13+
using OpenRA.Mods.Common;
14+
using OpenRA.Mods.Common.Traits;
15+
using OpenRA.Primitives;
16+
using OpenRA.Traits;
17+
18+
namespace OpenRA.Mods.CA.Traits
19+
{
20+
[Desc("Player receives a unit for free once the building is placed. This also works for structures.",
21+
"If you want more than one unit to appear copy this section and assign IDs like FreeActor@2, ...")]
22+
public class FreeActorCAInfo : ConditionalTraitInfo, IEditorActorOptions
23+
{
24+
[ActorReference]
25+
[FieldLoader.Require]
26+
[Desc("Name of the actor.")]
27+
public readonly string Actor = null;
28+
29+
[Desc("Offset relative to the top-left cell of the building.")]
30+
public readonly CVec SpawnOffset = CVec.Zero;
31+
32+
[Desc("Which direction the unit should face.")]
33+
public readonly WAngle Facing = WAngle.Zero;
34+
35+
[Desc("Whether another actor should spawn upon re-enabling the trait.")]
36+
public readonly bool AllowRespawn = false;
37+
38+
[Desc("Display order for the free actor checkbox in the map editor")]
39+
public readonly int EditorFreeActorDisplayOrder = 4;
40+
41+
IEnumerable<EditorActorOption> IEditorActorOptions.ActorOptions(ActorInfo ai, World world)
42+
{
43+
yield return new EditorActorCheckbox("Spawn Child Actor", EditorFreeActorDisplayOrder,
44+
actor =>
45+
{
46+
var init = actor.GetInitOrDefault<FreeActorInit>(this);
47+
if (init != null)
48+
return init.Value;
49+
50+
return true;
51+
},
52+
(actor, value) => actor.ReplaceInit(new FreeActorInit(this, value), this));
53+
}
54+
55+
public override object Create(ActorInitializer init) { return new FreeActorCA(init, this); }
56+
}
57+
58+
public class FreeActorCA : ConditionalTrait<FreeActorCAInfo>
59+
{
60+
protected bool allowSpawn;
61+
62+
public FreeActorCA(ActorInitializer init, FreeActorCAInfo info)
63+
: base(info)
64+
{
65+
allowSpawn = init.GetValue<FreeActorInit, bool>(info, true);
66+
}
67+
68+
protected override void TraitEnabled(Actor self)
69+
{
70+
if (!allowSpawn)
71+
return;
72+
73+
allowSpawn = Info.AllowRespawn;
74+
75+
self.World.AddFrameEndTask(w =>
76+
{
77+
var td = new TypeDictionary
78+
{
79+
new ParentActorInit(self),
80+
new OwnerInit(self.Owner),
81+
};
82+
83+
var ai = w.Map.Rules.Actors[Info.Actor];
84+
85+
if (self.Info.HasTraitInfo<IOccupySpaceInfo>() && ai.HasTraitInfo<IOccupySpaceInfo>())
86+
td.Add(new LocationInit(self.Location + Info.SpawnOffset));
87+
88+
if (self.Info.HasTraitInfo<IFacingInfo>() && ai.HasTraitInfo<IFacingInfo>())
89+
td.Add(new FacingInit(Info.Facing));
90+
91+
w.CreateActor(Info.Actor, td);
92+
});
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)