Merge pull request #6574 from pchote/celltrigger

Implement cell triggers.
This commit is contained in:
Matthias Mailänder
2014-09-30 20:49:03 +02:00
4 changed files with 184 additions and 37 deletions

View File

@@ -33,8 +33,58 @@ namespace OpenRA.Traits
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 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 List<Actor>[] actors;
@@ -166,8 +216,17 @@ namespace OpenRA.Traits
public void AddInfluence(Actor self, IOccupySpace ios)
{
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)
@@ -180,6 +239,11 @@ namespace OpenRA.Traits
var temp = influence[c.First];
RemoveInfluenceInner(ref temp, self);
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();
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)

View File

@@ -197,6 +197,65 @@ namespace OpenRA.Mods.RA.Scripting
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.")]
public void ClearAll(Actor a)
{

View File

@@ -1,14 +1,16 @@
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" }
GDIReinforcementsPart2 = { "e2", "e2", "e2", "e2", "e2" }
TownAttackWave1 = { "bggy", "bggy" }
TownAttackWave2 = { "ltnk", "ltnk" }
TownAttackWave3 = { "e1", "e1", "e1", "e1", "e3", "e3", "e3", "e3" }
TownAttackWpts = { waypoint1, waypoint2 }
TownAttackWpts = { waypoint1, waypoint2 }
Civvie1Wpts = { waypoint3, waypoint17 }
Civvie2Wpts = { waypoint26, waypoint3, waypoint9, waypoint4, waypoint5, waypoint6, waypoint8, waypoint7, waypoint1, waypoint2 }
Civvie1Wpts = { waypoint3, waypoint17 }
Civvie2Wpts = { waypoint26, waypoint3, waypoint9, waypoint4, waypoint5, waypoint6, waypoint8, waypoint7, waypoint1, waypoint2 }
FollowCivvieWpts = function(actor, wpts)
Utils.Do(wpts, function(wpt)
@@ -34,8 +36,6 @@ TownAttackAction = function(actor)
end
AttackTown = function()
TownAttackTriggered = true
Reinforcements.Reinforce(nod, TownAttackWave1, { NodReinfEntry.Location, waypoint0.Location }, Utils.Seconds(0.25), TownAttackAction)
Trigger.AfterDelay(Utils.Seconds(2), function()
Reinforcements.Reinforce(nod, TownAttackWave2, { NodReinfEntry.Location, waypoint0.Location }, Utils.Seconds(1), TownAttackAction)
@@ -46,8 +46,6 @@ AttackTown = function()
end
SendGDIReinforcements = function()
GDIReinforcementsTriggered = true
Reinforcements.Reinforce(player, GDIReinforcementsPart1, { GDIReinfEntry1.Location, waypoint12.Location }, Utils.Seconds(1), function(actor)
Media.PlaySpeechNotification(player, "Reinforce")
actor.Move(waypoint10.Location)
@@ -63,20 +61,9 @@ SendGDIReinforcements = function()
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()
player = Player.GetPlayer("GDI")
nod = Player.GetPlayer("Nod")
player = Player.GetPlayer("GDI")
nod = Player.GetPlayer("Nod")
nodObjective = nod.AddPrimaryObjective("Destroy all GDI troops")
gdiObjective1 = player.AddPrimaryObjective("Defend the town of Białystok")
@@ -107,6 +94,20 @@ WorldLoaded = function()
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)
unit.Stance = "Defend"
end)
@@ -121,8 +122,6 @@ WorldLoaded = function()
Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("gdi4a.vqa", function() Media.PlayMovieFullscreen("nodsweep.vqa") end) end)
end
TownAttackTriggered = false
GDIReinforcementsTriggered = false
Tick = function()
if player.HasNoRequiredUnits() then
nod.MarkCompletedObjective(nodObjective)
@@ -131,10 +130,4 @@ Tick = function()
player.MarkCompletedObjective(gdiObjective1)
player.MarkCompletedObjective(gdiObjective2)
end
if not TownAttackTriggered then
CellTrigger(player, TownAttackTrigger, 2, AttackTown)
elseif not GDIReinforcementsTriggered then
CellTrigger(player, GDIReinfTrigger, 2, SendGDIReinforcements)
end
end

View File

@@ -860,19 +860,12 @@ Actors:
waypoint0: waypoint
Location: 55,54
Owner: Neutral
TownAttackTrigger: waypoint
# Location: 46,44
Location: 56,38
Owner: Neutral
TownAttackWpt: waypoint
Location: 24,48
Owner: Neutral
NodReinfEntry: waypoint
Location: 61,55
Owner: Neutral
GDIReinfTrigger: waypoint
Location: 32,52
Owner: Neutral
GDIReinfEntry1: waypoint
Location: 14,51
Owner: Neutral