Files
OpenRA/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs
reaperrr ea2e452075 Add SpawnActorEffect
And use it to drop DelayedAction from ReinforcementsGlobal.
2019-03-11 12:54:58 +01:00

238 lines
7.6 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Eluant;
using OpenRA.Activities;
using OpenRA.Effects;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Effects;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptGlobal("Reinforcements")]
public class ReinforcementsGlobal : ScriptGlobal
{
readonly DomainIndex domainIndex;
public ReinforcementsGlobal(ScriptContext context)
: base(context)
{
domainIndex = context.World.WorldActor.Trait<DomainIndex>();
}
Actor CreateActor(Player owner, string actorType, bool addToWorld, CPos? entryLocation = null, CPos? nextLocation = null)
{
ActorInfo ai;
if (!Context.World.Map.Rules.Actors.TryGetValue(actorType, out ai))
throw new LuaException("Unknown actor type '{0}'".F(actorType));
var initDict = new TypeDictionary();
initDict.Add(new OwnerInit(owner));
if (entryLocation.HasValue)
{
var pi = ai.TraitInfoOrDefault<AircraftInfo>();
initDict.Add(new CenterPositionInit(owner.World.Map.CenterOfCell(entryLocation.Value) + new WVec(0, 0, pi != null ? pi.CruiseAltitude.Length : 0)));
initDict.Add(new LocationInit(entryLocation.Value));
}
if (entryLocation.HasValue && nextLocation.HasValue)
initDict.Add(new FacingInit(Context.World.Map.FacingBetween(CPos.Zero, CPos.Zero + (nextLocation.Value - entryLocation.Value), 0)));
var actor = Context.World.CreateActor(addToWorld, actorType, initDict);
return actor;
}
void Move(Actor actor, CPos dest)
{
var move = actor.TraitOrDefault<IMove>();
if (move == null)
return;
actor.QueueActivity(move.MoveTo(dest, 2));
}
[Desc("Send reinforcements consisting of multiple units. Supports ground-based, naval and air units. " +
"The first member of the entryPath array will be the units' spawnpoint, " +
"while the last one will be their destination. If actionFunc is given, " +
"it will be executed once a unit has reached its destination. actionFunc " +
"will be called as actionFunc(Actor actor). " +
"Returns a table containing the deployed units.")]
public Actor[] Reinforce(Player owner, string[] actorTypes, CPos[] entryPath, int interval = 25, LuaFunction actionFunc = null)
{
var actors = new List<Actor>();
for (var i = 0; i < actorTypes.Length; i++)
{
var af = actionFunc != null ? (LuaFunction)actionFunc.CopyReference() : null;
var actor = CreateActor(owner, actorTypes[i], false, entryPath[0], entryPath.Length > 1 ? entryPath[1] : (CPos?)null);
actors.Add(actor);
var actionDelay = i * interval;
Activity queuedActivity = null;
if (af != null)
{
queuedActivity = new CallFunc(() =>
{
using (af)
using (var a = actor.ToLuaValue(Context))
af.Call(a);
});
}
// We need to exclude the spawn location from the movement path
var path = entryPath.Skip(1).ToArray();
Context.World.AddFrameEndTask(w => w.Add(new SpawnActorEffect(actor, actionDelay, path, queuedActivity)));
}
return actors.ToArray();
}
[Desc("Send reinforcements in a transport. A transport can be a ground unit (APC etc.), ships and aircraft. " +
"The first member of the entryPath array will be the spawnpoint for the transport, " +
"while the last one will be its destination. The last member of the exitPath array " +
"is be the place where the transport will be removed from the game. When the transport " +
"has reached the destination, it will unload its cargo unless a custom actionFunc has " +
"been supplied. Afterwards, the transport will follow the exitPath and leave the map, " +
"unless a custom exitFunc has been supplied. actionFunc will be called as " +
"actionFunc(Actor transport, Actor[] cargo). exitFunc will be called as exitFunc(Actor transport). " +
"dropRange determines how many cells away the transport will try to land " +
"if the actual destination is blocked (if the transport is an aircraft). " +
"Returns a table in which the first value is the transport, " +
"and the second a table containing the deployed units.")]
public LuaTable ReinforceWithTransport(Player owner, string actorType, string[] cargoTypes, CPos[] entryPath, CPos[] exitPath = null,
LuaFunction actionFunc = null, LuaFunction exitFunc = null, int dropRange = 3)
{
var transport = CreateActor(owner, actorType, true, entryPath[0], entryPath.Length > 1 ? entryPath[1] : (CPos?)null);
var cargo = transport.TraitOrDefault<Cargo>();
var passengers = new List<Actor>();
if (cargo != null && cargoTypes != null)
{
foreach (var cargoType in cargoTypes)
{
var passenger = CreateActor(owner, cargoType, false, entryPath[0]);
passengers.Add(passenger);
cargo.Load(transport, passenger);
}
}
for (var i = 1; i < entryPath.Length; i++)
Move(transport, entryPath[i]);
if (actionFunc != null)
{
var af = (LuaFunction)actionFunc.CopyReference();
transport.QueueActivity(new CallFunc(() =>
{
using (af)
using (LuaValue t = transport.ToLuaValue(Context), p = passengers.ToArray().ToLuaValue(Context))
af.Call(t, p);
}));
}
else
{
var aircraft = transport.TraitOrDefault<Aircraft>();
if (aircraft != null)
{
var destination = entryPath.Last();
// Try to find an alternative landing spot if we can't land at the current destination
if (!aircraft.CanLand(destination) && dropRange > 0)
{
var locomotors = cargo.Passengers
.Select(a => a.Info.TraitInfoOrDefault<MobileInfo>())
.Where(m => m != null)
.Distinct()
.Select(m => m.LocomotorInfo)
.ToList();
foreach (var c in transport.World.Map.FindTilesInCircle(destination, dropRange))
{
if (!aircraft.CanLand(c))
continue;
if (!locomotors.All(m => domainIndex.IsPassable(destination, c, m)))
continue;
destination = c;
break;
}
}
if (aircraft.Info.VTOL)
{
if (destination != entryPath.Last())
Move(transport, destination);
transport.QueueActivity(new Turn(transport, aircraft.Info.InitialFacing));
transport.QueueActivity(new HeliLand(transport, true));
}
else
{
transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, destination)));
}
transport.QueueActivity(new Wait(15));
}
if (cargo != null)
{
transport.QueueActivity(new UnloadCargo(transport, true));
transport.QueueActivity(new WaitFor(() => cargo.IsEmpty(transport)));
}
transport.QueueActivity(new Wait(aircraft != null ? 50 : 25));
}
if (exitFunc != null)
{
var ef = (LuaFunction)exitFunc.CopyReference();
transport.QueueActivity(new CallFunc(() =>
{
using (ef)
using (var t = transport.ToLuaValue(Context))
ef.Call(t);
}));
}
else if (exitPath != null)
{
foreach (var wpt in exitPath)
Move(transport, wpt);
transport.QueueActivity(new RemoveSelf());
}
var ret = Context.CreateTable();
using (LuaValue
tKey = 1,
tValue = transport.ToLuaValue(Context),
pKey = 2,
pValue = passengers.ToArray().ToLuaValue(Context))
{
ret.Add(tKey, tValue);
ret.Add(pKey, pValue);
}
return ret;
}
}
}