Merge pull request #6574 from pchote/celltrigger
Implement cell triggers.
This commit is contained in:
@@ -33,8 +33,58 @@ namespace OpenRA.Traits
|
|||||||
public Actor Actor;
|
public Actor Actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CellTrigger
|
||||||
|
{
|
||||||
|
public readonly int Id;
|
||||||
|
public readonly CPos[] Footprint;
|
||||||
|
public bool Dirty;
|
||||||
|
|
||||||
|
Action<Actor> onActorEntered;
|
||||||
|
Action<Actor> onActorExited;
|
||||||
|
|
||||||
|
IEnumerable<Actor> currentActors = Enumerable.Empty<Actor>();
|
||||||
|
|
||||||
|
public CellTrigger(int id, CPos[] footprint, Action<Actor> onActorEntered, Action<Actor> onActorExited)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Footprint = footprint;
|
||||||
|
|
||||||
|
this.onActorEntered = onActorEntered;
|
||||||
|
this.onActorExited = onActorExited;
|
||||||
|
|
||||||
|
// Notify any actors that are initially inside the trigger zone
|
||||||
|
Dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick(ActorMap am)
|
||||||
|
{
|
||||||
|
if (!Dirty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var oldActors = currentActors;
|
||||||
|
currentActors = Footprint.SelectMany(c => am.GetUnitsAt(c)).ToList();
|
||||||
|
|
||||||
|
var entered = currentActors.Except(oldActors);
|
||||||
|
var exited = oldActors.Except(currentActors);
|
||||||
|
|
||||||
|
if (onActorEntered != null)
|
||||||
|
foreach (var a in entered)
|
||||||
|
onActorEntered(a);
|
||||||
|
|
||||||
|
if (onActorExited != null)
|
||||||
|
foreach (var a in exited)
|
||||||
|
onActorExited(a);
|
||||||
|
|
||||||
|
Dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
readonly ActorMapInfo info;
|
readonly ActorMapInfo info;
|
||||||
readonly Map map;
|
readonly Map map;
|
||||||
|
readonly Dictionary<int, CellTrigger> cellTriggers = new Dictionary<int, CellTrigger>();
|
||||||
|
readonly Dictionary<CPos, List<CellTrigger>> cellTriggerInfluence = new Dictionary<CPos, List<CellTrigger>>();
|
||||||
|
int nextTriggerId;
|
||||||
|
|
||||||
readonly CellLayer<InfluenceNode> influence;
|
readonly CellLayer<InfluenceNode> influence;
|
||||||
|
|
||||||
readonly List<Actor>[] actors;
|
readonly List<Actor>[] actors;
|
||||||
@@ -166,8 +216,17 @@ namespace OpenRA.Traits
|
|||||||
public void AddInfluence(Actor self, IOccupySpace ios)
|
public void AddInfluence(Actor self, IOccupySpace ios)
|
||||||
{
|
{
|
||||||
foreach (var c in ios.OccupiedCells())
|
foreach (var c in ios.OccupiedCells())
|
||||||
if (map.Contains(c.First))
|
{
|
||||||
influence[c.First] = new InfluenceNode { Next = influence[c.First], SubCell = c.Second, Actor = self };
|
if (!map.Contains(c.First))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
influence[c.First] = new InfluenceNode { Next = influence[c.First], SubCell = c.Second, Actor = self };
|
||||||
|
|
||||||
|
List<CellTrigger> triggers;
|
||||||
|
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
||||||
|
foreach (var t in triggers)
|
||||||
|
t.Dirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveInfluence(Actor self, IOccupySpace ios)
|
public void RemoveInfluence(Actor self, IOccupySpace ios)
|
||||||
@@ -180,6 +239,11 @@ namespace OpenRA.Traits
|
|||||||
var temp = influence[c.First];
|
var temp = influence[c.First];
|
||||||
RemoveInfluenceInner(ref temp, self);
|
RemoveInfluenceInner(ref temp, self);
|
||||||
influence[c.First] = temp;
|
influence[c.First] = temp;
|
||||||
|
|
||||||
|
List<CellTrigger> triggers;
|
||||||
|
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
||||||
|
foreach (var t in triggers)
|
||||||
|
t.Dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,6 +277,44 @@ namespace OpenRA.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
addActorPosition.Clear();
|
addActorPosition.Clear();
|
||||||
|
|
||||||
|
foreach (var t in cellTriggers)
|
||||||
|
t.Value.Tick(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AddCellTrigger(CPos[] cells, Action<Actor> onEntry, Action<Actor> onExit)
|
||||||
|
{
|
||||||
|
var id = nextTriggerId++;
|
||||||
|
var t = new CellTrigger(id, cells, onEntry, onExit);
|
||||||
|
cellTriggers.Add(id, t);
|
||||||
|
|
||||||
|
foreach (var c in cells)
|
||||||
|
{
|
||||||
|
if (!map.Contains(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!cellTriggerInfluence.ContainsKey(c))
|
||||||
|
cellTriggerInfluence.Add(c, new List<CellTrigger>());
|
||||||
|
|
||||||
|
cellTriggerInfluence[c].Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveCellTrigger(int id)
|
||||||
|
{
|
||||||
|
CellTrigger trigger;
|
||||||
|
if (!cellTriggers.TryGetValue(id, out trigger))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var c in trigger.Footprint)
|
||||||
|
{
|
||||||
|
if (!cellTriggerInfluence.ContainsKey(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cellTriggerInfluence[c].RemoveAll(t => t == trigger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddPosition(Actor a, IOccupySpace ios)
|
public void AddPosition(Actor a, IOccupySpace ios)
|
||||||
|
|||||||
@@ -197,6 +197,65 @@ namespace OpenRA.Mods.RA.Scripting
|
|||||||
GetScriptTriggers(a).RegisterCallback(Trigger.OnCapture, func, context);
|
GetScriptTriggers(a).RegisterCallback(Trigger.OnCapture, func, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CPos[] MakeCellFootprint(LuaTable table)
|
||||||
|
{
|
||||||
|
var cells = new List<CPos>();
|
||||||
|
foreach (var kv in table)
|
||||||
|
{
|
||||||
|
CPos cell;
|
||||||
|
if (!kv.Value.TryGetClrValue<CPos>(out cell))
|
||||||
|
throw new LuaException("Cell footprints must be specified as a table of int,Cell pairs. Recieved {0},{1}".F(kv.Key.GetType().Name, kv.Value.GetType().Name));
|
||||||
|
|
||||||
|
cells.Add(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cells.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Desc("Call a function when a ground-based actor enters this cell footprint." +
|
||||||
|
"Returns the trigger id for later removal using RemoveFootprintTrigger(int id)." +
|
||||||
|
"The callback function will be called as func(Actor a, int id).")]
|
||||||
|
public int OnEnteredFootprint(LuaTable cells, LuaFunction func)
|
||||||
|
{
|
||||||
|
var triggerId = 0;
|
||||||
|
var onEntry = (LuaFunction)func.CopyReference();
|
||||||
|
Action<Actor> invokeEntry = a =>
|
||||||
|
{
|
||||||
|
using (var luaActor = a.ToLuaValue(context))
|
||||||
|
using (var id = triggerId.ToLuaValue(context))
|
||||||
|
onEntry.Call(luaActor, id).Dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
triggerId = context.World.ActorMap.AddCellTrigger(MakeCellFootprint(cells), invokeEntry, null);
|
||||||
|
|
||||||
|
return triggerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Desc("Call a function when a ground-based actor leaves this cell footprint." +
|
||||||
|
"Returns the trigger id for later removal using RemoveFootprintTrigger(int id)." +
|
||||||
|
"The callback function will be called as func(Actor a, int id).")]
|
||||||
|
public int OnExitedFootprint(LuaTable cells, LuaFunction func)
|
||||||
|
{
|
||||||
|
var triggerId = 0;
|
||||||
|
var onExit = (LuaFunction)func.CopyReference();
|
||||||
|
Action<Actor> invokeExit = a =>
|
||||||
|
{
|
||||||
|
using (var luaActor = a.ToLuaValue(context))
|
||||||
|
using (var id = triggerId.ToLuaValue(context))
|
||||||
|
onExit.Call(luaActor, id).Dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
triggerId = context.World.ActorMap.AddCellTrigger(MakeCellFootprint(cells), null, invokeExit);
|
||||||
|
|
||||||
|
return triggerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Desc("Removes a previously created footprint trigger.")]
|
||||||
|
public void RemoveFootprintTrigger(int id)
|
||||||
|
{
|
||||||
|
context.World.ActorMap.RemoveCellTrigger(id);
|
||||||
|
}
|
||||||
|
|
||||||
[Desc("Removes all triggers from this actor.")]
|
[Desc("Removes all triggers from this actor.")]
|
||||||
public void ClearAll(Actor a)
|
public void ClearAll(Actor a)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
LoseTriggerHouses = { TrigLos2Farm1, TrigLos2Farm2, TrigLos2Farm3, TrigLos2Farm4 }
|
LoseTriggerHouses = { TrigLos2Farm1, TrigLos2Farm2, TrigLos2Farm3, TrigLos2Farm4 }
|
||||||
|
TownAttackTrigger = { CPos.New(54, 38), CPos.New(55, 38), CPos.New(56, 38), CPos.New(57, 38) }
|
||||||
|
GDIReinforcementsTrigger = { CPos.New(32, 51), CPos.New(32, 52), CPos.New(33, 52) }
|
||||||
|
|
||||||
GDIReinforcementsPart1 = { "jeep", "jeep" }
|
GDIReinforcementsPart1 = { "jeep", "jeep" }
|
||||||
GDIReinforcementsPart2 = { "e2", "e2", "e2", "e2", "e2" }
|
GDIReinforcementsPart2 = { "e2", "e2", "e2", "e2", "e2" }
|
||||||
TownAttackWave1 = { "bggy", "bggy" }
|
TownAttackWave1 = { "bggy", "bggy" }
|
||||||
TownAttackWave2 = { "ltnk", "ltnk" }
|
TownAttackWave2 = { "ltnk", "ltnk" }
|
||||||
TownAttackWave3 = { "e1", "e1", "e1", "e1", "e3", "e3", "e3", "e3" }
|
TownAttackWave3 = { "e1", "e1", "e1", "e1", "e3", "e3", "e3", "e3" }
|
||||||
TownAttackWpts = { waypoint1, waypoint2 }
|
TownAttackWpts = { waypoint1, waypoint2 }
|
||||||
|
|
||||||
Civvie1Wpts = { waypoint3, waypoint17 }
|
Civvie1Wpts = { waypoint3, waypoint17 }
|
||||||
Civvie2Wpts = { waypoint26, waypoint3, waypoint9, waypoint4, waypoint5, waypoint6, waypoint8, waypoint7, waypoint1, waypoint2 }
|
Civvie2Wpts = { waypoint26, waypoint3, waypoint9, waypoint4, waypoint5, waypoint6, waypoint8, waypoint7, waypoint1, waypoint2 }
|
||||||
|
|
||||||
FollowCivvieWpts = function(actor, wpts)
|
FollowCivvieWpts = function(actor, wpts)
|
||||||
Utils.Do(wpts, function(wpt)
|
Utils.Do(wpts, function(wpt)
|
||||||
@@ -34,8 +36,6 @@ TownAttackAction = function(actor)
|
|||||||
end
|
end
|
||||||
|
|
||||||
AttackTown = function()
|
AttackTown = function()
|
||||||
TownAttackTriggered = true
|
|
||||||
|
|
||||||
Reinforcements.Reinforce(nod, TownAttackWave1, { NodReinfEntry.Location, waypoint0.Location }, Utils.Seconds(0.25), TownAttackAction)
|
Reinforcements.Reinforce(nod, TownAttackWave1, { NodReinfEntry.Location, waypoint0.Location }, Utils.Seconds(0.25), TownAttackAction)
|
||||||
Trigger.AfterDelay(Utils.Seconds(2), function()
|
Trigger.AfterDelay(Utils.Seconds(2), function()
|
||||||
Reinforcements.Reinforce(nod, TownAttackWave2, { NodReinfEntry.Location, waypoint0.Location }, Utils.Seconds(1), TownAttackAction)
|
Reinforcements.Reinforce(nod, TownAttackWave2, { NodReinfEntry.Location, waypoint0.Location }, Utils.Seconds(1), TownAttackAction)
|
||||||
@@ -46,8 +46,6 @@ AttackTown = function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
SendGDIReinforcements = function()
|
SendGDIReinforcements = function()
|
||||||
GDIReinforcementsTriggered = true
|
|
||||||
|
|
||||||
Reinforcements.Reinforce(player, GDIReinforcementsPart1, { GDIReinfEntry1.Location, waypoint12.Location }, Utils.Seconds(1), function(actor)
|
Reinforcements.Reinforce(player, GDIReinforcementsPart1, { GDIReinfEntry1.Location, waypoint12.Location }, Utils.Seconds(1), function(actor)
|
||||||
Media.PlaySpeechNotification(player, "Reinforce")
|
Media.PlaySpeechNotification(player, "Reinforce")
|
||||||
actor.Move(waypoint10.Location)
|
actor.Move(waypoint10.Location)
|
||||||
@@ -63,20 +61,9 @@ SendGDIReinforcements = function()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- FIXME: replace with real cell trigger when available
|
|
||||||
CellTrigger = function(player, trigger, radius, func)
|
|
||||||
local units = Map.ActorsInCircle(trigger.CenterPosition, WRange.FromCells(radius), function(actor)
|
|
||||||
return actor.Owner == player and actor.HasProperty("Move")
|
|
||||||
end)
|
|
||||||
|
|
||||||
if #units > 0 then
|
|
||||||
func()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
WorldLoaded = function()
|
WorldLoaded = function()
|
||||||
player = Player.GetPlayer("GDI")
|
player = Player.GetPlayer("GDI")
|
||||||
nod = Player.GetPlayer("Nod")
|
nod = Player.GetPlayer("Nod")
|
||||||
|
|
||||||
nodObjective = nod.AddPrimaryObjective("Destroy all GDI troops")
|
nodObjective = nod.AddPrimaryObjective("Destroy all GDI troops")
|
||||||
gdiObjective1 = player.AddPrimaryObjective("Defend the town of Białystok")
|
gdiObjective1 = player.AddPrimaryObjective("Defend the town of Białystok")
|
||||||
@@ -107,6 +94,20 @@ WorldLoaded = function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
Trigger.OnExitedFootprint(TownAttackTrigger, function(a, id)
|
||||||
|
if a.Owner == player then
|
||||||
|
Trigger.RemoveFootprintTrigger(id)
|
||||||
|
AttackTown()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
Trigger.OnEnteredFootprint(GDIReinforcementsTrigger, function(a, id)
|
||||||
|
if a.Owner == player then
|
||||||
|
Trigger.RemoveFootprintTrigger(id)
|
||||||
|
SendGDIReinforcements()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
Utils.Do(player.GetGroundAttackers(), function(unit)
|
Utils.Do(player.GetGroundAttackers(), function(unit)
|
||||||
unit.Stance = "Defend"
|
unit.Stance = "Defend"
|
||||||
end)
|
end)
|
||||||
@@ -121,8 +122,6 @@ WorldLoaded = function()
|
|||||||
Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("gdi4a.vqa", function() Media.PlayMovieFullscreen("nodsweep.vqa") end) end)
|
Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("gdi4a.vqa", function() Media.PlayMovieFullscreen("nodsweep.vqa") end) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
TownAttackTriggered = false
|
|
||||||
GDIReinforcementsTriggered = false
|
|
||||||
Tick = function()
|
Tick = function()
|
||||||
if player.HasNoRequiredUnits() then
|
if player.HasNoRequiredUnits() then
|
||||||
nod.MarkCompletedObjective(nodObjective)
|
nod.MarkCompletedObjective(nodObjective)
|
||||||
@@ -131,10 +130,4 @@ Tick = function()
|
|||||||
player.MarkCompletedObjective(gdiObjective1)
|
player.MarkCompletedObjective(gdiObjective1)
|
||||||
player.MarkCompletedObjective(gdiObjective2)
|
player.MarkCompletedObjective(gdiObjective2)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not TownAttackTriggered then
|
|
||||||
CellTrigger(player, TownAttackTrigger, 2, AttackTown)
|
|
||||||
elseif not GDIReinforcementsTriggered then
|
|
||||||
CellTrigger(player, GDIReinfTrigger, 2, SendGDIReinforcements)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -860,19 +860,12 @@ Actors:
|
|||||||
waypoint0: waypoint
|
waypoint0: waypoint
|
||||||
Location: 55,54
|
Location: 55,54
|
||||||
Owner: Neutral
|
Owner: Neutral
|
||||||
TownAttackTrigger: waypoint
|
|
||||||
# Location: 46,44
|
|
||||||
Location: 56,38
|
|
||||||
Owner: Neutral
|
|
||||||
TownAttackWpt: waypoint
|
TownAttackWpt: waypoint
|
||||||
Location: 24,48
|
Location: 24,48
|
||||||
Owner: Neutral
|
Owner: Neutral
|
||||||
NodReinfEntry: waypoint
|
NodReinfEntry: waypoint
|
||||||
Location: 61,55
|
Location: 61,55
|
||||||
Owner: Neutral
|
Owner: Neutral
|
||||||
GDIReinfTrigger: waypoint
|
|
||||||
Location: 32,52
|
|
||||||
Owner: Neutral
|
|
||||||
GDIReinfEntry1: waypoint
|
GDIReinfEntry1: waypoint
|
||||||
Location: 14,51
|
Location: 14,51
|
||||||
Owner: Neutral
|
Owner: Neutral
|
||||||
|
|||||||
Reference in New Issue
Block a user