Compare commits

...

56 Commits

Author SHA1 Message Date
abcdefg30
bf0a300682 Revert "Add conyard.corrino"
This reverts commit bdaff27656.
2018-02-08 21:09:41 +01:00
abcdefg30
d7310be875 Revert "Limit D2K Palace SWs to their Faction's Palaces'"
This reverts commit d110b9c648.
2018-02-08 21:09:41 +01:00
abcdefg30
a150800a1e Fix a "granted condition is never consumed" warning in D2k 2018-02-07 21:23:22 +01:00
Mustafa Alperen Seki
904b71b890 Seperate Imperial and Harkonnen Sardaukars 2018-02-06 21:21:27 +01:00
Mustafa Alperen Seki
bdaff27656 Add conyard.corrino 2018-02-06 21:11:17 +01:00
Mustafa Alperen Seki
5b0875f66f Make SPM not fire from an actor with disabled instance of a SW. 2018-02-06 21:11:15 +01:00
Mustafa Alperen Seki
d110b9c648 Limit D2K Palace SWs to their Faction's Palaces' 2018-02-06 21:11:14 +01:00
Mustafa Alperen Seki
94028c8246 Make SupportPowerChargeBar conditional 2018-02-06 21:11:13 +01:00
Mustafa Alperen Seki
766c091c1c Make PrimaryBuilding conditional. 2018-02-06 21:11:11 +01:00
Mustafa Alperen Seki
645b181af7 Add GrantConditionOnFaction. 2018-02-06 21:10:59 +01:00
Muh-Muh
6864b2462f Add Capture as a possibility for completing the objectives on Soviet06 2018-02-06 12:06:00 +01:00
Muh-Muh
16e63a9487 Add myself to authors 2018-02-06 12:03:00 +01:00
Muh-Muh
3c87adbad5 Make only the right warfactory infiltratable. don't break the game if a player manages without infiltration. 2018-02-06 12:02:58 +01:00
netnazgul
cff7c01de0 Revise the map pool of RA mod 2018-02-04 16:40:14 +01:00
Paul Chote
eba1f86572 Preserve arguments passed into the windows launcher. 2018-02-04 16:17:26 +01:00
Paul Chote
4113ac0771 Don't create invalid targets (dead actors) in UOG. 2018-02-04 15:52:02 +01:00
Paul Chote
3c445ef029 Prevent injected unpause orders from restarting a finished game. 2018-02-04 15:37:06 +01:00
Paul Chote
a88687257e Fix missing PauseStateLocked check on "/pause" chat command. 2018-02-04 15:36:53 +01:00
Paul Chote
60749b250f Fix missing click sounds on observer stat hotkeys. 2018-02-03 19:30:40 +01:00
Paul Chote
5f60fba445 Use EffectiveOwner for actor tooltips. 2018-02-03 18:35:16 +01:00
Paul Chote
ae03c66059 Add support for disabling IIssueDeployOrders. 2018-01-28 18:24:23 +01:00
RoosterDragon
53950fa7f4 Fix order serialization issues. 2018-01-28 15:45:31 +01:00
Paul Chote
efb51c19bd Revert "Remove CreateGroup order as the ActorGroupProxy is gone." 2018-01-28 15:08:02 +01:00
Paul Chote
88526d661d Work around duplicated notification sound when the ingame menu is open. 2018-01-28 14:58:04 +01:00
Paul Chote
d7b117ca92 Remove AddChatLine registration on IngameChatLogic dispose. 2018-01-28 14:57:48 +01:00
Smittytron
52d93d4654 Dial back Atry V2 min range change 2018-01-21 10:53:56 +01:00
Mustafa Alperen Seki
afa6e5ee10 Fix Flamers sometimes explode with UnitExplode 2018-01-20 23:59:10 +01:00
AoAGeneral
f4dba7f9c7 Changes in TD:
APC vs none decreased from 35 to 30

APC vs wood decreased from 35 to 25

APC vs heavy decreased from 35 to 25

Obelisk HP Increased from 600 (60000) to 750 (75000)

APCs are doing to much damage vs structures and heavy armor. They are managing to kill Guard Towers, light tanks, and power plants to effectively in numbers between 5 and 10. This will help to prevent this from happening. A small decrease vs infantry allows rocket infantry to last just a little bit longer as well.

Obelisk is getting a HP increase. Going from the old notes and responses happening now leaves me to believe its HP is to small. In one testing example an obelisk getting airstriked leaves it with no HP left. 4 minigunner shots and then it dies. Increasing the HP allows an expensive structure to stand longer.
2018-01-16 23:46:23 +01:00
Paul Chote
2b18c57b41 Remove helicopter RTB from TD command bar deploy description. 2018-01-14 23:26:13 +01:00
Paul Chote
adacbb1b9d Remove ReturnToBase order feedback if no RearmBuildings are defined.
ReturnToBase requires RearmBuildings to function.
2018-01-14 23:26:10 +01:00
Mustafa Alperen Seki
e4ae5a64b3 Add PrimaryBuilding: to D2K Palaces 2018-01-13 17:57:32 +01:00
Mustafa Alperen Seki
8f29983bd5 Make ProduceActorPower care for PrimaryBuilding 2018-01-13 17:57:18 +01:00
Arular101
49fd2ed6da Cast to long to avoid overflow when multiplying by the health (part 2) 2018-01-13 17:33:21 +01:00
Arular101
c9f9a8923a Cast to long to avoid overflow when multiplying by the health 2018-01-13 17:33:10 +01:00
Oliver Brakmann
bab8d70cd0 Fix primary building flag not working properly in D2k 2018-01-13 17:26:23 +01:00
Alexis Hunt
ce5911f47a Correct UI for unslowed queues in low power.
Because of the way the tick logic works, 0 (or any negative number) for
LowPowerSlowdown is functionally equivalent to 1. But LowPowerSlowdown
is multipled by a time in several cases, so while 1 will produce the
correct result (no slowdown), 0 will say that the time remaining is
00:00. Forbid nonpositive values, and correct the d2k mod which was
using 0.

Additionally, in the production tooltip, the colour should display as
white even in low power if there is no slowdown.
2018-01-13 16:14:33 +01:00
Mustafa Alperen Seki
ee0f7bc98a Make /kill and /dispose use orders. 2018-01-13 15:55:20 +01:00
Mustafa Alperen Seki
77ddb2386a Fix building armors in D2k 2018-01-11 18:06:35 +01:00
C. Helmig
73413a4e20 Fix d2k walls being always visible through f.o.w. 2018-01-11 17:35:13 +01:00
Alexis Hunt
dad11df42b Do not speed D2K upgrades up based on buildings. 2018-01-11 12:01:18 +01:00
C. Helmig
2bf1640e2b Added primary building and "PRIMARY" text to high tech factory. 2018-01-09 22:41:09 +01:00
C. Helmig
405d59a8ea Add production bar to high tech factory. 2018-01-09 22:41:09 +01:00
Paul Chote
eadf381843 Refresh lobby server list when switching to the Servers tab. 2018-01-09 21:55:47 +01:00
Paul Chote
7e8b0fd288 Use https for web services. 2018-01-09 20:52:42 +01:00
Mustafa Alperen Seki
dfd37ea7b6 Make 1st missions use same way as 2nd to disable upgrades 2018-01-08 20:53:00 +01:00
Mustafa Alperen Seki
c19c922931 Disable upgrades in 2nd missions 2018-01-08 20:52:36 +01:00
Mustafa Alperen Seki
0d2f8013a6 Fix the crash on Har5 2018-01-07 23:18:41 +01:00
Paul Chote
5a6afcb01e Remove "Mission Aborted" notification.
This has been causing confusion for players who have already completed a mission.
2018-01-06 20:38:51 +00:00
RoosterDragon
327f417024 ScreenMap should TickRender, rather than just Tick.
This is as FrozenUnderFog.TickRender queues an update to the screen map. If this is not processed in the same tick, this results in screen bounds for the frozen actor being 1 tick behind. By making ScreenMap TickRender, it ensures changes from both Tick and TickRender traits are processed, rather than just Tick traits.
2018-01-06 15:21:49 +01:00
RoosterDragon
4207717749 Revert "Fix actors disappearing for a tick when swapping to the frozen actor."
This reverts commit 2eb090f153.

This fixes flicker for the actor->frozen transition, but then introduces the same flicker for the frozen->actor transition instead.
2018-01-06 15:21:37 +01:00
Paul Chote
6922f7653e Fix crash when the mouse-overed support power loses all instances. 2018-01-06 14:36:02 +01:00
Mustafa Alperen Seki
ec54eca7f4 Remove ^Tank default from RA mod 2018-01-06 12:10:42 +01:00
GSonderling
9d8fea871c AI will now defend MCV as if it was a harvester or a building.
Adjusted comments.
2018-01-04 22:20:01 +01:00
FrameLimiter
aa50ce266e Fixes STEK TargetTypes in mission allies-06a
The Soviet Tech Centers were automatically being targeted by units.
2018-01-04 08:23:37 +01:00
FrameLimiter
716dd78058 Fixes STEK TargetTypes in mission allies-06b
The Soviet Tech Center was automatically being targeted by units.
2018-01-04 08:23:37 +01:00
Smittytron
6a1972f8bb Reduce value of TD Civilian to 10 2018-01-03 11:03:03 +01:00
139 changed files with 6411 additions and 350 deletions

View File

@@ -101,6 +101,7 @@ Also thanks to:
* Michael Rätzel
* Michael Silber (frühstück)
* Michael Sztolcman (s1w_)
* Muh
* Mustafa Alperen Seki (MustaphaTR)
* Neil Shivkar (havok13888)
* Nooze

View File

@@ -24,7 +24,8 @@ namespace OpenRA
TargetString = 0x04,
Queued = 0x08,
ExtraLocation = 0x10,
ExtraData = 0x20
ExtraData = 0x20,
TargetIsCell = 0x40
}
static class OrderFieldsExts
@@ -45,6 +46,7 @@ namespace OpenRA
public CPos ExtraLocation;
public uint ExtraData;
public bool IsImmediate;
public bool SuppressVisualFeedback;
public Actor VisualFeedbackTarget;
@@ -60,7 +62,7 @@ namespace OpenRA
{
get
{
return Target.SerializableCell.HasValue ? Target.SerializableCell.Value : CPos.Zero;
return Target.SerializableCell ?? CPos.Zero;
}
}
@@ -68,7 +70,7 @@ namespace OpenRA
Order(string orderString, Actor subject, Target target, string targetString, bool queued, CPos extraLocation, uint extraData)
{
OrderString = orderString;
OrderString = orderString ?? "";
Subject = subject;
Target = target;
TargetString = targetString;
@@ -127,8 +129,18 @@ namespace OpenRA
case TargetType.Terrain:
{
if (world != null)
target = Target.FromCell(world, (CPos)r.ReadInt2());
if (flags.HasField(OrderFields.TargetIsCell))
{
var cell = new CPos(r.ReadInt32(), r.ReadInt32(), r.ReadByte());
var subCell = (SubCell)r.ReadInt32();
if (world != null)
target = Target.FromCell(world, cell, subCell);
}
else
{
var pos = new WPos(r.ReadInt32(), r.ReadInt32(), r.ReadInt32());
target = Target.FromPos(pos);
}
break;
}
@@ -137,7 +149,7 @@ namespace OpenRA
var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null;
var queued = flags.HasField(OrderFields.Queued);
var extraLocation = (CPos)(flags.HasField(OrderFields.ExtraLocation) ? r.ReadInt2() : int2.Zero);
var extraLocation = flags.HasField(OrderFields.ExtraLocation) ? new CPos(r.ReadInt32(), r.ReadInt32(), r.ReadByte()) : CPos.Zero;
var extraData = flags.HasField(OrderFields.ExtraData) ? r.ReadUInt32() : 0;
if (world == null)
@@ -235,10 +247,6 @@ namespace OpenRA
public Order(string orderString, Actor subject, Target target, bool queued)
: this(orderString, subject, target, null, queued, CPos.Zero, 0) { }
public Order(string orderstring, Order order)
: this(orderstring, order.Subject, order.Target,
order.TargetString, order.Queued, order.ExtraLocation, order.ExtraData) { }
public byte[] Serialize()
{
var minLength = OrderString.Length + 1 + (IsImmediate ? 1 + TargetString.Length + 1 : 6);
@@ -273,6 +281,9 @@ namespace OpenRA
if (ExtraData != 0)
fields |= OrderFields.ExtraData;
if (Target.SerializableCell != null)
fields |= OrderFields.TargetIsCell;
w.Write((byte)fields);
if (fields.HasField(OrderFields.Target))
@@ -288,8 +299,13 @@ namespace OpenRA
w.Write(Target.FrozenActor.ID);
break;
case TargetType.Terrain:
// SerializableCell is guaranteed to be non-null if Type == TargetType.Terrain
w.Write(Target.SerializableCell.Value);
if (fields.HasField(OrderFields.TargetIsCell))
{
w.Write(Target.SerializableCell.Value);
w.Write((int)Target.SerializableSubCell);
}
else
w.Write(Target.SerializablePos);
break;
}
}
@@ -310,7 +326,8 @@ namespace OpenRA
{
return ("OrderString: \"{0}\" \n\t Subject: \"{1}\". \n\t TargetActor: \"{2}\" \n\t TargetLocation: {3}." +
"\n\t TargetString: \"{4}\".\n\t IsImmediate: {5}.\n\t Player(PlayerName): {6}\n").F(
OrderString, Subject, TargetActor != null ? TargetActor.Info.Name : null, TargetLocation, TargetString, IsImmediate, Player != null ? Player.PlayerName : null);
OrderString, Subject, TargetActor != null ? TargetActor.Info.Name : null, TargetLocation,
TargetString, IsImmediate, Player != null ? Player.PlayerName : null);
}
}
}

View File

@@ -65,6 +65,14 @@ namespace OpenRA.Network
{
w.Write(cell.X);
w.Write(cell.Y);
w.Write(cell.Layer);
}
public static void Write(this BinaryWriter w, WPos pos)
{
w.Write(pos.X);
w.Write(pos.Y);
w.Write(pos.Z);
}
}
}

View File

@@ -125,6 +125,11 @@ namespace OpenRA.Network
if (client != null)
{
var pause = order.TargetString == "Pause";
// Prevent injected unpause orders from restarting a finished game
if (orderManager.World.PauseStateLocked && !pause)
break;
if (orderManager.World.Paused != pause && world != null && world.LobbyInfo.NonBotClients.Count() > 1)
{
var pausetext = "The game is {0} by {1}".F(pause ? "paused" : "un-paused", client.Name);

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Orders
static Target TargetForInput(World world, CPos cell, int2 worldPixel, MouseInput mi)
{
var actor = world.ScreenMap.ActorsAtMouse(mi)
.Where(a => a.Actor.Info.HasTraitInfo<ITargetableInfo>() && !world.FogObscures(a.Actor))
.Where(a => !a.Actor.IsDead && a.Actor.Info.HasTraitInfo<ITargetableInfo>() && !world.FogObscures(a.Actor))
.WithHighestSelectionPriority(worldPixel);
if (actor != null)
@@ -51,6 +51,13 @@ namespace OpenRA.Orders
if (!actorsInvolved.Any())
yield break;
// HACK: This is required by the hacky player actions-per-minute calculation
// TODO: Reimplement APM properly and then remove this
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false)
{
TargetString = actorsInvolved.Select(a => a.ActorID).JoinWith(",")
};
foreach (var o in orders)
yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift)));
}
@@ -81,7 +88,10 @@ namespace OpenRA.Orders
// Used for classic mouse orders, determines whether or not action at xy is move or select
public virtual bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi)
{
var actor = world.ScreenMap.ActorsAtMouse(xy).WithHighestSelectionPriority(xy);
var actor = world.ScreenMap.ActorsAtMouse(xy)
.Where(a => !a.Actor.IsDead)
.WithHighestSelectionPriority(xy);
if (actor == null)
return true;

View File

@@ -254,38 +254,33 @@ namespace OpenRA.Traits
void ITick.Tick(Actor self)
{
// Update visibility at the end of the tick to make sure that
// the fog/shroud state has been updated for the tick
self.World.AddFrameEndTask(w =>
UpdateDirtyFrozenActorsFromDirtyBins();
var frozenActorsToRemove = new List<FrozenActor>();
VisibilityHash = 0;
FrozenHash = 0;
foreach (var kvp in frozenActorsById)
{
UpdateDirtyFrozenActorsFromDirtyBins();
var id = kvp.Key;
var hash = (int)id;
FrozenHash += hash;
var frozenActorsToRemove = new List<FrozenActor>();
VisibilityHash = 0;
FrozenHash = 0;
var frozenActor = kvp.Value;
frozenActor.Tick();
if (dirtyFrozenActorIds.Contains(id))
frozenActor.UpdateVisibility();
foreach (var kvp in frozenActorsById)
{
var id = kvp.Key;
var hash = (int)id;
FrozenHash += hash;
if (frozenActor.Visible)
VisibilityHash += hash;
else if (frozenActor.Actor == null)
frozenActorsToRemove.Add(frozenActor);
}
var frozenActor = kvp.Value;
frozenActor.Tick();
if (dirtyFrozenActorIds.Contains(id))
frozenActor.UpdateVisibility();
dirtyFrozenActorIds.Clear();
if (frozenActor.Visible)
VisibilityHash += hash;
else if (frozenActor.Actor == null)
frozenActorsToRemove.Add(frozenActor);
}
dirtyFrozenActorIds.Clear();
foreach (var fa in frozenActorsToRemove)
Remove(fa);
});
foreach (var fa in frozenActorsToRemove)
Remove(fa);
}
void UpdateDirtyFrozenActorsFromDirtyBins()

View File

@@ -26,12 +26,19 @@ namespace OpenRA.Traits
FrozenActor frozen;
WPos pos;
CPos? cell;
SubCell? subCell;
int generation;
public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; }
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell)
{
return new Target { pos = w.Map.CenterOfSubCell(c, subCell), cell = c, type = TargetType.Terrain };
return new Target
{
pos = w.Map.CenterOfSubCell(c, subCell),
cell = c,
subCell = subCell,
type = TargetType.Terrain
};
}
/// <summary>
@@ -201,5 +208,7 @@ namespace OpenRA.Traits
internal TargetType SerializableType { get { return type; } }
internal Actor SerializableActor { get { return actor; } }
internal CPos? SerializableCell { get { return cell; } }
internal SubCell? SerializableSubCell { get { return subCell; } }
internal WPos SerializablePos { get { return pos; } }
}
}

View File

@@ -245,7 +245,7 @@ namespace OpenRA.Traits
return rect;
}
public void Tick()
public void TickRender()
{
foreach (var a in addOrUpdateActors)
{

View File

@@ -353,7 +353,6 @@ namespace OpenRA
ActorsWithTrait<ITick>().DoTimed(x => x.Trait.Tick(x.Actor), "Trait");
effects.DoTimed(e => e.Tick(this), "Effect");
ScreenMap.Tick();
}
while (frameEndActions.Count != 0)
@@ -364,6 +363,7 @@ namespace OpenRA
public void TickRender(WorldRenderer wr)
{
ActorsWithTrait<ITickRender>().DoTimed(x => x.Trait.TickRender(wr, x.Actor), "Render");
ScreenMap.TickRender();
}
public IEnumerable<Actor> Actors { get { return actors.Values; } }

View File

@@ -129,6 +129,8 @@ namespace OpenRA.Mods.Cnc.Traits
return new Order("Detonate", self, false);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return true; }
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
return info.Voice;

View File

@@ -86,6 +86,8 @@ namespace OpenRA.Mods.Cnc.Traits
return new Order("PlaceMine", self, Target.FromCell(self.World, self.Location), false);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return true; }
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "BeginMinefield")

View File

@@ -198,7 +198,8 @@ namespace OpenRA.Mods.Common.AI
if (sumOfMaxHp == 0)
return 0.0f;
return (sumOfHp * normalizeByValue) / sumOfMaxHp;
// Cast to long to avoid overflow when multiplying by the health
return (int)((long)sumOfHp * normalizeByValue / sumOfMaxHp);
}
static float RelativePower(IEnumerable<Actor> own, IEnumerable<Actor> enemy)

View File

@@ -1257,8 +1257,8 @@ namespace OpenRA.Mods.Common.AI
if (!e.Attacker.Info.HasTraitInfo<ITargetableInfo>())
return;
// Protected harvesters or building
if ((self.Info.HasTraitInfo<HarvesterInfo>() || self.Info.HasTraitInfo<BuildingInfo>()) &&
// Protected priority assets, MCVs, harvesters and buildings
if ((self.Info.HasTraitInfo<HarvesterInfo>() || self.Info.HasTraitInfo<BuildingInfo>() || self.Info.HasTraitInfo<BaseBuildingInfo>()) &&
Player.Stances[e.Attacker.Owner] == Stance.Enemy)
{
defenseCenter = e.Attacker.Location;

View File

@@ -160,7 +160,12 @@ namespace OpenRA.Mods.Common.AI
case DecisionMetric.Health:
var health = a.TraitOrDefault<Health>();
return (health != null) ? (health.HP / health.MaxHP) * Attractiveness : 0;
if (health == null)
return 0;
// Cast to long to avoid overflow when multiplying by the health
return (int)((long)health.HP * Attractiveness / health.MaxHP);
default:
return Attractiveness;

View File

@@ -58,7 +58,9 @@ namespace OpenRA.Mods.Common.Activities
return;
var capturesInfo = activeCaptures.Info;
var lowEnoughHealth = health.HP <= capturable.Info.CaptureThreshold * health.MaxHP / 100;
// Cast to long to avoid overflow when multiplying by the health
var lowEnoughHealth = health.HP <= (int)(capturable.Info.CaptureThreshold * (long)health.MaxHP / 100);
if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant)
{
var oldOwner = actor.Owner;
@@ -80,7 +82,8 @@ namespace OpenRA.Mods.Common.Activities
}
else
{
var damage = health.MaxHP * capturesInfo.SabotageHPRemoval / 100;
// Cast to long to avoid overflow when multiplying by the health
var damage = (int)((long)health.MaxHP * capturesInfo.SabotageHPRemoval / 100);
actor.InflictDamage(self, new Damage(damage));
}

View File

@@ -98,7 +98,9 @@ namespace OpenRA.Mods.Common.Activities
{
var unitCost = self.Info.TraitInfo<ValuedInfo>().Cost;
var hpToRepair = repairsUnits.Info.HpPerStep;
var cost = Math.Max(1, (hpToRepair * unitCost * repairsUnits.Info.ValuePercentage) / (health.MaxHP * 100));
// Cast to long to avoid overflow when multiplying by the health
var cost = Math.Max(1, (int)(((long)hpToRepair * unitCost * repairsUnits.Info.ValuePercentage) / (health.MaxHP * 100L)));
if (!played)
{

View File

@@ -34,9 +34,12 @@ namespace OpenRA.Mods.Common.Activities
public override Activity Tick(Actor self)
{
var cost = self.GetSellValue();
var sellValue = self.GetSellValue();
var refund = (cost * sellableInfo.RefundPercent * (health == null ? 1 : health.HP)) / (100 * (health == null ? 1 : health.MaxHP));
// Cast to long to avoid overflow when multiplying by the health
var hp = health != null ? (long)health.HP : 1L;
var maxHP = health != null ? (long)health.MaxHP : 1L;
var refund = (int)((sellValue * sellableInfo.RefundPercent * hp) / (100 * maxHP));
playerResources.GiveCash(refund);
foreach (var ns in self.TraitsImplementing<INotifySold>())

View File

@@ -122,7 +122,8 @@ namespace OpenRA.Mods.Common.Activities
var health = self.TraitOrDefault<Health>();
if (health != null)
{
var newHP = ForceHealthPercentage > 0 ? ForceHealthPercentage : (health.HP * 100) / health.MaxHP;
// Cast to long to avoid overflow when multiplying by the health
var newHP = ForceHealthPercentage > 0 ? ForceHealthPercentage : (int)(health.HP * 100L / health.MaxHP);
init.Add(new HealthInit(newHP));
}

View File

@@ -134,20 +134,12 @@ namespace OpenRA.Mods.Common.Commands
break;
case "kill":
var args = arg.Split(' ');
var damageTypes = new HashSet<string>();
foreach (var damageType in args)
damageTypes.Add(damageType);
foreach (var actor in world.Selection.Actors)
{
if (actor.IsDead)
continue;
var health = actor.TraitOrDefault<Health>();
if (health != null)
health.InflictDamage(actor, actor, new Damage(health.HP, damageTypes), true);
world.IssueOrder(new Order("DevKill", world.LocalPlayer.PlayerActor, Target.FromActor(actor), false) { TargetString = arg });
}
break;
@@ -158,7 +150,7 @@ namespace OpenRA.Mods.Common.Commands
if (actor.Disposed)
continue;
actor.Dispose();
world.IssueOrder(new Order("DevDispose", world.LocalPlayer.PlayerActor, Target.FromActor(actor), false));
}
break;

View File

@@ -39,10 +39,7 @@ namespace OpenRA.Mods.Common.Commands
{
case "pause":
if (Game.IsHost || (world.LocalPlayer != null && world.LocalPlayer.WinState != WinState.Lost))
world.IssueOrder(new Order("PauseGame", null, false)
{
TargetString = world.Paused ? "UnPause" : "Pause"
});
world.SetPauseState(!world.Paused);
break;
case "surrender":

View File

@@ -511,6 +511,7 @@
<Compile Include="Traits\Conditions\ProximityExternalCondition.cs" />
<Compile Include="Traits\Conditions\GrantConditionOnAttack.cs" />
<Compile Include="Traits\Conditions\GrantConditionOnDamageState.cs" />
<Compile Include="Traits\Conditions\GrantConditionOnFaction.cs" />
<Compile Include="Traits\Conditions\GrantConditionOnTerrain.cs" />
<Compile Include="Traits\Conditions\GrantConditionOnMovement.cs" />
<Compile Include="Traits\Conditions\GrantConditionOnPrerequisite.cs" />

View File

@@ -663,9 +663,14 @@ namespace OpenRA.Mods.Common.Traits
Order IIssueDeployOrder.IssueDeployOrder(Actor self)
{
if (!Info.RearmBuildings.Any())
return null;
return new Order("ReturnToBase", self, false);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return Info.RearmBuildings.Any(); }
public string VoicePhraseForOrder(Actor self, Order order)
{
if (!Info.MoveIntoShroud && !self.Owner.Shroud.IsExplored(order.TargetLocation))
@@ -675,9 +680,10 @@ namespace OpenRA.Mods.Common.Traits
{
case "Move":
case "Enter":
case "ReturnToBase":
case "Stop":
return Info.Voice;
case "ReturnToBase":
return Info.RearmBuildings.Any() ? Info.Voice : null;
default: return null;
}
}
@@ -764,7 +770,7 @@ namespace OpenRA.Mods.Common.Traits
self.QueueActivity(new HeliLand(self, true));
}
}
else if (order.OrderString == "ReturnToBase")
else if (order.OrderString == "ReturnToBase" && Info.RearmBuildings.Any())
{
UnReserve();
self.CancelActivity();

View File

@@ -58,6 +58,8 @@ namespace OpenRA.Mods.Common.Traits
return new Order("Detonate", self, false);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return true; }
public string VoicePhraseForOrder(Actor self, Order order)
{
return info.Voice;

View File

@@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits
}
[Desc("Used together with ClassicProductionQueue.")]
public class PrimaryBuildingInfo : ITraitInfo
public class PrimaryBuildingInfo : ConditionalTraitInfo
{
[GrantedConditionReference]
[Desc("The condition to grant to self while this is the primary building.")]
@@ -35,23 +35,24 @@ namespace OpenRA.Mods.Common.Traits
[Desc("The speech notification to play when selecting a primary building.")]
public readonly string SelectionNotification = "PrimaryBuildingSelected";
public object Create(ActorInitializer init) { return new PrimaryBuilding(init.Self, this); }
[Desc("List of production queues for which the primary flag should be set.",
"If empty, the list given in the `Produces` property of the `Production` trait will be used.")]
public readonly string[] ProductionQueues = { };
public override object Create(ActorInitializer init) { return new PrimaryBuilding(init.Self, this); }
}
public class PrimaryBuilding : INotifyCreated, IIssueOrder, IResolveOrder
public class PrimaryBuilding : ConditionalTrait<PrimaryBuildingInfo>, INotifyCreated, IIssueOrder, IResolveOrder
{
const string OrderID = "PrimaryProducer";
readonly PrimaryBuildingInfo info;
ConditionManager conditionManager;
int primaryToken = ConditionManager.InvalidConditionToken;
public bool IsPrimary { get; private set; }
public PrimaryBuilding(Actor self, PrimaryBuildingInfo info)
{
this.info = info;
}
: base(info) { }
void INotifyCreated.Created(Actor self)
{
@@ -60,7 +61,13 @@ namespace OpenRA.Mods.Common.Traits
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get { yield return new DeployOrderTargeter(OrderID, 1); }
get
{
if (IsTraitDisabled)
yield break;
yield return new DeployOrderTargeter(OrderID, 1);
}
}
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
@@ -86,7 +93,8 @@ namespace OpenRA.Mods.Common.Traits
{
// Cancel existing primaries
// TODO: THIS IS SHIT
foreach (var p in self.Info.TraitInfo<ProductionInfo>().Produces)
var queues = Info.ProductionQueues.Length == 0 ? self.Info.TraitInfos<ProductionInfo>().SelectMany(pi => pi.Produces) : Info.ProductionQueues;
foreach (var q in queues)
{
foreach (var b in self.World
.ActorsWithTrait<PrimaryBuilding>()
@@ -94,17 +102,25 @@ namespace OpenRA.Mods.Common.Traits
a.Actor != self &&
a.Actor.Owner == self.Owner &&
a.Trait.IsPrimary &&
a.Actor.Info.TraitInfo<ProductionInfo>().Produces.Contains(p)))
a.Actor.Info.TraitInfos<ProductionInfo>().Any(pi => pi.Produces.Contains(q))))
b.Trait.SetPrimaryProducer(b.Actor, false);
}
if (conditionManager != null && primaryToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(info.PrimaryCondition))
primaryToken = conditionManager.GrantCondition(self, info.PrimaryCondition);
if (conditionManager != null && primaryToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(Info.PrimaryCondition))
primaryToken = conditionManager.GrantCondition(self, Info.PrimaryCondition);
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.SelectionNotification, self.Owner.Faction.InternalName);
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.SelectionNotification, self.Owner.Faction.InternalName);
}
else if (primaryToken != ConditionManager.InvalidConditionToken)
primaryToken = conditionManager.RevokeCondition(self, primaryToken);
}
protected override void TraitEnabled(Actor self) { }
protected override void TraitDisabled(Actor self)
{
if (IsPrimary)
SetPrimaryProducer(self, !IsPrimary);
}
}
}

View File

@@ -115,7 +115,9 @@ namespace OpenRA.Mods.Common.Traits
// The cost is the same regardless of the amount of people repairing
var hpToRepair = Math.Min(Info.RepairStep, health.MaxHP - health.HP);
var cost = Math.Max(1, (hpToRepair * Info.RepairPercent * buildingValue) / (health.MaxHP * 100));
// Cast to long to avoid overflow when multiplying by the health
var cost = Math.Max(1, (int)(((long)hpToRepair * Info.RepairPercent * buildingValue) / (health.MaxHP * 100L)));
// TakeCash will return false if the player can't pay, and will stop him from contributing this Tick
var activePlayers = Repairers.Count(player => player.PlayerActor.Trait<PlayerResources>().TakeCash(cost, true));

View File

@@ -109,7 +109,9 @@ namespace OpenRA.Mods.Common.Traits
}
var health = target.Trait<Health>();
var lowEnoughHealth = health.HP <= c.Info.CaptureThreshold * health.MaxHP / 100;
// Cast to long to avoid overflow when multiplying by the health
var lowEnoughHealth = health.HP <= (int)(c.Info.CaptureThreshold * (long)health.MaxHP / 100);
cursor = !capturesInfo.Sabotage || lowEnoughHealth || target.Owner.NonCombatant
? capturesInfo.EnterCursor : capturesInfo.SabotageCursor;
@@ -129,7 +131,9 @@ namespace OpenRA.Mods.Common.Traits
}
var health = target.Info.TraitInfoOrDefault<HealthInfo>();
var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP / 100;
// Cast to long to avoid overflow when multiplying by the health
var lowEnoughHealth = target.HP <= (int)(c.CaptureThreshold * (long)health.HP / 100);
cursor = !capturesInfo.Sabotage || lowEnoughHealth || target.Owner.NonCombatant
? capturesInfo.EnterCursor : capturesInfo.SabotageCursor;

View File

@@ -178,6 +178,8 @@ namespace OpenRA.Mods.Common.Traits
return new Order("Unload", self, false);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return true; }
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Unload")

View File

@@ -138,6 +138,8 @@ namespace OpenRA.Mods.Common.Traits
return new Order("GrantConditionOnDeploy", self, false);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return true; }
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString != "GrantConditionOnDeploy" || deployState == DeployState.Deploying || deployState == DeployState.Undeploying)

View File

@@ -0,0 +1,79 @@
#region Copyright & License Information
/*
* Copyright 2007-2018 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 System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Grants a condition while the trait is active.")]
class GrantConditionOnFactionInfo : ConditionalTraitInfo
{
[FieldLoader.Require]
[GrantedConditionReference]
[Desc("Condition to grant.")]
public readonly string Condition = null;
[Desc("Only grant this condition for certain factions.")]
public readonly HashSet<string> Factions = new HashSet<string>();
[Desc("Should it recheck everything when it is captured?")]
public readonly bool ResetOnOwnerChange = false;
public override object Create(ActorInitializer init) { return new GrantConditionOnFaction(init, this); }
}
class GrantConditionOnFaction : ConditionalTrait<GrantConditionOnFactionInfo>, INotifyOwnerChanged
{
ConditionManager conditionManager;
int conditionToken = ConditionManager.InvalidConditionToken;
string faction;
public GrantConditionOnFaction(ActorInitializer init, GrantConditionOnFactionInfo info)
: base(info)
{
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
}
protected override void Created(Actor self)
{
conditionManager = self.Trait<ConditionManager>();
base.Created(self);
}
public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)
{
if (Info.ResetOnOwnerChange && faction != newOwner.Faction.InternalName)
{
faction = newOwner.Faction.InternalName;
TraitDisabled(self);
TraitEnabled(self);
}
}
protected override void TraitEnabled(Actor self)
{
if (conditionToken == ConditionManager.InvalidConditionToken && Info.Factions.Contains(faction))
conditionToken = conditionManager.GrantCondition(self, Info.Condition);
}
protected override void TraitDisabled(Actor self)
{
if (conditionToken == ConditionManager.InvalidConditionToken)
return;
conditionToken = conditionManager.RevokeCondition(self, conditionToken);
}
}
}

View File

@@ -69,7 +69,8 @@ namespace OpenRA.Mods.Common.Traits
if (totalTiles == 0)
return;
damageThreshold = (Info.DamageThreshold * health.MaxHP + (100 - Info.DamageThreshold) * safeTiles * health.MaxHP / totalTiles) / 100;
// Cast to long to avoid overflow when multiplying by the health
damageThreshold = (int)((Info.DamageThreshold * (long)health.MaxHP + (100 - Info.DamageThreshold) * safeTiles * (long)health.MaxHP / totalTiles) / 100);
// Actors start with maximum damage applied
var delta = health.HP - damageThreshold;

View File

@@ -60,8 +60,9 @@ namespace OpenRA.Mods.Common.Traits
var dudesValue = info.ValuePercent * cost / 100;
if (health != null)
{
if (100 * health.HP >= info.MinHpPercent * health.MaxHP)
dudesValue = health.HP * dudesValue / health.MaxHP;
// Cast to long to avoid overflow when multiplying by the health
if (100L * health.HP >= info.MinHpPercent * (long)health.MaxHP)
dudesValue = (int)((long)health.HP * dudesValue / health.MaxHP);
else
dudesValue = 0;
}

View File

@@ -134,7 +134,8 @@ namespace OpenRA.Mods.Common.Traits
if (Info.DamageThreshold == 0)
return;
if (health.HP * 100 < Info.DamageThreshold * health.MaxHP)
// Cast to long to avoid overflow when multiplying by the health
if (health.HP * 100L < Info.DamageThreshold * (long)health.MaxHP)
self.World.AddFrameEndTask(w => self.Kill(e.Attacker));
}
}

View File

@@ -43,7 +43,8 @@ namespace OpenRA.Mods.Common.Traits
Info = info;
MaxHP = info.HP > 0 ? info.HP : 1;
hp = init.Contains<HealthInit>() ? init.Get<HealthInit, int>() * MaxHP / 100 : MaxHP;
// Cast to long to avoid overflow when multiplying by the health
hp = init.Contains<HealthInit>() ? (int)(init.Get<HealthInit, int>() * (long)MaxHP / 100) : MaxHP;
DisplayHP = hp;
}

View File

@@ -213,6 +213,34 @@ namespace OpenRA.Mods.Common.Traits
break;
}
case "DevKill":
{
if (order.Target.Type != TargetType.Actor)
break;
var actor = order.Target.Actor;
var health = actor.TraitOrDefault<Health>();
var args = order.TargetString.Split(' ');
var damageTypes = new HashSet<string>();
foreach (var damageType in args)
damageTypes.Add(damageType);
if (health != null)
health.InflictDamage(actor, actor, new Damage(health.HP, damageTypes), true);
break;
}
case "DevDispose":
{
if (order.Target.Type != TargetType.Actor)
break;
order.Target.Actor.Dispose();
break;
}
default:
return;
}

View File

@@ -63,6 +63,11 @@ namespace OpenRA.Mods.Common.Traits
public readonly string CancelledAudio = "Cancelled";
public virtual object Create(ActorInitializer init) { return new ProductionQueue(init, init.Self.Owner.PlayerActor, this); }
public void RulesetLoaded(Ruleset rules, ActorInfo ai) {
if (LowPowerSlowdown <= 0)
throw new YamlException("Production queue must have LowPowerSlowdown of at least 1.");
}
}
public class ProductionQueue : IResolveOrder, ITick, ITechTreeElement, INotifyOwnerChanged, INotifyKilled, INotifySold, ISync, INotifyTransform, INotifyCreated

View File

@@ -32,7 +32,8 @@ namespace OpenRA.Mods.Common.Traits
int IPowerModifier.GetPowerModifier()
{
return 100 * health.HP / health.MaxHP;
// Cast to long to avoid overflow when multiplying by the health
return (int)(100L * health.HP / health.MaxHP);
}
void INotifyDamage.Damaged(Actor self, AttackInfo e) { power.UpdateActor(self); }

View File

@@ -16,43 +16,45 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("Display the time remaining until the super weapon attached to the actor is ready.")]
class SupportPowerChargeBarInfo : ITraitInfo
class SupportPowerChargeBarInfo : ConditionalTraitInfo
{
[Desc("Defines to which players the bar is to be shown.")]
public readonly Stance DisplayStances = Stance.Ally;
public readonly Color Color = Color.Magenta;
public object Create(ActorInitializer init) { return new SupportPowerChargeBar(init.Self, this); }
public override object Create(ActorInitializer init) { return new SupportPowerChargeBar(init.Self, this); }
}
class SupportPowerChargeBar : ISelectionBar, INotifyOwnerChanged
class SupportPowerChargeBar : ConditionalTrait<SupportPowerChargeBarInfo>, ISelectionBar, INotifyOwnerChanged
{
readonly Actor self;
readonly SupportPowerChargeBarInfo info;
SupportPowerManager spm;
public SupportPowerChargeBar(Actor self, SupportPowerChargeBarInfo info)
: base(info)
{
this.self = self;
this.info = info;
spm = self.Owner.PlayerActor.Trait<SupportPowerManager>();
}
float ISelectionBar.GetValue()
{
if (IsTraitDisabled)
return 0;
var power = spm.GetPowersForActor(self).FirstOrDefault(sp => !sp.Disabled);
if (power == null)
return 0;
var viewer = self.World.RenderPlayer ?? self.World.LocalPlayer;
if (viewer != null && !info.DisplayStances.HasStance(self.Owner.Stances[viewer]))
if (viewer != null && !Info.DisplayStances.HasStance(self.Owner.Stances[viewer]))
return 0;
return 1 - (float)power.RemainingTime / power.TotalTime;
}
Color ISelectionBar.GetColor() { return info.Color; }
Color ISelectionBar.GetColor() { return Info.Color; }
bool ISelectionBar.DisplayWhenEmpty { get { return false; } }
void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)

View File

@@ -55,7 +55,8 @@ namespace OpenRA.Mods.Common.Traits
if (self.IsDead || IsTraitDisabled)
return;
if (health.HP >= Info.HealIfBelow * health.MaxHP / 100)
// Cast to long to avoid overflow when multiplying by the health
if (health.HP >= Info.HealIfBelow * (long)health.MaxHP / 100)
return;
if (damageTicks > 0)
@@ -67,7 +68,9 @@ namespace OpenRA.Mods.Common.Traits
if (--ticks <= 0)
{
ticks = Info.Delay;
self.InflictDamage(self, new Damage(-(Info.Step + Info.PercentageStep * health.MaxHP / 100), Info.DamageTypes));
// Cast to long to avoid overflow when multiplying by the health
self.InflictDamage(self, new Damage((int)(-(Info.Step + Info.PercentageStep * (long)health.MaxHP / 100)), Info.DamageTypes));
}
}

View File

@@ -94,14 +94,14 @@ namespace OpenRA.Mods.Common.Traits
{
get
{
var sellValue = self.GetSellValue() * info.RefundPercent / 100;
if (health.Value != null)
{
sellValue *= health.Value.HP;
sellValue /= health.Value.MaxHP;
}
var sellValue = self.GetSellValue();
return "Refund: $" + sellValue;
// Cast to long to avoid overflow when multiplying by the health
var hp = health != null ? (long)health.Value.HP : 1L;
var maxHP = health != null ? (long)health.Value.MaxHP : 1L;
var refund = (int)((sellValue * info.RefundPercent * hp) / (100 * maxHP));
return "Refund: $" + refund;
}
}
}

View File

@@ -57,14 +57,18 @@ namespace OpenRA.Mods.Common.Traits
base.Activate(self, order, manager);
var info = Info as ProduceActorPowerInfo;
var sp = self.TraitsImplementing<Production>()
.FirstOrDefault(p => p.Info.Produces.Contains(info.Type));
var producers = self.World.ActorsWithTrait<Production>()
.Where(x => x.Actor.Owner == self.Owner
&& !x.Trait.IsTraitDisabled
&& x.Trait.Info.Produces.Contains(info.Type))
.OrderByDescending(x => x.Actor.IsPrimaryBuilding())
.ThenByDescending(x => x.Actor.ActorID);
// TODO: The power should not reset if the production fails.
// Fixing this will require a larger rework of the support power code
var activated = false;
if (sp != null)
foreach (var p in producers)
{
foreach (var name in info.Actors)
{
@@ -75,8 +79,11 @@ namespace OpenRA.Mods.Common.Traits
new FactionInit(BuildableInfo.GetInitialFaction(ai, faction))
};
activated |= sp.Produce(self, ai, info.Type, inits);
activated |= p.Trait.Produce(p.Actor, ai, info.Type, inits);
}
if (activated)
break;
}
if (activated)

View File

@@ -124,7 +124,8 @@ namespace OpenRA.Mods.Common.Traits
return NoInstances;
return a.TraitsImplementing<SupportPower>()
.Select(t => Powers[MakeKey(t)]);
.Select(t => Powers[MakeKey(t)])
.Where(p => p.Instances.Any(i => !i.IsTraitDisabled && i.Self == a));
}
public void PrerequisitesAvailable(string key)
@@ -232,7 +233,7 @@ namespace OpenRA.Mods.Common.Traits
if (!Ready)
return;
var power = Instances.Where(i => !i.IsTraitPaused)
var power = Instances.Where(i => !i.IsTraitPaused && !i.IsTraitDisabled)
.MinByOrDefault(a =>
{
if (a.Self.OccupiesSpace == null)

View File

@@ -77,7 +77,14 @@ namespace OpenRA.Mods.Common.Traits
readonly TooltipInfo info;
public ITooltipInfo TooltipInfo { get { return info; } }
public Player Owner { get { return self.Owner; } }
public Player Owner
{
get
{
return self.EffectiveOwner != null ? self.EffectiveOwner.Owner : self.Owner;
}
}
public Tooltip(Actor self, TooltipInfo info)
: base(info)

View File

@@ -105,6 +105,8 @@ namespace OpenRA.Mods.Common.Traits
return new Order("DeployTransform", self, false);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return !IsTraitPaused && !IsTraitDisabled; }
public void DeployTransform(bool queued)
{
if (!queued && !CanDeploy())

View File

@@ -323,6 +323,7 @@ namespace OpenRA.Mods.Common.Traits
public interface IIssueDeployOrder
{
Order IssueDeployOrder(Actor self);
bool CanIssueDeployOrder(Actor self);
}
public enum ActorPreviewType { PlaceBuilding, ColorPicker, MapEditorSidebar }

View File

@@ -19,11 +19,11 @@ namespace OpenRA.Mods.Common
public class WebServices : IGlobalModData
{
public readonly string ServerList = "http://master.openra.net/games";
public readonly string ServerAdvertise = "http://master.openra.net/ping";
public readonly string MapRepository = "http://resource.openra.net/map/";
public readonly string GameNews = "http://master.openra.net/gamenews";
public readonly string VersionCheck = "http://master.openra.net/versioncheck";
public readonly string ServerList = "https://master.openra.net/games";
public readonly string ServerAdvertise = "https://master.openra.net/ping";
public readonly string MapRepository = "https://resource.openra.net/map/";
public readonly string GameNews = "https://master.openra.net/gamenews";
public readonly string VersionCheck = "https://master.openra.net/versioncheck";
public ModVersionStatus ModVersionStatus { get; private set; }
const int VersionCheckProtocol = 1;

View File

@@ -155,7 +155,7 @@ namespace OpenRA.Mods.Common.Widgets
{
BindButtonIcon(deployButton);
deployButton.IsDisabled = () => { UpdateStateIfNecessary(); return !selectedDeploys.Any(pair => pair.Trait.IsTraitEnabled()); };
deployButton.IsDisabled = () => { UpdateStateIfNecessary(); return !selectedDeploys.Any(pair => pair.Trait.CanIssueDeployOrder(pair.Actor)); };
deployButton.IsHighlighted = () => deployHighlighted > 0;
deployButton.OnClick = () =>
{
@@ -307,8 +307,9 @@ namespace OpenRA.Mods.Common.Widgets
UpdateStateIfNecessary();
var orders = selectedDeploys
.Where(pair => pair.Trait.IsTraitEnabled())
.Where(pair => pair.Trait.CanIssueDeployOrder(pair.Actor))
.Select(d => d.Trait.IssueDeployOrder(d.Actor))
.Where(d => d != null)
.ToArray();
foreach (var o in orders)

View File

@@ -145,7 +145,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
AddChatLine(chatLine.Color, chatLine.Name, chatLine.Text, true);
orderManager.AddChatLine += AddChatLineWrapper;
Game.BeforeGameStart += UnregisterEvents;
chatText.IsDisabled = () => world.IsReplay && !Game.Settings.Debug.EnableDebugCommandsInReplays;
@@ -177,12 +176,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return true;
}
void UnregisterEvents()
{
orderManager.AddChatLine -= AddChatLineWrapper;
Game.BeforeGameStart -= UnregisterEvents;
}
public void OpenChat()
{
chatText.Text = "";
@@ -203,14 +196,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public void AddChatLineWrapper(Color c, string from, string text)
{
AddChatLine(c, from, text, false);
}
void AddChatLine(Color c, string from, string text, bool replayCache)
{
if (!replayCache && chatOverlayDisplay != null)
if (chatOverlayDisplay != null)
chatOverlayDisplay.AddLine(c, from, text);
// HACK: Force disable the chat notification sound for the in-menu chat dialog
// This works around our inability to disable the sounds for the in-game dialog when it is hidden
AddChatLine(c, from, text, chatOverlay == null);
}
void AddChatLine(Color c, string from, string text, bool suppressSound)
{
var template = chatTemplate.Clone();
var nameLabel = template.Get<LabelWidget>("NAME");
var textLabel = template.Get<LabelWidget>("TEXT");
@@ -243,8 +238,20 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (scrolledToBottom)
chatScrollPanel.ScrollToBottom(smooth: true);
if (!replayCache)
if (!suppressSound)
Game.Sound.PlayNotification(modRules, null, "Sounds", "ChatLine", null);
}
bool disposed = false;
protected override void Dispose(bool disposing)
{
if (!disposed)
{
orderManager.AddChatLine -= AddChatLineWrapper;
disposed = true;
}
base.Dispose(disposing);
}
}
}

View File

@@ -107,6 +107,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
if (statsHotkeys[i].IsActivatedBy(e))
{
Game.Sound.PlayNotification(modData.DefaultRules, null, "Sounds", "ClickSound", null);
OpenMenuPanel(stats, new WidgetArgs() { { "activePanel", i } });
return true;
}

View File

@@ -128,6 +128,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
if (statsHotkeys[i].IsActivatedBy(e))
{
Game.Sound.PlayNotification(modData.DefaultRules, null, "Sounds", "ClickSound", null);
statsDropDownOptions[i].OnClick();
return true;
}

View File

@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var timeMultiplier = pm.PowerState != PowerState.Normal ? tooltipIcon.ProductionQueue.Info.LowPowerSlowdown : 1;
timeLabel.Text = formatBuildTime.Update(buildTime * timeMultiplier);
timeLabel.TextColor = pm.PowerState != PowerState.Normal ? Color.Red : Color.White;
timeLabel.TextColor = (pm.PowerState != PowerState.Normal && tooltipIcon.ProductionQueue.Info.LowPowerSlowdown > 1) ? Color.Red : Color.White;
var timeSize = font.Measure(timeLabel.Text);
costLabel.Text = cost.ToString();
@@ -143,4 +143,4 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return a;
}
}
}
}

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
tooltipContainer.BeforeRender = () =>
{
var icon = palette.TooltipIcon;
if (icon == null)
if (icon == null || icon.Power == null || icon.Power.Instances.Count == 0)
return;
var sp = icon.Power;

View File

@@ -298,6 +298,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
});
musicBin.IsVisible = () => panel == PanelType.Music;
ServerListLogic serverListLogic = null;
if (!skirmishMode)
{
Action<GameServer> doNothingWithServer = _ => { };
@@ -307,6 +308,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ "onJoin", doNothingWithServer },
});
serverListLogic = serversBin.LogicObjects.Select(l => l as ServerListLogic).FirstOrDefault(l => l != null);
serversBin.IsVisible = () => panel == PanelType.Servers;
}
@@ -334,7 +336,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
serversTab.IsHighlighted = () => panel == PanelType.Servers;
serversTab.IsDisabled = () => panel == PanelType.Kick || panel == PanelType.ForceStart;
serversTab.OnClick = () => panel = PanelType.Servers;
serversTab.OnClick = () =>
{
// Refresh the list when switching to the servers tab
if (serverListLogic != null && panel != PanelType.Servers)
serverListLogic.RefreshServerList();
panel = PanelType.Servers;
};
}
// Force start panel

View File

@@ -306,7 +306,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
game.Spectators > 0 ? ", {0} Spectator{1}".F(game.Spectators, game.Spectators != 1 ? "s" : "") : "");
}
void RefreshServerList()
public void RefreshServerList()
{
// Query in progress
if (currentQuery != null)

View File

@@ -84,7 +84,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands
{ 360, Pair.New("light_inf", "Harkonnen") },
{ 361, Pair.New("trooper", "Harkonnen") },
{ 362, Pair.New("fremen", "Harkonnen") },
{ 363, Pair.New("sardaukar", "Harkonnen") },
{ 363, Pair.New("mpsardaukar", "Harkonnen") },
{ 364, Pair.New("engineer", "Harkonnen") },
{ 365, Pair.New("harvester", "Harkonnen") },
{ 366, Pair.New("mcv", "Harkonnen") },

View File

@@ -18,51 +18,57 @@ namespace OpenRA.Test
[TestFixture]
public class OrderTest
{
Order order;
Order targetInvalid;
Order immediateOrder;
[SetUp]
public void SetUp()
byte[] RoundTripOrder(byte[] bytes)
{
order = new Order("TestOrder", null, false)
{
TargetString = "TestTarget",
ExtraData = 1234,
ExtraLocation = new CPos(555, 555)
};
return Order.Deserialize(null, new BinaryReader(new MemoryStream(bytes))).Serialize();
}
targetInvalid = new Order("TestOrder", null, Target.Invalid, false);
[TestCase(TestName = "Order data persists over serialization (empty)")]
public void SerializeEmpty()
{
var o = new Order().Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
immediateOrder = new Order("TestOrderImmediate", null, false)
[TestCase(TestName = "Order data persists over serialization (unqueued)")]
public void SerializeUnqueued()
{
var o = new Order("Test", null, false).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (queued)")]
public void SerializeQueued()
{
var o = new Order("Test", null, true).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (pos target)")]
public void SerializePos()
{
var o = new Order("Test", null, Target.FromPos(new WPos(int.MinValue, 0, int.MaxValue)), false).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (invalid target)")]
public void SerializeInvalid()
{
var o = new Order("Test", null, Target.Invalid, false).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (extra fields)")]
public void SerializeExtra()
{
var o = new Order("Test", null, Target.Invalid, true)
{
TargetString = "TargetString",
ExtraLocation = new CPos(int.MinValue, int.MaxValue, 128),
ExtraData = uint.MaxValue,
IsImmediate = true,
TargetString = "TestTarget"
};
}
Order RoundTripOrder(Order o)
{
var serializedData = new MemoryStream(o.Serialize());
return Order.Deserialize(null, new BinaryReader(serializedData));
}
[TestCase(TestName = "Data persists over serialization")]
public void SerializeA()
{
Assert.That(RoundTripOrder(order).ToString(), Is.EqualTo(order.ToString()));
}
[TestCase(TestName = "Data persists over serialization (Immediate order)")]
public void SerializeB()
{
Assert.That(RoundTripOrder(immediateOrder).ToString(), Is.EqualTo(immediateOrder.ToString()));
}
[TestCase(TestName = "Data persists over serialization (Invalid target)")]
public void SerializeC()
{
Assert.That(RoundTripOrder(targetInvalid).ToString(), Is.EqualTo(targetInvalid.ToString()));
}.Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
}
}

View File

@@ -388,7 +388,7 @@ Container@PLAYER_WIDGETS:
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Deploy
TooltipDesc: Selected units will perform their default deploy activity\n - MCVs will unpack into a Construction Yard\n - Construction Yards will re-pack into a MCV\n - Transports will unload their passengers\n - Helicopters will return to base\n\nActs immediately on selected units.
TooltipDesc: Selected units will perform their default deploy activity\n - MCVs will unpack into a Construction Yard\n - Construction Yards will re-pack into a MCV\n - Transports will unload their passengers\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
Children:
Image@ICON:

View File

@@ -441,7 +441,7 @@
^CivInfantry:
Inherits: ^Infantry
Valued:
Cost: 70
Cost: 10
Tooltip:
Name: Civilian
GenericVisibility: None

View File

@@ -840,7 +840,7 @@ OBLI:
DecorationBounds: 22,44,0,-10
SelectionDecorations:
Health:
HP: 60000
HP: 75000
Armor:
Type: Heavy
RevealsShroud:

View File

@@ -138,10 +138,10 @@ APCGun:
Spread: 128
Damage: 2000
Versus:
None: 35
Wood: 35
None: 30
Wood: 25
Light: 75
Heavy: 35
Heavy: 25
DamageTypes: Prone50Percent, TriggerProne, DefaultDeath
Warhead@2Eff: CreateEffect
Explosions: small_poof

View File

@@ -28,7 +28,7 @@ Speech:
Guarding: GUARD
HarvesterAttack: HATTK
InsufficientFunds: MONEY
Leave: ABORT
Leave:
Lose: MFAIL
LowPower: POWER
MissileLaunchDetected: LAUNC

View File

@@ -19,9 +19,21 @@ World:
hard: Hard
Default: easy
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -19,9 +19,21 @@ World:
hard: Hard
Default: easy
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -23,9 +23,21 @@ carryall.reinforce:
Cargo:
MaxWeight: 10
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -23,9 +23,21 @@ carryall.reinforce:
Cargo:
MaxWeight: 10
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -57,7 +57,7 @@ grenadier:
Buildable:
Prerequisites: ~disabled
sardaukar:
mpsardaukar:
Buildable:
Prerequisites: ~disabled

View File

@@ -19,9 +19,21 @@ World:
hard: Hard
Default: easy
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -19,9 +19,21 @@ World:
hard: Hard
Default: easy
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -23,9 +23,21 @@ carryall.reinforce:
Cargo:
MaxWeight: 10
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -23,9 +23,21 @@ carryall.reinforce:
Cargo:
MaxWeight: 10
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -280,6 +280,6 @@ WorldLoaded = function()
SendHarkonnenReinforcements(DateTime.Seconds(35), 2)
local ordosCondition = function() return player.IsObjectiveCompleted(KillOrdos) end
TriggerCarryallReinforcements(player, ordos_main, BaseAreaTriggers[1], OrdosHunters[1], OrdosHunterPaths[3], ordosCondition)
TriggerCarryallReinforcements(player, ordos_main, BaseAreaTriggers[2], OrdosHunters[2], OrdosHunterPaths[2], ordosCondition)
TriggerCarryallReinforcements(player, ordos_main, BaseAreaTriggers[1], OrdosHunters[1], OrdosHunterPaths[2], ordosCondition)
TriggerCarryallReinforcements(player, ordos_main, BaseAreaTriggers[2], OrdosHunters[2], OrdosHunterPaths[1], ordosCondition)
end

View File

@@ -54,7 +54,7 @@ siege_tank:
Buildable:
Prerequisites: ~disabled
sardaukar:
mpsardaukar:
Buildable:
Prerequisites: ~disabled

View File

@@ -58,7 +58,7 @@ missile_tank:
Buildable:
Prerequisites: ~heavy.missile_tank, upgrade.heavy, research_centre
sardaukar:
mpsardaukar:
Buildable:
Prerequisites: ~disabled

View File

@@ -58,7 +58,7 @@ missile_tank:
Buildable:
Prerequisites: ~heavy.missile_tank, upgrade.heavy, research_centre
sardaukar:
mpsardaukar:
Buildable:
Prerequisites: ~disabled

View File

@@ -69,6 +69,10 @@ sardaukar:
Buildable:
Prerequisites: ~barracks, ~player.corrino
mpsardaukar:
Buildable:
Prerequisites: ~disabled
grenadier:
Buildable:
Prerequisites: ~disabled

View File

@@ -36,7 +36,7 @@ frigate:
LandableTerrainTypes: Sand, Rock, Transition, Spice, SpiceSand, Dune, Concrete
VTOL: true # The frigate would teleport to land otherwise
sardaukar:
mpsardaukar:
Buildable:
Prerequisites: ~disabled

View File

@@ -37,14 +37,9 @@ frigate:
LandableTerrainTypes: Sand, Rock, Transition, Spice, SpiceSand, Dune, Concrete
VTOL: true # The frigate would teleport to land otherwise
palace:
ProvidesPrerequisite@sardaukar:
Prerequisite: palace.sardaukar
Factions: corrino
sardaukar:
mpsardaukar:
Buildable:
Prerequisites: barracks, ~palace.sardaukar
Prerequisites: ~disabled
grenadier:
Buildable:

View File

@@ -40,14 +40,9 @@ frigate:
LandableTerrainTypes: Sand, Rock, Transition, Spice, SpiceSand, Dune, Concrete
VTOL: true # The frigate would teleport to land otherwise
palace:
ProvidesPrerequisite@sardaukar:
Prerequisite: palace.sardaukar
Factions: corrino
sardaukar:
mpsardaukar:
Buildable:
Prerequisites: barracks, ~palace.sardaukar
Prerequisites: ~disabled
grenadier:
Buildable:

View File

@@ -19,9 +19,21 @@ World:
hard: Hard
Default: easy
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -19,9 +19,21 @@ World:
hard: Hard
Default: easy
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -23,9 +23,21 @@ carryall.reinforce:
Cargo:
MaxWeight: 10
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -23,9 +23,21 @@ carryall.reinforce:
Cargo:
MaxWeight: 10
construction_yard:
Production:
Produces: Building
upgrade.conyard:
Buildable:
Prerequisites: ~disabled
upgrade.barracks:
Buildable:
Prerequisites: ~disabled
upgrade.light:
Buildable:
Prerequisites: ~disabled
upgrade.heavy:
Buildable:
Prerequisites: ~disabled
concreteb:
Buildable:

View File

@@ -38,7 +38,7 @@ AtreidesVehicleTypes = { "trike", "trike", "quad" }
AtreidesTankTypes = { "combat_tank_a", "combat_tank_a", "combat_tank_a", "siege_tank" }
AtreidesStarportTypes = { "trike.starport", "quad.starport", "siege_tank.starport", "missile_tank.starport", "combat_tank_a.starport" }
HarkonnenInfantryTypes = { "light_inf", "light_inf", "light_inf", "trooper", "trooper", "sardaukar" }
HarkonnenInfantryTypes = { "light_inf", "light_inf", "light_inf", "trooper", "trooper", "mpsardaukar" }
HarkonnenVehicleTypes = { "trike", "quad", "quad" }
HarkonnenTankTypes = { "combat_tank_h", "combat_tank_h", "combat_tank_h", "siege_tank" }
HarkonnenStarportTypes = { "trike.starport", "quad.starport", "siege_tank.starport", "missile_tank.starport", "combat_tank_h.starport" }

View File

@@ -656,7 +656,7 @@ Actors:
SubCell: 3
Facing: 0
TurretFacing: 0
har_sardaukar: sardaukar
har_sardaukar: mpsardaukar
Owner: Harkonnen
Location: 82,28
SubCell: 3

View File

@@ -61,6 +61,12 @@ grenadier:
Cost: 0
sardaukar:
Buildable:
Prerequisites: ~player.corrino
Valued:
Cost: 0
mpsardaukar:
Valued:
Cost: 0

View File

@@ -51,9 +51,9 @@ Player:
carryall: 1%
light_inf: 65%
trooper: 40%
sardaukar: 20%
harvester: 1%
mpsardaukar: 20%
grenadier: 20%
harvester: 1%
trike.starport: 5%
quad.starport: 7.5%
siege_tank.starport: 5%
@@ -172,7 +172,7 @@ Player:
carryall: 1%
light_inf: 65%
trooper: 40%
sardaukar: 20%
mpsardaukar: 20%
grenadier: 20%
harvester: 1%
trike.starport: 7.5%
@@ -292,7 +292,7 @@ Player:
carryall: 1%
light_inf: 65%
trooper: 40%
sardaukar: 20%
mpsardaukar: 20%
grenadier: 20%
harvester: 1%
trike.starport: 5%

View File

@@ -25,7 +25,7 @@ engineer:
Inherits: ^Infantry
Buildable:
Queue: Infantry
BuildPaletteOrder: 50
BuildPaletteOrder: 30
Prerequisites: upgrade.barracks, ~techlevel.medium
BuildDuration: 108
BuildDurationModifier: 40
@@ -82,7 +82,7 @@ thumper:
-RevealOnFire:
Buildable:
Queue: Infantry
BuildPaletteOrder: 60
BuildPaletteOrder: 40
Prerequisites: upgrade.barracks, ~techlevel.high
BuildDuration: 108
BuildDurationModifier: 40
@@ -129,7 +129,7 @@ fremen:
Name: Fremen
Buildable:
Queue: Infantry
BuildPaletteOrder: 100
BuildPaletteOrder: 80
Prerequisites: ~disabled
Description: Elite infantry unit armed with assault rifles and rockets\n Strong vs Infantry, Vehicles\n Weak vs Artillery\n Special Ability: Invisibility
Mobile:
@@ -169,7 +169,7 @@ grenadier:
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Infantry
BuildPaletteOrder: 80
BuildPaletteOrder: 60
Prerequisites: ~barracks.atreides, upgrade.barracks, high_tech_factory, ~techlevel.medium
BuildDuration: 81 ## Wasn't converted, copied from Sardauker who has same value in TibEd.
BuildDurationModifier: 40
@@ -200,13 +200,13 @@ sardaukar:
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Infantry
BuildPaletteOrder: 80
Prerequisites: ~barracks.harkonnen, upgrade.barracks, high_tech_factory, ~techlevel.medium
BuildPaletteOrder: 50
Prerequisites: ~palace.sardaukar, ~techlevel.high
BuildDuration: 81
BuildDurationModifier: 40
Description: Elite assault infantry\n Strong vs Infantry, Vehicles\n Weak vs Artillery
Description: Elite assault infantry of Corrino\n Strong vs Infantry, Vehicles\n Weak vs Artillery
Valued:
Cost: 200
Cost: 120
Tooltip:
Name: Sardaukar
Health:
@@ -229,6 +229,23 @@ sardaukar:
EmptyWeapon: SardDeath
Chance: 100
mpsardaukar:
Inherits: sardaukar
Buildable:
Queue: Infantry
BuildPaletteOrder: 70
Prerequisites: ~barracks.harkonnen, upgrade.barracks, high_tech_factory, ~techlevel.medium
BuildDuration: 133
Description: Elite assault infantry of Harkonnen\n Strong vs Infantry, Vehicles\n Weak vs Artillery
Valued:
Cost: 200
Armament@PRIMARY:
Weapon: M_LMG_H
Armament@SECONDARY:
Weapon: M_HMG_H
RenderSprites:
Image: sardaukar
saboteur:
Inherits: ^Infantry
Buildable:
@@ -266,7 +283,7 @@ nsfremen:
Inherits: fremen
Tooltip:
Buildable:
BuildPaletteOrder: 105
BuildPaletteOrder: 90
Prerequisites: ~disabled
Description: Elite infantry unit armed with assault rifles and rockets\n Strong vs Infantry, Vehicles\n Weak vs Artillery
RenderSprites:

View File

@@ -32,7 +32,7 @@ Player:
ClassicProductionQueue@Starport:
Type: Starport
BuildDurationModifier: 212
LowPowerSlowdown: 0
LowPowerSlowdown: 1
BlockedAudio: NoRoom
QueuedAudio: OrderPlaced
ReadyAudio:
@@ -46,11 +46,10 @@ Player:
ClassicProductionQueue@Upgrade: # Upgrade is defined after others so it won't be automatically selected by ProductionQueueFromSelection.
Type: Upgrade
BuildDurationModifier: 250
LowPowerSlowdown: 0
LowPowerSlowdown: 1
QueuedAudio: Upgrading
ReadyAudio: NewOptions
BlockedAudio: NoRoom
SpeedUp: true
PlaceBuilding:
SupportPowerManager:
ScriptTriggers:

View File

@@ -102,6 +102,7 @@ construction_yard:
Palette: d2k
PrimaryBuilding:
PrimaryCondition: primary
ProductionQueues: Building
ProvidesPrerequisite@buildingname:
GrantConditionOnPrerequisite:
Prerequisites: upgrade.conyard
@@ -194,7 +195,7 @@ barracks:
TopLeft: -1024, -1024
BottomRight: 1024, 1024
Armor:
Type: wood
Type: building
RevealsShroud:
Range: 3c768
RallyPoint:
@@ -209,6 +210,7 @@ barracks:
Produces: Infantry, Upgrade
PrimaryBuilding:
PrimaryCondition: primary
ProductionQueues: Infantry
ProductionBar:
ProvidesPrerequisite@atreides:
Prerequisite: barracks.atreides
@@ -279,7 +281,7 @@ refinery:
TopLeft: -1536, 0
BottomRight: 512, 1024
Armor:
Type: building
Type: heavy
RevealsShroud:
Range: 4c768
Refinery:
@@ -333,7 +335,7 @@ silo:
Health:
HP: 15000
Armor:
Type: wall
Type: building
RevealsShroud:
Range: 2c768
RenderSprites:
@@ -389,7 +391,7 @@ light_factory:
TopLeft: -1536, -1024
BottomRight: 1536, 1024
Armor:
Type: light
Type: building
RevealsShroud:
Range: 4c768
RenderSprites:
@@ -408,6 +410,7 @@ light_factory:
Produces: Vehicle, Upgrade
PrimaryBuilding:
PrimaryCondition: primary
ProductionQueues: Vehicle
ProductionBar:
ProvidesPrerequisite@atreides:
Prerequisite: light.atreides
@@ -480,7 +483,7 @@ heavy_factory:
TopLeft: -512, -1536
BottomRight: 512, -512
Armor:
Type: wood
Type: building
RevealsShroud:
Range: 4c768
RallyPoint:
@@ -492,6 +495,7 @@ heavy_factory:
Produces: Armor, Upgrade
PrimaryBuilding:
PrimaryCondition: primary
ProductionQueues: Armor
ProductionBar:
ProvidesPrerequisite@atreides:
Prerequisite: heavy.atreides
@@ -574,7 +578,7 @@ outpost:
TopLeft: -1536, -1024
BottomRight: 1536, 1024
Armor:
Type: light
Type: building
RevealsShroud:
Range: 4c768
ProvidesRadar:
@@ -626,7 +630,7 @@ starport:
TopLeft: -512, 512
BottomRight: 512, 1536
Armor:
Type: building
Type: heavy
RevealsShroud:
Range: 4c768
RallyPoint:
@@ -653,6 +657,7 @@ starport:
ProductionBar:
PrimaryBuilding:
PrimaryCondition: primary
ProductionQueues: Starport
ProvidesPrerequisite@atreides:
Prerequisite: starport.atreides
Factions: atreides
@@ -685,7 +690,7 @@ wall:
Inherits@1: ^SpriteActor
Interactable:
CombatDebugOverlay:
HiddenUnderShroud:
FrozenUnderFog:
ScriptTriggers:
Buildable:
Queue: Building
@@ -816,7 +821,7 @@ large_gun_turret:
Health:
HP: 30000
Armor:
Type: concrete
Type: heavy
RevealsShroud:
Range: 5c768
BodyOrientation:
@@ -902,6 +907,10 @@ high_tech_factory:
Name: High Tech Factory
ProductionFromMapEdge:
Produces: Aircraft, Upgrade
ProductionBar:
PrimaryBuilding:
PrimaryCondition: primary
ProductionQueues: Aircraft
Exit:
SpawnOffset: 0,0,728
ExitCell: 0,0
@@ -923,7 +932,7 @@ high_tech_factory:
TopLeft: -512, -1536
BottomRight: 512, -512
Armor:
Type: wood
Type: building
RevealsShroud:
Range: 4c768
RenderSprites:
@@ -964,6 +973,12 @@ high_tech_factory:
ReferencePoint: Top, Right
ZOffset: 256
RequiresCondition: stardecoration
WithTextDecoration@primary:
RequiresSelection: true
Text: PRIMARY
ReferencePoint: Top
ZOffset: 256
RequiresCondition: primary
research_centre:
Inherits: ^Building
@@ -998,7 +1013,7 @@ research_centre:
TopLeft: -512, -1536
BottomRight: 512, -512
Armor:
Type: wood
Type: building
RevealsShroud:
Range: 4c768
RenderSprites:
@@ -1050,7 +1065,7 @@ palace:
TopLeft: -512, 512
BottomRight: 1536, 1536
Armor:
Type: wood
Type: heavy
RevealsShroud:
Range: 4c768
RenderSprites:
@@ -1071,6 +1086,17 @@ palace:
ProvidesPrerequisite@saboteur:
Prerequisite: palace.saboteur
Factions: ordos
ProvidesPrerequisite@sardaukar:
Prerequisite: palace.sardaukar
Factions: corrino
PrimaryBuilding:
PrimaryCondition: primary
WithTextDecoration@primary:
RequiresSelection: true
Text: PRIMARY
ReferencePoint: Top
ZOffset: 256
RequiresCondition: primary
NukePower:
Cursor: nuke
Icon: deathhand

View File

@@ -169,6 +169,7 @@ World:
DebugPauseState:
RadarPings:
ObjectivesPanel:
ExitDelay: 0
PanelName: SKIRMISH_STATS
LoadWidgetAtGameStart:

View File

@@ -35,6 +35,10 @@ M_LMG:
Inherits: ^MG
ReloadDelay: 40
M_LMG_H:
Inherits: M_LMG
ReloadDelay: 50
M_HMG:
Inherits: ^MG
ReloadDelay: 40
@@ -54,6 +58,10 @@ M_HMG:
cy: 20
harvester: 50
M_HMG_H:
Inherits: M_HMG
ReloadDelay: 50
Fremen_L:
Inherits: M_HMG
Report: BAZOOK2.WAV

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
World:
WeatherOverlay:
ChangingWindLevel: true
WindLevels: -5, -3, -2, 0, 2, 3, 5, 6
WindTick: 150, 550
InstantWindChanges: false
UseSquares: true
ParticleSize: 2, 3
ScatterDirection: -1, 1
Gravity: 1.00, 2.00
SwingOffset: 1.0, 1.5
SwingSpeed: 0.001, 0.025
SwingAmplitude: 1.0, 1.5
ParticleColors: ECECEC, E4E4E4, D0D0D0, BCBCBC
LineTailAlphaValue: 0
GlobalLightingPaletteEffect:
Red: 0.88
Green: 0.93
Blue: 1.06
Ambient: 1.09

Binary file not shown.

View File

@@ -240,6 +240,10 @@ InitTriggers = function()
end)
Trigger.OnInfiltrated(Prison, function()
if not greece.IsObjectiveCompleted(infWarfactory) then
Media.DisplayMessage("Good work! But next time skip the heroics!", "Battlefield Control")
greece.MarkCompletedObjective(infWarfactory)
end
Trigger.ClearAll(Spy)
Trigger.AfterDelay(DateTime.Seconds(2), MissInfiltrated)
end)

View File

@@ -1399,7 +1399,7 @@ Actors:
Prison: miss
Location: 25,106
Owner: USSR
Warfactory: weap
Warfactory: weap.infiltratable
Location: 43,50
Owner: USSR
Warfactory2: weap

View File

@@ -82,7 +82,16 @@ SPY:
WEAP:
-InfiltrateForSupportPower:
Targetable:
TargetTypes: Ground, C4, DetonateAttack, Structure
WEAP.infiltratable:
Inherits: WEAP
Buildable:
Prerequisites: ~disabled
Targetable@Spy:
TargetTypes: Ground, C4, DetonateAttack, Structure, Mission Objectives
RenderSprites:
Image: WEAP
MISS:
Tooltip:

View File

@@ -48,7 +48,7 @@ TRUK:
STEK:
Targetable:
TargetTypes: Ground, Water, Structure, C4, DetonateAttack, SpyInfiltrate
TargetTypes: Ground, Structure, C4, DetonateAttack, SpyInfiltrate
TECH.CAM:
Inherits: CAMERA

View File

@@ -50,7 +50,7 @@ TECH.CAM:
STEK:
Targetable:
TargetTypes: Ground, Water, Structure, C4, DetonateAttack, SpyInfiltrate
TargetTypes: Ground, Structure, C4, DetonateAttack, SpyInfiltrate
APWR:
Buildable:
@@ -90,4 +90,4 @@ MSUB:
SS:
AutoTarget:
InitialStanceAI: AttackAnything
InitialStanceAI: AttackAnything

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