Add map scripting support
This commit is contained in:
@@ -22,7 +22,7 @@ namespace OpenRA.FileFormats
|
|||||||
Folders, MapFolders, Rules, ServerTraits,
|
Folders, MapFolders, Rules, ServerTraits,
|
||||||
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||||
Weapons, Voices, Notifications, Music, Movies, Translations, TileSets,
|
Weapons, Voices, Notifications, Music, Movies, Translations, TileSets,
|
||||||
ChromeMetrics, PackageContents;
|
ChromeMetrics, PackageContents, LuaScripts;
|
||||||
|
|
||||||
public readonly Dictionary<string, string> Packages;
|
public readonly Dictionary<string, string> Packages;
|
||||||
public readonly MiniYaml LoadScreen;
|
public readonly MiniYaml LoadScreen;
|
||||||
@@ -59,6 +59,7 @@ namespace OpenRA.FileFormats
|
|||||||
TileSets = YamlList(yaml, "TileSets");
|
TileSets = YamlList(yaml, "TileSets");
|
||||||
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
|
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
|
||||||
PackageContents = YamlList(yaml, "PackageContents");
|
PackageContents = YamlList(yaml, "PackageContents");
|
||||||
|
LuaScripts = YamlList(yaml, "LuaScripts");
|
||||||
|
|
||||||
LoadScreen = yaml["LoadScreen"];
|
LoadScreen = yaml["LoadScreen"];
|
||||||
LobbyDefaults = yaml["LobbyDefaults"];
|
LobbyDefaults = yaml["LobbyDefaults"];
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
public class Map
|
public class Map
|
||||||
{
|
{
|
||||||
[FieldLoader.Ignore] IFolder container;
|
[FieldLoader.Ignore] public IFolder Container;
|
||||||
public string Path { get; private set; }
|
public string Path { get; private set; }
|
||||||
|
|
||||||
// Yaml map data
|
// Yaml map data
|
||||||
@@ -132,7 +132,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
void AssertExists(string filename)
|
void AssertExists(string filename)
|
||||||
{
|
{
|
||||||
using (var s = container.GetContent(filename))
|
using (var s = Container.GetContent(filename))
|
||||||
if (s == null)
|
if (s == null)
|
||||||
throw new InvalidOperationException("Required file {0} not present in this map".F(filename));
|
throw new InvalidOperationException("Required file {0} not present in this map".F(filename));
|
||||||
}
|
}
|
||||||
@@ -142,12 +142,12 @@ namespace OpenRA
|
|||||||
public Map(string path)
|
public Map(string path)
|
||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
container = FileSystem.OpenPackage(path, null, int.MaxValue);
|
Container = FileSystem.OpenPackage(path, null, int.MaxValue);
|
||||||
|
|
||||||
AssertExists("map.yaml");
|
AssertExists("map.yaml");
|
||||||
AssertExists("map.bin");
|
AssertExists("map.bin");
|
||||||
|
|
||||||
var yaml = new MiniYaml(null, MiniYaml.FromStream(container.GetContent("map.yaml")));
|
var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml")));
|
||||||
FieldLoader.Load(this, yaml);
|
FieldLoader.Load(this, yaml);
|
||||||
Uid = ComputeHash();
|
Uid = ComputeHash();
|
||||||
|
|
||||||
@@ -263,17 +263,17 @@ namespace OpenRA
|
|||||||
|
|
||||||
// Create a new map package
|
// Create a new map package
|
||||||
// TODO: Add other files (custom assets) to the entries list
|
// TODO: Add other files (custom assets) to the entries list
|
||||||
container = FileSystem.CreatePackage(Path, int.MaxValue, entries);
|
Container = FileSystem.CreatePackage(Path, int.MaxValue, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update existing package
|
// Update existing package
|
||||||
container.Write(entries);
|
Container.Write(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileReference<ushort, byte>[,] LoadMapTiles()
|
public TileReference<ushort, byte>[,] LoadMapTiles()
|
||||||
{
|
{
|
||||||
var tiles = new TileReference<ushort, byte>[MapSize.X, MapSize.Y];
|
var tiles = new TileReference<ushort, byte>[MapSize.X, MapSize.Y];
|
||||||
using (var dataStream = container.GetContent("map.bin"))
|
using (var dataStream = Container.GetContent("map.bin"))
|
||||||
{
|
{
|
||||||
if (dataStream.ReadUInt8() != 1)
|
if (dataStream.ReadUInt8() != 1)
|
||||||
throw new InvalidDataException("Unknown binary map format");
|
throw new InvalidDataException("Unknown binary map format");
|
||||||
@@ -305,7 +305,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
var resources = new TileReference<byte, byte>[MapSize.X, MapSize.Y];
|
var resources = new TileReference<byte, byte>[MapSize.X, MapSize.Y];
|
||||||
|
|
||||||
using (var dataStream = container.GetContent("map.bin"))
|
using (var dataStream = Container.GetContent("map.bin"))
|
||||||
{
|
{
|
||||||
if (dataStream.ReadUInt8() != 1)
|
if (dataStream.ReadUInt8() != 1)
|
||||||
throw new InvalidDataException("Unknown binary map format");
|
throw new InvalidDataException("Unknown binary map format");
|
||||||
@@ -391,8 +391,8 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
// UID is calculated by taking an SHA1 of the yaml and binary data
|
// UID is calculated by taking an SHA1 of the yaml and binary data
|
||||||
// Read the relevant data into a buffer
|
// Read the relevant data into a buffer
|
||||||
var data = container.GetContent("map.yaml").ReadAllBytes()
|
var data = Container.GetContent("map.yaml").ReadAllBytes()
|
||||||
.Concat(container.GetContent("map.bin").ReadAllBytes()).ToArray();
|
.Concat(Container.GetContent("map.bin").ReadAllBytes()).ToArray();
|
||||||
|
|
||||||
// Take the SHA1
|
// Take the SHA1
|
||||||
using (var csp = SHA1.Create())
|
using (var csp = SHA1.Create())
|
||||||
|
|||||||
@@ -338,8 +338,10 @@
|
|||||||
<Compile Include="RepairsUnits.cs" />
|
<Compile Include="RepairsUnits.cs" />
|
||||||
<Compile Include="Reservable.cs" />
|
<Compile Include="Reservable.cs" />
|
||||||
<Compile Include="ScaredyCat.cs" />
|
<Compile Include="ScaredyCat.cs" />
|
||||||
|
<Compile Include="Scripting\LuaScriptInterface.cs" />
|
||||||
<Compile Include="Scripting\Media.cs" />
|
<Compile Include="Scripting\Media.cs" />
|
||||||
<Compile Include="Scripting\RASpecialPowers.cs" />
|
<Compile Include="Scripting\RASpecialPowers.cs" />
|
||||||
|
<Compile Include="Scripting\LuaScriptContext.cs" />
|
||||||
<Compile Include="SeedsResource.cs" />
|
<Compile Include="SeedsResource.cs" />
|
||||||
<Compile Include="SelfHealing.cs" />
|
<Compile Include="SelfHealing.cs" />
|
||||||
<Compile Include="Sellable.cs" />
|
<Compile Include="Sellable.cs" />
|
||||||
@@ -476,6 +478,10 @@
|
|||||||
<Compile Include="Effects\Rank.cs" />
|
<Compile Include="Effects\Rank.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LuaInterface\LuaInterface.csproj">
|
||||||
|
<Project>{e915a0a4-2641-4f7e-8a88-8f123fa88bf1}</Project>
|
||||||
|
<Name>LuaInterface</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||||
<Project>{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}</Project>
|
<Project>{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}</Project>
|
||||||
<Name>OpenRA.FileFormats</Name>
|
<Name>OpenRA.FileFormats</Name>
|
||||||
|
|||||||
134
OpenRA.Mods.RA/Scripting/LuaScriptContext.cs
Normal file
134
OpenRA.Mods.RA/Scripting/LuaScriptContext.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2013 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 System.Reflection;
|
||||||
|
using LuaInterface;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.RA.Scripting
|
||||||
|
{
|
||||||
|
public class LuaScriptContext : IDisposable
|
||||||
|
{
|
||||||
|
public Lua Lua { get; private set; }
|
||||||
|
|
||||||
|
public LuaScriptContext()
|
||||||
|
{
|
||||||
|
Log.Write("debug", "Creating Lua script context");
|
||||||
|
Lua = new Lua();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterObject(object target, string tableName, bool exposeAllMethods)
|
||||||
|
{
|
||||||
|
Log.Write("debug", "Registering object {0}", target);
|
||||||
|
|
||||||
|
if (tableName != null && Lua.GetTable(tableName) == null)
|
||||||
|
Lua.NewTable(tableName);
|
||||||
|
|
||||||
|
var type = target.GetType();
|
||||||
|
|
||||||
|
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
RegisterMethods(tableName, target, methods, exposeAllMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterType(Type type, string tableName, bool exposeAllMethods)
|
||||||
|
{
|
||||||
|
Log.Write("debug", "Registering type {0}", type);
|
||||||
|
|
||||||
|
if (tableName != null && Lua.GetTable(tableName) == null)
|
||||||
|
Lua.NewTable(tableName);
|
||||||
|
|
||||||
|
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
|
||||||
|
RegisterMethods(tableName, null, methods, exposeAllMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterMethods(string tableName, object target, IEnumerable<MethodInfo> methods, bool allMethods)
|
||||||
|
{
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
string methodName;
|
||||||
|
|
||||||
|
var attr = method.GetCustomAttributes<LuaGlobalAttribute>(true).FirstOrDefault();
|
||||||
|
if (attr == null)
|
||||||
|
{
|
||||||
|
if (allMethods)
|
||||||
|
methodName = method.Name;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
methodName = attr.Name ?? method.Name;
|
||||||
|
|
||||||
|
var methodTarget = method.IsStatic ? null : target;
|
||||||
|
|
||||||
|
if (tableName != null)
|
||||||
|
Lua.RegisterFunction(tableName + "." + methodName, methodTarget, method);
|
||||||
|
else
|
||||||
|
Lua.RegisterFunction(methodName, methodTarget, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogException(Exception e)
|
||||||
|
{
|
||||||
|
Game.Debug("{0}", e.Message);
|
||||||
|
Game.Debug("See debug.log for details");
|
||||||
|
Log.Write("debug", "{0}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadLuaScripts(Func<string, string> getFileContents, params string[] files)
|
||||||
|
{
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Write("debug", "Loading Lua script {0}", file);
|
||||||
|
var content = getFileContents(file);
|
||||||
|
Lua.DoString(content, file);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object[] InvokeLuaFunction(string name, params object[] args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var function = Lua.GetFunction(name);
|
||||||
|
if (function == null)
|
||||||
|
return null;
|
||||||
|
return function.Call(args);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogException(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Lua == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
Lua.Dispose();
|
||||||
|
Lua = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
~LuaScriptContext()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
165
OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs
Normal file
165
OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2013 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 LuaInterface;
|
||||||
|
using OpenRA.Effects;
|
||||||
|
using OpenRA.FileFormats;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
using WorldRenderer = OpenRA.Graphics.WorldRenderer;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.RA.Scripting
|
||||||
|
{
|
||||||
|
public class LuaScriptInterfaceInfo : ITraitInfo, Requires<SpawnMapActorsInfo>
|
||||||
|
{
|
||||||
|
public readonly string[] LuaScripts = { };
|
||||||
|
|
||||||
|
public object Create(ActorInitializer init) { return new LuaScriptInterface(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LuaScriptInterface : IWorldLoaded, ITick
|
||||||
|
{
|
||||||
|
World world;
|
||||||
|
readonly LuaScriptContext context = new LuaScriptContext();
|
||||||
|
readonly LuaScriptInterfaceInfo info;
|
||||||
|
|
||||||
|
public LuaScriptInterface(LuaScriptInterfaceInfo info)
|
||||||
|
{
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WorldLoaded(World w, WorldRenderer wr)
|
||||||
|
{
|
||||||
|
world = w;
|
||||||
|
AddMapActorGlobals();
|
||||||
|
context.Lua["World"] = w;
|
||||||
|
context.Lua["WorldRenderer"] = wr;
|
||||||
|
context.RegisterObject(this, "_OpenRA", false);
|
||||||
|
context.RegisterType(typeof(WVec), "WVec", true);
|
||||||
|
context.RegisterType(typeof(WPos), "WPos", true);
|
||||||
|
context.RegisterType(typeof(CPos), "CPos", true);
|
||||||
|
context.RegisterType(typeof(WRot), "WRot", true);
|
||||||
|
context.RegisterType(typeof(WAngle), "WAngle", true);
|
||||||
|
context.RegisterType(typeof(WRange), "WRange", true);
|
||||||
|
context.RegisterType(typeof(int2), "int2", true);
|
||||||
|
context.RegisterType(typeof(float2), "float2", true);
|
||||||
|
var sharedScripts = Game.modData.Manifest.LuaScripts ?? new string[0];
|
||||||
|
if (sharedScripts.Any())
|
||||||
|
context.LoadLuaScripts(f => FileSystem.Open(f).ReadAllText(), sharedScripts);
|
||||||
|
context.LoadLuaScripts(f => w.Map.Container.GetContent(f).ReadAllText(), info.LuaScripts);
|
||||||
|
context.InvokeLuaFunction("WorldLoaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddMapActorGlobals()
|
||||||
|
{
|
||||||
|
foreach (var kv in world.WorldActor.Trait<SpawnMapActors>().Actors)
|
||||||
|
context.Lua[kv.Key] = kv.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick(Actor self)
|
||||||
|
{
|
||||||
|
context.InvokeLuaFunction("Tick");
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public object New(string typeName, LuaTable args)
|
||||||
|
{
|
||||||
|
var type = Game.modData.ObjectCreator.FindType(typeName);
|
||||||
|
if (type == null)
|
||||||
|
throw new InvalidOperationException("Cannot locate type: {0}".F(typeName));
|
||||||
|
if (args == null)
|
||||||
|
return Activator.CreateInstance(type);
|
||||||
|
var argsArray = ConvertArgs(args);
|
||||||
|
return Activator.CreateInstance(type, argsArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
object[] ConvertArgs(LuaTable args)
|
||||||
|
{
|
||||||
|
var argsArray = new object[args.Keys.Count];
|
||||||
|
for (var i = 1; i <= args.Keys.Count; i++)
|
||||||
|
{
|
||||||
|
var arg = args[i] as LuaTable;
|
||||||
|
if (arg != null && arg[1] != null && arg[2] != null)
|
||||||
|
argsArray[i - 1] = Convert.ChangeType(arg[1], Enum<TypeCode>.Parse(arg[2].ToString()));
|
||||||
|
else
|
||||||
|
argsArray[i - 1] = args[i];
|
||||||
|
}
|
||||||
|
return argsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public void Debug(object obj)
|
||||||
|
{
|
||||||
|
if (obj != null)
|
||||||
|
Game.Debug(obj.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public object TraitOrDefault(Actor actor, string className)
|
||||||
|
{
|
||||||
|
var type = Game.modData.ObjectCreator.FindType(className);
|
||||||
|
if (type == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var method = typeof(Actor).GetMethod("TraitOrDefault");
|
||||||
|
var genericMethod = method.MakeGenericMethod(type);
|
||||||
|
return genericMethod.Invoke(actor, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public object Trait(Actor actor, string className)
|
||||||
|
{
|
||||||
|
var ret = TraitOrDefault(actor, className);
|
||||||
|
if (ret == null)
|
||||||
|
throw new InvalidOperationException("Actor {0} does not have trait of type {1}".F(actor, className));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public bool HasTrait(Actor actor, string className)
|
||||||
|
{
|
||||||
|
var ret = TraitOrDefault(actor, className);
|
||||||
|
return ret != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public object TraitInfoOrDefault(string actorType, string className)
|
||||||
|
{
|
||||||
|
var type = Game.modData.ObjectCreator.FindType(className);
|
||||||
|
if (type == null || !Rules.Info.ContainsKey(actorType))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Rules.Info[actorType].Traits.GetOrDefault(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public object TraitInfo(string actorType, string className)
|
||||||
|
{
|
||||||
|
var ret = TraitInfoOrDefault(actorType, className);
|
||||||
|
if (ret == null)
|
||||||
|
throw new InvalidOperationException("Actor type {0} does not have trait info of type {1}".F(actorType, className));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public bool HasTraitInfo(string actorType, string className)
|
||||||
|
{
|
||||||
|
var ret = TraitInfoOrDefault(actorType, className);
|
||||||
|
return ret != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LuaGlobal]
|
||||||
|
public void RunAfterDelay(double delay, Action func)
|
||||||
|
{
|
||||||
|
world.AddFrameEndTask(w => w.Add(new DelayedAction((int)delay, func)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user