diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index 01f3e2520f..f77f30a290 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -539,6 +539,7 @@
+
diff --git a/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs
index c4bd716d91..d67f17035e 100644
--- a/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs
+++ b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs
@@ -171,6 +171,27 @@ namespace OpenRA.Mods.RA.Scripting
GetScriptTriggers(a).RegisterCallback(Trigger.OnRemovedFromWorld, func, context);
}
+ [Desc("Call a function when all of the actors in a group have been removed from the world. " +
+ "The callback function will be called as func().")]
+ public void OnAllRemovedFromWorld(Actor[] actors, LuaFunction func)
+ {
+ var group = actors.ToList();
+
+ var copy = (LuaFunction)func.CopyReference();
+ Action OnMemberRemoved = m =>
+ {
+ group.Remove(m);
+ if (!group.Any())
+ {
+ copy.Call().Dispose();
+ copy.Dispose();
+ }
+ };
+
+ foreach (var a in group)
+ GetScriptTriggers(a).OnRemovedInternal += OnMemberRemoved;
+ }
+
[Desc("Call a function when this actor is captured. The callback function " +
"will be called as func(Actor self, Actor captor, Player oldOwner, Player newOwner).")]
public void OnCapture(Actor a, LuaFunction func)
diff --git a/OpenRA.Mods.RA/Scripting/Properties/TransformProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/TransformProperties.cs
new file mode 100644
index 0000000000..6f5f656bc4
--- /dev/null
+++ b/OpenRA.Mods.RA/Scripting/Properties/TransformProperties.cs
@@ -0,0 +1,34 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2014 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. For more information,
+ * see COPYING.
+ */
+#endregion
+
+using OpenRA.Scripting;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA.Scripting
+{
+ [ScriptPropertyGroup("Transform")]
+ public class TransformProperties : ScriptActorProperties, Requires
+ {
+ readonly Transforms transforms;
+
+ public TransformProperties(ScriptContext context, Actor self)
+ : base(context, self)
+ {
+ transforms = self.Trait();
+ }
+
+ [ScriptActorPropertyActivity]
+ [Desc("Queue a new transformation.")]
+ public void Deploy()
+ {
+ transforms.DeployTransform(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs
index c507a09616..b7d226c742 100644
--- a/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs
+++ b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs
@@ -26,7 +26,8 @@ namespace OpenRA.Mods.RA.Scripting
public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyObjectivesUpdated, INotifyCapture, INotifyAddedToWorld, INotifyRemovedFromWorld, IDisposable
{
- public event Action OnKilledInternal = _ => {};
+ public event Action OnKilledInternal = _ => { };
+ public event Action OnRemovedInternal = _ => { };
public Dictionary>> Triggers = new Dictionary>>();
@@ -58,7 +59,7 @@ namespace OpenRA.Mods.RA.Scripting
public void Killed(Actor self, AttackInfo e)
{
- // Run lua callbacks
+ // Run Lua callbacks
foreach (var f in Triggers[Trigger.OnKilled])
using (var a = self.ToLuaValue(f.Second))
using (var b = e.Attacker.ToLuaValue(f.Second))
@@ -133,9 +134,13 @@ namespace OpenRA.Mods.RA.Scripting
public void RemovedFromWorld(Actor self)
{
+ // Run Lua callbacks
foreach (var f in Triggers[Trigger.OnRemovedFromWorld])
using (var a = self.ToLuaValue(f.Second))
f.First.Call(a).Dispose();
+
+ // Run any internally bound callbacks
+ OnRemovedInternal(self);
}
public void Clear(Trigger trigger)
diff --git a/mods/ra/maps/allies-02-classic/allies02.lua b/mods/ra/maps/allies-02-classic/allies02.lua
index 616478a37c..3c3899417b 100644
--- a/mods/ra/maps/allies-02-classic/allies02.lua
+++ b/mods/ra/maps/allies-02-classic/allies02.lua
@@ -1,76 +1,132 @@
-JeepReinforcements = { "e1", "e1", "e1", "jeep" }
-JeepReinforcementsInterval = 15
-TruckNames = { "truk", "truk", "truk" }
-TruckInterval = 25
-TruckDelay = 75
-FirstJeepReinforcementsDelay = 125
-SecondJeepReinforcementsDelay = 250
+ConstructionVehicleReinforcements = { "mcv" }
+ConstructionVehiclePath = { ReinforcementsEntryPoint.Location, DeployPoint.Location }
-SendMcvReinforcements = function()
- Media.PlaySpeechNotification("ReinforcementsArrived")
- local mcv = Actor.Create("mcv", { Owner = player, Location = ReinforcementsEntryPoint.Location })
- Actor.Move(mcv, McvDeployPoint.Location)
- Actor.DeployTransform(mcv)
+JeepReinforcements = { "e1", "e1", "e1", "jeep" }
+JeepPath = { ReinforcementsEntryPoint.Location, ReinforcementsRallyPoint.Location }
+
+TruckReinforcements = { "truk", "truk", "truk" }
+TruckPath = { TruckEntryPoint.Location, TruckRallyPoint.Location }
+
+SendConstructionVehicleReinforcements = function()
+ local mcv = Reinforcements.Reinforce(player, ConstructionVehicleReinforcements, ConstructionVehiclePath)[1]
end
SendJeepReinforcements = function()
- Media.PlaySpeechNotification("ReinforcementsArrived")
- Reinforcements.Reinforce(player, JeepReinforcements, ReinforcementsEntryPoint.Location, ReinforcementsRallyPoint.Location, JeepReinforcementsInterval)
+ Media.PlaySpeechNotification(player, "ReinforcementsArrived")
+ Reinforcements.Reinforce(player, JeepReinforcements, JeepPath, Utils.Seconds(1))
end
RunInitialActivities = function()
- Actor.Harvest(Harvester)
+ Harvester.FindResources()
end
MissionAccomplished = function()
- Mission.MissionOver({ player }, nil, true)
- Media.PlayMovieFullscreen("montpass.vqa")
+ Media.PlaySpeechNotification(player, "Win")
+ Trigger.AfterDelay(Utils.Seconds(1), function()
+ Media.PlayMovieFullscreen("montpass.vqa")
+ end)
end
MissionFailed = function()
- Mission.MissionOver(nil, { player }, true)
- Media.PlayMovieFullscreen("frozen.vqa")
+ Media.PlaySpeechNotification(player, "Lose")
+ Trigger.AfterDelay(Utils.Seconds(1), function()
+ Media.PlayMovieFullscreen("frozen.vqa")
+ end)
end
Tick = function()
- Mission.TickTakeOre(ussr)
+ ussr.Resources = ussr.Resources - (0.01 * ussr.ResourceCapacity / 25)
- if Mission.RequiredUnitsAreDestroyed(player) then
- MissionFailed()
- end
- if not trucksSent and Mission.RequiredUnitsAreDestroyed(ussr) and Mission.RequiredUnitsAreDestroyed(badGuy) then
+ if ukraine.HasNoRequiredUnits() then
SendTrucks()
- trucksSent = true
+ player.MarkCompletedObjective(ConquestObjective)
+ end
+
+ if player.HasNoRequiredUnits() then
+ player.MarkFailedObjective(ConquestObjective)
end
end
+ConvoyOnSite = false
SendTrucks = function()
- Media.PlaySpeechNotification("ConvoyApproaching")
- OpenRA.RunAfterDelay(TruckDelay, function()
- local trucks = Reinforcements.Reinforce(france, TruckNames, TruckEntryPoint.Location, TruckRallyPoint.Location, TruckInterval,
- function(truck)
- Actor.Move(truck, TruckExitPoint.Location)
- Actor.RemoveSelf(truck)
+ if not ConvoyOnSite then
+ ConvoyOnSite = true
+ ConvoyObjective = player.AddPrimaryObjective("Escort the convoy")
+ Media.PlaySpeechNotification(player, "ConvoyApproaching")
+ Trigger.AfterDelay(Utils.Seconds(3), function()
+ ConvoyUnharmed = true
+ local trucks = Reinforcements.Reinforce(france, TruckReinforcements, TruckPath, Utils.Seconds(1),
+ function(truck)
+ Trigger.OnIdle(truck, function() truck.Move(TruckExitPoint.Location) end)
+ end)
+ count = 0
+ Trigger.OnEnteredFootprint( { TruckExitPoint.Location }, function(a, id)
+ if a.Owner == france then
+ count = count + 1
+ a.Destroy()
+ if count == 3 then
+ player.MarkCompletedObjective(ConvoyObjective)
+ Trigger.RemoveFootprintTrigger(id)
+ end
+ end
end)
- local trucksTeam = Team.New(trucks)
- Team.AddEventHandler(trucksTeam.OnAllRemovedFromWorld, MissionAccomplished)
- Team.AddEventHandler(trucksTeam.OnAnyKilled, MissionFailed)
+ Trigger.OnAnyKilled(trucks, ConvoyCasualites)
+ end)
+ end
+end
+
+ConvoyCasualites = function()
+ Media.PlaySpeechNotification(player, "ConvoyUnitLost")
+ if ConvoyUnharmed then
+ ConvoyUnharmed = false
+ Trigger.AfterDelay(Utils.Seconds(1), function() player.MarkFailedObjective(ConvoyObjective) end)
+ end
+end
+
+ConvoyTimer = function(delay, notification)
+ Trigger.AfterDelay(delay, function()
+ if not ConvoyOnSite then
+ Media.PlaySpeechNotification(player, notification)
+ end
end)
end
WorldLoaded = function()
- player = OpenRA.GetPlayer("Greece")
- france = OpenRA.GetPlayer("France")
- ussr = OpenRA.GetPlayer("USSR")
- badGuy = OpenRA.GetPlayer("BadGuy")
-
+ player = Player.GetPlayer("Greece")
+ france = Player.GetPlayer("France")
+ ussr = Player.GetPlayer("USSR")
+ ukraine = Player.GetPlayer("Ukraine")
+
+ ConquestObjective = player.AddPrimaryObjective("Secure the area.")
+ ussr.AddPrimaryObjective("Defend your base.")
+ ukraine.AddPrimaryObjective("Destroy the convoy.")
+
RunInitialActivities()
-
- SendMcvReinforcements()
- OpenRA.RunAfterDelay(FirstJeepReinforcementsDelay, SendJeepReinforcements)
- OpenRA.RunAfterDelay(SecondJeepReinforcementsDelay, SendJeepReinforcements)
-
- OpenRA.SetViewportCenterPosition(ReinforcementsEntryPoint.CenterPosition)
-
- Media.PlayMovieFullscreen("ally2.vqa", function() Media.PlayMovieFullscreen("mcv.vqa", Media.PlayRandomMusic) end)
+
+ SendConstructionVehicleReinforcements()
+ Trigger.AfterDelay(Utils.Seconds(5), SendJeepReinforcements)
+ Trigger.AfterDelay(Utils.Seconds(10), SendJeepReinforcements)
+
+ Trigger.AfterDelay(Utils.Minutes(10), SendTrucks)
+
+ Trigger.OnPlayerLost(player, MissionFailed)
+ Trigger.OnPlayerWon(player, MissionAccomplished)
+
+ Trigger.OnObjectiveCompleted(player, function(p, id)
+ Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed")
+ end)
+ Trigger.OnObjectiveFailed(player, function(p, id)
+ Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed")
+ end)
+
+ Camera.Position = ReinforcementsEntryPoint.CenterPosition
+
+ Media.PlayMovieFullscreen("ally2.vqa", function() Media.PlayMovieFullscreen("mcv.vqa") end)
+
+ ConvoyTimer(Utils.Seconds(3), "TenMinutesRemaining")
+ ConvoyTimer(Utils.Minutes(5), "WarningFiveMinutesRemaining")
+ ConvoyTimer(Utils.Minutes(6), "WarningFourMinutesRemaining")
+ ConvoyTimer(Utils.Minutes(7), "WarningThreeMinutesRemaining")
+ ConvoyTimer(Utils.Minutes(8), "WarningTwoMinutesRemaining")
+ ConvoyTimer(Utils.Minutes(9), "WarningOneMinuteRemaining")
end
\ No newline at end of file
diff --git a/mods/ra/maps/allies-02-classic/map.yaml b/mods/ra/maps/allies-02-classic/map.yaml
index b96f01489c..18b4d8771c 100644
--- a/mods/ra/maps/allies-02-classic/map.yaml
+++ b/mods/ra/maps/allies-02-classic/map.yaml
@@ -6,7 +6,7 @@ RequiresMod: ra
Title: Allies 02: Five to one
-Description: A critical supply convoy is due through this area in 25 minutes, but Soviet forces have blocked the road in several places.\n\nUnless you can clear them out, those supplies will never make it to the front.\n\nThe convoy will come from the northwest, and time is short so work quickly.
+Description: A critical supply convoy is due through this area in 10 minutes, but Soviet forces have blocked the road in several places.\n\nUnless you can clear them out, those supplies will never make it to the front.\n\nThe convoy will come from the northwest, and time is short so work quickly.
Author: Westwood Studios
@@ -34,26 +34,26 @@ Players:
Name: USSR
Race: soviet
ColorRamp: 3,255,127
- Allies: BadGuy
- Enemies: Greece
+ Allies: Ukraine
+ Enemies: Greece, France
PlayerReference@France:
Name: France
Race: allies
+ NonCombatant: True
ColorRamp: 115,115,143
- Allies: Greece
- Enemies: USSR,BadGuy
+ Allies: Greece, France
+ Enemies: USSR, Ukraine
PlayerReference@Neutral:
Name: Neutral
OwnsWorld: True
NonCombatant: True
Race: allies
- Enemies: Greece
- PlayerReference@BadGuy:
- Name: BadGuy
+ PlayerReference@Ukraine:
+ Name: Ukraine
Race: soviet
ColorRamp: 3,255,127
Allies: USSR
- Enemies: Greece
+ Enemies: Greece, France
PlayerReference@Greece:
Name: Greece
Playable: True
@@ -66,7 +66,7 @@ Players:
LockSpawn: True
LockTeam: True
Allies: France
- Enemies: USSR,BadGuy
+ Enemies: USSR, Ukraine
Actors:
EntryPoint: t06
@@ -240,7 +240,7 @@ Actors:
Actor56: tc03
Location: 43,70
Owner: Neutral
- Actor57: weap
+ SovietWarfactory: weap
Location: 60,66
Owner: USSR
Health: 1
@@ -480,7 +480,7 @@ Actors:
SubCell: 0
Actor103: e1
Location: 77,74
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 128
SubCell: 2
@@ -612,67 +612,67 @@ Actors:
SubCell: 1
Actor125: dog
Location: 78,75
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 160
SubCell: 1
Actor126: e1
Location: 71,61
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 160
SubCell: 0
Actor127: dog
Location: 70,61
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 96
SubCell: 4
Actor128: e1
Location: 50,46
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 32
SubCell: 1
Actor129: e1
Location: 49,47
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 64
SubCell: 0
Actor130: e2
Location: 49,49
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 160
SubCell: 1
Actor131: e2
Location: 47,46
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 96
SubCell: 3
Actor132: e2
Location: 48,63
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 0
SubCell: 1
Actor133: e1
Location: 49,63
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 96
SubCell: 2
Actor134: e1
Location: 74,81
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 64
SubCell: 3
Actor135: e2
Location: 75,83
- Owner: BadGuy
+ Owner: Ukraine
Health: 1
Facing: 96
SubCell: 0
@@ -751,7 +751,7 @@ Actors:
waypoint94: waypoint
Location: 90,46
Owner: Neutral
- McvDeployPoint: waypoint
+ DeployPoint: waypoint
Location: 89,51
Owner: Neutral
waypoint96: waypoint
@@ -875,8 +875,8 @@ Rules:
-CrateSpawner:
-SpawnMPUnits:
-MPStartLocations:
- LuaScriptInterface:
- LuaScripts: allies02.lua
+ LuaScript:
+ Scripts: allies02.lua
ObjectivesPanel:
PanelName: MISSION_OBJECTIVES
^Vehicle:
diff --git a/mods/ra/notifications.yaml b/mods/ra/notifications.yaml
index 29d5620211..b88fa806ce 100644
--- a/mods/ra/notifications.yaml
+++ b/mods/ra/notifications.yaml
@@ -28,12 +28,22 @@ Speech:
SignalFlareNorth: flaren1
AlliedReinforcementsArrived: aarrive1
ConvoyApproaching: convyap1
+ ConvoyUnitLost: convlst1
TargetFreed: targfre1
TargetRescued: targres1
ObjectiveMet: objmet1
ObjectiveNotMet: objnmet1
ObjectiveReached: objrch1
ObjectiveNotReached: objnrch1
+ TenMinutesRemaining: 10minr
+ TwentyMinutesRemaining: 20minr
+ ThirtyMinutesRemaining: 30minr
+ FourtyMinutesRemaining: 40minr
+ WarningOneMinuteRemaining: 1minr
+ WarningTwoMinutesRemaining: 2minr
+ WarningThreeMinutesRemaining: 3minr
+ WarningFourMinutesRemaining: 4minr
+ WarningFiveMinutesRemaining: 5minr
Sounds:
Notifications: