Compare commits

...

92 Commits

Author SHA1 Message Date
Paul Chote
3deaf6c7bb Fix infantry idle animations playing immediately after creation. 2019-10-20 16:42:36 +02:00
Paul Chote
8bbf43f45d Don't consider unit creation as movement. 2019-10-20 16:42:26 +02:00
abcdefg30
5cd674445c Explain that CanEnterCell ignores 'subCell' if there is a free subcell 2019-10-19 23:15:10 +01:00
abcdefg30
27f1c37eca Let Mobile's CanEnterCell consider ToSubCell 2019-10-19 23:15:10 +01:00
abcdefg30
258259af4a Add a subCell parameter to IPositionableInfo.CanEnterCell 2019-10-19 23:15:10 +01:00
Paul Chote
276b1a9509 Don't override spawn CenterPosition for non-aircraft reinforcements. 2019-10-19 23:45:59 +02:00
Paul Chote
5b19dbe20c Disable Carryable while submerged. 2019-10-19 13:18:54 +02:00
Paul Chote
40424814a4 Allow carryall pickup orders on deployed vehicles. 2019-10-19 13:18:54 +02:00
Paul Chote
c0ea92850b Add UndeployOnPickup to GrantConditionOnDeploy. 2019-10-19 13:18:54 +02:00
Paul Chote
dc1f11f412 Prevent movement pausing at invalid position. 2019-10-19 13:18:54 +02:00
Paul Chote
e4602f8db2 Replace MoveIntoWorld with ReturnToCell/AssociateWithAirfield. 2019-10-17 23:32:28 +02:00
Paul Chote
b4b4412664 Revert "Suppress MoveIntoWorldInit for map-placed Mobile actors."
This reverts commit f0c28cc153.
2019-10-17 23:32:26 +02:00
reaperrr
a23634897a Fix zombie stand2 sequence and run tickrate 2019-10-14 17:29:24 +02:00
abc013
082efe1b08 Adjusted die sequence length of zombie 2019-10-14 17:29:22 +02:00
abc013
c6c05802d2 Fixed zombie.shp
Added missing zombie attack frame
and other fixes.
2019-10-14 17:29:20 +02:00
tovl
c091d7dc67 Fix lastVisibleTarget not being set in FlyAttack and AttackActivity. 2019-10-14 01:23:22 +02:00
abcdefg30
ac1d95c863 Guard against overlaps on HiDPI by having a 5px border on graphs 2019-10-14 01:05:38 +02:00
abcdefg30
f15bb98f4a Special case the TD spectator UI to fit the minimum width 2019-10-14 01:05:24 +02:00
abcdefg30
1353ad38a0 Reduce the width of the combat stats tab 2019-10-14 01:05:10 +02:00
abcdefg30
508f74822c Reduce the Width of INGAME_OBSERVERSTATS_BG 2019-10-14 01:04:55 +02:00
reaperrr
4b5ae12536 Fix missing rules in prep update path 2019-10-13 22:37:23 +01:00
abc013
4ad4e143b7 Add isDead-check to the flamethrowers in allies06b 2019-10-13 18:21:12 +02:00
abc013
94aa310612 Make normal difficulty on allies06b easier 2019-10-13 18:21:10 +02:00
Nakarin Srijumrat
06d5826a0b increased ingame edge scrollspeed to 30 from 10 2019-10-13 13:23:02 +02:00
Paul Chote
a64f0eb0f5 Update macOS launcher package.
This pulls in a fix for the missing libmono-native-compat.dylib
2019-10-11 22:03:20 +02:00
abcdefg30
814878126b Minor style fixes 2019-10-11 21:17:46 +02:00
abcdefg30
3438dd3f74 Reduce string allocations in ObserverStatsLogic 2019-10-11 21:17:31 +02:00
abcdefg30
e6b9bc07e9 Keep army and income graph disabled if they were disabled once 2019-10-11 21:17:17 +02:00
abcdefg30
cee511f59d Replace "$/min" by "Income" and increase graph update rates 2019-10-11 21:17:04 +02:00
abcdefg30
992559d317 Remove $/min from the basic stats 2019-10-11 21:16:49 +02:00
abcdefg30
f27440d67e Add an XAxisTicksPerLabel property to LineGraphWidget 2019-10-11 21:16:37 +02:00
abcdefg30
6b1aac4d98 Work around a recursive loop in TargetAndAttack 2019-10-11 21:04:22 +02:00
Paul Chote
96b95c8980 Suppress MoveIntoWorldInit for map-placed Mobile actors. 2019-10-07 19:09:42 +02:00
teinarss
088288fee1 PlayerStatistics should stop ticking after lost/win 2019-10-06 20:50:04 +01:00
tovl
a93fbc3219 pause MovePart when Mobile is paused. 2019-10-06 20:29:47 +01:00
abcdefg30
58273c532c Fix FallsToEarth queueing an activity in its ctor 2019-10-06 14:43:26 +01:00
abcdefg30
4498f52723 Add an ICreationActivity interface 2019-10-06 14:43:22 +01:00
abcdefg30
28ede7ad8f Fix setting the position of the wrong actor 2019-10-06 14:43:16 +01:00
abcdefg30
fd663cc3f3 Fix the XAxis of LineGraphWidget not being updated properly 2019-10-06 13:12:07 +01:00
abcdefg30
75d6495003 Cache method call results in variables in Draw of LineGraphWidget 2019-10-06 13:11:58 +01:00
Paul Chote
eb4ba7e793 Remove double-negative from appimage wrapper. 2019-10-05 18:31:51 +02:00
Paul Chote
4532cf9b55 Reset environment variables before switching mods. 2019-10-05 18:31:49 +02:00
Paul Chote
2fba47151c Add Engine.LaunchWrapper launch argument for mod switching. 2019-10-05 18:31:47 +02:00
Paul Chote
d4f1ea54e7 Compile using Mono 6.4.0. 2019-10-05 18:26:25 +02:00
Paul Chote
4a6efc99ee Update packaged mono to 6.4.0. 2019-10-05 18:26:22 +02:00
Paul Chote
c150ed373c Filter invalid actors when loading and saving games. 2019-10-05 17:51:10 +02:00
Paul Chote
d99b22db51 Replace actor list with count in UnitBuilderBotModule. 2019-10-05 17:50:51 +02:00
abcdefg30
51ad7e6b59 Fix double clicking a save in the save game dialogue loading it 2019-10-05 17:38:57 +02:00
abcdefg30
493294c61e Fix harvesters idling on Infiltration 2019-10-05 16:13:39 +02:00
abcdefg30
ea7d00df8d Fix the town attackers in Infiltration not stopping 2019-10-05 16:13:26 +02:00
abcdefg30
3218249a2d Remove unnecessary SearchFromHarvesterRadius overwrites from TD missions 2019-10-05 16:02:56 +02:00
abcdefg30
2be9200ce7 Add SearchFromProcRadius to TD missions that need it 2019-10-05 16:02:36 +02:00
abcdefg30
90ca7cedc2 Fix a crash when MaxLevel of GainsExperience is zero 2019-10-05 15:59:42 +02:00
teinarss
4f884f9835 Remove CanEnterCell from OccupiedCells 2019-10-05 14:45:58 +02:00
teinarss
1f8aa67593 Mark cells that have changed MovementType as dirty 2019-10-05 14:45:53 +02:00
Ivaylo Draganov
44af88bb49 Set duplicates flag for hotkeys in HotkeyManager 2019-10-05 13:17:44 +02:00
tovl
522ee22845 Fix deployed units being nudgeable. 2019-10-05 11:15:18 +01:00
Ivaylo Draganov
9d98276c7d Add duplicate hotkey tracking with a boolean property on the definition 2019-10-05 11:08:35 +01:00
Punsho
7ecc653183 RA balance changes for September 2019 2019-10-05 11:03:52 +01:00
Michael Silber
de330daf85 Fix destroyed truck escaping ra mission sarin-gas-1 2019-10-04 22:41:49 +01:00
Paul Chote
e2a74f21b7 Expire invalid instances from the SupportPowerBotModule cache. 2019-10-01 19:30:59 +02:00
Paul Chote
7bb594e11b Drop invalid power references when loading save games. 2019-10-01 19:29:54 +02:00
abcdefg30
4055952be3 Increase the SearchFromProcRadius radius in soviet05 2019-09-30 20:31:04 +02:00
abcdefg30
77daea61bb Remove the now unnecessary Helper refinery 2019-09-30 20:31:01 +02:00
Punsho
fec1e244f3 Make mine targetable on attack everything stance for AutoTargetGround 2019-09-28 14:31:15 +02:00
teinarss
1e26672b70 Add check to see if transport is dead to UnreserveSpace 2019-09-28 14:10:01 +02:00
abcdefg30
aead03d9b3 Fix the expansion mcv in soviet05 being transported off the map 2019-09-28 14:06:38 +02:00
abcdefg30
a9d44d4e44 Revert the search radius decrease in D2k 2019-09-28 13:57:49 +02:00
tovl
7047ba3a31 Skip rally point if order is queued after resupply. 2019-09-27 13:44:31 +02:00
tovl
e169368cf4 Add missing target line to aircraft taking off from resupplying. 2019-09-27 13:44:30 +02:00
tovl
90f3788187 Refactor unreserve actions. 2019-09-27 13:44:30 +02:00
tovl
96b65aa969 Prevent bogus attackmove on take-off. 2019-09-27 13:44:30 +02:00
Punsho
0771a42749 Fix ctank and stank building faster then they should 2019-09-27 13:44:29 +02:00
SoScared
8afa1d8dde Add map Climax to RA map pool 2019-09-27 13:44:29 +02:00
Paul Chote
79f6a51deb Fix player viewport saving for non-spectators. 2019-09-27 13:44:29 +02:00
Oliver Brakmann
4bb4897ba3 Fix idling aircraft on Intervention 2019-09-27 13:44:29 +02:00
abcdefg30
f82aa58d2b Remove selling from Infilitration
It is weird, unsatisfying for the player and inconsistent with the rest of our missions
2019-09-27 13:44:28 +02:00
abcdefg30
30ee546fb3 Fix potentially bogus usages of OnAllRemovedFromWorld 2019-09-27 13:44:28 +02:00
abcdefg30
2dad293328 Prevent users from selecting a directional target outside the map 2019-09-27 13:44:28 +02:00
tovl
14c4e2978f Fix crash with dead cargo. 2019-09-27 13:44:27 +02:00
Jan Beich
cb75a85341 Extend Linux dllmap to other systems
- Drop `os` in Eluant config as it's only used on Linux
- Make generic to help BSDs and Solaris
- Update OpenAL-CS and SDL2-CS to get the same

Exception of type `System.DllNotFoundException`: lua51.dll
TypeName=``
  at (wrapper managed-to-native) Eluant.LuaApi.lua_newstate(Eluant.LuaRuntime/LuaAllocator,intptr)
  at Eluant.LuaRuntime..ctor ()
  at Eluant.MemoryConstrainedLuaRuntime..ctor ()
  at OpenRA.Scripting.ScriptContext..ctor (OpenRA.World world, OpenRA.Graphics.WorldRenderer worldRenderer, System.Collections.Generic.IEnumerable`1[T] scripts)
  at OpenRA.Mods.Common.Scripting.LuaScript.OpenRA.Traits.IWorldLoaded.WorldLoaded (OpenRA.World world, OpenRA.Graphics.WorldRenderer worldRenderer)
  at OpenRA.World.LoadComplete (OpenRA.Graphics.WorldRenderer wr)
  at OpenRA.Game.StartGame (System.String mapUID, OpenRA.WorldType type)
  at OpenRA.Game.LoadShellMap ()
  at OpenRA.Mods.Common.LoadScreens.BlankLoadScreen.StartGame (OpenRA.Arguments args)
  at OpenRA.Game.InitializeMod (System.String mod, OpenRA.Arguments args)
  at OpenRA.Game.Initialize (OpenRA.Arguments args)
  at OpenRA.Game.InitializeAndRun (System.String[] args)
  at OpenRA.Program.Main (System.String[] args)
2019-09-27 13:44:27 +02:00
Jan Beich
dbbbb2c782 command -v with more than one argument is non-portable
On FreeBSD build fails, so check if `msbuild` exists without arguments.

$ gmake
command: wrong number of arguments
OpenRA requires the 'msbuild -verbosity:m -nologo' tool provided by Mono >= 5.4.
gmake: *** [Makefile:154: core] Error 1

# FreeBSD sh
$ command -v echo ls
command: wrong number of arguments

# dash
$ command -v echo ls
echo

# ksh, bash, zsh
$ command -v echo ls
echo
/bin/ls
2019-09-27 13:44:27 +02:00
teinarss
bf432df830 Locomotor cache should handle custom layers 2019-09-27 13:44:27 +02:00
abcdefg30
c42e2db542 Replaced "Earned this min" by an Oil Derrick count in the economy statistics 2019-09-27 13:44:26 +02:00
abcdefg30
bc55717b6b Add an "UpdatesDerrickCount" trait 2019-09-27 13:44:26 +02:00
teinarss
19551063a8 Remove IronCurtainable from RA aircraft. 2019-09-08 12:33:02 +02:00
teinarss
6d0f3a0008 Fix crushable logic for actors in cell 2019-09-07 13:32:08 +01:00
abcdefg30
88d11b4c66 Fix a division by zero error in FindAndDeliverResources
by preventing an overflow through dividing directly
2019-09-07 10:49:00 +01:00
puritylake
9f7c3d6bac #17018 Gets rif of cutoff line in chat window 2019-09-06 23:04:54 +02:00
teinarss
55ba6b9e26 More robust logic for ThisMinute stats 2019-09-06 14:03:31 +02:00
teinarss
79627d036e GetAvailableSubCell should block cells outside the map 2019-09-06 13:33:13 +02:00
abcdefg30
2438ee487a Remove the duplicate Selectable trait on TENF 2019-09-06 13:26:42 +02:00
119 changed files with 1199 additions and 887 deletions

View File

@@ -3,7 +3,7 @@
dist: xenial
language: csharp
mono: 5.20.1
mono: 6.4.0
cache:
directories:

View File

@@ -151,7 +151,7 @@ test: core
all: dependencies core
core:
@command -v $(MSBUILD) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.4."; exit 1)
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.4."; exit 1)
ifeq ($(WIN32), $(filter $(WIN32),true yes y on 1))
@$(MSBUILD) -t:build -p:Configuration="Release-x86"
else

View File

@@ -82,6 +82,7 @@ namespace OpenRA
readonly INotifyIdle[] tickIdles;
readonly ITargetablePositions[] targetablePositions;
WPos[] staticTargetablePositions;
bool created;
internal Actor(World world, string name, TypeDictionary initDict)
{
@@ -138,6 +139,35 @@ namespace OpenRA
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
}
internal void Created()
{
created = true;
foreach (var t in TraitsImplementing<INotifyCreated>())
t.Created(this);
// The initial activity should run before any activities queued by INotifyCreated.Created
// However, we need to know which traits are enabled (via conditions), so wait for after the calls and insert the activity as the first
ICreationActivity creationActivity = null;
foreach (var ica in TraitsImplementing<ICreationActivity>())
{
if (!ica.IsTraitEnabled())
continue;
if (creationActivity != null)
throw new InvalidOperationException("More than one enabled ICreationActivity trait: {0} and {1}".F(creationActivity.GetType().Name, ica.GetType().Name));
var activity = ica.GetCreationActivity();
if (activity == null)
continue;
creationActivity = ica;
activity.Queue(CurrentActivity);
CurrentActivity = activity;
}
}
public void Tick()
{
var wasIdle = IsIdle;
@@ -214,11 +244,15 @@ namespace OpenRA
{
if (!queued)
CancelActivity();
QueueActivity(nextActivity);
}
public void QueueActivity(Activity nextActivity)
{
if (!created)
throw new InvalidOperationException("An activity was queued before the actor was created. Queue it inside the INotifyCreated.Created callback instead.");
if (CurrentActivity == null)
CurrentActivity = nextActivity;
else

View File

@@ -41,6 +41,7 @@ namespace OpenRA
public static ICursor Cursor;
public static bool HideCursor;
static WorldRenderer worldRenderer;
static string modLaunchWrapper;
internal static OrderManager OrderManager;
static Server.Server server;
@@ -351,6 +352,8 @@ namespace OpenRA
foreach (var mod in Mods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Metadata.Title, mod.Value.Metadata.Version);
modLaunchWrapper = args.GetValue("Engine.LaunchWrapper", null);
ExternalMods = new ExternalMods();
Manifest currentMod;
@@ -509,8 +512,15 @@ namespace OpenRA
{
try
{
var path = mod.LaunchPath;
var args = launchArguments != null ? mod.LaunchArgs.Append(launchArguments) : mod.LaunchArgs;
var p = Process.Start(mod.LaunchPath, args.Select(a => "\"" + a + "\"").JoinWith(" "));
if (modLaunchWrapper != null)
{
path = modLaunchWrapper;
args = new[] { mod.LaunchPath }.Concat(args);
}
var p = Process.Start(path, args.Select(a => "\"" + a + "\"").JoinWith(" "));
if (p == null || p.HasExited)
onFailed();
else

View File

@@ -20,6 +20,7 @@ namespace OpenRA
public readonly Hotkey Default = Hotkey.Invalid;
public readonly string Description = "";
public readonly HashSet<string> Types = new HashSet<string>();
public bool HasDuplicates { get; internal set; }
public HotkeyDefinition(string name, MiniYaml node)
{

View File

@@ -38,6 +38,9 @@ namespace OpenRA
if (definitions.ContainsKey(kv.Key))
keys[kv.Key] = kv.Value;
}
foreach (var hd in definitions)
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value.Name, this[hd.Value.Name].GetValue(), hd.Value) != null;
}
internal Func<Hotkey> GetHotkeyReference(string name)
@@ -65,6 +68,20 @@ namespace OpenRA
settings[name] = value;
else
settings.Remove(name);
var hadDuplicates = definition.HasDuplicates;
definition.HasDuplicates = GetFirstDuplicate(definition.Name, this[definition.Name].GetValue(), definition) != null;
if (hadDuplicates || definition.HasDuplicates)
{
foreach (var hd in definitions)
{
if (hd.Value == definition)
continue;
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value.Name, this[hd.Value.Name].GetValue(), hd.Value) != null;
}
}
}
public HotkeyDefinition GetFirstDuplicate(string name, Hotkey value, HotkeyDefinition definition)

View File

@@ -202,7 +202,7 @@ namespace OpenRA
public MouseScrollType MiddleMouseScroll = MouseScrollType.Standard;
public MouseScrollType RightMouseScroll = MouseScrollType.Disabled;
public MouseButtonPreference MouseButtonPreference = new MouseButtonPreference();
public float ViewportEdgeScrollStep = 10f;
public float ViewportEdgeScrollStep = 30f;
public float UIScrollSpeed = 50f;
public int SelectionDeadzone = 24;
public int MouseScrollDeadzone = 8;

View File

@@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using OpenRA.Activities;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Network;
@@ -260,6 +261,7 @@ namespace OpenRA.Traits
WDist LargestActorRadius { get; }
WDist LargestBlockingActorRadius { get; }
void UpdateOccupiedCells(IOccupySpace ios);
event Action<CPos> CellUpdated;
}
@@ -312,7 +314,7 @@ namespace OpenRA.Traits
public interface IPositionableInfo : IOccupySpaceInfo
{
bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true);
bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, bool checkTransientActors = true);
}
public interface IPositionable : IOccupySpace
@@ -527,4 +529,7 @@ namespace OpenRA.Traits
[RequireExplicitImplementation]
public interface IUnlocksRenderPlayer { bool RenderPlayerUnlocked { get; } }
[RequireExplicitImplementation]
public interface ICreationActivity { Activity GetCreationActivity(); }
}

View File

@@ -329,10 +329,10 @@ namespace OpenRA
public Actor CreateActor(bool addToWorld, string name, TypeDictionary initDict)
{
var a = new Actor(this, name, initDict);
foreach (var t in a.TraitsImplementing<INotifyCreated>())
t.Created(a);
a.Created();
if (addToWorld)
Add(a);
return a;
}

View File

@@ -47,12 +47,9 @@ namespace OpenRA.Mods.Cnc.Traits
bool IOccupySpaceInfo.SharesCell { get { return false; } }
// Used to determine if actor can spawn
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = false)
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, bool checkTransientActors = false)
{
if (!world.Map.Contains(cell))
return false;
return true;
return world.Map.Contains(cell);
}
}
@@ -191,7 +188,7 @@ namespace OpenRA.Mods.Cnc.Traits
WPos? initialTargetPosition = null, Color? targetLineColor = null) { return null; }
public Activity MoveFollow(Actor self, Target target, WDist minRange, WDist maxRange,
WPos? initialTargetPosition = null, Color? targetLineColor = null) { return null; }
public Activity MoveIntoWorld(Actor self, int delay = 0) { return null; }
public Activity ReturnToCell(Actor self) { return null; }
public Activity MoveToTarget(Actor self, Target target,
WPos? initialTargetPosition = null, Color? targetLineColor = null) { return null; }
public Activity MoveIntoTarget(Actor self, Target target) { return null; }

View File

@@ -129,7 +129,7 @@ namespace OpenRA.Mods.Common.Activities
if (aircraft.Info.CanHover && !skipHeightAdjustment && dat != aircraft.Info.CruiseAltitude)
{
if (dat <= aircraft.LandAltitude)
QueueChild(new TakeOff(self, target));
QueueChild(new TakeOff(self));
else
VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude);
@@ -140,7 +140,7 @@ namespace OpenRA.Mods.Common.Activities
}
else if (dat <= aircraft.LandAltitude)
{
QueueChild(new TakeOff(self, target));
QueueChild(new TakeOff(self));
return false;
}

View File

@@ -99,6 +99,17 @@ namespace OpenRA.Mods.Common.Activities
lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes();
}
// The target may become hidden in the same tick the FlyAttack constructor is called,
// causing lastVisible* to remain uninitialized.
// Fix the fallback values based on the frozen actor properties
else if (target.Type == TargetType.FrozenActor && !lastVisibleTarget.IsValidFor(self))
{
lastVisibleTarget = Target.FromTargetPositions(target);
lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target);
lastVisibleOwner = target.FrozenActor.Owner;
lastVisibleTargetTypes = target.FrozenActor.TargetTypes;
}
useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);
// Target is hidden or dead, and we don't have a fallback position to move towards

View File

@@ -21,19 +21,13 @@ namespace OpenRA.Mods.Common.Activities
{
readonly Aircraft aircraft;
readonly IMove move;
Target fallbackTarget;
bool movedToTarget = false;
public TakeOff(Actor self, Target fallbackTarget)
public TakeOff(Actor self)
{
aircraft = self.Trait<Aircraft>();
move = self.Trait<IMove>();
this.fallbackTarget = fallbackTarget;
}
public TakeOff(Actor self)
: this(self, Target.Invalid) { }
protected override void OnFirstRun(Actor self)
{
if (aircraft.ForceLanding)
@@ -42,8 +36,7 @@ namespace OpenRA.Mods.Common.Activities
if (self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition).Length >= aircraft.Info.MinAirborneAltitude)
return;
// We are taking off, so remove reservation and influence in ground cells.
aircraft.UnReserve();
// We are taking off, so remove influence in ground cells.
aircraft.RemoveInfluence();
if (aircraft.Info.TakeoffSounds.Length > 0)
@@ -73,24 +66,7 @@ namespace OpenRA.Mods.Common.Activities
return false;
}
// Only move to the fallback target if we don't have anything better to do
if (NextActivity == null && fallbackTarget.IsValidFor(self) && !movedToTarget)
{
QueueChild(new AttackMoveActivity(self, () => move.MoveToTarget(self, fallbackTarget, targetLineColor: Color.OrangeRed)));
movedToTarget = true;
return false;
}
return true;
}
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
{
if (ChildActivity != null)
foreach (var n in ChildActivity.TargetLineNodes(self))
yield return n;
else
yield return new TargetLineNode(fallbackTarget, Color.OrangeRed);
}
}
}

View File

@@ -138,7 +138,7 @@ namespace OpenRA.Mods.Common.Activities
case EnterState.Exiting:
{
QueueChild(move.MoveIntoWorld(self));
QueueChild(move.ReturnToCell(self));
lastState = EnterState.Finished;
return false;
}

View File

@@ -214,7 +214,7 @@ namespace OpenRA.Mods.Common.Activities
if (b != WVec.Zero && c != WVec.Zero)
{
var cosA = (int)(1024 * (b.LengthSquared + c.LengthSquared - a.LengthSquared) / (2 * b.Length * c.Length));
var cosA = (int)(512 * (b.LengthSquared + c.LengthSquared - a.LengthSquared) / b.Length / c.Length);
// Cost modifier varies between 0 and ResourceRefineryDirectionPenalty
return Math.Abs(harvInfo.ResourceRefineryDirectionPenalty / 2) + harvInfo.ResourceRefineryDirectionPenalty * cosA / 2048;

View File

@@ -208,6 +208,13 @@ namespace OpenRA.Mods.Common.Activities
var nextCell = path[path.Count - 1];
// Something else might have moved us, so the path is no longer valid.
if (!Util.AreAdjacentCells(mobile.ToCell, nextCell))
{
path = EvalPath();
return null;
}
var containsTemporaryBlocker = WorldUtils.ContainsTemporaryBlocker(self.World, nextCell, self);
// Next cell in the move is blocked by another actor
@@ -331,9 +338,6 @@ namespace OpenRA.Mods.Common.Activities
public override bool Tick(Actor self)
{
if (Move.mobile.IsTraitDisabled)
return false;
var ret = InnerTick(self, Move.mobile);
if (moveFraction > MoveFractionTotal)
@@ -412,7 +416,7 @@ namespace OpenRA.Mods.Common.Activities
var nextCell = parent.PopPath(self);
if (nextCell != null)
{
if (IsTurn(mobile, nextCell.Value.First))
if (!mobile.IsTraitPaused && !mobile.IsTraitDisabled && IsTurn(mobile, nextCell.Value.First))
{
var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.Second);
var ret = new MoveFirstHalf(

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Activities;
using OpenRA.Mods.Common.Traits;
@@ -20,20 +21,18 @@ namespace OpenRA.Mods.Common.Activities
public class PickupUnit : Activity
{
readonly Actor cargo;
readonly IMove movement;
readonly Carryall carryall;
readonly IFacing carryallFacing;
readonly Carryable carryable;
readonly IFacing carryableFacing;
readonly BodyOrientation carryableBody;
readonly int delay;
enum PickupState { Intercept, LockCarryable, Pickup }
// TODO: Expose this to yaml
readonly WDist targetLockRange = WDist.FromCells(4);
PickupState state;
enum PickupState { Intercept, LockCarryable, Pickup }
PickupState state = PickupState.Intercept;
public PickupUnit(Actor self, Actor cargo, int delay)
{
@@ -43,16 +42,20 @@ namespace OpenRA.Mods.Common.Activities
carryableFacing = cargo.Trait<IFacing>();
carryableBody = cargo.Trait<BodyOrientation>();
movement = self.Trait<IMove>();
carryall = self.Trait<Carryall>();
carryallFacing = self.Trait<IFacing>();
state = PickupState.Intercept;
ChildHasPriority = false;
}
protected override void OnFirstRun(Actor self)
{
carryall.ReserveCarryable(self, cargo);
if (carryall.ReserveCarryable(self, cargo))
{
// Fly to the target and wait for it to be locked for pickup
// These activities will be cancelled and replaced by Land once the target has been locked
QueueChild(new Fly(self, Target.FromActor(cargo)));
QueueChild(new FlyCircle(self));
}
}
public override bool Tick(Actor self)
@@ -74,26 +77,21 @@ namespace OpenRA.Mods.Common.Activities
return true;
}
if (carryall.State != Carryall.CarryallState.Reserved)
return true;
// Wait until we are near the target before we try to lock it
var distSq = (cargo.CenterPosition - self.CenterPosition).HorizontalLengthSquared;
if (state == PickupState.Intercept && distSq <= targetLockRange.LengthSquared)
state = PickupState.LockCarryable;
switch (state)
if (state == PickupState.LockCarryable)
{
case PickupState.Intercept:
QueueChild(movement.MoveWithinRange(Target.FromActor(cargo), WDist.FromCells(4)));
state = PickupState.LockCarryable;
return false;
case PickupState.LockCarryable:
if (!carryable.LockForPickup(cargo, self))
Cancel(self);
state = PickupState.Pickup;
return false;
case PickupState.Pickup:
var lockResponse = carryable.LockForPickup(cargo, self);
if (lockResponse == LockResponse.Failed)
Cancel(self);
else if (lockResponse == LockResponse.Success)
{
// Land at the target location
// Pickup position and facing are now known - swap the fly/wait activity with Land
ChildActivity.Cancel(self);
var localOffset = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation));
QueueChild(new Land(self, Target.FromActor(cargo), -carryableBody.LocalToWorld(localOffset), carryableFacing.Facing));
@@ -104,11 +102,13 @@ namespace OpenRA.Mods.Common.Activities
// Remove our carryable from world
QueueChild(new AttachUnit(self, cargo));
QueueChild(new TakeOff(self));
return false;
state = PickupState.Pickup;
}
}
return true;
// Return once we are in the pickup state and the pickup activities have completed
return TickChild(self) && state == PickupState.Pickup;
}
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)

View File

@@ -187,10 +187,12 @@ namespace OpenRA.Mods.Common.Activities
{
if (wasRepaired || isHostInvalid || (!stayOnResupplier && aircraft.Info.TakeOffOnResupply))
{
if (rp != null)
QueueChild(move.MoveTo(rp.Location, repairableNear != null ? null : host.Actor));
if (self.CurrentActivity.NextActivity == null && rp != null)
QueueChild(move.MoveTo(rp.Location, repairableNear != null ? null : host.Actor, targetLineColor: Color.Green));
else
QueueChild(new TakeOff(self));
aircraft.UnReserve();
}
// Aircraft without TakeOffOnResupply remain on the resupplier until something else needs it

View File

@@ -53,6 +53,9 @@ namespace OpenRA.Mods.Common.Activities
{
self.World.AddFrameEndTask(w =>
{
if (self.IsDead)
return;
// Make sure the target hasn't changed while entering
// OnEnterComplete is only called if targetActor is alive
if (targetActor != enterActor)

View File

@@ -121,7 +121,7 @@ namespace OpenRA.Mods.Common.Activities
var move = actor.Trait<IMove>();
var pos = actor.Trait<IPositionable>();
pos.SetPosition(self, exitSubCell.Value.First, exitSubCell.Value.Second);
pos.SetPosition(actor, exitSubCell.Value.First, exitSubCell.Value.Second);
pos.SetVisualPosition(actor, spawn);
actor.CancelActivity();

View File

@@ -24,13 +24,13 @@ namespace OpenRA.Mods.Common
public int Value(World world) { return value; }
}
public class MoveIntoWorldDelayInit : IActorInit<int>
public class CreationActivityDelayInit : IActorInit<int>
{
[FieldFromYamlKey]
readonly int value = 0;
public MoveIntoWorldDelayInit() { }
public MoveIntoWorldDelayInit(int init) { value = init; }
public CreationActivityDelayInit() { }
public CreationActivityDelayInit(int init) { value = init; }
public int Value(World world) { return value; }
}

View File

@@ -47,9 +47,11 @@ namespace OpenRA.Mods.Common.Scripting
if (entryLocation.HasValue)
{
var pi = ai.TraitInfoOrDefault<AircraftInfo>();
initDict.Add(new CenterPositionInit(owner.World.Map.CenterOfCell(entryLocation.Value) + new WVec(0, 0, pi != null ? pi.CruiseAltitude.Length : 0)));
initDict.Add(new LocationInit(entryLocation.Value));
var pi = ai.TraitInfoOrDefault<AircraftInfo>();
if (pi != null)
initDict.Add(new CenterPositionInit(owner.World.Map.CenterOfCell(entryLocation.Value) + new WVec(0, 0, pi.CruiseAltitude.Length)));
}
if (entryLocation.HasValue && nextLocation.HasValue)

View File

@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Scripting
var pos = Self.CenterPosition;
mobile.SetPosition(Self, cell);
mobile.SetVisualPosition(Self, pos);
Self.QueueActivity(mobile.MoveIntoWorld(Self));
Self.QueueActivity(mobile.ReturnToCell(Self));
}
[ScriptActorPropertyActivity]

View File

@@ -10,6 +10,7 @@
#endregion
using System.Linq;
using Eluant;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
@@ -35,7 +36,13 @@ namespace OpenRA.Mods.Common.Scripting
public int PassengerCount { get { return cargo.Passengers.Count(); } }
[Desc("Teleport an existing actor inside this transport.")]
public void LoadPassenger(Actor a) { cargo.Load(Self, a); }
public void LoadPassenger(Actor a)
{
if (!a.IsIdle)
throw new LuaException("LoadPassenger requires the passenger to be idle.");
cargo.Load(Self, a);
}
[Desc("Remove the first actor from the transport. This actor is not added to the world.")]
public Actor UnloadPassenger() { return cargo.Unload(Self); }

View File

@@ -158,7 +158,7 @@ namespace OpenRA.Mods.Common.Traits
bool IOccupySpaceInfo.SharesCell { get { return false; } }
// Used to determine if an aircraft can spawn landed
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, bool checkTransientActors = true)
{
if (!world.Map.Contains(cell))
return false;
@@ -173,6 +173,7 @@ namespace OpenRA.Mods.Common.Traits
if (!checkTransientActors)
return true;
// Since aircraft don't share cells, we don't pass the subCell parameter
return !world.ActorMap.GetActorsAt(cell).Any(x => x != ignoreActor);
}
@@ -190,7 +191,7 @@ namespace OpenRA.Mods.Common.Traits
public class Aircraft : ITick, ISync, IFacing, IPositionable, IMove, IIssueOrder, IResolveOrder, IOrderVoice, IDeathActorInitModifier,
INotifyCreated, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyActorDisposing, INotifyBecomingIdle,
IActorPreviewInitModifier, IIssueDeployOrder, IObservesVariables
IActorPreviewInitModifier, IIssueDeployOrder, IObservesVariables, ICreationActivity
{
static readonly Pair<CPos, SubCell>[] NoCells = { };
@@ -220,7 +221,7 @@ namespace OpenRA.Mods.Common.Traits
IEnumerable<CPos> landingCells = Enumerable.Empty<CPos>();
bool requireForceMove;
int moveIntoWorldDelay;
int creationActivityDelay;
public static WPos GroundPosition(Actor self)
{
@@ -251,7 +252,9 @@ namespace OpenRA.Mods.Common.Traits
SetPosition(self, init.Get<CenterPositionInit, WPos>());
Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : Info.InitialFacing;
moveIntoWorldDelay = init.Contains<MoveIntoWorldDelayInit>() ? init.Get<MoveIntoWorldDelayInit, int>() : 0;
if (init.Contains<CreationActivityDelayInit>())
creationActivityDelay = init.Get<CreationActivityDelayInit, int>();
}
public WDist LandAltitude
@@ -309,8 +312,6 @@ namespace OpenRA.Mods.Common.Traits
notifyMoving = self.TraitsImplementing<INotifyMoving>().ToArray();
positionOffsets = self.TraitsImplementing<IAircraftCenterPositionOffset>().ToArray();
overrideAircraftLanding = self.TraitOrDefault<IOverrideAircraftLanding>();
self.QueueActivity(MoveIntoWorld(self, moveIntoWorldDelay));
}
void INotifyAddedToWorld.AddedToWorld(Actor self)
@@ -506,26 +507,15 @@ namespace OpenRA.Mods.Common.Traits
MayYieldReservation = true;
}
public void UnReserve(bool takeOff = false)
public void UnReserve()
{
if (reservation == null)
return;
// Move to the host's rally point if it has one
var rp = ReservedActor != null ? ReservedActor.TraitOrDefault<RallyPoint>() : null;
reservation.Dispose();
reservation = null;
ReservedActor = null;
MayYieldReservation = false;
if (takeOff && self.World.Map.DistanceAboveTerrain(CenterPosition).Length <= LandAltitude.Length)
{
if (rp != null)
self.QueueActivity(new TakeOff(self, Target.FromCell(self.World, rp.Location)));
else
self.QueueActivity(new TakeOff(self));
}
}
bool AircraftCanEnter(Actor a, TargetModifiers modifiers)
@@ -858,18 +848,15 @@ namespace OpenRA.Mods.Common.Traits
initialTargetPosition, targetLineColor);
}
public Activity MoveIntoWorld(Actor self, int delay = 0)
{
return new MoveIntoWorldActivity(self, delay);
}
public Activity ReturnToCell(Actor self) { return null; }
class MoveIntoWorldActivity : Activity
class AssociateWithAirfieldActivity : Activity
{
readonly Actor self;
readonly Aircraft aircraft;
readonly int delay;
public MoveIntoWorldActivity(Actor self, int delay = 0)
public AssociateWithAirfieldActivity(Actor self, int delay = 0)
{
this.self = self;
aircraft = self.Trait<Aircraft>();
@@ -1179,6 +1166,11 @@ namespace OpenRA.Mods.Common.Traits
inits.Add(new DynamicFacingInit(() => Facing));
}
Activity ICreationActivity.GetCreationActivity()
{
return new AssociateWithAirfieldActivity(self, creationActivityDelay);
}
public class AircraftMoveOrderTargeter : IOrderTargeter
{
readonly Aircraft aircraft;

View File

@@ -42,18 +42,24 @@ namespace OpenRA.Mods.Common.Traits
}
}
public class FallsToEarth : IEffectiveOwner
public class FallsToEarth : IEffectiveOwner, INotifyCreated
{
readonly FallsToEarthInfo info;
readonly Player effectiveOwner;
public FallsToEarth(ActorInitializer init, FallsToEarthInfo info)
{
init.Self.QueueActivity(false, new FallToEarth(init.Self, info));
this.info = info;
effectiveOwner = init.Contains<EffectiveOwnerInit>() ? init.Get<EffectiveOwnerInit, Player>() : init.Self.Owner;
}
// We return init.Self.Owner if there's no effective owner
bool IEffectiveOwner.Disguised { get { return true; } }
Player IEffectiveOwner.Owner { get { return effectiveOwner; } }
void INotifyCreated.Created(Actor self)
{
self.QueueActivity(false, new FallToEarth(self, info));
}
}
}

View File

@@ -293,6 +293,17 @@ namespace OpenRA.Mods.Common.Traits
}
}
// The target may become hidden in the same tick the AttackActivity constructor is called,
// causing lastVisible* to remain uninitialized.
// Fix the fallback values based on the frozen actor properties
else if (target.Type == TargetType.FrozenActor && !lastVisibleTarget.IsValidFor(self))
{
lastVisibleTarget = Target.FromTargetPositions(target);
lastVisibleMaximumRange = attack.GetMaximumRangeVersusTarget(target);
lastVisibleOwner = target.FrozenActor.Owner;
lastVisibleTargetTypes = target.FrozenActor.TargetTypes;
}
var maxRange = lastVisibleMaximumRange;
var minRange = lastVisibleMinimumRange;
useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

View File

@@ -102,10 +102,10 @@ namespace OpenRA.Mods.Common.Traits
}
// Prepare for transport pickup
public override bool LockForPickup(Actor self, Actor carrier)
public override LockResponse LockForPickup(Actor self, Actor carrier)
{
if (state == State.Locked || !WantsTransport)
return false;
if ((state == State.Locked && Carrier != carrier) || !WantsTransport)
return LockResponse.Failed;
// Last chance to change our mind...
var delta = self.World.Map.CenterOfCell(Destination.Value) - self.CenterPosition;
@@ -113,7 +113,7 @@ namespace OpenRA.Mods.Common.Traits
{
// Cancel pickup
MovementCancelled(self);
return false;
return LockResponse.Failed;
}
return base.LockForPickup(self, carrier);

View File

@@ -229,7 +229,10 @@ namespace OpenRA.Mods.Common.Traits
return new List<MiniYamlNode>()
{
new MiniYamlNode("InitialBaseCenter", FieldSaver.FormatValue(initialBaseCenter)),
new MiniYamlNode("ActiveMCVs", FieldSaver.FormatValue(activeMCVs.Select(a => a.ActorID).ToArray()))
new MiniYamlNode("ActiveMCVs", FieldSaver.FormatValue(activeMCVs
.Where(a => !unitCannotBeOrdered(a))
.Select(a => a.ActorID)
.ToArray()))
};
}
@@ -247,7 +250,7 @@ namespace OpenRA.Mods.Common.Traits
{
activeMCVs.Clear();
activeMCVs.AddRange(FieldLoader.GetValue<uint[]>("ActiveMCVs", activeMCVsNode.Value.Value)
.Select(a => world.GetActorById(a)));
.Select(a => world.GetActorById(a)).Where(a => a != null));
}
}
}

View File

@@ -357,8 +357,14 @@ namespace OpenRA.Mods.Common.Traits
{
new MiniYamlNode("Squads", "", Squads.Select(s => new MiniYamlNode("Squad", s.Serialize())).ToList()),
new MiniYamlNode("InitialBaseCenter", FieldSaver.FormatValue(initialBaseCenter)),
new MiniYamlNode("UnitsHangingAroundTheBase", FieldSaver.FormatValue(unitsHangingAroundTheBase.Select(a => a.ActorID).ToArray())),
new MiniYamlNode("ActiveUnits", FieldSaver.FormatValue(activeUnits.Select(a => a.ActorID).ToArray())),
new MiniYamlNode("UnitsHangingAroundTheBase", FieldSaver.FormatValue(unitsHangingAroundTheBase
.Where(a => !unitCannotBeOrdered(a))
.Select(a => a.ActorID)
.ToArray())),
new MiniYamlNode("ActiveUnits", FieldSaver.FormatValue(activeUnits
.Where(a => !unitCannotBeOrdered(a))
.Select(a => a.ActorID)
.ToArray())),
new MiniYamlNode("RushTicks", FieldSaver.FormatValue(rushTicks)),
new MiniYamlNode("AssignRolesTicks", FieldSaver.FormatValue(assignRolesTicks)),
new MiniYamlNode("AttackForceTicks", FieldSaver.FormatValue(attackForceTicks)),
@@ -380,7 +386,7 @@ namespace OpenRA.Mods.Common.Traits
{
unitsHangingAroundTheBase.Clear();
unitsHangingAroundTheBase.AddRange(FieldLoader.GetValue<uint[]>("UnitsHangingAroundTheBase", unitsHangingAroundTheBaseNode.Value.Value)
.Select(a => self.World.GetActorById(a)));
.Select(a => self.World.GetActorById(a)).Where(a => a != null));
}
var activeUnitsNode = data.FirstOrDefault(n => n.Key == "ActiveUnits");
@@ -388,7 +394,7 @@ namespace OpenRA.Mods.Common.Traits
{
activeUnits.Clear();
activeUnits.AddRange(FieldLoader.GetValue<uint[]>("ActiveUnits", activeUnitsNode.Value.Value)
.Select(a => self.World.GetActorById(a)));
.Select(a => self.World.GetActorById(a)).Where(a => a != null));
}
var rushTicksNode = data.FirstOrDefault(n => n.Key == "RushTicks");

View File

@@ -40,9 +40,10 @@ namespace OpenRA.Mods.Common.Traits
{
readonly World world;
readonly Player player;
readonly Dictionary<SupportPowerInstance, int> waitingPowers = new Dictionary<SupportPowerInstance, int>();
readonly Dictionary<string, SupportPowerDecision> powerDecisions = new Dictionary<string, SupportPowerDecision>();
readonly List<SupportPowerInstance> stalePowers = new List<SupportPowerInstance>();
SupportPowerManager supportPowerManager;
Dictionary<SupportPowerInstance, int> waitingPowers = new Dictionary<SupportPowerInstance, int>();
Dictionary<string, SupportPowerDecision> powerDecisions = new Dictionary<string, SupportPowerDecision>();
public SupportPowerBotModule(Actor self, SupportPowerBotModuleInfo info)
: base(info)
@@ -108,6 +109,13 @@ namespace OpenRA.Mods.Common.Traits
bot.QueueOrder(new Order(sp.Key, supportPowerManager.Self, Target.FromCell(world, attackLocation.Value), false) { SuppressVisualFeedback = true });
}
}
// Remove stale powers
stalePowers.AddRange(waitingPowers.Keys.Where(wp => !supportPowerManager.Powers.ContainsKey(wp.Key)));
foreach (var p in stalePowers)
waitingPowers.Remove(p);
stalePowers.Clear();
}
/// <summary>Scans the map in chunks, evaluating all actors in each.</summary>
@@ -208,8 +216,14 @@ namespace OpenRA.Mods.Common.Traits
var waitingPowersNode = data.FirstOrDefault(n => n.Key == "WaitingPowers");
if (waitingPowersNode != null)
{
foreach (var n in waitingPowersNode.Value.Nodes)
waitingPowers[supportPowerManager.Powers[n.Key]] = FieldLoader.GetValue<int>("WaitingPowers", n.Value.Value);
{
SupportPowerInstance instance;
if (supportPowerManager.Powers.TryGetValue(n.Key, out instance))
waitingPowers[instance] = FieldLoader.GetValue<int>("WaitingPowers", n.Value.Value);
}
}
}
}
}

View File

@@ -49,8 +49,7 @@ namespace OpenRA.Mods.Common.Traits
readonly List<string> queuedBuildRequests = new List<string>();
IBotRequestPauseUnitProduction[] requestPause;
List<Actor> idleUnits = new List<Actor>();
int idleUnitCount;
int ticks;
@@ -68,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits
void IBotNotifyIdleBaseUnits.UpdatedIdleBaseUnits(List<Actor> idleUnits)
{
this.idleUnits = idleUnits;
idleUnitCount = idleUnits.Count;
}
void IBotTick.BotTick(IBot bot)
@@ -88,7 +87,7 @@ namespace OpenRA.Mods.Common.Traits
}
foreach (var q in Info.UnitQueues)
BuildUnit(bot, q, idleUnits.Count < Info.IdleBaseUnitsMaximum);
BuildUnit(bot, q, idleUnitCount < Info.IdleBaseUnitsMaximum);
}
}
@@ -218,7 +217,7 @@ namespace OpenRA.Mods.Common.Traits
return new List<MiniYamlNode>()
{
new MiniYamlNode("QueuedBuildRequests", FieldSaver.FormatValue(queuedBuildRequests.ToArray())),
new MiniYamlNode("IdleUnits", FieldSaver.FormatValue(idleUnits.Select(a => a.ActorID).ToArray()))
new MiniYamlNode("IdleUnitCount", FieldSaver.FormatValue(idleUnitCount))
};
}
@@ -234,13 +233,9 @@ namespace OpenRA.Mods.Common.Traits
queuedBuildRequests.AddRange(FieldLoader.GetValue<string[]>("QueuedBuildRequests", queuedBuildRequestsNode.Value.Value));
}
var idleUnitsNode = data.FirstOrDefault(n => n.Key == "IdleUnits");
if (idleUnitsNode != null)
{
idleUnits.Clear();
idleUnits.AddRange(FieldLoader.GetValue<uint[]>("IdleUnits", idleUnitsNode.Value.Value)
.Select(a => world.GetActorById(a)));
}
var idleUnitCountNode = data.FirstOrDefault(n => n.Key == "IdleUnitCount");
if (idleUnitCountNode != null)
idleUnitCount = FieldLoader.GetValue<int>("IdleUnitCount", idleUnitCountNode.Value.Value);
}
}
}

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using OpenRA.Mods.Common.Activities;
using OpenRA.Primitives;
using OpenRA.Traits;
@@ -18,10 +19,16 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Reserve landing places for aircraft.")]
class ReservableInfo : TraitInfo<Reservable> { }
public class Reservable : ITick, INotifyOwnerChanged, INotifySold, INotifyActorDisposing
public class Reservable : ITick, INotifyOwnerChanged, INotifySold, INotifyActorDisposing, INotifyCreated
{
Actor reservedFor;
Aircraft reservedForAircraft;
RallyPoint rallyPoint;
void INotifyCreated.Created(Actor self)
{
rallyPoint = self.TraitOrDefault<RallyPoint>();
}
void ITick.Tick(Actor self)
{
@@ -41,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits
public IDisposable Reserve(Actor self, Actor forActor, Aircraft forAircraft)
{
if (reservedForAircraft != null && reservedForAircraft.MayYieldReservation)
reservedForAircraft.UnReserve(true);
UnReserve(self);
reservedFor = forActor;
reservedForAircraft = forAircraft;
@@ -71,17 +78,27 @@ namespace OpenRA.Mods.Common.Traits
return res == null || res.reservedForAircraft == null || res.reservedForAircraft.MayYieldReservation || res.reservedFor == forActor;
}
private void UnReserve()
void UnReserve(Actor self)
{
if (reservedForAircraft != null)
reservedForAircraft.UnReserve(true);
{
if (reservedForAircraft.GetActorBelow() == self)
{
if (rallyPoint != null)
reservedFor.QueueActivity(reservedForAircraft.MoveTo(rallyPoint.Location, null, targetLineColor: Color.Green));
else
reservedFor.QueueActivity(new TakeOff(reservedFor));
}
reservedForAircraft.UnReserve();
}
}
void INotifyActorDisposing.Disposing(Actor self) { UnReserve(); }
void INotifyActorDisposing.Disposing(Actor self) { UnReserve(self); }
void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { UnReserve(); }
void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { UnReserve(self); }
void INotifySold.Selling(Actor self) { UnReserve(); }
void INotifySold.Sold(Actor self) { UnReserve(); }
void INotifySold.Selling(Actor self) { UnReserve(self); }
void INotifySold.Sold(Actor self) { UnReserve(self); }
}
}

View File

@@ -221,6 +221,11 @@ namespace OpenRA.Mods.Common.Traits
if (captures == null)
return false;
// HACK: Make sure the target is not moving and at its normal position with respect to the cell grid
var enterMobile = target.TraitOrDefault<Mobile>();
if (enterMobile != null && enterMobile.IsMovingBetweenCells)
return false;
if (progressWatchers.Any() || targetManager.progressWatchers.Any())
{
currentTargetTotal = captures.Info.CaptureDelay;

View File

@@ -229,7 +229,7 @@ namespace OpenRA.Mods.Common.Traits
}
return !IsEmpty(self) && (aircraft == null || aircraft.CanLand(self.Location, blockedByMobile: false))
&& CurrentAdjacentCells != null && CurrentAdjacentCells.Any(c => Passengers.Any(p => p.Trait<IPositionable>().CanEnterCell(c, null, immediate)));
&& CurrentAdjacentCells != null && CurrentAdjacentCells.Any(c => Passengers.Any(p => !p.IsDead && p.Trait<IPositionable>().CanEnterCell(c, null, immediate)));
}
public bool CanLoad(Actor self, Actor a)
@@ -258,7 +258,7 @@ namespace OpenRA.Mods.Common.Traits
internal void UnreserveSpace(Actor a)
{
if (!reserves.Contains(a))
if (!reserves.Contains(a) || self.IsDead)
return;
reservedWeight -= GetWeight(a);
@@ -409,11 +409,11 @@ namespace OpenRA.Mods.Common.Traits
// If not initialized then this will be notified in the first tick
if (initialized)
{
foreach (var npe in self.TraitsImplementing<INotifyPassengerEntered>())
npe.OnPassengerEntered(self, a);
foreach (var nec in a.TraitsImplementing<INotifyEnteredCargo>())
nec.OnEnteredCargo(a, self);
foreach (var npe in self.TraitsImplementing<INotifyPassengerEntered>())
npe.OnPassengerEntered(self, a);
}
var p = a.Trait<Passenger>();
@@ -510,11 +510,11 @@ namespace OpenRA.Mods.Common.Traits
{
c.Trait<Passenger>().Transport = self;
foreach (var npe in self.TraitsImplementing<INotifyPassengerEntered>())
npe.OnPassengerEntered(self, c);
foreach (var nec in c.TraitsImplementing<INotifyEnteredCargo>())
nec.OnEnteredCargo(c, self);
foreach (var npe in self.TraitsImplementing<INotifyPassengerEntered>())
npe.OnPassengerEntered(self, c);
}
initialized = true;

View File

@@ -9,7 +9,7 @@
*/
#endregion
using OpenRA.Mods.Common.Activities;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -35,6 +35,13 @@ namespace OpenRA.Mods.Common.Traits
public override object Create(ActorInitializer init) { return new Carryable(init.Self, this); }
}
public enum LockResponse { Success, Pending, Failed }
public interface IDelayCarryallPickup
{
bool TryLockForPickup(Actor self, Actor carrier);
}
public class Carryable : ConditionalTrait<CarryableInfo>
{
ConditionManager conditionManager;
@@ -42,6 +49,9 @@ namespace OpenRA.Mods.Common.Traits
int carriedToken = ConditionManager.InvalidConditionToken;
int lockedToken = ConditionManager.InvalidConditionToken;
Mobile mobile;
IDelayCarryallPickup[] delayPickups;
public Actor Carrier { get; private set; }
public bool Reserved { get { return state != State.Free; } }
public CPos? Destination { get; protected set; }
@@ -57,6 +67,8 @@ namespace OpenRA.Mods.Common.Traits
protected override void Created(Actor self)
{
conditionManager = self.Trait<ConditionManager>();
mobile = self.TraitOrDefault<Mobile>();
delayPickups = self.TraitsImplementing<IDelayCarryallPickup>().ToArray();
base.Created(self);
}
@@ -111,18 +123,28 @@ namespace OpenRA.Mods.Common.Traits
}
// Prepare for transport pickup
public virtual bool LockForPickup(Actor self, Actor carrier)
public virtual LockResponse LockForPickup(Actor self, Actor carrier)
{
if (state == State.Locked)
return false;
if (state == State.Locked && Carrier != carrier)
return LockResponse.Failed;
state = State.Locked;
Carrier = carrier;
if (delayPickups.Any(d => d.IsTraitEnabled() && !d.TryLockForPickup(self, carrier)))
return LockResponse.Pending;
if (lockedToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(Info.LockedCondition))
lockedToken = conditionManager.GrantCondition(self, Info.LockedCondition);
if (state != State.Locked)
{
state = State.Locked;
Carrier = carrier;
return true;
if (lockedToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(Info.LockedCondition))
lockedToken = conditionManager.GrantCondition(self, Info.LockedCondition);
}
// Make sure we are not moving and at our normal position with respect to the cell grid
if (mobile != null && mobile.IsMovingBetweenCells)
return LockResponse.Pending;
return LockResponse.Success;
}
}
}

View File

@@ -58,6 +58,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Undeploy before the actor tries to move?")]
public readonly bool UndeployOnMove = false;
[Desc("Undeploy before the actor is picked up by a Carryall?")]
public readonly bool UndeployOnPickup = false;
[VoiceReference]
public readonly string Voice = "Action";
@@ -67,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits
public enum DeployState { Undeployed, Deploying, Deployed, Undeploying }
public class GrantConditionOnDeploy : PausableConditionalTrait<GrantConditionOnDeployInfo>, IResolveOrder, IIssueOrder,
INotifyDeployComplete, IIssueDeployOrder, IOrderVoice, IWrapMove
INotifyDeployComplete, IIssueDeployOrder, IOrderVoice, IWrapMove, IDelayCarryallPickup
{
readonly Actor self;
readonly bool checkTerrainType;
@@ -137,6 +140,17 @@ namespace OpenRA.Mods.Common.Traits
return activity;
}
bool IDelayCarryallPickup.TryLockForPickup(Actor self, Actor carrier)
{
if (!Info.UndeployOnPickup || deployState == DeployState.Undeployed || IsTraitDisabled)
return true;
if (deployState == DeployState.Deployed && !IsTraitPaused)
Undeploy();
return false;
}
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get

View File

@@ -40,8 +40,9 @@ namespace OpenRA.Mods.Common.Traits
bool IOccupySpaceInfo.SharesCell { get { return false; } }
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, bool checkTransientActors = true)
{
// Since crates don't share cells and GetAvailableSubCell only returns SubCell.Full or SubCell.Invalid, we ignore the subCell parameter
return GetAvailableSubCell(world, cell, ignoreActor, checkTransientActors) != SubCell.Invalid;
}

View File

@@ -90,12 +90,12 @@ namespace OpenRA.Mods.Common.Traits
protected override void TraitEnabled(Actor self)
{
self.World.ActorMap.UpdatePosition(self, self.OccupiesSpace);
self.World.ActorMap.UpdateOccupiedCells(self.OccupiesSpace);
}
protected override void TraitDisabled(Actor self)
{
self.World.ActorMap.UpdatePosition(self, self.OccupiesSpace);
self.World.ActorMap.UpdateOccupiedCells(self.OccupiesSpace);
}
}
}

View File

@@ -96,6 +96,9 @@ namespace OpenRA.Mods.Common.Traits
public void GiveLevels(int numLevels, bool silent = false)
{
if (MaxLevel == 0)
return;
var newLevel = Math.Min(Level + numLevels, MaxLevel);
GiveExperience(nextLevel[newLevel - 1].First - experience, silent);
}
@@ -105,6 +108,9 @@ namespace OpenRA.Mods.Common.Traits
if (amount < 0)
throw new ArgumentException("Revoking experience is not implemented.", "amount");
if (MaxLevel == 0)
return;
experience = (experience + amount).Clamp(0, nextLevel[MaxLevel - 1].First);
while (Level < MaxLevel && experience >= nextLevel[Level].First)

View File

@@ -130,8 +130,6 @@ namespace OpenRA.Mods.Common.Traits
mobile = self.Trait<Mobile>();
resLayer = self.World.WorldActor.Trait<ResourceLayer>();
claimLayer = self.World.WorldActor.Trait<ResourceClaimLayer>();
self.QueueActivity(new CallFunc(() => ChooseNewProc(self, null)));
}
void INotifyCreated.Created(Actor self)
@@ -141,6 +139,8 @@ namespace OpenRA.Mods.Common.Traits
conditionManager = self.TraitOrDefault<ConditionManager>();
UpdateCondition(self);
self.QueueActivity(new CallFunc(() => ChooseNewProc(self, null)));
// Note: This is queued in a FrameEndTask because otherwise the activity is dropped/overridden while moving out of a factory.
if (Info.SearchOnCreation)
self.World.AddFrameEndTask(w => self.QueueActivity(new FindAndDeliverResources(self)));

View File

@@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.Traits
bool IOccupySpaceInfo.SharesCell { get { return false; } }
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, bool checkTransientActors = true)
{
// IPositionable*Info*.CanEnterCell is only ever used for things like exiting production facilities,
// all places relevant for husks check IPositionable.CanEnterCell instead, so we can safely set this to true.

View File

@@ -82,7 +82,10 @@ namespace OpenRA.Mods.Common.Traits
// initialized and used by CanEnterCell
Locomotor locomotor;
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
/// <summary>
/// Note: If the target <paramref name="cell"/> has any free subcell, the value of <paramref name="subCell"/> is ignored.
/// </summary>
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, bool checkTransientActors = true)
{
// PERF: Avoid repeated trait queries on the hot path
if (locomotor == null)
@@ -93,7 +96,7 @@ namespace OpenRA.Mods.Common.Traits
return false;
var check = checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers;
return locomotor.CanMoveFreelyInto(self, cell, ignoreActor, check);
return locomotor.CanMoveFreelyInto(self, cell, subCell, ignoreActor, check);
}
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
@@ -139,12 +142,15 @@ namespace OpenRA.Mods.Common.Traits
}
}
public class Mobile : PausableConditionalTrait<MobileInfo>, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, ITick,
public class Mobile : PausableConditionalTrait<MobileInfo>, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, ITick, ICreationActivity,
IFacing, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier, INotifyBecomingIdle
{
readonly Actor self;
readonly Lazy<IEnumerable<int>> speedModifiers;
readonly int moveIntoWorldDelay;
readonly bool returnToCellOnCreation;
readonly bool returnToCellOnCreationRecalculateSubCell = true;
readonly int creationActivityDelay;
#region IMove CurrentMovementTypes
MovementType movementTypes;
@@ -160,8 +166,11 @@ namespace OpenRA.Mods.Common.Traits
var oldValue = movementTypes;
movementTypes = value;
if (value != oldValue)
{
self.World.ActorMap.UpdateOccupiedCells(self.OccupiesSpace);
foreach (var n in notifyMoving)
n.MovementTypeChanged(self, value);
}
}
}
#endregion
@@ -177,6 +186,11 @@ namespace OpenRA.Mods.Common.Traits
IWrapMove[] moveWrappers;
bool requireForceMove;
public bool IsMovingBetweenCells
{
get { return FromCell != ToCell; }
}
#region IFacing
[Sync]
public int Facing
@@ -210,8 +224,6 @@ namespace OpenRA.Mods.Common.Traits
{
if (FromCell == ToCell)
return new[] { Pair.New(FromCell, FromSubCell) };
if (CanEnterCell(ToCell))
return new[] { Pair.New(ToCell, ToSubCell) };
return new[] { Pair.New(FromCell, FromSubCell), Pair.New(ToCell, ToSubCell) };
}
@@ -226,7 +238,10 @@ namespace OpenRA.Mods.Common.Traits
ToSubCell = FromSubCell = info.LocomotorInfo.SharesCell ? init.World.Map.Grid.DefaultSubCell : SubCell.FullCell;
if (init.Contains<SubCellInit>())
{
FromSubCell = ToSubCell = init.Get<SubCellInit, SubCell>();
returnToCellOnCreationRecalculateSubCell = false;
}
if (init.Contains<LocationInit>())
{
@@ -234,14 +249,19 @@ namespace OpenRA.Mods.Common.Traits
SetVisualPosition(self, init.World.Map.CenterOfSubCell(FromCell, FromSubCell));
}
Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : info.InitialFacing;
Facing = oldFacing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : info.InitialFacing;
// Sets the visual position to WPos accuracy
// Use LocationInit if you want to insert the actor into the ActorMap!
// Sets the initial visual position
// Unit will move into the cell grid (defined by LocationInit) as its initial activity
if (init.Contains<CenterPositionInit>())
SetVisualPosition(self, init.Get<CenterPositionInit, WPos>());
{
oldPos = init.Get<CenterPositionInit, WPos>();
SetVisualPosition(self, oldPos);
returnToCellOnCreation = true;
}
moveIntoWorldDelay = init.Contains<MoveIntoWorldDelayInit>() ? init.Get<MoveIntoWorldDelayInit, int>() : 0;
if (init.Contains<CreationActivityDelayInit>())
creationActivityDelay = init.Get<CreationActivityDelayInit, int>();
}
protected override void Created(Actor self)
@@ -254,7 +274,6 @@ namespace OpenRA.Mods.Common.Traits
Locomotor = self.World.WorldActor.TraitsImplementing<Locomotor>()
.Single(l => l.Info.Name == Info.Locomotor);
self.QueueActivity(MoveIntoWorld(self, moveIntoWorldDelay));
base.Created(self);
}
@@ -295,7 +314,7 @@ namespace OpenRA.Mods.Common.Traits
public void Nudge(Actor self, Actor nudger, bool force)
{
if (IsTraitDisabled || IsTraitPaused)
if (IsTraitDisabled || IsTraitPaused || requireForceMove)
return;
// Initial fairly braindead implementation.
@@ -455,7 +474,7 @@ namespace OpenRA.Mods.Common.Traits
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
{
return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors);
return Info.CanEnterCell(self.World, self, cell, ToSubCell, ignoreActor, checkTransientActors);
}
#endregion
@@ -567,27 +586,27 @@ namespace OpenRA.Mods.Common.Traits
return WrapMove(new Follow(self, target, minRange, maxRange, initialTargetPosition, targetLineColor));
}
public Activity MoveIntoWorld(Actor self, int delay = 0)
public Activity ReturnToCell(Actor self)
{
return new MoveIntoWorldActivity(self, delay);
return new ReturnToCellActivity(self);
}
class MoveIntoWorldActivity : Activity
class ReturnToCellActivity : Activity
{
readonly Actor self;
readonly Mobile mobile;
readonly bool recalculateSubCell;
CPos cell;
SubCell subCell;
WPos pos;
int delay;
public MoveIntoWorldActivity(Actor self, int delay = 0)
public ReturnToCellActivity(Actor self, int delay = 0, bool recalculateSubCell = false)
{
this.self = self;
mobile = self.Trait<Mobile>();
IsInterruptible = false;
this.delay = delay;
this.recalculateSubCell = recalculateSubCell;
}
protected override void OnFirstRun(Actor self)
@@ -603,7 +622,7 @@ namespace OpenRA.Mods.Common.Traits
cell = mobile.ToCell;
subCell = mobile.ToSubCell;
if (subCell == SubCell.Any)
if (recalculateSubCell)
subCell = mobile.Info.LocomotorInfo.SharesCell ? self.World.ActorMap.FreeSubCell(cell, subCell) : SubCell.FullCell;
// TODO: solve/reduce cell is full problem
@@ -890,6 +909,11 @@ namespace OpenRA.Mods.Common.Traits
}
}
Activity ICreationActivity.GetCreationActivity()
{
return returnToCellOnCreation ? new ReturnToCellActivity(self, creationActivityDelay, returnToCellOnCreationRecalculateSubCell) : null;
}
class MoveOrderTargeter : IOrderTargeter
{
readonly Mobile mobile;

View File

@@ -131,6 +131,16 @@ namespace OpenRA.Mods.Common.Traits
if (specificCargoToken == ConditionManager.InvalidConditionToken && Info.CargoConditions.TryGetValue(cargo.Info.Name, out specificCargoCondition))
specificCargoToken = conditionManager.GrantCondition(self, specificCargoCondition);
}
// Allow scripted / initial actors to move from the unload point back into the cell grid on unload
// This is handled by the RideTransport activity for player-loaded cargo
if (self.IsIdle)
{
// IMove is not used anywhere else in this trait, there is no benefit to caching it from Created.
var move = self.TraitOrDefault<IMove>();
if (move != null)
self.QueueActivity(move.ReturnToCell(self));
}
}
void INotifyExitedCargo.OnExitedCargo(Actor self, Actor cargo)

View File

@@ -28,14 +28,6 @@ namespace OpenRA.Mods.Common.Traits
public int OrderCount;
public int EarnedThisMinute
{
get
{
return resources != null ? resources.Earned - earnedAtBeginningOfMinute : 0;
}
}
public int Experience
{
get
@@ -44,10 +36,12 @@ namespace OpenRA.Mods.Common.Traits
}
}
public Queue<int> EarnedSamples = new Queue<int>(100);
int earnedAtBeginningOfMinute;
// Low resolution (every 30 seconds) record of earnings, covering the entire game
public List<int> IncomeSamples = new List<int>(100);
public int Income;
public int DisplayIncome;
public Queue<int> ArmySamples = new Queue<int>(100);
public List<int> ArmySamples = new List<int>(100);
public int KillsCost;
public int DeathsCost;
@@ -59,40 +53,63 @@ namespace OpenRA.Mods.Common.Traits
public int BuildingsDead;
public int ArmyValue;
// High resolution (every second) record of earnings, limited to the last minute
readonly Queue<int> earnedSeconds = new Queue<int>(60);
int lastIncome;
int lastIncomeTick;
int ticks;
int replayTimestep;
bool armyGraphDisabled;
bool incomeGraphDisabled;
public PlayerStatistics(Actor self) { }
void INotifyCreated.Created(Actor self)
{
resources = self.TraitOrDefault<PlayerResources>();
experience = self.TraitOrDefault<PlayerExperience>();
}
void UpdateEarnedThisMinute()
{
EarnedSamples.Enqueue(EarnedThisMinute);
earnedAtBeginningOfMinute = resources != null ? resources.Earned : 0;
if (EarnedSamples.Count > 100)
EarnedSamples.Dequeue();
}
void UpdateArmyThisMinute()
{
ArmySamples.Enqueue(ArmyValue);
if (ArmySamples.Count > 100)
ArmySamples.Dequeue();
incomeGraphDisabled = resources == null;
}
void ITick.Tick(Actor self)
{
var timestep = self.World.IsReplay ? replayTimestep : self.World.Timestep;
ticks++;
if (timestep * self.World.WorldTick % 60000 == 0)
var timestep = self.World.IsReplay ? replayTimestep : self.World.Timestep;
if (ticks * timestep >= 30000)
{
UpdateEarnedThisMinute();
UpdateArmyThisMinute();
ticks = 0;
if (!armyGraphDisabled && (ArmyValue != 0 || self.Owner.WinState == WinState.Undefined))
ArmySamples.Add(ArmyValue);
else
armyGraphDisabled = true;
if (!incomeGraphDisabled && (Income != 0 || self.Owner.WinState == WinState.Undefined))
IncomeSamples.Add(Income);
else
incomeGraphDisabled = true;
}
if (resources == null)
return;
var tickDelta = self.World.WorldTick - lastIncomeTick;
if (tickDelta * timestep >= 1000)
{
lastIncomeTick = self.World.WorldTick;
var lastEarned = earnedSeconds.Count > 59 ? earnedSeconds.Dequeue() : 0;
lastIncome = DisplayIncome = Income;
Income = resources.Earned - lastEarned;
earnedSeconds.Enqueue(resources.Earned);
}
else
DisplayIncome = int2.Lerp(lastIncome, Income, tickDelta * timestep, 1000);
}
public void ResolveOrder(Actor self, Order order)
@@ -126,8 +143,11 @@ namespace OpenRA.Mods.Common.Traits
if (w.IsReplay)
replayTimestep = w.WorldActor.Trait<MapOptions>().GameSpeed.Timestep;
UpdateEarnedThisMinute();
UpdateArmyThisMinute();
if (!armyGraphDisabled)
ArmySamples.Add(ArmyValue);
if (!incomeGraphDisabled)
IncomeSamples.Add(Income);
}
}

View File

@@ -74,7 +74,7 @@ namespace OpenRA.Mods.Common.Traits
td.Add(new CenterPositionInit(spawn));
td.Add(new FacingInit(initialFacing));
if (exitinfo != null)
td.Add(new MoveIntoWorldDelayInit(exitinfo.ExitDelay));
td.Add(new CreationActivityDelayInit(exitinfo.ExitDelay));
}
self.World.AddFrameEndTask(w =>
@@ -130,7 +130,7 @@ namespace OpenRA.Mods.Common.Traits
self.NotifyBlocker(self.Location + s.ExitCell);
return mobileInfo == null ||
mobileInfo.CanEnterCell(self.World, self, self.Location + s.ExitCell, self);
mobileInfo.CanEnterCell(self.World, self, self.Location + s.ExitCell, ignoreActor: self);
}
}
}

View File

@@ -127,7 +127,7 @@ namespace OpenRA.Mods.Common.Traits
td.Add(new LocationInit(exit));
td.Add(new CenterPositionInit(spawn));
td.Add(new FacingInit(initialFacing));
td.Add(new MoveIntoWorldDelayInit(exitinfo.ExitDelay));
td.Add(new CreationActivityDelayInit(exitinfo.ExitDelay));
}
self.World.AddFrameEndTask(w =>

View File

@@ -93,6 +93,7 @@ namespace OpenRA.Mods.Common.Traits.Render
protected override void Created(Actor self)
{
rsm = self.TraitOrDefault<IRenderInfantrySequenceModifier>();
idleDelay = self.World.SharedRandom.Next(Info.MinIdleDelay, Info.MaxIdleDelay);
base.Created(self);
}

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.Traits
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down)
{
if (!activated)
if (!activated && world.Map.Contains(cell))
{
targetCell = cell;
targetLocation = mi.Location;
@@ -119,7 +119,10 @@ namespace OpenRA.Mods.Common.Traits
IEnumerable<IRenderable> IOrderGenerator.RenderAboveShroud(WorldRenderer wr, World world) { yield break; }
string IOrderGenerator.GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { return cursor; }
string IOrderGenerator.GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi)
{
return world.Map.Contains(cell) ? cursor : "generic-blocked";
}
bool IOrderGenerator.HandleKeyPress(KeyInput e) { return false; }

View File

@@ -0,0 +1,20 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, 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 OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Tag trait for updating the 'Oil Derrick' count economy statistic.")]
public class UpdatesDerrickCountInfo : TraitInfo<UpdatesDerrickCount> { }
public class UpdatesDerrickCount { }
}

View File

@@ -410,6 +410,15 @@ namespace OpenRA.Mods.Common.Traits
influenceNode = influenceNode.Next;
}
public void UpdateOccupiedCells(IOccupySpace ios)
{
if (CellUpdated == null)
return;
foreach (var c in ios.OccupiedCells())
CellUpdated(c.First);
}
void ITick.Tick(Actor self)
{
// Position updates are done in one pass

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Traits
// TODO: This won't make sense for MP saves
var localPlayer = worldRenderer.World.LocalPlayer;
if ((localPlayer != null && localPlayer.PlayerActor != self) ||
self.Owner != self.World.Players.FirstOrDefault(p => p.IsBot))
(localPlayer == null && self.Owner != self.World.Players.FirstOrDefault(p => p.IsBot)))
return null;
var nodes = new List<MiniYamlNode>()

View File

@@ -220,6 +220,9 @@ namespace OpenRA.Mods.Common.Traits
CellLayer<short> cellsCost;
CellLayer<CellCache> blockingCache;
readonly Dictionary<byte, CellLayer<short>> customLayerCellsCost = new Dictionary<byte, CellLayer<short>>();
readonly Dictionary<byte, CellLayer<CellCache>> customLayerBlockingCache = new Dictionary<byte, CellLayer<CellCache>>();
LocomotorInfo.TerrainInfo[] terrainInfos;
World world;
readonly HashSet<CPos> dirtyCells = new HashSet<CPos>();
@@ -238,7 +241,7 @@ namespace OpenRA.Mods.Common.Traits
if (!world.Map.Contains(cell))
return short.MaxValue;
return cellsCost[cell];
return cell.Layer == 0 ? cellsCost[cell] : customLayerCellsCost[cell.Layer][cell];
}
public short MovementCostToEnterCell(Actor actor, CPos destNode, Actor ignoreActor, CellConditions check)
@@ -246,7 +249,7 @@ namespace OpenRA.Mods.Common.Traits
if (!world.Map.Contains(destNode))
return short.MaxValue;
var cellCost = cellsCost[destNode];
var cellCost = destNode.Layer == 0 ? cellsCost[destNode] : customLayerCellsCost[destNode.Layer][destNode];
if (cellCost == short.MaxValue ||
!CanMoveFreelyInto(actor, destNode, ignoreActor, check))
@@ -257,6 +260,11 @@ namespace OpenRA.Mods.Common.Traits
// Determines whether the actor is blocked by other Actors
public bool CanMoveFreelyInto(Actor actor, CPos cell, Actor ignoreActor, CellConditions check)
{
return CanMoveFreelyInto(actor, cell, SubCell.FullCell, ignoreActor, check);
}
public bool CanMoveFreelyInto(Actor actor, CPos cell, SubCell subCell, Actor ignoreActor, CellConditions check)
{
var cellCache = GetCache(cell);
var cellFlag = cellCache.CellFlag;
@@ -289,7 +297,8 @@ namespace OpenRA.Mods.Common.Traits
return false;
}
foreach (var otherActor in world.ActorMap.GetActorsAt(cell))
var otherActors = subCell == SubCell.FullCell ? world.ActorMap.GetActorsAt(cell) : world.ActorMap.GetActorsAt(cell, subCell);
foreach (var otherActor in otherActors)
if (IsBlockedBy(actor, otherActor, ignoreActor, check, cellFlag))
return false;
@@ -298,8 +307,7 @@ namespace OpenRA.Mods.Common.Traits
public SubCell GetAvailableSubCell(Actor self, CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, CellConditions check = CellConditions.All)
{
var cost = cellsCost[cell];
if (cost == short.MaxValue)
if (MovementCostForCell(cell) == short.MaxValue)
return SubCell.Invalid;
if (check.HasCellCondition(CellConditions.TransientActors))
@@ -377,16 +385,40 @@ namespace OpenRA.Mods.Common.Traits
var map = w.Map;
actorMap = w.ActorMap;
actorMap.CellUpdated += CellUpdated;
terrainInfos = Info.TilesetTerrainInfo[map.Rules.TileSet];
blockingCache = new CellLayer<CellCache>(map);
cellsCost = new CellLayer<short>(map);
terrainInfos = Info.TilesetTerrainInfo[map.Rules.TileSet];
foreach (var cell in map.AllCells)
UpdateCellCost(cell);
map.CustomTerrain.CellEntryChanged += UpdateCellCost;
map.Tiles.CellEntryChanged += UpdateCellCost;
// This section needs to run after WorldLoaded() because we need to be sure that all types of ICustomMovementLayer have been initialized.
w.AddFrameEndTask(_ =>
{
var customMovementLayers = w.WorldActor.TraitsImplementing<ICustomMovementLayer>();
foreach (var cml in customMovementLayers)
{
var cellLayer = new CellLayer<short>(map);
customLayerCellsCost[cml.Index] = cellLayer;
customLayerBlockingCache[cml.Index] = new CellLayer<CellCache>(map);
foreach (var cell in map.AllCells)
{
var index = cml.GetTerrainIndex(cell);
var cost = short.MaxValue;
if (index != byte.MaxValue)
cost = terrainInfos[index].Cost;
cellLayer[cell] = cost;
}
}
});
}
CellCache GetCache(CPos cell)
@@ -397,7 +429,9 @@ namespace OpenRA.Mods.Common.Traits
dirtyCells.Remove(cell);
}
return blockingCache[cell];
var cache = cell.Layer == 0 ? blockingCache : customLayerBlockingCache[cell.Layer];
return cache[cell];
}
void CellUpdated(CPos cell)
@@ -416,24 +450,28 @@ namespace OpenRA.Mods.Common.Traits
if (index != byte.MaxValue)
cost = terrainInfos[index].Cost;
cellsCost[cell] = cost;
var cache = cell.Layer == 0 ? cellsCost : customLayerCellsCost[cell.Layer];
cache[cell] = cost;
}
void UpdateCellBlocking(CPos cell)
{
using (new PerfSample("locomotor_cache"))
{
var cache = cell.Layer == 0 ? blockingCache : customLayerBlockingCache[cell.Layer];
var actors = actorMap.GetActorsAt(cell);
if (!actors.Any())
{
blockingCache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
return;
}
if (sharesCell && actorMap.HasFreeSubCell(cell))
{
blockingCache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
return;
}
@@ -444,6 +482,8 @@ namespace OpenRA.Mods.Common.Traits
foreach (var actor in actors)
{
var actorBlocksPlayers = world.AllPlayersMask;
var actorCrushablePlayers = world.NoPlayersMask;
var crushables = actor.TraitsImplementing<ICrushable>();
var mobile = actor.OccupiesSpace as Mobile;
var isMoving = mobile != null && mobile.CurrentMovementTypes.HasMovementType(MovementType.Horizontal);
@@ -452,10 +492,8 @@ namespace OpenRA.Mods.Common.Traits
{
cellFlag |= CellFlag.HasCrushableActor;
foreach (var crushable in crushables)
cellCrushablePlayers = cellCrushablePlayers.Intersect(crushable.CrushableBy(actor, Info.Crushes));
actorCrushablePlayers = actorCrushablePlayers.Union(crushable.CrushableBy(actor, Info.Crushes));
}
else
cellCrushablePlayers = world.NoPlayersMask;
if (isMoving)
{
@@ -471,10 +509,11 @@ namespace OpenRA.Mods.Common.Traits
cellFlag |= CellFlag.HasTemporaryBlocker;
}
cellCrushablePlayers = cellCrushablePlayers.Intersect(actorCrushablePlayers);
cellBlockedPlayers = cellBlockedPlayers.Union(actorBlocksPlayers);
}
blockingCache[cell] = new CellCache(cellBlockedPlayers, cellFlag, cellCrushablePlayers);
cache[cell] = new CellCache(cellBlockedPlayers, cellFlag, cellCrushablePlayers);
}
}
}

View File

@@ -435,7 +435,7 @@ namespace OpenRA.Mods.Common.Traits
WPos? initialTargetPosition = null, Color? targetLineColor = null);
Activity MoveToTarget(Actor self, Target target,
WPos? initialTargetPosition = null, Color? targetLineColor = null);
Activity MoveIntoWorld(Actor self, int delay = 0);
Activity ReturnToCell(Actor self);
Activity MoveIntoTarget(Actor self, Target target);
Activity VisualMove(Actor self, WPos fromPos, WPos toPos);
int EstimatedMoveDuration(Actor self, WPos fromPos, WPos toPos);

View File

@@ -129,10 +129,12 @@ namespace OpenRA.Mods.Common.UpdateRules
new RemovePlaceBuildingPalettes(),
new RenameHoversOffsetModifier(),
new AddAirAttackTypes(),
new MoveAbortOnResupply(),
new RenameCarryallDelays(),
new AddCanSlide(),
new AddAircraftIdleBehavior(),
new RenameSearchRadius(),
new RenameChronoshiftFootprint(),
new RemoveMoveIntoWorldFromExit(),
})
};

View File

@@ -141,6 +141,12 @@ namespace OpenRA.Mods.Common
}
}
public static bool AreAdjacentCells(CPos a, CPos b)
{
var offset = b - a;
return Math.Abs(offset.X) < 2 && Math.Abs(offset.Y) < 2;
}
public static IEnumerable<CPos> ExpandFootprint(IEnumerable<CPos> cells, bool allowDiagonal)
{
return cells.SelectMany(c => Neighbours(c, allowDiagonal)).Distinct();

View File

@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Widgets
var textSize = font.Measure(text).Y;
var offset = font.TopOffset;
if (chatPos.Y < pos.Y)
if (chatPos.Y - font.TopOffset < pos.Y)
break;
var textLineHeight = lineHeight;

View File

@@ -35,6 +35,7 @@ namespace OpenRA.Mods.Common.Widgets
public string YAxisValueFormat = "{0}";
public int XAxisSize = 10;
public int YAxisSize = 10;
public int XAxisTicksPerLabel = 1;
public string XAxisLabel = "";
public string YAxisLabel = "";
public bool DisplayFirstYAxisValue = false;
@@ -77,6 +78,7 @@ namespace OpenRA.Mods.Common.Widgets
YAxisValueFormat = other.YAxisValueFormat;
XAxisSize = other.XAxisSize;
YAxisSize = other.YAxisSize;
XAxisTicksPerLabel = other.XAxisTicksPerLabel;
XAxisLabel = other.XAxisLabel;
YAxisLabel = other.YAxisLabel;
DisplayFirstYAxisValue = other.DisplayFirstYAxisValue;
@@ -89,14 +91,21 @@ namespace OpenRA.Mods.Common.Widgets
public override void Draw()
{
if (GetSeries == null || !GetSeries().Any()
|| GetLabelFont == null || GetLabelFont() == null)
if (GetSeries == null || GetLabelFont == null)
return;
var series = GetSeries();
if (!series.Any())
return;
var font = GetLabelFont();
if (font == null)
return;
var cr = Game.Renderer.RgbaColorRenderer;
var rect = RenderBounds;
var labelFont = Game.Renderer.Fonts[GetLabelFont()];
var labelFont = Game.Renderer.Fonts[font];
var axisFont = Game.Renderer.Fonts[GetAxisFont()];
var xAxisSize = GetXAxisSize();
@@ -110,8 +119,8 @@ namespace OpenRA.Mods.Common.Widgets
var graphBottomOffset = Padding * 2 + xAxisLabelSize.Y + xAxisPointLabelHeight;
var height = rect.Height - (graphBottomOffset + Padding);
var maxValue = GetSeries().Select(p => p.Points).SelectMany(d => d).Concat(new[] { 0f }).Max();
var longestName = GetSeries().Select(s => s.Key).OrderByDescending(s => s.Length).FirstOrDefault() ?? "";
var maxValue = series.Select(p => p.Points).SelectMany(d => d).Concat(new[] { 0f }).Max();
var longestName = series.Select(s => s.Key).OrderByDescending(s => s.Length).FirstOrDefault() ?? "";
var scale = 200 / Math.Max(5000, (float)Math.Ceiling(maxValue / 1000) * 1000);
@@ -127,7 +136,7 @@ namespace OpenRA.Mods.Common.Widgets
var xStep = width / xAxisSize;
var yStep = height / yAxisSize;
var pointCount = GetSeries().First().Points.Count();
var pointCount = series.Max(s => s.Points.Count());
var pointStart = Math.Max(0, pointCount - xAxisSize);
var pointEnd = Math.Max(pointCount, xAxisSize);
@@ -136,11 +145,11 @@ namespace OpenRA.Mods.Common.Widgets
var origin = new float2(rect.Left, rect.Bottom);
var keyOffset = 0;
foreach (var series in GetSeries())
foreach (var s in series)
{
var key = series.Key;
var color = series.Color;
var points = series.Points;
var key = s.Key;
var color = s.Color;
var points = s.Points;
if (points.Any())
{
points = points.Reverse().Take(xAxisSize).Reverse();
@@ -171,7 +180,10 @@ namespace OpenRA.Mods.Common.Widgets
for (int n = pointStart, x = 0; n <= pointEnd; n++, x += xStep)
{
cr.DrawLine(graphOrigin + new float2(x, 0), graphOrigin + new float2(x, -5), 1, Color.White);
var xAxisText = GetXAxisValueFormat().F(n);
if (n % XAxisTicksPerLabel != 0)
continue;
var xAxisText = GetXAxisValueFormat().F(n / XAxisTicksPerLabel);
var xAxisTickTextWidth = labelFont.Measure(xAxisText).X;
var xLocation = x - (xAxisTickTextWidth / 2);
labelFont.DrawTextWithShadow(xAxisText, graphOrigin + new float2(xLocation, 2), Color.White, BackgroundColorDark, BackgroundColorLight, 1);

View File

@@ -91,7 +91,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}
if (Directory.Exists(baseSavePath))
LoadGames(gameTemplate, newTemplate);
LoadGames(gameTemplate, newTemplate, world);
var renameButton = panel.Get<ButtonWidget>("RENAME_BUTTON");
renameButton.IsDisabled = () => selectedSave == null;
@@ -171,7 +171,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
SelectFirstVisible();
}
void LoadGames(ScrollItemWidget gameTemplate, ScrollItemWidget newTemplate)
void LoadGames(ScrollItemWidget gameTemplate, ScrollItemWidget newTemplate, World world)
{
gameList.RemoveChildren();
if (isSavePanel)
@@ -198,7 +198,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
item.IsVisible = () => true;
item.IsSelected = () => selectedSave == item.ItemKey;
item.OnClick = () => Select(item.ItemKey);
item.OnDoubleClick = Load;
if (isSavePanel)
item.OnDoubleClick = () => Save(world);
else
item.OnDoubleClick = Load;
var title = Path.GetFileNameWithoutExtension(savePath);
var label = item.Get<LabelWithTooltipWidget>("TITLE");

View File

@@ -38,10 +38,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly ScrollItemWidget productionPlayerTemplate;
readonly ScrollItemWidget supportPowersPlayerTemplate;
readonly ScrollItemWidget combatPlayerTemplate;
readonly ContainerWidget earnedThisMinuteGraphContainer;
readonly ContainerWidget armyThisMinuteGraphContainer;
readonly LineGraphWidget earnedThisMinuteGraph;
readonly LineGraphWidget armyThisMinuteGraph;
readonly ContainerWidget incomeGraphContainer;
readonly ContainerWidget armyValueGraphContainer;
readonly LineGraphWidget incomeGraph;
readonly LineGraphWidget armyValueGraph;
readonly ScrollItemWidget teamTemplate;
readonly IEnumerable<Player> players;
readonly IOrderedEnumerable<IGrouping<int, Player>> teams;
@@ -95,11 +95,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
supportPowersPlayerTemplate = playerStatsPanel.Get<ScrollItemWidget>("SUPPORT_POWERS_PLAYER_TEMPLATE");
combatPlayerTemplate = playerStatsPanel.Get<ScrollItemWidget>("COMBAT_PLAYER_TEMPLATE");
earnedThisMinuteGraphContainer = widget.Get<ContainerWidget>("EARNED_THIS_MIN_GRAPH_CONTAINER");
earnedThisMinuteGraph = earnedThisMinuteGraphContainer.Get<LineGraphWidget>("EARNED_THIS_MIN_GRAPH");
incomeGraphContainer = widget.Get<ContainerWidget>("INCOME_GRAPH_CONTAINER");
incomeGraph = incomeGraphContainer.Get<LineGraphWidget>("INCOME_GRAPH");
armyThisMinuteGraphContainer = widget.Get<ContainerWidget>("ARMY_THIS_MIN_GRAPH_CONTAINER");
armyThisMinuteGraph = armyThisMinuteGraphContainer.Get<LineGraphWidget>("ARMY_THIS_MIN_GRAPH");
armyValueGraphContainer = widget.Get<ContainerWidget>("ARMY_VALUE_GRAPH_CONTAINER");
armyValueGraph = armyValueGraphContainer.Get<LineGraphWidget>("ARMY_VALUE_GRAPH");
teamTemplate = playerStatsPanel.Get<ScrollItemWidget>("TEAM_TEMPLATE");
@@ -144,8 +144,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
createStatsOption("Production", ObserverStatsPanel.Production, productionPlayerTemplate, () => DisplayStats(ProductionStats)),
createStatsOption("Support Powers", ObserverStatsPanel.SupportPowers, supportPowersPlayerTemplate, () => DisplayStats(SupportPowerStats)),
createStatsOption("Combat", ObserverStatsPanel.Combat, combatPlayerTemplate, () => DisplayStats(CombatStats)),
createStatsOption("Earnings (graph)", ObserverStatsPanel.Graph, null, () => EarnedThisMinuteGraph()),
createStatsOption("Army (graph)", ObserverStatsPanel.ArmyGraph, null, () => ArmyThisMinuteGraph()),
createStatsOption("Earnings (graph)", ObserverStatsPanel.Graph, null, () => IncomeGraph()),
createStatsOption("Army (graph)", ObserverStatsPanel.ArmyGraph, null, () => ArmyValueGraph()),
};
Func<StatsDropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
@@ -192,31 +192,31 @@ namespace OpenRA.Mods.Common.Widgets.Logic
supportPowerStatsHeaders.Visible = false;
combatStatsHeaders.Visible = false;
earnedThisMinuteGraphContainer.Visible = false;
armyThisMinuteGraphContainer.Visible = false;
incomeGraphContainer.Visible = false;
armyValueGraphContainer.Visible = false;
earnedThisMinuteGraph.GetSeries = null;
armyThisMinuteGraph.GetSeries = null;
incomeGraph.GetSeries = null;
armyValueGraph.GetSeries = null;
}
void EarnedThisMinuteGraph()
void IncomeGraph()
{
playerStatsPanel.Visible = false;
earnedThisMinuteGraphContainer.Visible = true;
incomeGraphContainer.Visible = true;
earnedThisMinuteGraph.GetSeries = () =>
incomeGraph.GetSeries = () =>
players.Select(p => new LineGraphSeries(
p.PlayerName,
p.Color,
(p.PlayerActor.TraitOrDefault<PlayerStatistics>() ?? new PlayerStatistics(p.PlayerActor)).EarnedSamples.Select(s => (float)s)));
(p.PlayerActor.TraitOrDefault<PlayerStatistics>() ?? new PlayerStatistics(p.PlayerActor)).IncomeSamples.Select(s => (float)s)));
}
void ArmyThisMinuteGraph()
void ArmyValueGraph()
{
playerStatsPanel.Visible = false;
armyThisMinuteGraphContainer.Visible = true;
armyValueGraphContainer.Visible = true;
armyThisMinuteGraph.GetSeries = () =>
armyValueGraph.GetSeries = () =>
players.Select(p => new LineGraphSeries(
p.PlayerName,
p.Color,
@@ -233,7 +233,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
tt.IgnoreMouseOver = true;
var teamLabel = tt.Get<LabelWidget>("TEAM");
teamLabel.GetText = () => team.Key == 0 ? "No Team" : "Team " + team.Key;
var teamText = team.Key == 0 ? "No Team" : "Team " + team.Key;
teamLabel.GetText = () => teamText;
tt.Bounds.Width = teamLabel.Bounds.Width = Game.Renderer.Fonts[tt.Font].Measure(tt.Get<LabelWidget>("TEAM").GetText()).X;
var colorBlockWidget = tt.Get<ColorBlockWidget>("TEAM_COLOR");
@@ -273,14 +274,29 @@ namespace OpenRA.Mods.Common.Widgets.Logic
SetupPlayerColor(player, template, playerColor, playerGradient);
var stats = player.PlayerActor.TraitOrDefault<PlayerStatistics>();
if (stats == null) return template;
template.Get<LabelWidget>("ASSETS_DESTROYED").GetText = () => "$" + stats.KillsCost;
template.Get<LabelWidget>("ASSETS_LOST").GetText = () => "$" + stats.DeathsCost;
template.Get<LabelWidget>("UNITS_KILLED").GetText = () => stats.UnitsKilled.ToString();
template.Get<LabelWidget>("UNITS_DEAD").GetText = () => stats.UnitsDead.ToString();
template.Get<LabelWidget>("BUILDINGS_KILLED").GetText = () => stats.BuildingsKilled.ToString();
template.Get<LabelWidget>("BUILDINGS_DEAD").GetText = () => stats.BuildingsDead.ToString();
template.Get<LabelWidget>("ARMY_VALUE").GetText = () => "$" + stats.ArmyValue.ToString();
if (stats == null)
return template;
var destroyedText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("ASSETS_DESTROYED").GetText = () => destroyedText.Update(stats.KillsCost);
var lostText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("ASSETS_LOST").GetText = () => lostText.Update(stats.DeathsCost);
var unitsKilledText = new CachedTransform<int, string>(i => i.ToString());
template.Get<LabelWidget>("UNITS_KILLED").GetText = () => unitsKilledText.Update(stats.UnitsKilled);
var unitsDeadText = new CachedTransform<int, string>(i => i.ToString());
template.Get<LabelWidget>("UNITS_DEAD").GetText = () => unitsDeadText.Update(stats.UnitsDead);
var buildingsKilledText = new CachedTransform<int, string>(i => i.ToString());
template.Get<LabelWidget>("BUILDINGS_KILLED").GetText = () => buildingsKilledText.Update(stats.BuildingsKilled);
var buildingsDeadText = new CachedTransform<int, string>(i => i.ToString());
template.Get<LabelWidget>("BUILDINGS_DEAD").GetText = () => buildingsDeadText.Update(stats.BuildingsDead);
var armyText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("ARMY_VALUE").GetText = () => armyText.Update(stats.ArmyValue);
return template;
}
@@ -344,22 +360,34 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var res = player.PlayerActor.Trait<PlayerResources>();
var stats = player.PlayerActor.TraitOrDefault<PlayerStatistics>();
if (stats == null) return template;
if (stats == null)
return template;
template.Get<LabelWidget>("CASH").GetText = () => "$" + (res.Cash + res.Resources);
template.Get<LabelWidget>("EARNED_MIN").GetText = () => AverageEarnedPerMinute(res.Earned);
template.Get<LabelWidget>("EARNED_THIS_MIN").GetText = () => "$" + stats.EarnedThisMinute;
template.Get<LabelWidget>("EARNED").GetText = () => "$" + res.Earned;
template.Get<LabelWidget>("SPENT").GetText = () => "$" + res.Spent;
var cashText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("CASH").GetText = () => cashText.Update(res.Cash + res.Resources);
var incomeText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("INCOME").GetText = () => incomeText.Update(stats.DisplayIncome);
var earnedText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("EARNED").GetText = () => earnedText.Update(res.Earned);
var spentText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("SPENT").GetText = () => spentText.Update(res.Spent);
var assetsText = new CachedTransform<int, string>(i => "$" + i);
var assets = template.Get<LabelWidget>("ASSETS");
assets.GetText = () => "$" + world.ActorsHavingTrait<Valued>()
assets.GetText = () => assetsText.Update(world.ActorsHavingTrait<Valued>()
.Where(a => a.Owner == player && !a.IsDead)
.Sum(a => a.Info.TraitInfos<ValuedInfo>().First().Cost);
.Sum(a => a.Info.TraitInfos<ValuedInfo>().First().Cost));
var harvesters = template.Get<LabelWidget>("HARVESTERS");
harvesters.GetText = () => world.ActorsHavingTrait<Harvester>().Count(a => a.Owner == player && !a.IsDead).ToString();
var derricks = template.GetOrNull<LabelWidget>("DERRICKS");
if (derricks != null)
derricks.GetText = () => world.ActorsHavingTrait<UpdatesDerrickCount>().Count(a => a.Owner == player && !a.IsDead).ToString();
return template;
}
@@ -379,25 +407,39 @@ namespace OpenRA.Mods.Common.Widgets.Logic
SetupPlayerColor(player, template, playerColor, playerGradient);
var res = player.PlayerActor.Trait<PlayerResources>();
template.Get<LabelWidget>("CASH").GetText = () => "$" + (res.Cash + res.Resources);
template.Get<LabelWidget>("EARNED_MIN").GetText = () => AverageEarnedPerMinute(res.Earned);
var cashText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("CASH").GetText = () => cashText.Update(res.Cash + res.Resources);
var powerRes = player.PlayerActor.TraitOrDefault<PowerManager>();
if (powerRes != null)
{
var power = template.Get<LabelWidget>("POWER");
power.GetText = () => powerRes.PowerDrained + "/" + powerRes.PowerProvided;
var powerText = new CachedTransform<Pair<int, int>, string>(p => p.First + "/" + p.Second);
power.GetText = () => powerText.Update(new Pair<int, int>(powerRes.PowerDrained, powerRes.PowerProvided));
power.GetColor = () => GetPowerColor(powerRes.PowerState);
}
var stats = player.PlayerActor.TraitOrDefault<PlayerStatistics>();
if (stats == null) return template;
template.Get<LabelWidget>("KILLS").GetText = () => (stats.UnitsKilled + stats.BuildingsKilled).ToString();
template.Get<LabelWidget>("DEATHS").GetText = () => (stats.UnitsDead + stats.BuildingsDead).ToString();
template.Get<LabelWidget>("ASSETS_DESTROYED").GetText = () => "$" + stats.KillsCost;
template.Get<LabelWidget>("ASSETS_LOST").GetText = () => "$" + stats.DeathsCost;
template.Get<LabelWidget>("EXPERIENCE").GetText = () => stats.Experience.ToString();
template.Get<LabelWidget>("ACTIONS_MIN").GetText = () => AverageOrdersPerMinute(stats.OrderCount);
if (stats == null)
return template;
var killsText = new CachedTransform<int, string>(i => i.ToString());
template.Get<LabelWidget>("KILLS").GetText = () => killsText.Update(stats.UnitsKilled + stats.BuildingsKilled);
var deathsText = new CachedTransform<int, string>(i => i.ToString());
template.Get<LabelWidget>("DEATHS").GetText = () => deathsText.Update(stats.UnitsDead + stats.BuildingsDead);
var destroyedText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("ASSETS_DESTROYED").GetText = () => destroyedText.Update(stats.KillsCost);
var lostText = new CachedTransform<int, string>(i => "$" + i);
template.Get<LabelWidget>("ASSETS_LOST").GetText = () => lostText.Update(stats.DeathsCost);
var experienceText = new CachedTransform<int, string>(i => i.ToString());
template.Get<LabelWidget>("EXPERIENCE").GetText = () => experienceText.Update(stats.Experience);
var actionsText = new CachedTransform<double, string>(d => AverageOrdersPerMinute(d));
template.Get<LabelWidget>("ACTIONS_MIN").GetText = () => actionsText.Update(stats.OrderCount);
return template;
}
@@ -446,9 +488,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
headerTemplate.Get<GradientColorBlockWidget>("HEADER_GRADIENT").Bounds.X += offset;
foreach (var headerLabel in headerTemplate.Children.OfType<LabelWidget>())
{
headerLabel.Bounds.X += offset;
}
}
static void AddPlayerFlagAndName(ScrollItemWidget template, Player player)
@@ -482,15 +522,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return (world.WorldTick == 0 ? 0 : orders / (world.WorldTick / 1500.0)).ToString("F1");
}
string AverageEarnedPerMinute(double earned)
{
return "$" + (world.WorldTick == 0 ? 0 : earned / (world.WorldTick / 1500.0)).ToString("F0");
}
static Color GetPowerColor(PowerState state)
{
if (state == PowerState.Critical) return Color.Red;
if (state == PowerState.Low) return Color.Orange;
if (state == PowerState.Critical)
return Color.Red;
if (state == PowerState.Low)
return Color.Orange;
return Color.LimeGreen;
}

View File

@@ -149,9 +149,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
remapButton.GetColor = () =>
{
return modData.Hotkeys.GetFirstDuplicate(hd.Name, modData.Hotkeys[hd.Name].GetValue(), hd) != null ?
hotkeyInvalidColor :
hotkeyValidColor;
return hd.HasDuplicates ? hotkeyInvalidColor : hotkeyValidColor;
};
if (selectedHotkeyDefinition == hd)

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<dllmap dll="freetype6" os="linux" target="libfreetype.so.6" />
<dllmap dll="freetype6" os="!windows" target="libfreetype.so.6" />
<dllmap dll="freetype6" os="openbsd" target="libfreetype.so" />
<dllmap dll="freetype6" os="osx" target="libfreetype.6.dylib" />
<dllmap dll="freetype6" os="freebsd" target="libfreetype.so.6" />
</configuration>

View File

@@ -268,7 +268,7 @@ Container@OBSERVER_WIDGETS:
StatisticsArmyGraphKey: StatisticsArmyGraph
X: 5
Y: 5
Width: 765
Width: 730
Height: 240
Children:
DropDownButton@STATS_DROPDOWN:
@@ -288,7 +288,7 @@ Container@OBSERVER_WIDGETS:
Container@BASIC_STATS_HEADERS:
X: 0
Y: 0
Width: 765
Width: 705
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -322,17 +322,8 @@ Container@OBSERVER_WIDGETS:
Text: Cash
Align: Right
Shadow: True
Label@EARNED_MIN_HEADER:
X: 240
Y: 0
Width: 60
Height: PARENT_BOTTOM
Font: Bold
Text: $/min
Align: Right
Shadow: True
Label@POWER_HEADER:
X: 300
X: 240
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -341,7 +332,7 @@ Container@OBSERVER_WIDGETS:
Align: Center
Shadow: True
Label@KILLS_HEADER:
X: 380
X: 320
Y: 0
Width: 40
Height: PARENT_BOTTOM
@@ -350,7 +341,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@DEATHS_HEADER:
X: 420
X: 360
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -359,7 +350,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ASSETS_DESTROYED_HEADER:
X: 480
X: 420
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -368,7 +359,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ASSETS_LOST_HEADER:
X: 560
X: 500
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -377,7 +368,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@EXPERIENCE_HEADER:
X: 640
X: 580
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -386,7 +377,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ACTIONS_MIN_HEADER:
X: 700
X: 640
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -397,7 +388,7 @@ Container@OBSERVER_WIDGETS:
Container@ECONOMY_STATS_HEADERS:
X: 0
Y: 0
Width: 745
Width: 730
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -428,24 +419,16 @@ Container@OBSERVER_WIDGETS:
Text: Cash
Align: Right
Shadow: True
Label@EARNED_MIN_HEADER:
Label@INCOME_HEADER:
X: 240
Width: 60
Width: 80
Height: PARENT_BOTTOM
Font: Bold
Text: $/min
Align: Right
Shadow: True
Label@EARNED_THIS_MIN_HEADER:
X: 300
Width: 120
Height: PARENT_BOTTOM
Font: Bold
Text: Earned this min
Text: Income
Align: Right
Shadow: True
Label@ASSETS_HEADER:
X: 420
X: 320
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -453,7 +436,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@EARNED_HEADER:
X: 500
X: 400
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -461,7 +444,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@SPENT_HEADER:
X: 580
X: 480
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -469,13 +452,21 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@HARVESTERS_HEADER:
X: 660
X: 560
Width: 80
Height: PARENT_BOTTOM
Font: Bold
Text: Harvesters
Align: Right
Shadow: True
Label@DERRICKS_HEADER:
X: 650
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Oil Derricks
Align: Right
Shadow: True
Container@PRODUCTION_STATS_HEADERS:
X: 0
Y: 0
@@ -551,7 +542,7 @@ Container@OBSERVER_WIDGETS:
Container@COMBAT_STATS_HEADERS:
X: 0
Y: 0
Width: 775
Width: 730
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -579,32 +570,32 @@ Container@OBSERVER_WIDGETS:
Label@ASSETS_DESTROYED_HEADER:
X: 160
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Destroyed
Align: Right
Shadow: True
Label@ASSETS_LOST_HEADER:
X: 240
X: 235
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Lost
Align: Right
Shadow: True
Label@UNITS_KILLED_HEADER:
X: 320
X: 310
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Font: Bold
Text: Units Killed
Align: Right
Shadow: True
Label@UNITS_DEAD_HEADER:
X: 420
X: 400
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -613,27 +604,27 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@BUILDINGS_KILLED_HEADER:
X: 500
X: 480
Y: 0
Width: 90
Width: 85
Height: PARENT_BOTTOM
Font: Bold
Text: Bldg Killed
Align: Right
Shadow: True
Label@BUILDINGS_DEAD_HEADER:
X: 590
X: 565
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Bldg Lost
Align: Right
Shadow: True
Label@ARMY_VALUE_HEADER:
X: 670
X: 640
Y: 0
Width: 100
Width: 85
Height: PARENT_BOTTOM
Font: Bold
Text: Army Value
@@ -679,7 +670,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@BASIC_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 765
Width: 705
Height: 24
BaseName: scrollitem-nohover
Children:
@@ -714,57 +705,50 @@ Container@OBSERVER_WIDGETS:
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_MIN:
X: 240
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@POWER:
X: 300
X: 240
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Center
Shadow: True
Label@KILLS:
X: 380
X: 320
Y: 0
Width: 40
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@DEATHS:
X: 420
X: 360
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_DESTROYED:
X: 480
X: 420
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_LOST:
X: 560
X: 500
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EXPERIENCE:
X: 640
X: 580
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ACTIONS_MIN:
X: 700
X: 640
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -773,7 +757,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@ECONOMY_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 745
Width: 730
Height: 24
BaseName: scrollitem-nohover
Children:
@@ -808,48 +792,48 @@ Container@OBSERVER_WIDGETS:
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_MIN:
Label@INCOME:
X: 240
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_THIS_MIN:
X: 300
Y: 0
Width: 120
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS:
X: 420
X: 320
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED:
X: 500
X: 400
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@SPENT:
X: 580
X: 480
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@HARVESTERS:
X: 660
X: 560
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@DERRICKS:
X: 650
Y: 0
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
ScrollItem@PRODUCTION_PLAYER_TEMPLATE:
X: 0
Y: 0
@@ -927,7 +911,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@COMBAT_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 775
Width: 730
Height: 24
BaseName: scrollitem-nohover
Children:
@@ -958,53 +942,53 @@ Container@OBSERVER_WIDGETS:
Label@ASSETS_DESTROYED:
X: 160
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_LOST:
X: 240
X: 235
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@UNITS_KILLED:
X: 320
X: 310
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@UNITS_DEAD:
X: 420
X: 400
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@BUILDINGS_KILLED:
X: 500
X: 480
Y: 0
Width: 90
Width: 85
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@BUILDINGS_DEAD:
X: 590
X: 565
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ARMY_VALUE:
X: 670
X: 640
Y: 0
Width: 100
Width: 85
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Container@EARNED_THIS_MIN_GRAPH_CONTAINER:
Container@INCOME_GRAPH_CONTAINER:
X: 0
Y: 30
Width: PARENT_RIGHT
@@ -1017,21 +1001,20 @@ Container@OBSERVER_WIDGETS:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Color: 00000090
LineGraph@EARNED_THIS_MIN_GRAPH:
LineGraph@INCOME_GRAPH:
X: 0
Y: 0
Width: PARENT_RIGHT
Width: PARENT_RIGHT - 5
Height: PARENT_BOTTOM
ValueFormat: ${0}
XAxisValueFormat: {0}
YAxisValueFormat: ${0:F0}
XAxisSize: 20
YAxisSize: 10
XAxisSize: 40
XAxisTicksPerLabel: 2
XAxisLabel: Game Minute
YAxisLabel: Earnings
LabelFont: TinyBold
AxisFont: TinyBold
Container@ARMY_THIS_MIN_GRAPH_CONTAINER:
Container@ARMY_VALUE_GRAPH_CONTAINER:
X: 0
Y: 30
Width: PARENT_RIGHT
@@ -1044,16 +1027,15 @@ Container@OBSERVER_WIDGETS:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Color: 00000090
LineGraph@ARMY_THIS_MIN_GRAPH:
LineGraph@ARMY_VALUE_GRAPH:
X: 0
Y: 0
Width: PARENT_RIGHT
Width: PARENT_RIGHT - 5
Height: PARENT_BOTTOM
ValueFormat: ${0}
XAxisValueFormat: {0}
YAxisValueFormat: ${0:F0}
XAxisSize: 20
YAxisSize: 10
XAxisSize: 40
XAxisTicksPerLabel: 2
XAxisLabel: Game Minute
YAxisLabel: Army Value
LabelFont: TinyBold

View File

@@ -72,8 +72,6 @@ RMBO:
Prerequisites: ~disabled
HARV:
Harvester:
SearchFromHarvesterRadius: 24
Buildable:
Prerequisites: ~disabled

View File

@@ -91,6 +91,7 @@ HARV:
Buildable:
Prerequisites: ~disabled
Harvester:
SearchFromProcRadius: 24
SearchFromHarvesterRadius: 24
FTNK:

View File

@@ -67,6 +67,7 @@ E6:
HARV:
Harvester:
SearchFromProcRadius: 45
SearchFromHarvesterRadius: 45
HTNK:

View File

@@ -63,6 +63,7 @@ E5:
HARV:
Harvester:
SearchFromProcRadius: 45
SearchFromHarvesterRadius: 45
MTNK:

View File

@@ -70,6 +70,7 @@ E5:
HARV:
Harvester:
SearchFromProcRadius: 30
SearchFromHarvesterRadius: 30
HTNK:

View File

@@ -78,10 +78,6 @@ E5:
E6:
-RepairsBridges:
HARV:
Harvester:
SearchFromHarvesterRadius: 30
MTNK:
Buildable:
Prerequisites: ~weap

View File

@@ -97,6 +97,7 @@ E6:
HARV:
Harvester:
SearchFromProcRadius: 30
SearchFromHarvesterRadius: 30
MTNK:

View File

@@ -83,10 +83,6 @@ E5:
Buildable:
Prerequisites: ~disabled
HARV:
Harvester:
SearchFromHarvesterRadius: 45
HTNK:
Buildable:
Prerequisites: ~disabled

View File

@@ -18,6 +18,7 @@ V19:
ValidStances: Neutral, Enemy
SpawnActorOnDeath:
Actor: V19.Husk
UpdatesDerrickCount:
V19.Husk:
Inherits: ^CivBuildingHusk

View File

@@ -172,7 +172,7 @@ Container@OBSERVER_WIDGETS:
StatisticsArmyGraphKey: StatisticsArmyGraph
X: 5
Y: 5
Width: 765
Width: 760
Height: 250
Children:
DropDownButton@STATS_DROPDOWN:
@@ -192,7 +192,7 @@ Container@OBSERVER_WIDGETS:
Container@BASIC_STATS_HEADERS:
X: 0
Y: 0
Width: 760
Width: 700
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -226,17 +226,8 @@ Container@OBSERVER_WIDGETS:
Text: Cash
Align: Right
Shadow: True
Label@EARNED_MIN_HEADER:
X: 235
Y: 0
Width: 60
Height: PARENT_BOTTOM
Font: Bold
Text: $/min
Align: Right
Shadow: True
Label@POWER_HEADER:
X: 295
X: 235
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -245,7 +236,7 @@ Container@OBSERVER_WIDGETS:
Align: Center
Shadow: True
Label@KILLS_HEADER:
X: 375
X: 315
Y: 0
Width: 40
Height: PARENT_BOTTOM
@@ -254,7 +245,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@DEATHS_HEADER:
X: 415
X: 355
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -263,7 +254,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ASSETS_DESTROYED_HEADER:
X: 475
X: 415
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -272,7 +263,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ASSETS_LOST_HEADER:
X: 555
X: 495
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -281,7 +272,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@EXPERIENCE_HEADER:
X: 635
X: 575
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -290,7 +281,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ACTIONS_MIN_HEADER:
X: 695
X: 635
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -301,7 +292,7 @@ Container@OBSERVER_WIDGETS:
Container@ECONOMY_STATS_HEADERS:
X: 0
Y: 0
Width: 740
Width: 640
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -332,24 +323,16 @@ Container@OBSERVER_WIDGETS:
Text: Cash
Align: Right
Shadow: True
Label@EARNED_MIN_HEADER:
Label@INCOME_HEADER:
X: 235
Width: 60
Width: 80
Height: PARENT_BOTTOM
Font: Bold
Text: $/min
Align: Right
Shadow: True
Label@EARNED_THIS_MIN_HEADER:
X: 295
Width: 120
Height: PARENT_BOTTOM
Font: Bold
Text: Earned this min
Text: Income
Align: Right
Shadow: True
Label@ASSETS_HEADER:
X: 415
X: 315
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -357,7 +340,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@EARNED_HEADER:
X: 495
X: 395
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -365,7 +348,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@SPENT_HEADER:
X: 575
X: 475
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -373,7 +356,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@HARVESTERS_HEADER:
X: 655
X: 555
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -455,7 +438,7 @@ Container@OBSERVER_WIDGETS:
Container@COMBAT_STATS_HEADERS:
X: 0
Y: 0
Width: 770
Width: 735
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -483,32 +466,32 @@ Container@OBSERVER_WIDGETS:
Label@ASSETS_DESTROYED_HEADER:
X: 155
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Destroyed
Align: Right
Shadow: True
Label@ASSETS_LOST_HEADER:
X: 235
X: 230
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Lost
Align: Right
Shadow: True
Label@UNITS_KILLED_HEADER:
X: 315
X: 305
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Font: Bold
Text: Units Killed
Align: Right
Shadow: True
Label@UNITS_DEAD_HEADER:
X: 415
X: 395
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -517,16 +500,16 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@BUILDINGS_KILLED_HEADER:
X: 495
X: 475
Y: 0
Width: 90
Width: 85
Height: PARENT_BOTTOM
Font: Bold
Text: Bldg Killed
Align: Right
Shadow: True
Label@BUILDINGS_DEAD_HEADER:
X: 585
X: 560
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -535,9 +518,9 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ARMY_VALUE_HEADER:
X: 665
X: 640
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Font: Bold
Text: Army Value
@@ -583,7 +566,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@BASIC_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 760
Width: 700
Height: 25
BaseName: scrollitem-nohover
Children:
@@ -616,57 +599,50 @@ Container@OBSERVER_WIDGETS:
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_MIN:
X: 235
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@POWER:
X: 295
X: 235
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Center
Shadow: True
Label@KILLS:
X: 375
X: 315
Y: 0
Width: 40
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@DEATHS:
X: 415
X: 355
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_DESTROYED:
X: 475
X: 415
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_LOST:
X: 555
X: 495
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EXPERIENCE:
X: 635
X: 575
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ACTIONS_MIN:
X: 695
X: 635
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -675,7 +651,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@ECONOMY_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 740
Width: 640
Height: 25
BaseName: scrollitem-nohover
Children:
@@ -708,43 +684,36 @@ Container@OBSERVER_WIDGETS:
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_MIN:
Label@INCOME:
X: 235
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_THIS_MIN:
X: 295
Y: 0
Width: 120
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS:
X: 415
X: 315
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED:
X: 495
X: 395
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@SPENT:
X: 575
X: 475
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@HARVESTERS:
X: 655
X: 555
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -823,7 +792,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@COMBAT_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 770
Width: 735
Height: 25
BaseName: scrollitem-nohover
Children:
@@ -852,53 +821,53 @@ Container@OBSERVER_WIDGETS:
Label@ASSETS_DESTROYED:
X: 155
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_LOST:
X: 235
X: 230
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@UNITS_KILLED:
X: 315
X: 305
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@UNITS_DEAD:
X: 415
X: 395
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@BUILDINGS_KILLED:
X: 495
X: 475
Y: 0
Width: 90
Width: 85
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@BUILDINGS_DEAD:
X: 585
X: 560
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ARMY_VALUE:
X: 665
X: 640
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Container@EARNED_THIS_MIN_GRAPH_CONTAINER:
Container@INCOME_GRAPH_CONTAINER:
X: 0
Y: 30
Width: PARENT_RIGHT
@@ -911,21 +880,20 @@ Container@OBSERVER_WIDGETS:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Color: 00000090
LineGraph@EARNED_THIS_MIN_GRAPH:
LineGraph@INCOME_GRAPH:
X: 0
Y: 0
Width: PARENT_RIGHT
Width: PARENT_RIGHT - 5
Height: PARENT_BOTTOM
ValueFormat: ${0}
XAxisValueFormat: {0}
YAxisValueFormat: ${0:F0}
XAxisSize: 20
YAxisSize: 10
XAxisSize: 40
XAxisTicksPerLabel: 2
XAxisLabel: Game Minute
YAxisLabel: Earnings
LabelFont: TinyBold
AxisFont: TinyBold
Container@ARMY_THIS_MIN_GRAPH_CONTAINER:
Container@ARMY_VALUE_GRAPH_CONTAINER:
X: 0
Y: 30
Width: PARENT_RIGHT
@@ -938,16 +906,15 @@ Container@OBSERVER_WIDGETS:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Color: 00000090
LineGraph@ARMY_THIS_MIN_GRAPH:
LineGraph@ARMY_VALUE_GRAPH:
X: 0
Y: 0
Width: PARENT_RIGHT
Width: PARENT_RIGHT - 5
Height: PARENT_BOTTOM
ValueFormat: ${0}
XAxisValueFormat: {0}
YAxisValueFormat: ${0:F0}
XAxisSize: 20
YAxisSize: 10
XAxisSize: 40
XAxisTicksPerLabel: 2
XAxisLabel: Game Minute
YAxisLabel: Army Value
LabelFont: TinyBold

View File

@@ -73,8 +73,8 @@ harvester:
HarvestFacings: 8
Resources: Spice
BaleUnloadDelay: 5
SearchFromProcRadius: 15
SearchFromHarvesterRadius: 8
SearchFromProcRadius: 30
SearchFromHarvesterRadius: 15
CarryableHarvester:
Health:
HP: 45000

Binary file not shown.

View File

@@ -202,7 +202,7 @@ Container@OBSERVER_WIDGETS:
StatsDropDownPanelTemplate: SPECTATOR_LABEL_DROPDOWN_TEMPLATE
X: 5
Y: 5
Width: 765
Width: 760
Height: 240
Children:
DropDownButton@STATS_DROPDOWN:
@@ -224,7 +224,7 @@ Container@OBSERVER_WIDGETS:
Container@BASIC_STATS_HEADERS:
X: 0
Y: 0
Width: 765
Width: 705
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -258,17 +258,8 @@ Container@OBSERVER_WIDGETS:
Text: Cash
Align: Right
Shadow: True
Label@EARNED_MIN_HEADER:
X: 240
Y: 0
Width: 60
Height: PARENT_BOTTOM
Font: Bold
Text: $/min
Align: Right
Shadow: True
Label@POWER_HEADER:
X: 300
X: 240
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -277,7 +268,7 @@ Container@OBSERVER_WIDGETS:
Align: Center
Shadow: True
Label@KILLS_HEADER:
X: 380
X: 320
Y: 0
Width: 40
Height: PARENT_BOTTOM
@@ -286,7 +277,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@DEATHS_HEADER:
X: 420
X: 360
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -295,7 +286,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ASSETS_DESTROYED_HEADER:
X: 480
X: 420
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -304,7 +295,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ASSETS_LOST_HEADER:
X: 560
X: 500
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -313,7 +304,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@EXPERIENCE_HEADER:
X: 640
X: 580
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -322,7 +313,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ACTIONS_MIN_HEADER:
X: 700
X: 640
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -333,7 +324,7 @@ Container@OBSERVER_WIDGETS:
Container@ECONOMY_STATS_HEADERS:
X: 0
Y: 0
Width: 745
Width: 735
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -364,24 +355,16 @@ Container@OBSERVER_WIDGETS:
Text: Cash
Align: Right
Shadow: True
Label@EARNED_MIN_HEADER:
Label@INCOME_HEADER:
X: 240
Width: 60
Width: 80
Height: PARENT_BOTTOM
Font: Bold
Text: $/min
Align: Right
Shadow: True
Label@EARNED_THIS_MIN_HEADER:
X: 300
Width: 120
Height: PARENT_BOTTOM
Font: Bold
Text: Earned this min
Text: Income
Align: Right
Shadow: True
Label@ASSETS_HEADER:
X: 420
X: 320
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -389,7 +372,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@EARNED_HEADER:
X: 500
X: 400
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -397,7 +380,7 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@SPENT_HEADER:
X: 580
X: 480
Width: 80
Height: PARENT_BOTTOM
Font: Bold
@@ -405,13 +388,21 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@HARVESTERS_HEADER:
X: 660
X: 560
Width: 80
Height: PARENT_BOTTOM
Font: Bold
Text: Harvesters
Align: Right
Shadow: True
Label@DERRICKS_HEADER:
X: 650
Width: 80
Height: PARENT_BOTTOM
Font: Bold
Text: Oil Derricks
Align: Right
Shadow: True
Container@PRODUCTION_STATS_HEADERS:
X: 0
Y: 0
@@ -487,7 +478,7 @@ Container@OBSERVER_WIDGETS:
Container@COMBAT_STATS_HEADERS:
X: 0
Y: 0
Width: 775
Width: 740
Height: PARENT_BOTTOM
Children:
ColorBlock@HEADER_COLOR:
@@ -515,32 +506,32 @@ Container@OBSERVER_WIDGETS:
Label@ASSETS_DESTROYED_HEADER:
X: 160
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Destroyed
Align: Right
Shadow: True
Label@ASSETS_LOST_HEADER:
X: 240
X: 235
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Font: Bold
Text: Lost
Align: Right
Shadow: True
Label@UNITS_KILLED_HEADER:
X: 320
X: 310
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Font: Bold
Text: Units Killed
Align: Right
Shadow: True
Label@UNITS_DEAD_HEADER:
X: 420
X: 400
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -549,16 +540,16 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@BUILDINGS_KILLED_HEADER:
X: 500
X: 480
Y: 0
Width: 90
Width: 85
Height: PARENT_BOTTOM
Font: Bold
Text: Bldg Killed
Align: Right
Shadow: True
Label@BUILDINGS_DEAD_HEADER:
X: 590
X: 565
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -567,9 +558,9 @@ Container@OBSERVER_WIDGETS:
Align: Right
Shadow: True
Label@ARMY_VALUE_HEADER:
X: 670
X: 645
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Font: Bold
Text: Army Value
@@ -591,7 +582,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@TEAM_TEMPLATE:
X: 0
Y: 0
Width: 650 #PARENT_RIGHT - 35
Width: 650
Height: 24
Children:
ColorBlock@TEAM_COLOR:
@@ -617,7 +608,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@BASIC_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 765
Width: 705
Height: 24
BaseName: scrollitem-nohover
Children:
@@ -652,57 +643,50 @@ Container@OBSERVER_WIDGETS:
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_MIN:
X: 240
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@POWER:
X: 300
X: 240
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Center
Shadow: True
Label@KILLS:
X: 380
X: 320
Y: 0
Width: 40
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@DEATHS:
X: 420
X: 360
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_DESTROYED:
X: 480
X: 420
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_LOST:
X: 560
X: 500
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EXPERIENCE:
X: 640
X: 580
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ACTIONS_MIN:
X: 700
X: 640
Y: 0
Width: 60
Height: PARENT_BOTTOM
@@ -711,7 +695,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@ECONOMY_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 745
Width: 735
Height: 24
BaseName: scrollitem-nohover
Children:
@@ -746,43 +730,43 @@ Container@OBSERVER_WIDGETS:
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_MIN:
Label@INCOME:
X: 240
Y: 0
Width: 60
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED_THIS_MIN:
X: 300
Y: 0
Width: 120
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS:
X: 420
X: 320
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@EARNED:
X: 500
X: 400
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@SPENT:
X: 580
X: 480
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@HARVESTERS:
X: 660
X: 560
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@DERRICKS:
X: 650
Y: 0
Width: 80
Height: PARENT_BOTTOM
@@ -865,7 +849,7 @@ Container@OBSERVER_WIDGETS:
ScrollItem@COMBAT_PLAYER_TEMPLATE:
X: 0
Y: 0
Width: 775
Width: 740
Height: 24
BaseName: scrollitem-nohover
Children:
@@ -896,53 +880,53 @@ Container@OBSERVER_WIDGETS:
Label@ASSETS_DESTROYED:
X: 160
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ASSETS_LOST:
X: 240
X: 235
Y: 0
Width: 80
Width: 75
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@UNITS_KILLED:
X: 320
X: 310
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@UNITS_DEAD:
X: 420
X: 400
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@BUILDINGS_KILLED:
X: 500
X: 480
Y: 0
Width: 90
Width: 85
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@BUILDINGS_DEAD:
X: 590
X: 565
Y: 0
Width: 80
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Label@ARMY_VALUE:
X: 670
X: 645
Y: 0
Width: 100
Width: 90
Height: PARENT_BOTTOM
Align: Right
Shadow: True
Container@EARNED_THIS_MIN_GRAPH_CONTAINER:
Container@INCOME_GRAPH_CONTAINER:
X: 0
Y: 30
Width: PARENT_RIGHT
@@ -955,21 +939,20 @@ Container@OBSERVER_WIDGETS:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Color: 00000090
LineGraph@EARNED_THIS_MIN_GRAPH:
LineGraph@INCOME_GRAPH:
X: 0
Y: 0
Width: PARENT_RIGHT
Width: PARENT_RIGHT - 5
Height: PARENT_BOTTOM
ValueFormat: ${0}
XAxisValueFormat: {0}
YAxisValueFormat: ${0:F0}
XAxisSize: 20
YAxisSize: 10
XAxisSize: 40
XAxisTicksPerLabel: 2
XAxisLabel: Game Minute
YAxisLabel: Earnings
LabelFont: TinyBold
AxisFont: TinyBold
Container@ARMY_THIS_MIN_GRAPH_CONTAINER:
Container@ARMY_VALUE_GRAPH_CONTAINER:
X: 0
Y: 30
Width: PARENT_RIGHT
@@ -982,16 +965,15 @@ Container@OBSERVER_WIDGETS:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Color: 00000090
LineGraph@ARMY_THIS_MIN_GRAPH:
LineGraph@ARMY_VALUE_GRAPH:
X: 0
Y: 0
Width: PARENT_RIGHT
Width: PARENT_RIGHT - 5
Height: PARENT_BOTTOM
ValueFormat: ${0}
XAxisValueFormat: {0}
YAxisValueFormat: ${0:F0}
XAxisSize: 20
YAxisSize: 10
XAxisSize: 40
XAxisTicksPerLabel: 2
XAxisLabel: Game Minute
YAxisLabel: Army Value
LabelFont: TinyBold

View File

@@ -189,8 +189,8 @@ InitTriggers = function()
end
end)
end)
Trigger.OnAllRemovedFromWorld(FirstUSSRBase, function()
if baseCamera then
Trigger.OnAllKilledOrCaptured(FirstUSSRBase, function()
if baseCamera and baseCamera.IsInWorld then
baseCamera.Destroy()
end
end)

View File

@@ -252,8 +252,8 @@ InitTriggers = function()
AlertFirstBase()
end)
end)
Trigger.OnAllRemovedFromWorld(FirstUSSRBase, function()
if baseCamera then
Trigger.OnAllKilledOrCaptured(FirstUSSRBase, function()
if baseCamera and baseCamera.IsInWorld then
baseCamera.Destroy()
end
end)

View File

@@ -245,6 +245,10 @@ ProduceAircraft = function()
end
TargetAndAttack = function(yak, target)
if yak.IsDead then
return
end
if not target or target.IsDead or (not target.IsInWorld) then
local enemies = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == player and self.HasProperty("Health") and yak.CanTarget(self) end)
@@ -260,7 +264,11 @@ TargetAndAttack = function(yak, target)
end
yak.CallFunc(function()
TargetAndAttack(yak, target)
-- TODO: Replace this with an idle trigger once that works for aircraft
-- Add a delay of one tick to fix an endless recursive call
Trigger.AfterDelay(1, function()
TargetAndAttack(yak, target)
end)
end)
end

View File

@@ -264,6 +264,10 @@ ProduceAircraft = function()
end
TargetAndAttack = function(yak, target)
if yak.IsDead then
return
end
if not target or target.IsDead or (not target.IsInWorld) then
local enemies = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == greece and self.HasProperty("Health") and yak.CanTarget(self) end)
if #enemies > 0 then
@@ -278,7 +282,11 @@ TargetAndAttack = function(yak, target)
end
yak.CallFunc(function()
TargetAndAttack(yak, target)
-- TODO: Replace this with an idle trigger once that works for aircraft
-- Add a delay of one tick to fix an endless recursive call
Trigger.AfterDelay(1, function()
TargetAndAttack(yak, target)
end)
end)
end

View File

@@ -159,6 +159,10 @@ ProduceAircraft = function()
end
TargetAndAttack = function(yak, target)
if yak.IsDead then
return
end
if not target or target.IsDead or (not target.IsInWorld) then
local enemies = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == player and self.HasProperty("Health") and yak.CanTarget(self) end)
if #enemies > 0 then
@@ -173,7 +177,11 @@ TargetAndAttack = function(yak, target)
end
yak.CallFunc(function()
TargetAndAttack(yak, target)
-- TODO: Replace this with an idle trigger once that works for aircraft
-- Add a delay of one tick to fix an endless recursive call
Trigger.AfterDelay(1, function()
TargetAndAttack(yak, target)
end)
end)
end

View File

@@ -171,6 +171,10 @@ ProduceAircraft = function()
end
TargetAndAttack = function(yak, target)
if yak.IsDead then
return
end
if not target or target.IsDead or (not target.IsInWorld) then
local enemies = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == player and self.HasProperty("Health") and yak.CanTarget(self) end)
if #enemies > 0 then
@@ -185,7 +189,11 @@ TargetAndAttack = function(yak, target)
end
yak.CallFunc(function()
TargetAndAttack(yak, target)
-- TODO: Replace this with an idle trigger once that works for aircraft
-- Add a delay of one tick to fix an endless recursive call
Trigger.AfterDelay(1, function()
TargetAndAttack(yak, target)
end)
end)
end

View File

@@ -204,7 +204,7 @@ WorldLoaded = function()
Camera.Position = DefaultCameraPosition.CenterPosition
if Map.LobbyOption("difficulty") ~= "hard" then
if Map.LobbyOption("difficulty") == "easy" then
Trigger.OnEnteredProximityTrigger(SovietDefenseCam.CenterPosition, WDist.New(1024 * 7), function(a, id)
if a.Owner == player then
Trigger.RemoveProximityTrigger(id)
@@ -220,15 +220,19 @@ WorldLoaded = function()
end
end
end)
end
if Map.LobbyOption("difficulty") == "easy" then
Trigger.OnKilled(DefBrl1, function(a, b)
if Map.LobbyOption("difficulty") ~= "hard" then
Trigger.OnKilled(DefBrl1, function(a, b)
if not DefenseFlame1.IsDead then
DefenseFlame1.Kill()
end)
Trigger.OnKilled(DefBrl2, function(a, b)
end
end)
Trigger.OnKilled(DefBrl2, function(a, b)
if not DefenseFlame2.IsDead then
DefenseFlame2.Kill()
end)
end
end
end)
end
Utils.Do(BadGuys, function(a)

View File

@@ -134,6 +134,10 @@ ProduceAircraft = function()
end
TargetAndAttack = function(yak, target)
if yak.IsDead then
return
end
if not target or target.IsDead or (not target.IsInWorld) then
local enemies = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == greece and self.HasProperty("Health") and yak.CanTarget(self) end)
if #enemies > 0 then
@@ -148,7 +152,11 @@ TargetAndAttack = function(yak, target)
end
yak.CallFunc(function()
TargetAndAttack(yak, target)
-- TODO: Replace this with an idle trigger once that works for aircraft
-- Add a delay of one tick to fix an endless recursive call
Trigger.AfterDelay(1, function()
TargetAndAttack(yak, target)
end)
end)
end

View File

@@ -85,7 +85,7 @@ BaseRaids = function()
if Map.LobbyOption("difficulty") == "easy" then
return
else
Trigger.AfterDelay(Utils.RandomInteger(BaseRaidDelay1[1], BaseRaidDelay1[2]), function()
Trigger.AfterDelay(Utils.RandomInteger(BaseRaidDelay1[1], BaseRaidDelay1[2]), function()
local raiders = Reinforcements.ReinforceWithTransport(ussr, "lst", RaidingParty, RaidOnePath, { RaidOneEntry.Location })[2]
Utils.Do(raiders, function(a)
Trigger.OnAddedToWorld(a, function()
@@ -95,7 +95,7 @@ BaseRaids = function()
end)
end)
Trigger.AfterDelay(Utils.RandomInteger(BaseRaidDelay2[1], BaseRaidDelay2[2]), function()
Trigger.AfterDelay(Utils.RandomInteger(BaseRaidDelay2[1], BaseRaidDelay2[2]), function()
local raiders = Reinforcements.ReinforceWithTransport(ussr, "lst", RaidingParty, RaidTwoPath, { RaidTwoEntry.Location })[2]
Utils.Do(raiders, function(a)
Trigger.OnAddedToWorld(a, function()
@@ -126,7 +126,7 @@ FinishTimer = function()
Trigger.AfterDelay(DateTime.Seconds(6), function() UserInterface.SetMissionText("") end)
end
BattalionWays =
BattalionWays =
{
{ HardEntry1.Location, HardLanding1.Location },
{ HardEntry2.Location, HardLanding2.Location },
@@ -138,7 +138,7 @@ BattalionWays =
SendArmoredBattalion = function()
Media.PlaySpeechNotification(greece, "EnemyUnitsApproaching")
Utils.Do(BattalionWays, function(way)
Utils.Do(BattalionWays, function(way)
local units = { "3tnk", "3tnk", "3tnk", "4tnk", "4tnk" }
local armor = Reinforcements.ReinforceWithTransport(ussr, "lst", units , way, { way[2], way[1] })[2]
Utils.Do(armor, function(a)
@@ -146,7 +146,7 @@ SendArmoredBattalion = function()
a.AttackMove(PlayerBase.Location)
IdleHunt(a)
end)
end)
end)
end)
end
@@ -217,6 +217,6 @@ WorldLoaded = function()
Trigger.AfterDelay(ActivateAIDelay, ActivateAI)
Trigger.AfterDelay(StartTimerDelay, StartTimerFunction)
Trigger.OnAllRemovedFromWorld(DestroySubPensTriggerActivator, DestroySubPensCompleted)
Trigger.OnAllKilledOrCaptured(DestroySubPensTriggerActivator, DestroySubPensCompleted)
Trigger.OnAllRemovedFromWorld(ClearSubActivityTriggerActivator, ClearSubActivityCompleted)
end

View File

@@ -130,9 +130,13 @@ ProduceAircraft = function()
end
TargetAndAttack = function(mig, target)
if mig.IsDead then
return
end
if not target or target.IsDead or (not target.IsInWorld) then
local enemies = Utils.Where(greece.GetActors(), function(actor)
return actor.HasProperty("Health") and actor.Type ~= "brik"
return actor.HasProperty("Health") and actor.Type ~= "brik" and mig.CanTarget(target)
end)
if #enemies > 0 then
target = Utils.Random(enemies)
@@ -146,7 +150,11 @@ TargetAndAttack = function(mig, target)
end
mig.CallFunc(function()
TargetAndAttack(mig, target)
-- TODO: Replace this with an idle trigger once that works for aircraft
-- Add a delay of one tick to fix an endless recursive call
Trigger.AfterDelay(1, function()
TargetAndAttack(mig, target)
end)
end)
end

View File

@@ -129,9 +129,13 @@ ProduceAircraft = function()
end
TargetAndAttack = function(mig, target)
if mig.IsDead then
return
end
if not target or target.IsDead or (not target.IsInWorld) then
local enemies = Utils.Where(greece.GetActors(), function(actor)
return actor.HasProperty("Health") and actor.Type ~= "brik"
return actor.HasProperty("Health") and actor.Type ~= "brik" and mig.CanTarget(target)
end)
if #enemies > 0 then
target = Utils.Random(enemies)
@@ -145,7 +149,11 @@ TargetAndAttack = function(mig, target)
end
mig.CallFunc(function()
TargetAndAttack(mig, target)
-- TODO: Replace this with an idle trigger once that works for aircraft
-- Add a delay of one tick to fix an endless recursive call
Trigger.AfterDelay(1, function()
TargetAndAttack(mig, target)
end)
end)
end

Binary file not shown.

View File

@@ -222,6 +222,7 @@ end
StopHunt = function(unit)
if not unit.IsDead then
unit.Stop()
Trigger.Clear(unit, "OnIdle")
end
end
@@ -282,12 +283,10 @@ end
SovietBaseMaintenanceSetup = function()
local sovietbuildings = Utils.Where(Map.NamedActors, function(a)
return a.Owner == soviets
and a.HasProperty("StartBuildingRepairs") and a.HasProperty("Sell")
return a.Owner == soviets and a.HasProperty("StartBuildingRepairs")
end)
-- This includes killed, captured (actor is temporarily removed) and sold.
Trigger.OnAllRemovedFromWorld(sovietbuildings, function()
Trigger.OnAllKilledOrCaptured(sovietbuildings, function()
Utils.Do(humans, function(player)
player.MarkCompletedObjective(destroyBase)
end)
@@ -295,15 +294,9 @@ SovietBaseMaintenanceSetup = function()
Utils.Do(sovietbuildings, function(sovietbuilding)
Trigger.OnDamaged(sovietbuilding, function(building)
if building.Owner ~= soviets then
return
end
if building.Health < building.MaxHealth * 3/4 then
if building.Owner == soviets and building.Health < building.MaxHealth * 3/4 then
building.StartBuildingRepairs()
end
if building.Health < building.MaxHealth * 1/4 then
building.Sell()
end
end)
end)
end

View File

@@ -104,6 +104,11 @@ TRUK.Hijackable:
Voiced:
VoiceSet: SpyVoice
HARV:
Harvester:
SearchFromProcRadius: 25
SearchFromHarvesterRadius: 25
E7:
Buildable:
Prerequisites: ~disabled

View File

@@ -35,10 +35,10 @@ end
Village = { FarmHouse1, FarmHouse2, FarmHouse3, FarmHouse4, FarmHouse5, FarmHouse6, FarmHouse7, FarmHouse8, FarmHouse9, Church }
VillageRaidInterval = DateTime.Minutes(3)
VillageRaidAircraft = { "mig", "mig" }
VillageRaidAircraft = { "mig.scripted", "mig.scripted" }
VillageRaidWpts = { VillageRaidEntrypoint.Location, VillageRaidWpt1.Location, VillageRaidWpt2.Location }
BaseRaidAircraft = { "mig", "mig" }
BaseRaidAircraft = { "mig.scripted", "mig.scripted" }
BaseRaidWpts = { BaseRaidEntrypoint.Location, UboatPatrolWpt1.Location, BaseRaidWpt2.Location }
BaseFrontAttackUnits = { "e3", "e3", "e1", "e1", "e1", "3tnk", "3tnk", "apc" }
@@ -77,7 +77,7 @@ ParadropSovietUnits = function()
powerproxy.Destroy()
end
AirRaid = function(planeTypes, ingress, egress, target)
AirRaid = function(planeTypes, ingress, target)
if target == nil then
return
end
@@ -89,8 +89,6 @@ AirRaid = function(planeTypes, ingress, egress, target)
Utils.Do(ingress, function(wpt) plane.Move(wpt) end)
plane.Attack(target)
Utils.Do(egress, function(wpt) plane.Move(wpt) end)
plane.Destroy()
end)
end
end
@@ -106,7 +104,7 @@ BaseRaid = function()
local target = Utils.Random(targets)
AirRaid(BaseRaidAircraft, BaseRaidWpts, { VillageRaidEntrypoint.Location }, target)
AirRaid(BaseRaidAircraft, BaseRaidWpts, target)
Trigger.AfterDelay(BaseRaidInterval, BaseRaid)
end
@@ -124,7 +122,7 @@ VillageRaid = function()
return
end
AirRaid(VillageRaidAircraft, VillageRaidWpts, { BaseRaidEntrypoint.Location }, target)
AirRaid(VillageRaidAircraft, VillageRaidWpts, target)
Trigger.AfterDelay(VillageRaidInterval, VillageRaid)
end

View File

@@ -71,11 +71,16 @@ GIVEFIX:
Tooltip:
Name: Weapons Factory or Helipad
MIG:
MIG.SCRIPTED:
Inherits: MIG
Buildable:
Prerequisites: ~afld
Prerequisites: ~disabled
RenderSprites:
Image: mig
AmmoPool:
Ammo: 2
Aircraft:
IdleBehavior: LeaveMap
HELI:
Buildable:

View File

@@ -41,30 +41,6 @@ SetupTriggers = function()
Trigger.OnAllKilled(ConvoyTrucks, function()
greece.MarkCompletedObjective(objDestroyAllTrucks)
end)
Trigger.OnEnteredFootprint({ TruckEscapeCenter.Location }, function(actor, triggerlose1)
if actor.Owner == ussr and actor.Type == "truk" then
Trigger.RemoveProximityTrigger(triggerlose1)
actor.Destroy()
greece.MarkFailedObjective(objDestroyAllTrucks)
end
end)
Trigger.OnEnteredFootprint({ EscapeNorth10.Location }, function(actor, triggerlose2)
if actor.Owner == ussr and actor.Type == "truk" then
Trigger.RemoveProximityTrigger(triggerlose2)
actor.Destroy()
greece.MarkFailedObjective(objDestroyAllTrucks)
end
end)
Trigger.OnEnteredFootprint({ EscapeSouth5.Location }, function(actor, triggerlose3)
if actor.Owner == ussr and actor.Type == "truk" then
Trigger.RemoveProximityTrigger(triggerlose3)
actor.Destroy()
greece.MarkFailedObjective(objDestroyAllTrucks)
end
end)
end
MissionStart = function()
@@ -123,26 +99,23 @@ SendPatrol = function(mammoth)
end
end
MoveTruckNorth = function(truck)
MoveTruckEscapeRoute = function(truck, route)
if truck.IsDead then
return
else
Media.DisplayMessage("Convoy truck attempting to escape!")
Media.PlaySoundNotification(greece, "AlertBleep")
Utils.Do(TruckEscapeNorth, function(waypoint)
Utils.Do(route, function(waypoint)
truck.Move(waypoint.Location)
end)
end
end
MoveTruckSouth = function(truck)
if truck.IsDead then
return
else
Media.DisplayMessage("Convoy truck attempting to escape!")
Media.PlaySoundNotification(greece, "AlertBleep")
Utils.Do(TruckEscapeSouth, function(waypoint)
truck.Move(waypoint.Location)
Trigger.OnIdle(truck, function()
if truck.Location == route[#route].Location then
truck.Destroy()
greece.MarkFailedObjective(objDestroyAllTrucks)
else
truck.Move(route[#route].Location)
end
end)
end
end
@@ -195,10 +168,10 @@ WorldLoaded = function()
Camera.Position = DefaultCameraPosition.CenterPosition
Trigger.AfterDelay(DateTime.Minutes(5), function() SendPatrol(PatrolMammoth) end)
Trigger.AfterDelay(DateTime.Minutes(5), function() MoveTruckNorth(Truck1) end)
Trigger.AfterDelay(DateTime.Minutes(9), function() MoveTruckNorth(Truck2) end)
Trigger.AfterDelay(DateTime.Minutes(12), function() MoveTruckSouth(Truck3) end)
Trigger.AfterDelay(DateTime.Minutes(15), function() MoveTruckNorth(Truck4) end)
Trigger.AfterDelay(DateTime.Minutes(17), function() MoveTruckSouth(Truck5) end)
Trigger.AfterDelay(DateTime.Minutes(18), function() MoveTruckSouth(IntroTruck2) end)
Trigger.AfterDelay(DateTime.Minutes(5), function() MoveTruckEscapeRoute(Truck1, TruckEscapeNorth) end)
Trigger.AfterDelay(DateTime.Minutes(9), function() MoveTruckEscapeRoute(Truck2, TruckEscapeNorth) end)
Trigger.AfterDelay(DateTime.Minutes(12), function() MoveTruckEscapeRoute(Truck3, TruckEscapeSouth) end)
Trigger.AfterDelay(DateTime.Minutes(15), function() MoveTruckEscapeRoute(Truck4, TruckEscapeNorth) end)
Trigger.AfterDelay(DateTime.Minutes(17), function() MoveTruckEscapeRoute(Truck5, TruckEscapeSouth) end)
Trigger.AfterDelay(DateTime.Minutes(18), function() MoveTruckEscapeRoute(IntroTruck2, TruckEscapeSouth) end)
end

View File

@@ -477,10 +477,6 @@ Actors:
Location: 58,89
Facing: 60
TurretFacing: 60
Helper: proc
Owner: Greece
Location: 42,42
FreeActor: False
Actor181: mine
Owner: Neutral
Location: 63,65

View File

@@ -22,6 +22,10 @@ World:
hard: Hard
Default: easy
HARV:
Harvester:
SearchFromProcRadius: 44
MCV.CAM:
Inherits: CAMERA
RevealsShroud:

View File

@@ -6,6 +6,7 @@
the License, or (at your option) any later version. For more
information, see COPYING.
]]
CheckForBase = function()
baseBuildings = Map.ActorsInBox(Map.TopLeft, CFBPoint.CenterPosition, function(actor)
return actor.Type == "fact" or actor.Type == "powr"
@@ -36,7 +37,6 @@ RunInitialActivities = function()
Trigger.AfterDelay(1, function()
Harvester.FindResources()
Helper.Destroy()
IdlingUnits()
Media.PlaySpeechNotification(player, "ReinforcementsArrived")
@@ -75,32 +75,29 @@ RunInitialActivities = function()
end
Expand = function()
if ExpansionCheck then
return
elseif mcvtransport.IsDead then
return
elseif mcvGG.IsDead then
if ExpansionCheck or mcvtransport.IsDead or mcvGG.IsDead then
return
end
mcvGG.Move(mcvGGLoadPoint.Location)
mcvtransport.Move(lstBeachPoint.Location)
ExpansionCheck = true
Trigger.ClearAll(mcvGG)
Trigger.ClearAll(mcvtransport)
Media.DisplayMessage("Allied MCV detected moving to the island.")
Reinforcements.Reinforce(GoodGuy, { "dd", "dd" }, ShipArrivePath, 0, function(ddsquad)
ddsquad.AttackMove(NearExpPoint.Location) end)
ExpansionCheck = true
Trigger.ClearAll(mcvGG)
Trigger.ClearAll(mcvtransport)
Trigger.AfterDelay(DateTime.Seconds(3), function()
if mcvtransport.IsDead then
return
elseif mcvGG.IsDead then
mcvtransport.Move(lstBeachPoint.Location)
mcvGG.Move(mcvGGLoadPoint.Location)
mcvGG.EnterTransport(mcvtransport)
Trigger.AfterDelay(DateTime.Seconds(5), function()
if mcvtransport.IsDead or mcvGG.IsDead then
return
end
mcvGG.EnterTransport(mcvtransport)
mcvtransport.Move(GGUnloadPoint.Location)
mcvtransport.UnloadPassengers()
Trigger.AfterDelay(DateTime.Seconds(12), function()

View File

@@ -8,7 +8,7 @@ BADR:
Aircraft:
CruiseAltitude: 2560
TurnSpeed: 5
Speed: 149
Speed: 180
Repulsable: False
MaximumPitch: 56
Cargo:
@@ -47,11 +47,11 @@ BADR.Bomber:
Aircraft:
CruiseAltitude: 2560
TurnSpeed: 5
Speed: 149
Speed: 180
Repulsable: False
MaximumPitch: 56
AmmoPool:
Ammo: 7
Ammo: 5
-Selectable:
SelectionDecorations:
RenderSelectionBars: False
@@ -111,6 +111,7 @@ MIG:
AttackAircraft:
FacingTolerance: 20
PersistentTargeting: false
AttackTurnDelay: 30
Aircraft:
CruiseAltitude: 2560
InitialFacing: 192

View File

@@ -504,6 +504,7 @@ OILB:
AppearsOnMapPreview:
GivesCashOnCapture:
Amount: 100
UpdatesDerrickCount:
BR1:
Inherits: ^Bridge

View File

@@ -178,7 +178,7 @@
InvalidTargets: NoAutoTarget, WaterStructure
AutoTargetPriority@ATTACKANYTHING:
RequiresCondition: stance-attackanything
ValidTargets: Infantry, Vehicle, Water, Underwater, Structure, Defense
ValidTargets: Infantry, Vehicle, Water, Underwater, Structure, Defense, Mine
InvalidTargets: NoAutoTarget
^AutoTargetGroundAssaultMove:
@@ -542,7 +542,6 @@
^NeutralPlane:
Inherits@1: ^ExistsInWorld
Inherits@3: ^IronCurtainable
Inherits@4: ^SpriteActor
Inherits@bounty: ^GlobalBounty
Inherits@SELECTION_MODE: ^FlatSelectionMode

View File

@@ -65,8 +65,6 @@ TENF:
HitShape:
UseTargetableCellsOffsets: false
TargetableOffsets: 0,0,0, 630,-512,0, 355,512,0, -281,-512,0, -630,512,0
Selectable:
Bounds: 48,48
SYRF:
Inherits: ^FakeBuilding

Some files were not shown because too many files have changed in this diff Show More