Skip to content

Commit 2b383ba

Browse files
committed
Merge remote-tracking branch 'darkademic/dev'
2 parents 5387576 + d924b14 commit 2b383ba

39 files changed

Lines changed: 976 additions & 272 deletions
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#region Copyright & License Information
2+
/**
3+
* Copyright (c) The OpenRA Combined Arms Developers (see CREDITS).
4+
* This file is part of OpenRA Combined Arms, which is free software.
5+
* It is made available to you under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation, either version 3 of the License,
7+
* or (at your option) any later version. For more information, see COPYING.
8+
*/
9+
#endregion
10+
11+
using OpenRA.Traits;
12+
using System.Linq;
13+
14+
namespace OpenRA.Mods.CA.Traits
15+
{
16+
[Desc("On creation, adds specified number of ticks to a specified `ProvidesPrerequisitesOnTimeline` trait.")]
17+
public class AdvancesTimelineInfo : TraitInfo
18+
{
19+
[FieldLoader.Require]
20+
[Desc("Timeline type to advance.")]
21+
public readonly string Type = null;
22+
23+
[Desc("Number of ticks to advance.")]
24+
public readonly int Ticks = 1500;
25+
26+
public override object Create(ActorInitializer init) { return new AdvancesTimeline(init, this); }
27+
}
28+
29+
public class AdvancesTimeline : INotifyCreated
30+
{
31+
public AdvancesTimelineInfo Info { get; set; }
32+
33+
public AdvancesTimeline(ActorInitializer init, AdvancesTimelineInfo info)
34+
{
35+
Info = info;
36+
}
37+
38+
void INotifyCreated.Created(Actor self)
39+
{
40+
var timeline = self.Owner.PlayerActor.TraitsImplementing<ProvidesPrerequisitesOnTimeline>()
41+
.FirstOrDefault(c => c.Info.Type == Info.Type);
42+
43+
if (timeline != null)
44+
timeline.AddTicks(Info.Ticks);
45+
}
46+
}
47+
}

OpenRA.Mods.CA/Traits/Infiltration/InfiltrateToCreateProxyActor.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
#endregion
1010

11+
using System.Collections.Generic;
1112
using OpenRA.Mods.Common;
1213
using OpenRA.Mods.Common.Traits;
1314
using OpenRA.Primitives;
@@ -46,16 +47,21 @@ class InfiltrateToCreateProxyActorInfo : TraitInfo
4647
[Desc("If true, spawn at the location of the infiltrated actor.")]
4748
public readonly bool UseCenterPosition = false;
4849

50+
[Desc("If true, the spawned actor is destroyed if the parent actor dies, is sold, or is captured.")]
51+
public readonly bool LinkedToParent = false;
52+
4953
public override object Create(ActorInitializer init) { return new InfiltrateToCreateProxyActor(this); }
5054
}
5155

52-
class InfiltrateToCreateProxyActor : INotifyInfiltrated
56+
class InfiltrateToCreateProxyActor : INotifyInfiltrated, INotifyRemovedFromWorld
5357
{
5458
readonly InfiltrateToCreateProxyActorInfo info;
59+
List<Actor> spawnedActors;
5560

5661
public InfiltrateToCreateProxyActor(InfiltrateToCreateProxyActorInfo info)
5762
{
5863
this.info = info;
64+
spawnedActors = new List<Actor>();
5965
}
6066

6167
void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet<TargetableType> types)
@@ -89,7 +95,16 @@ void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet<Target
8995
else if (info.UseLocation)
9096
td.Add(new LocationInit(self.Location));
9197

92-
infiltrator.World.AddFrameEndTask(w => w.CreateActor(info.Proxy, td));
98+
infiltrator.World.AddFrameEndTask(w => spawnedActors.Add(w.CreateActor(info.Proxy, td)));
99+
}
100+
101+
void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)
102+
{
103+
foreach (var a in spawnedActors)
104+
{
105+
if (!a.IsDead)
106+
a.Dispose();
107+
}
93108
}
94109
}
95110
}
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
#region Copyright & License Information
2+
/**
3+
* Copyright (c) The OpenRA Combined Arms Developers (see CREDITS).
4+
* This file is part of OpenRA Combined Arms, which is free software.
5+
* It is made available to you under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation, either version 3 of the License,
7+
* or (at your option) any later version. For more information, see COPYING.
8+
*/
9+
#endregion
10+
11+
using System;
12+
using System.Collections.Generic;
13+
using System.Linq;
14+
using OpenRA.Mods.Common;
15+
using OpenRA.Mods.Common.Traits;
16+
using OpenRA.Primitives;
17+
using OpenRA.Traits;
18+
19+
namespace OpenRA.Mods.CA.Traits
20+
{
21+
[TraitLocation(SystemActors.Player)]
22+
public class ProvidesPrerequisitesOnTimelineInfo : TraitInfo, ITechTreePrerequisiteInfo
23+
{
24+
[Desc("Identifier.")]
25+
[FieldLoader.Require]
26+
public readonly string Type = null;
27+
28+
[Desc("Maximum number of ticks.")]
29+
public readonly int MaxTicks = 30000;
30+
31+
[Desc("Prerequesities provided at percentage thresholds.")]
32+
public readonly Dictionary<int, string> Prerequisites = null;
33+
34+
[Desc("List of factions that can affect this count. Leave blank for any faction.")]
35+
public readonly string[] Factions = { };
36+
37+
[NotificationReference("Speech")]
38+
[Desc("Speech notification to play when player reaches the required count.")]
39+
public readonly string PrerequisiteGrantedNotification = null;
40+
41+
[Desc("Text notification to display when player reaches the required count.")]
42+
public readonly string PrerequisiteGrantedTextNotification = null;
43+
44+
[Desc("Ticks before playing notification.")]
45+
public readonly int NotificationDelay = 0;
46+
47+
[Desc("Actor to spawn when player levels up.")]
48+
[ActorReference]
49+
public readonly string DummyActor = null;
50+
51+
[NotificationReference("Sounds")]
52+
[Desc("Sound notification to play when count is incremented.")]
53+
public readonly string PrerequisiteGrantedSound = null;
54+
55+
[Desc("Ticks before playing notification.")]
56+
public readonly int MaxBoostMultiplier = 1;
57+
58+
IEnumerable<string> ITechTreePrerequisiteInfo.Prerequisites(ActorInfo info)
59+
{
60+
return Prerequisites.Values;
61+
}
62+
63+
public override object Create(ActorInitializer init) { return new ProvidesPrerequisitesOnTimeline(init, this); }
64+
}
65+
66+
public class ProvidesPrerequisitesOnTimeline : ITechTreePrerequisite, INotifyCreated, ITick
67+
{
68+
public readonly ProvidesPrerequisitesOnTimelineInfo Info;
69+
readonly Actor self;
70+
readonly HashSet<string> prerequisitesGranted;
71+
readonly bool validFaction;
72+
TechTree techTree;
73+
74+
int ticksElapsed;
75+
HashSet<int> thresholdsPassed;
76+
bool notificationQueued;
77+
int ticksUntilNotification;
78+
bool dummyActorQueued;
79+
int ticksUntilSpawnDummyActor;
80+
81+
public event Action<int> PercentageChanged;
82+
83+
public ProvidesPrerequisitesOnTimeline(ActorInitializer init, ProvidesPrerequisitesOnTimelineInfo info)
84+
{
85+
Info = info;
86+
self = init.Self;
87+
ticksElapsed = 0;
88+
ticksUntilNotification = info.NotificationDelay;
89+
prerequisitesGranted = new HashSet<string>();
90+
thresholdsPassed = new HashSet<int>();
91+
92+
var player = self.Owner;
93+
validFaction = info.Factions.Length == 0 || info.Factions.Contains(player.Faction.InternalName);
94+
}
95+
96+
public bool Enabled => validFaction;
97+
public int TicksElapsed => ticksElapsed;
98+
public int TicksRemaining => Info.MaxTicks - ticksElapsed;
99+
public int PercentageComplete => ticksElapsed * 100 / Info.MaxTicks;
100+
public int[] Thresholds => Info.Prerequisites?.Keys.ToArray() ?? Array.Empty<int>();
101+
public int ThresholdsPassed => thresholdsPassed.Count;
102+
103+
public int TicksUntilNextThreshold
104+
{
105+
get
106+
{
107+
if (Info.Prerequisites == null || !Info.Prerequisites.Any())
108+
return 0;
109+
110+
var nextThreshold = Info.Prerequisites.Keys
111+
.Where(t => t > PercentageComplete)
112+
.OrderBy(t => t)
113+
.FirstOrDefault();
114+
115+
if (nextThreshold == 0)
116+
return 0;
117+
118+
var ticksNeededForThreshold = Info.MaxTicks * nextThreshold / 100;
119+
return ticksNeededForThreshold - ticksElapsed;
120+
}
121+
}
122+
123+
public string[] Factions => Info.Factions;
124+
125+
public IEnumerable<string> ProvidesPrerequisites => prerequisitesGranted;
126+
127+
void INotifyCreated.Created(Actor self)
128+
{
129+
// Special case handling is required for the Player actor.
130+
// Created is called before Player.PlayerActor is assigned,
131+
// so we must query other player traits from self, knowing that
132+
// it refers to the same actor as self.Owner.PlayerActor
133+
var playerActor = self.Info.Name == "player" ? self : self.Owner.PlayerActor;
134+
techTree = playerActor.Trait<TechTree>();
135+
techTree.ActorChanged(self);
136+
}
137+
138+
void HandlePrerequisiteThreshold(int percentage)
139+
{
140+
if (Info.Prerequisites == null || !Info.Prerequisites.ContainsKey(percentage) || thresholdsPassed.Contains(percentage))
141+
return;
142+
143+
thresholdsPassed.Add(percentage);
144+
var prerequisite = Info.Prerequisites[percentage];
145+
146+
if (!prerequisitesGranted.Contains(prerequisite))
147+
{
148+
prerequisitesGranted.Add(prerequisite);
149+
techTree.ActorChanged(self);
150+
151+
if (Info.PrerequisiteGrantedSound != null)
152+
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds",
153+
Info.PrerequisiteGrantedSound, self.Owner.Faction.InternalName);
154+
155+
if (Info.DummyActor != null)
156+
{
157+
notificationQueued = true;
158+
dummyActorQueued = true;
159+
ticksUntilSpawnDummyActor = 1;
160+
}
161+
}
162+
}
163+
164+
void ITick.Tick(Actor self)
165+
{
166+
if (!Enabled)
167+
return;
168+
169+
var previousPercentage = PercentageComplete;
170+
171+
if (ticksElapsed < Info.MaxTicks)
172+
{
173+
ticksElapsed++;
174+
175+
if (previousPercentage != PercentageComplete)
176+
PercentageChanged?.Invoke(PercentageComplete);
177+
178+
HandlePrerequisiteThreshold(PercentageComplete);
179+
}
180+
181+
if (notificationQueued && --ticksUntilNotification <= 0)
182+
{
183+
if (Info.PrerequisiteGrantedNotification != null)
184+
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.PrerequisiteGrantedNotification, self.Owner.Faction.InternalName);
185+
186+
if (Info.PrerequisiteGrantedTextNotification != null)
187+
TextNotificationsManager.AddTransientLine(Info.PrerequisiteGrantedTextNotification, self.Owner);
188+
189+
notificationQueued = false;
190+
ticksUntilNotification = Info.NotificationDelay;
191+
}
192+
193+
if (dummyActorQueued && --ticksUntilSpawnDummyActor <= 0)
194+
{
195+
self.World.AddFrameEndTask(w =>
196+
{
197+
w.CreateActor(Info.DummyActor, new TypeDictionary
198+
{
199+
new ParentActorInit(self),
200+
new LocationInit(CPos.Zero),
201+
new OwnerInit(self.Owner),
202+
new FacingInit(WAngle.Zero),
203+
});
204+
});
205+
}
206+
}
207+
208+
public void AddTicks(int ticks)
209+
{
210+
if (!Enabled || ticks <= 0)
211+
return;
212+
213+
var previousPercentage = PercentageComplete;
214+
var initialTicks = ticksElapsed;
215+
216+
ticksElapsed = Math.Min(ticksElapsed + ticks, Info.MaxTicks);
217+
218+
if (initialTicks == ticksElapsed)
219+
return;
220+
221+
if (previousPercentage != PercentageComplete)
222+
PercentageChanged?.Invoke(PercentageComplete);
223+
224+
if (Info.Prerequisites != null)
225+
{
226+
var startPercentage = initialTicks * 100 / Info.MaxTicks;
227+
var endPercentage = PercentageComplete;
228+
229+
foreach (var threshold in Info.Prerequisites)
230+
{
231+
if (threshold.Key > startPercentage && threshold.Key <= endPercentage)
232+
HandlePrerequisiteThreshold(threshold.Key);
233+
}
234+
}
235+
}
236+
}
237+
}

0 commit comments

Comments
 (0)