Merge pull request #7337 from reaperrr/common29

Moved Lint, UtilityCommands and Scripting properties to Mods.Common
This commit is contained in:
Matthias Mailänder
2015-01-17 14:53:48 +01:00
28 changed files with 58 additions and 71 deletions

View File

@@ -0,0 +1,68 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Reflection;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
public class CheckActorReferences : ILintPass
{
Action<string> emitError;
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
this.emitError = emitError;
foreach (var actorInfo in map.Rules.Actors)
foreach (var traitInfo in actorInfo.Value.Traits.WithInterface<ITraitInfo>())
CheckTrait(actorInfo.Value, traitInfo, map);
}
void CheckTrait(ActorInfo actorInfo, ITraitInfo traitInfo, Map map)
{
var actualType = traitInfo.GetType();
foreach (var field in actualType.GetFields())
{
if (field.HasAttribute<ActorReferenceAttribute>())
CheckReference(actorInfo, traitInfo, field, map.Rules.Actors, "actor");
if (field.HasAttribute<WeaponReferenceAttribute>())
CheckReference(actorInfo, traitInfo, field, map.Rules.Weapons, "weapon");
if (field.HasAttribute<VoiceReferenceAttribute>())
CheckReference(actorInfo, traitInfo, field, map.Rules.Voices, "voice");
}
}
string[] GetFieldValues(ITraitInfo traitInfo, FieldInfo fieldInfo)
{
var type = fieldInfo.FieldType;
if (type == typeof(string))
return new string[] { (string)fieldInfo.GetValue(traitInfo) };
if (type == typeof(string[]))
return (string[])fieldInfo.GetValue(traitInfo);
emitError("Bad type for reference on {0}.{1}. Supported types: string, string[]"
.F(traitInfo.GetType().Name, fieldInfo.Name));
return new string[] { };
}
void CheckReference<T>(ActorInfo actorInfo, ITraitInfo traitInfo, FieldInfo fieldInfo,
IReadOnlyDictionary<string, T> dict, string type)
{
var values = GetFieldValues(traitInfo, fieldInfo);
foreach (var v in values)
if (v != null && !dict.ContainsKey(v.ToLowerInvariant()))
emitError("{0}.{1}.{2}: Missing {3} `{4}`."
.F(actorInfo.Name, traitInfo.GetType().Name, fieldInfo.Name, type, v));
}
}
}

View File

@@ -0,0 +1,27 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
public class CheckActors : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
var actorTypes = map.Actors.Value.Values.Select(a => a.Type);
foreach (var actor in actorTypes)
if (!map.Rules.Actors.Keys.Contains(actor.ToLowerInvariant()))
emitError("Actor {0} is not defined by any rule.".F(actor));
}
}
}

View File

@@ -0,0 +1,28 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
public class CheckMapCordon : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
if (map.Bounds.Left == 0 || map.Bounds.Top == 0
|| map.Bounds.Right == map.MapSize.X || map.Bounds.Bottom == map.MapSize.Y)
emitError("This map does not define a valid cordon.\n"
+ "A one cell (or greater) border is required on all four sides "
+ "between the playable bounds and the map edges");
}
}
}

View File

@@ -0,0 +1,30 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
public class CheckMapRules : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
try
{
Game.ModData.RulesetCache.LoadMapRules(map);
}
catch (Exception e)
{
emitError(e.Message);
}
}
}
}

View File

@@ -0,0 +1,38 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
public class CheckPlayers : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
var playerNames = map.Players.Values.Select(p => p.Name);
foreach (var player in map.Players)
foreach (var ally in player.Value.Allies)
if (!playerNames.Contains(ally))
emitError("Allies contains player {0} that is not in list.".F(ally));
foreach (var player in map.Players)
foreach (var enemy in player.Value.Enemies)
if (!playerNames.Contains(enemy))
emitError("Enemies contains player {0} that is not in list.".F(enemy));
var races = map.Rules.Actors["world"].Traits.WithInterface<CountryInfo>().Select(c => c.Race);
foreach (var player in map.Players)
if (!string.IsNullOrWhiteSpace(player.Value.Race) && player.Value.Race != "Random" && !races.Contains(player.Value.Race))
emitError("Invalid race {0} chosen for player {1}.".F(player.Value.Race, player.Value.Name));
}
}
}

View File

@@ -0,0 +1,33 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
class CheckSequences : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
var sequences = MiniYaml.MergeLiberal(map.SequenceDefinitions, Game.ModData.Manifest.Sequences.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
foreach (var actorInfo in map.Rules.Actors)
foreach (var renderInfo in actorInfo.Value.Traits.WithInterface<RenderSimpleInfo>())
{
var image = renderInfo.Image ?? actorInfo.Value.Name;
if (!sequences.Any(s => s.Key == image.ToLowerInvariant()) && !actorInfo.Value.Name.Contains("^"))
emitWarning("Sprite image {0} from actor {1} has no sequence definition.".F(image, actorInfo.Value.Name));
}
}
}
}

View File

@@ -0,0 +1,41 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using System.Reflection;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
class CheckSyncAnnotations : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
/* first, check all the types implementing ISync */
foreach (var t in Game.ModData.ObjectCreator.GetTypesImplementing<ISync>())
if (!HasAnySyncFields(t))
emitWarning("{0} has ISync but nothing marked with [Sync]".F(t.Name));
}
bool HasAnySyncFields(Type t)
{
var flags = BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance;
var fs = t.GetFields(flags);
var ps = t.GetProperties(flags);
return fs.Any(f => f.HasAttribute<SyncAttribute>()) ||
ps.Any(p => p.HasAttribute<SyncAttribute>()) ||
HasAnySyncFields(t.BaseType);
}
}
}

View File

@@ -0,0 +1,34 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
public class CheckTraitPrerequisites : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
foreach (var actorInfo in map.Rules.Actors.Where(a => !a.Key.StartsWith("^")))
try
{
var traits = actorInfo.Value.TraitsInConstructOrder().ToArray();
if (traits.Length == 0)
emitWarning("Actor {0} has no traits. Is this intended?".F(actorInfo.Key));
}
catch (Exception e)
{
emitError("Actor {0} is not constructible; failure: {1}".F(actorInfo.Key, e.Message));
}
}
}
}

View File

@@ -0,0 +1,51 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
class LintBuildablePrerequisites : ILintPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{
// Buildings provide their actor names as a prerequisite
var buildingPrereqs = map.Rules.Actors.Where(a => a.Value.Traits.Contains<BuildingInfo>())
.Select(a => a.Key);
// ProvidesCustomPrerequisite allows arbitrary prereq definitions
var customPrereqs = map.Rules.Actors.SelectMany(a => a.Value.Traits
.WithInterface<ProvidesCustomPrerequisiteInfo>())
.Select(p => p.Prerequisite);
// ProvidesTechPrerequisite allows arbitrary prereq definitions
// (but only one group at a time during gameplay)
var techPrereqs = map.Rules.Actors.SelectMany(a => a.Value.Traits
.WithInterface<ProvidesTechPrerequisiteInfo>())
.SelectMany(p => p.Prerequisites);
var providedPrereqs = buildingPrereqs.Concat(customPrereqs).Concat(techPrereqs);
// TODO: this check is case insensitive while the real check in-game is not
foreach (var i in map.Rules.Actors)
{
var bi = i.Value.Traits.GetOrDefault<BuildableInfo>();
if (bi != null)
foreach (var prereq in bi.Prerequisites)
if (!prereq.StartsWith("~disabled"))
if (!providedPrereqs.Contains(prereq.Replace("!", "").Replace("~", "")))
emitError("Buildable actor {0} has prereq {1} not provided by anything.".F(i.Key, prereq));
}
}
}
}

View File

@@ -32,8 +32,12 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Eluant">
<HintPath>..\thirdparty\Eluant.dll</HintPath>
<Private>False</Private>
@@ -126,6 +130,15 @@
<Compile Include="Graphics\TextRenderable.cs" />
<Compile Include="Graphics\VoxelActorPreview.cs" />
<Compile Include="Graphics\VoxelRenderable.cs" />
<Compile Include="Lint\CheckSequences.cs" />
<Compile Include="Lint\CheckPlayers.cs" />
<Compile Include="Lint\CheckActors.cs" />
<Compile Include="Lint\CheckMapCordon.cs" />
<Compile Include="Lint\CheckMapRules.cs" />
<Compile Include="Lint\CheckActorReferences.cs" />
<Compile Include="Lint\CheckSyncAnnotations.cs" />
<Compile Include="Lint\CheckTraitPrerequisites.cs" />
<Compile Include="Lint\LintBuildablePrerequisites.cs" />
<Compile Include="LoadScreens\ModChooserLoadScreen.cs" />
<Compile Include="Orders\BeaconOrderGenerator.cs" />
<Compile Include="Orders\DeployOrderTargeter.cs" />
@@ -151,6 +164,7 @@
<Compile Include="Scripting\Global\MapGlobal.cs" />
<Compile Include="Scripting\Global\MediaGlobal.cs" />
<Compile Include="Scripting\Global\PlayerGlobal.cs" />
<Compile Include="Scripting\Global\ReinforcementsGlobal.cs" />
<Compile Include="Scripting\Global\TriggerGlobal.cs" />
<Compile Include="Scripting\Global\UtilsGlobal.cs" />
<Compile Include="Scripting\Properties\DiplomacyProperties.cs" />
@@ -158,6 +172,18 @@
<Compile Include="Scripting\Properties\PowerProperties.cs" />
<Compile Include="Scripting\Properties\ResourceProperties.cs" />
<Compile Include="Scripting\Properties\UpgradeProperties.cs" />
<Compile Include="Scripting\Properties\AirstrikeProperties.cs" />
<Compile Include="Scripting\Properties\ProductionProperties.cs" />
<Compile Include="Scripting\Properties\MissionObjectiveProperties.cs" />
<Compile Include="Scripting\Properties\MobileProperties.cs" />
<Compile Include="Scripting\Properties\GeneralProperties.cs" />
<Compile Include="Scripting\Properties\CombatProperties.cs" />
<Compile Include="Scripting\Properties\PlayerProperties.cs" />
<Compile Include="Scripting\Properties\HelicopterProperties.cs" />
<Compile Include="Scripting\Properties\PlaneProperties.cs" />
<Compile Include="Scripting\Properties\TransformProperties.cs" />
<Compile Include="Scripting\Properties\RepairableBuildingProperties.cs" />
<Compile Include="Scripting\Properties\GuardProperties.cs" />
<Compile Include="TraitsInterfaces.cs" />
<Compile Include="Traits\Air\Aircraft.cs" />
<Compile Include="Traits\Air\AttackBomber.cs" />
@@ -254,6 +280,7 @@
<Compile Include="Traits\PaletteEffects\WaterPaletteRotation.cs" />
<Compile Include="Traits\Player\ActorGroupProxy.cs" />
<Compile Include="Traits\Player\AllyRepair.cs" />
<Compile Include="Traits\Player\BaseAttackNotifier.cs" />
<Compile Include="Traits\Player\ClassicProductionQueue.cs" />
<Compile Include="Traits\Player\ConquestVictoryConditions.cs" />
<Compile Include="Traits\Player\GlobalUpgradeManager.cs" />
@@ -374,8 +401,11 @@
<Compile Include="Traits\World\SpawnMPUnits.cs" />
<Compile Include="Traits\World\StartGameNotification.cs" />
<Compile Include="Traits\World\TerrainGeometryOverlay.cs" />
<Compile Include="UtilityCommands\ActorStatsExport.cs" />
<Compile Include="UtilityCommands\ConvertPngToShpCommand.cs" />
<Compile Include="UtilityCommands\ConvertSpriteToPngCommand.cs" />
<Compile Include="UtilityCommands\ExportCharacterSeparatedRules.cs" />
<Compile Include="UtilityCommands\Extensions.cs" />
<Compile Include="UtilityCommands\ExtractFilesCommand.cs" />
<Compile Include="UtilityCommands\ExtractLanguageStringsCommand.cs" />
<Compile Include="UtilityCommands\ExtractLuaDocsCommand.cs" />

View File

@@ -0,0 +1,180 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 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.Traits;
using OpenRA.Primitives;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptGlobal("Reinforcements")]
public class ReinforcementsGlobal : ScriptGlobal
{
public ReinforcementsGlobal(ScriptContext context) : base(context) { }
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.Traits.GetOrDefault<AircraftInfo>();
initDict.Add(new CenterPositionInit(owner.World.Map.CenterOfCell(entryLocation.Value) + new WVec(0, 0, pi != null ? pi.CruiseAltitude.Range : 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)")]
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 ? actionFunc.CopyReference() as LuaFunction : null;
var actor = CreateActor(owner, actorTypes[i], false, entryPath[0], entryPath.Length > 1 ? entryPath[1] : (CPos?)null);
actors.Add(actor);
var actionDelay = i * interval;
Action actorAction = () =>
{
Context.World.Add(actor);
for (var j = 1; j < entryPath.Length; j++)
Move(actor, entryPath[j]);
if (af != null)
{
actor.QueueActivity(new CallFunc(() =>
{
af.Call(actor.ToLuaValue(Context));
af.Dispose();
}));
}
};
Context.World.AddFrameEndTask(w => w.Add(new DelayedAction(actionDelay, actorAction)));
}
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).")]
public LuaTable ReinforceWithTransport(Player owner, string actorType, string[] cargoTypes, CPos[] entryPath, CPos[] exitPath = null,
LuaFunction actionFunc = null, LuaFunction exitFunc = null)
{
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 = actionFunc.CopyReference() as LuaFunction;
transport.QueueActivity(new CallFunc(() =>
{
af.Call(transport.ToLuaValue(Context), passengers.ToArray().ToLuaValue(Context));
af.Dispose();
}));
}
else
{
var heli = transport.TraitOrDefault<Helicopter>();
if (heli != null)
{
transport.QueueActivity(new Turn(transport, heli.Info.InitialFacing));
transport.QueueActivity(new HeliLand(true));
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(heli != null ? 50 : 25));
}
if (exitFunc != null)
{
var ef = exitFunc.CopyReference() as LuaFunction;
transport.QueueActivity(new CallFunc(() =>
{
ef.Call(transport.ToLuaValue(Context));
ef.Dispose();
}));
}
else if (exitPath != null)
{
foreach (var wpt in exitPath)
Move(transport, wpt);
transport.QueueActivity(new RemoveSelf());
}
var ret = Context.CreateTable();
ret.Add(1, transport.ToLuaValue(Context));
ret.Add(2, passengers.ToArray().ToLuaValue(Context));
return ret;
}
}
}

View File

@@ -0,0 +1,36 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System.Linq;
using Eluant;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptGlobal("Air Support Powers")]
public class AirstrikeProperties : ScriptActorProperties, Requires<AirstrikePowerInfo>
{
readonly AirstrikePower ap;
public AirstrikeProperties(ScriptContext context, Actor self)
: base(context, self)
{
ap = self.TraitsImplementing<AirstrikePower>().First();
}
[Desc("Activate the actor's Airstrike Power.")]
public void SendAirstrike(WPos target, bool randomize = true, int facing = 0)
{
ap.SendAirstrike(Self, target, randomize, facing);
}
}
}

View File

@@ -0,0 +1,77 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using Eluant;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Combat")]
public class CombatProperties : ScriptActorProperties, Requires<AttackBaseInfo>, Requires<IMoveInfo>
{
readonly IMove move;
public CombatProperties(ScriptContext context, Actor self)
: base(context, self)
{
move = self.Trait<IMove>();
}
[ScriptActorPropertyActivity]
[Desc("Seek out and attack nearby targets.")]
public void Hunt()
{
Self.QueueActivity(new Hunt(Self));
}
[ScriptActorPropertyActivity]
[Desc("Move to a cell, but stop and attack anything within range on the way. " +
"closeEnough defines an optional range (in cells) that will be considered " +
"close enough to complete the activity.")]
public void AttackMove(CPos cell, int closeEnough = 0)
{
Self.QueueActivity(new AttackMoveActivity(Self, move.MoveTo(cell, closeEnough)));
}
[ScriptActorPropertyActivity]
[Desc("Patrol along a set of given waypoints. The action is repeated by default, " +
"and the actor will wait for `wait` ticks at each waypoint.")]
public void Patrol(CPos[] waypoints, bool loop = true, int wait = 0)
{
foreach (var wpt in waypoints)
{
Self.QueueActivity(new AttackMoveActivity(Self, move.MoveTo(wpt, 2)));
Self.QueueActivity(new Wait(wait));
}
if (loop)
Self.QueueActivity(new CallFunc(() => Patrol(waypoints, loop, wait)));
}
[ScriptActorPropertyActivity]
[Desc("Patrol along a set of given waypoints until a condition becomes true. " +
"The actor will wait for `wait` ticks at each waypoint.")]
public void PatrolUntil(CPos[] waypoints, LuaFunction func, int wait = 0)
{
Patrol(waypoints, false, wait);
var repeat = func.Call(Self.ToLuaValue(Context)).First().ToBoolean();
if (repeat)
using (var f = func.CopyReference() as LuaFunction)
Self.QueueActivity(new CallFunc(() => PatrolUntil(waypoints, f, wait)));
}
}
}

View File

@@ -0,0 +1,165 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 Eluant;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ExposedForDestroyedActors]
[ScriptPropertyGroup("General")]
public class BaseActorProperties : ScriptActorProperties
{
// Note: This class must not make any trait queries so that this
// remains safe to call on dead actors.
public BaseActorProperties(ScriptContext context, Actor self)
: base(context, self) { }
[Desc("Specifies whether the actor is in the world.")]
public bool IsInWorld
{
get
{
return Self.IsInWorld;
}
set
{
if (value)
Self.World.AddFrameEndTask(w => w.Add(Self));
else
Self.World.AddFrameEndTask(w => w.Remove(Self));
}
}
[Desc("Specifies whether the actor is alive or dead.")]
public bool IsDead { get { return Self.IsDead; } }
[Desc("Specifies whether the actor is idle (not performing any activities).")]
public bool IsIdle { get { return Self.IsIdle; } }
[Desc("The player that owns the actor.")]
public Player Owner
{
get
{
return Self.Owner;
}
set
{
if (Self.Owner != value)
Self.ChangeOwner(value);
}
}
[Desc("The type of the actor (e.g. \"e1\").")]
public string Type { get { return Self.Info.Name; } }
[Desc("Test whether an actor has a specific property.")]
public bool HasProperty(string name)
{
return Self.HasScriptProperty(name);
}
}
[ScriptPropertyGroup("General")]
public class GeneralProperties : ScriptActorProperties
{
readonly IFacing facing;
readonly AutoTarget autotarget;
public GeneralProperties(ScriptContext context, Actor self)
: base(context, self)
{
facing = self.TraitOrDefault<IFacing>();
autotarget = self.TraitOrDefault<AutoTarget>();
}
[Desc("The actor position in cell coordinates.")]
public CPos Location { get { return Self.Location; } }
[Desc("The actor position in world coordinates.")]
public WPos CenterPosition { get { return Self.CenterPosition; } }
[Desc("The direction that the actor is facing.")]
public int Facing
{
get
{
if (facing == null)
throw new LuaException("Actor '{0}' doesn't define a facing".F(Self));
return facing.Facing;
}
}
[ScriptActorPropertyActivity]
[Desc("Instantly moves the actor to the specified cell.")]
public void Teleport(CPos cell)
{
Self.QueueActivity(new SimpleTeleport(cell));
}
[ScriptActorPropertyActivity]
[Desc("Run an arbitrary Lua function.")]
public void CallFunc(LuaFunction func)
{
Self.QueueActivity(new CallLuaFunc(func, Context));
}
[ScriptActorPropertyActivity]
[Desc("Wait for a specified number of game ticks (25 ticks = 1 second).")]
public void Wait(int ticks)
{
Self.QueueActivity(new Wait(ticks));
}
[ScriptActorPropertyActivity]
[Desc("Remove the actor from the game, without triggering any death notification.")]
public void Destroy()
{
Self.QueueActivity(new RemoveSelf());
}
[Desc("Attempt to cancel any active activities.")]
public void Stop()
{
Self.CancelActivity();
}
[Desc("Current actor stance. Returns nil if this actor doesn't support stances.")]
public string Stance
{
get
{
if (autotarget == null)
return null;
return autotarget.Stance.ToString();
}
set
{
if (autotarget == null)
return;
UnitStance stance;
if (!Enum<UnitStance>.TryParse(value, true, out stance))
throw new LuaException("Unknown stance type '{0}'".F(value));
autotarget.Stance = stance;
}
}
}
}

View File

@@ -0,0 +1,35 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Guard")]
public class GuardProperties : ScriptActorProperties, Requires<GuardInfo>, Requires<IMoveInfo>
{
Guard guard;
public GuardProperties(ScriptContext context, Actor self)
: base(context, self)
{
guard = self.Trait<Guard>();
}
[ScriptActorPropertyActivity]
[Desc("Guard the target actor.")]
public void Guard(Actor targetActor)
{
if (targetActor.HasTrait<Guardable>())
guard.GuardTarget(Self, Target.FromActor(targetActor));
}
}
}

View File

@@ -0,0 +1,31 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Movement")]
public class HelicopterProperties : ScriptActorProperties, Requires<HelicopterInfo>
{
public HelicopterProperties(ScriptContext context, Actor self)
: base(context, self) { }
[ScriptActorPropertyActivity]
[Desc("Fly within the cell grid.")]
public void Move(CPos cell)
{
Self.QueueActivity(new HeliFly(Self, Target.FromCell(Self.World, cell)));
}
}
}

View File

@@ -0,0 +1,118 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using Eluant;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("MissionObjectives")]
public class MissionObjectiveProperties : ScriptPlayerProperties
{
readonly MissionObjectives mo;
public MissionObjectiveProperties(ScriptContext context, Player player)
: base(context, player)
{
mo = player.PlayerActor.Trait<MissionObjectives>();
}
[ScriptActorPropertyActivity]
[Desc("Add a primary mission objective for this player. The function returns the " +
"ID of the newly created objective, so that it can be referred to later.")]
public int AddPrimaryObjective(string description)
{
return mo.Add(Player, description, ObjectiveType.Primary);
}
[ScriptActorPropertyActivity]
[Desc("Add a secondary mission objective for this player. The function returns the " +
"ID of the newly created objective, so that it can be referred to later.")]
public int AddSecondaryObjective(string description)
{
return mo.Add(Player, description, ObjectiveType.Secondary);
}
[ScriptActorPropertyActivity]
[Desc("Mark an objective as completed. This needs the objective ID returned " +
"by AddObjective as argument. When this player has completed all primary " +
"objectives, (s)he has won the game.")]
public void MarkCompletedObjective(int id)
{
if (id < 0 || id >= mo.Objectives.Count)
throw new LuaException("Objective ID is out of range.");
mo.MarkCompleted(Player, id);
}
[ScriptActorPropertyActivity]
[Desc("Mark an objective as failed. This needs the objective ID returned " +
"by AddObjective as argument. Secondary objectives do not have any " +
"influence whatsoever on the outcome of the game.")]
public void MarkFailedObjective(int id)
{
if (id < 0 || id >= mo.Objectives.Count)
throw new LuaException("Objective ID is out of range.");
mo.MarkFailed(Player, id);
}
[ScriptActorPropertyActivity]
[Desc("Returns true if the objective has been successfully completed, false otherwise.")]
public bool IsObjectiveCompleted(int id)
{
if (id < 0 || id >= mo.Objectives.Count)
throw new LuaException("Objective ID is out of range.");
return mo.Objectives[id].State == ObjectiveState.Completed;
}
[ScriptActorPropertyActivity]
[Desc("Returns true if the objective has been failed, false otherwise.")]
public bool IsObjectiveFailed(int id)
{
if (id < 0 || id >= mo.Objectives.Count)
throw new LuaException("Objective ID is out of range.");
return mo.Objectives[id].State == ObjectiveState.Failed;
}
[ScriptActorPropertyActivity]
[Desc("Returns the description of an objective.")]
public string GetObjectiveDescription(int id)
{
if (id < 0 || id >= mo.Objectives.Count)
throw new LuaException("Objective ID is out of range.");
return mo.Objectives[id].Description;
}
[ScriptActorPropertyActivity]
[Desc("Returns the type of an objective.")]
public string GetObjectiveType(int id)
{
if (id < 0 || id >= mo.Objectives.Count)
throw new LuaException("Objective ID is out of range.");
return mo.Objectives[id].Type == ObjectiveType.Primary ? "Primary" : "Secondary";
}
[ScriptActorPropertyActivity]
[Desc("Returns true if this player has lost all units/actors that have" +
"the MustBeDestroyed trait (according to the short game option).")]
public bool HasNoRequiredUnits()
{
return Player.HasNoRequiredUnits();
}
}
}

View File

@@ -0,0 +1,58 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Movement")]
public class MobileProperties : ScriptActorProperties, Requires<MobileInfo>
{
readonly Mobile mobile;
public MobileProperties(ScriptContext context, Actor self)
: base(context, self)
{
mobile = self.Trait<Mobile>();
}
[ScriptActorPropertyActivity]
[Desc("Moves within the cell grid. closeEnough defines an optional range " +
"(in cells) that will be considered close enough to complete the activity.")]
public void Move(CPos cell, int closeEnough = 0)
{
Self.QueueActivity(new Move(Self, cell, WRange.FromCells(closeEnough)));
}
[ScriptActorPropertyActivity]
[Desc("Moves within the cell grid, ignoring lane biases.")]
public void ScriptedMove(CPos cell)
{
Self.QueueActivity(new Move(Self, cell));
}
[ScriptActorPropertyActivity]
[Desc("Moves from outside the world into the cell grid")]
public void MoveIntoWorld(CPos cell)
{
Self.QueueActivity(mobile.MoveIntoWorld(Self, cell, mobile.ToSubCell));
}
[ScriptActorPropertyActivity]
[Desc("Leave the current position in a random direction.")]
public void Scatter()
{
Self.Trait<Mobile>().Nudge(Self, Self, true);
}
}
}

View File

@@ -0,0 +1,51 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Movement")]
public class PlaneProperties : ScriptActorProperties, Requires<PlaneInfo>
{
public PlaneProperties(ScriptContext context, Actor self)
: base(context, self) { }
[ScriptActorPropertyActivity]
[Desc("Fly within the cell grid.")]
public void Move(CPos cell)
{
Self.QueueActivity(new Fly(Self, Target.FromCell(Self.World, cell)));
}
[ScriptActorPropertyActivity]
[Desc("Return to the base, which is either the airfield given, or an auto-selected one otherwise.")]
public void ReturnToBase(Actor airfield = null)
{
Self.QueueActivity(new ReturnToBase(Self, airfield));
}
}
[ScriptPropertyGroup("Combat")]
public class PlaneCombatProperties : ScriptActorProperties, Requires<AttackPlaneInfo>
{
public PlaneCombatProperties(ScriptContext context, Actor self)
: base(context, self) { }
[Desc("Fly an attack against the target actor.")]
public void Attack(Actor target)
{
Self.QueueActivity(new FlyAttack(Target.FromActor(target)));
}
}
}

View File

@@ -0,0 +1,36 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Linq;
using Eluant;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Player")]
public class PlayerProperties : ScriptPlayerProperties
{
public PlayerProperties(ScriptContext context, Player player)
: base(context, player) { }
[Desc("The player's name.")]
public string Name { get { return Player.PlayerName; } }
[Desc("Returns an array of actors representing all ground attack units of this player.")]
public Actor[] GetGroundAttackers()
{
return Player.World.ActorsWithTrait<AttackBase>().Select(a => a.Actor)
.Where(a => a.Owner == Player && !a.IsDead && a.IsInWorld && a.HasTrait<Mobile>())
.ToArray();
}
}
}

View File

@@ -0,0 +1,278 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Collections.Generic;
using System.Linq;
using Eluant;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Scripting;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Production")]
public class ProductionProperties : ScriptActorProperties, Requires<ProductionInfo>
{
readonly Production p;
public ProductionProperties(ScriptContext context, Actor self)
: base(context, self)
{
p = self.Trait<Production>();
}
[ScriptActorPropertyActivity]
[Desc("Build a unit, ignoring the production queue. The activity will wait if the exit is blocked.")]
public void Produce(string actorType, string raceVariant = null)
{
ActorInfo actorInfo;
if (!Self.World.Map.Rules.Actors.TryGetValue(actorType, out actorInfo))
throw new LuaException("Unknown actor type '{0}'".F(actorType));
Self.QueueActivity(new WaitFor(() => p.Produce(Self, actorInfo, raceVariant)));
}
}
[ScriptPropertyGroup("Production")]
public class RallyPointProperties : ScriptActorProperties, Requires<RallyPointInfo>
{
readonly RallyPoint rp;
public RallyPointProperties(ScriptContext context, Actor self)
: base(context, self)
{
rp = self.Trait<RallyPoint>();
}
[Desc("Query or set a factory's rally point")]
public CPos RallyPoint
{
get { return rp.Location; }
set { rp.Location = value; }
}
}
[ScriptPropertyGroup("Production")]
public class PrimaryBuildingProperties : ScriptActorProperties, Requires<PrimaryBuildingInfo>
{
readonly PrimaryBuilding pb;
public PrimaryBuildingProperties(ScriptContext context, Actor self)
: base(context, self)
{
pb = self.Trait<PrimaryBuilding>();
}
[Desc("Query or set the factory's primary building status")]
public bool IsPrimaryBuilding
{
get { return pb.IsPrimary; }
set { pb.SetPrimaryProducer(Self, value); }
}
}
[ScriptPropertyGroup("Production")]
public class ProductionQueueProperties : ScriptActorProperties, Requires<ProductionQueueInfo>, Requires<ScriptTriggersInfo>
{
readonly List<ProductionQueue> queues;
readonly ScriptTriggers triggers;
public ProductionQueueProperties(ScriptContext context, Actor self)
: base(context, self)
{
queues = self.TraitsImplementing<ProductionQueue>().Where(q => q.Enabled).ToList();
triggers = TriggerGlobal.GetScriptTriggers(self);
}
[Desc("Build the specified set of actors using a TD-style (per building) production queue. " +
"The function will return true if production could be started, false otherwise. " +
"If an actionFunc is given, it will be called as actionFunc(Actor[] actors) once " +
"production of all actors has been completed. The actors array is guaranteed to " +
"only contain alive actors.")]
public bool Build(string[] actorTypes, LuaFunction actionFunc = null)
{
if (triggers.Triggers[Trigger.OnProduction].Any())
return false;
var queue = queues.Where(q => actorTypes.All(t => GetBuildableInfo(t).Queue.Contains(q.Info.Type)))
.FirstOrDefault(q => q.CurrentItem() == null);
if (queue == null)
return false;
if (actionFunc != null)
{
var playerIndex = Self.Owner.ClientIndex;
var squadSize = actorTypes.Length;
var squad = new List<Actor>();
var func = actionFunc.CopyReference() as LuaFunction;
Action<Actor, Actor> productionHandler = (_, __) => { };
productionHandler = (factory, unit) =>
{
if (playerIndex != factory.Owner.ClientIndex)
{
triggers.OnProducedInternal -= productionHandler;
return;
}
squad.Add(unit);
if (squad.Count >= squadSize)
{
using (func)
using (var luaSquad = squad.Where(u => !u.IsDead).ToArray().ToLuaValue(Context))
func.Call(luaSquad).Dispose();
triggers.OnProducedInternal -= productionHandler;
}
};
triggers.OnProducedInternal += productionHandler;
}
foreach (var actorType in actorTypes)
queue.ResolveOrder(Self, Order.StartProduction(Self, actorType, 1));
return true;
}
[Desc("Check whether the factory's production queue that builds this type of actor is currently busy." +
"Note: it does not check whether this particular type of actor is being produced.")]
public bool IsProducing(string actorType)
{
if (triggers.Triggers[Trigger.OnProduction].Any())
return true;
return queues.Where(q => GetBuildableInfo(actorType).Queue.Contains(q.Info.Type))
.Any(q => q.CurrentItem() != null);
}
BuildableInfo GetBuildableInfo(string actorType)
{
var ri = Self.World.Map.Rules.Actors[actorType];
var bi = ri.Traits.GetOrDefault<BuildableInfo>();
if (bi == null)
throw new LuaException("Actor of type {0} cannot be produced".F(actorType));
else
return bi;
}
}
[ScriptPropertyGroup("Production")]
public class ClassicProductionQueueProperties : ScriptPlayerProperties, Requires<ClassicProductionQueueInfo>, Requires<ScriptTriggersInfo>
{
readonly Dictionary<string, Action<Actor, Actor>> productionHandlers;
readonly Dictionary<string, ClassicProductionQueue> queues;
public ClassicProductionQueueProperties(ScriptContext context, Player player)
: base(context, player)
{
productionHandlers = new Dictionary<string, Action<Actor, Actor>>();
queues = new Dictionary<string, ClassicProductionQueue>();
foreach (var q in player.PlayerActor.TraitsImplementing<ClassicProductionQueue>().Where(q => q.Enabled))
queues.Add(q.Info.Type, q);
Action<Actor, Actor> globalProductionHandler = (factory, unit) =>
{
if (factory.Owner != player)
return;
var queue = GetBuildableInfo(unit.Info.Name).Queue.First();
if (productionHandlers.ContainsKey(queue))
productionHandlers[queue](factory, unit);
};
var triggers = TriggerGlobal.GetScriptTriggers(player.PlayerActor);
triggers.OnOtherProducedInternal += globalProductionHandler;
}
[Desc("Build the specified set of actors using classic (RA-style) production queues. " +
"The function will return true if production could be started, false otherwise. " +
"If an actionFunc is given, it will be called as actionFunc(Actor[] actors) once " +
"production of all actors has been completed. The actors array is guaranteed to " +
"only contain alive actors. Note: This function will fail to work when called " +
"during the first tick")]
public bool Build(string[] actorTypes, LuaFunction actionFunc = null)
{
var typeToQueueMap = new Dictionary<string, string>();
foreach (var actorType in actorTypes.Distinct())
typeToQueueMap.Add(actorType, GetBuildableInfo(actorType).Queue.First());
var queueTypes = typeToQueueMap.Values.Distinct();
if (queueTypes.Any(t => !queues.ContainsKey(t) || productionHandlers.ContainsKey(t)))
return false;
if (queueTypes.Any(t => queues[t].CurrentItem() != null))
return false;
if (actionFunc != null)
{
var squadSize = actorTypes.Length;
var squad = new List<Actor>();
var func = actionFunc.CopyReference() as LuaFunction;
Action<Actor, Actor> productionHandler = (factory, unit) =>
{
squad.Add(unit);
if (squad.Count >= squadSize)
{
using (func)
using (var luaSquad = squad.Where(u => !u.IsDead).ToArray().ToLuaValue(Context))
func.Call(luaSquad).Dispose();
foreach (var q in queueTypes)
productionHandlers.Remove(q);
}
};
foreach (var q in queueTypes)
productionHandlers.Add(q, productionHandler);
}
foreach (var actorType in actorTypes)
{
var queue = queues[typeToQueueMap[actorType]];
queue.ResolveOrder(queue.Actor, Order.StartProduction(queue.Actor, actorType, 1));
}
return true;
}
[Desc("Check whether the production queue that builds this type of actor is currently busy." +
"Note: it does not check whether this particular type of actor is being produced.")]
public bool IsProducing(string actorType)
{
var queue = GetBuildableInfo(actorType).Queue.First();
if (!queues.ContainsKey(queue))
return true;
return productionHandlers.ContainsKey(queue) || queues[queue].CurrentItem() != null;
}
BuildableInfo GetBuildableInfo(string actorType)
{
var ri = Player.World.Map.Rules.Actors[actorType];
var bi = ri.Traits.GetOrDefault<BuildableInfo>();
if (bi == null)
throw new LuaException("Actor of type {0} cannot be produced".F(actorType));
else
return bi;
}
}
}

View File

@@ -0,0 +1,46 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("General")]
public class RepairableBuildingProperties : ScriptActorProperties, Requires<RepairableBuildingInfo>
{
readonly RepairableBuilding rb;
public RepairableBuildingProperties(ScriptContext context, Actor self)
: base(context, self)
{
rb = self.Trait<RepairableBuilding>();
}
[Desc("Start repairs on this building. `repairer` can be an allied player.")]
public void StartBuildingRepairs(Player repairer = null)
{
repairer = repairer ?? Self.Owner;
if (!rb.Repairers.Contains(repairer))
rb.RepairBuilding(Self, repairer);
}
[Desc("Stop repairs on this building. `repairer` can be an allied player.")]
public void StopBuildingRepairs(Player repairer = null)
{
repairer = repairer ?? Self.Owner;
if (rb.RepairActive && rb.Repairers.Contains(repairer))
rb.RepairBuilding(Self, repairer);
}
}
}

View File

@@ -0,0 +1,35 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptPropertyGroup("Transform")]
public class TransformProperties : ScriptActorProperties, Requires<TransformsInfo>
{
readonly Transforms transforms;
public TransformProperties(ScriptContext context, Actor self)
: base(context, self)
{
transforms = self.Trait<Transforms>();
}
[ScriptActorPropertyActivity]
[Desc("Queue a new transformation.")]
public void Deploy()
{
transforms.DeployTransform(true);
}
}
}

View File

@@ -0,0 +1,77 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System.Drawing;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Plays an audio notification and shows a radar ping when a building is attacked.",
"Attach this to the player actor.")]
public class BaseAttackNotifierInfo : ITraitInfo
{
[Desc("Minimum duration (in seconds) between notification events.")]
public readonly int NotifyInterval = 30;
public readonly Color RadarPingColor = Color.Red;
[Desc("Length of time (in ticks) to display a location ping in the minimap.")]
public readonly int RadarPingDuration = 10 * 25;
[Desc("The audio notification type to play.")]
public string Notification = "BaseAttack";
public object Create(ActorInitializer init) { return new BaseAttackNotifier(init.Self, this); }
}
public class BaseAttackNotifier : INotifyDamage
{
readonly RadarPings radarPings;
readonly BaseAttackNotifierInfo info;
int lastAttackTime;
public BaseAttackNotifier(Actor self, BaseAttackNotifierInfo info)
{
radarPings = self.World.WorldActor.TraitOrDefault<RadarPings>();
this.info = info;
lastAttackTime = -info.NotifyInterval * 25;
}
public void Damaged(Actor self, AttackInfo e)
{
// only track last hit against our base
if (!self.HasTrait<Building>())
return;
if (e.Attacker == null)
return;
if (e.Attacker.Owner == self.Owner)
return;
if (e.Attacker == self.World.WorldActor)
return;
if (e.Attacker.Owner.IsAlliedWith(self.Owner) && e.Damage <= 0)
return;
if (self.World.WorldTick - lastAttackTime > info.NotifyInterval * 25)
{
Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.Notification, self.Owner.Country.Race);
if (radarPings != null)
radarPings.Add(() => self.Owner == self.World.LocalPlayer, self.CenterPosition, info.RadarPingColor, info.RadarPingDuration);
}
lastAttackTime = self.World.WorldTick;
}
}
}

View File

@@ -0,0 +1,100 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.UtilityCommands
{
public static class ActorStatsExport
{
public static DataTable GenerateTable()
{
var rules = Game.ModData.RulesetCache.LoadDefaultRules();
var table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Cost", typeof(int));
table.Columns.Add("HitPoints", typeof(int));
table.Columns.Add("Armor", typeof(string));
table.Columns.Add("Damage /s", typeof(float));
var armorList = new List<string>();
foreach (var actorInfo in rules.Actors.Values)
{
var armor = actorInfo.Traits.GetOrDefault<ArmorInfo>();
if (armor != null)
if (!armorList.Contains(armor.Type))
armorList.Add(armor.Type);
}
armorList.Sort();
foreach (var armorType in armorList)
table.Columns.Add("vs. " + armorType, typeof(float));
foreach (var actorInfo in rules.Actors.Values)
{
if (actorInfo.Name.StartsWith("^"))
continue;
var buildable = actorInfo.Traits.GetOrDefault<BuildableInfo>();
if (buildable == null)
continue;
var row = table.NewRow();
var tooltip = actorInfo.Traits.GetOrDefault<TooltipInfo>();
row["Name"] = tooltip != null ? tooltip.Name : actorInfo.Name;
var value = actorInfo.Traits.GetOrDefault<ValuedInfo>();
row["Cost"] = value != null ? value.Cost : 0;
var health = actorInfo.Traits.GetOrDefault<HealthInfo>();
row["HitPoints"] = health != null ? health.HP : 0;
var armor = actorInfo.Traits.GetOrDefault<ArmorInfo>();
row["Armor"] = armor != null ? armor.Type : "";
var armaments = actorInfo.Traits.WithInterface<ArmamentInfo>();
if (armaments.Any())
{
var weapons = armaments.Select(a => rules.Weapons[a.Weapon.ToLowerInvariant()]);
foreach (var weapon in weapons)
{
var warhead = weapon.Warheads.FirstOrDefault(w => (w is DamageWarhead)) as DamageWarhead;
if (warhead != null)
{
var rateOfFire = weapon.ReloadDelay > 1 ? weapon.ReloadDelay : 1;
var burst = weapon.Burst;
var delay = weapon.BurstDelay;
var damage = warhead.Damage;
var damagePerSecond = (1000f / Game.Timestep) * (damage * burst) / (delay + burst * rateOfFire);
row["Damage /s"] = Math.Round(damagePerSecond, 1, MidpointRounding.AwayFromZero);
foreach (var armorType in armorList)
{
var vs = warhead.Versus.ContainsKey(armorType) ? warhead.Versus[armorType] : 100;
row["vs. " + armorType] = Math.Round(damagePerSecond * vs / 100, 1, MidpointRounding.AwayFromZero);
}
}
}
}
table.Rows.Add(row);
}
return table;
}
}
}

View File

@@ -0,0 +1,32 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.IO;
namespace OpenRA.Mods.Common.UtilityCommands
{
class ExportCharacterSeparatedRules : IUtilityCommand
{
public string Name { get { return "--generate-dps-table"; } }
[Desc("Export the damage per second evaluation into a CSV file for inspection.")]
public void Run(ModData modData, string[] args)
{
Game.ModData = modData;
var table = ActorStatsExport.GenerateTable();
var filename = "{0}-mod-dps.csv".F(Game.ModData.Manifest.Mod.Id);
using (var outfile = new StreamWriter(filename))
outfile.Write(table.ToCharacterSeparatedValues(";", true));
Console.WriteLine("{0} has been saved.".F(filename));
Console.WriteLine("Open as values separated by semicolon.");
}
}
}

View File

@@ -0,0 +1,54 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 System;
using System.Data;
using System.Text;
namespace OpenRA.Mods.Common.UtilityCommands
{
public static class Extensions
{
public static string ToCharacterSeparatedValues(this DataTable table, string delimiter, bool includeHeader)
{
var result = new StringBuilder();
if (includeHeader)
{
foreach (DataColumn column in table.Columns)
{
result.Append(column.ColumnName);
result.Append(delimiter);
}
result.Remove(result.Length, 0);
result.AppendLine();
}
foreach (DataRow row in table.Rows)
{
for (var x = 0; x < table.Columns.Count; x++)
{
if (x != 0)
result.Append(delimiter);
result.Append(row[table.Columns[x]]);
}
result.AppendLine();
}
result.Remove(result.Length, 0);
result.AppendLine();
return result.ToString();
}
}
}