diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index 1ba63077b9..e1576b05db 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -557,6 +557,7 @@
+
diff --git a/OpenRA.Mods.Common/UtilityCommands/ExtractZeroBraneStudioLuaAPI.cs b/OpenRA.Mods.Common/UtilityCommands/ExtractZeroBraneStudioLuaAPI.cs
new file mode 100644
index 0000000000..32a0f84d64
--- /dev/null
+++ b/OpenRA.Mods.Common/UtilityCommands/ExtractZeroBraneStudioLuaAPI.cs
@@ -0,0 +1,152 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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.Linq;
+using System.Reflection;
+using OpenRA.Scripting;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.UtilityCommands
+{
+ // See https://studio.zerobrane.com/doc-api-auto-complete for reference
+ class ExtractZeroBraneStudioLuaAPI : IUtilityCommand
+ {
+ string IUtilityCommand.Name { get { return "--zbstudio-lua-api"; } }
+
+ bool IUtilityCommand.ValidateArguments(string[] args)
+ {
+ return true;
+ }
+
+ [Desc("Generate ZeroBrane Studio Lua API and auto-complete descriptions.")]
+ void IUtilityCommand.Run(Utility utility, string[] args)
+ {
+ // HACK: The engine code assumes that Game.modData is set.
+ Game.ModData = utility.ModData;
+
+ Console.WriteLine("local interpreter = {");
+ Console.WriteLine(" name = \"OpenRA\",");
+ Console.WriteLine(" description = \"OpenRA map scripting Lua API\",");
+ Console.WriteLine(" api = {\"baselib\", \"openra\"},");
+ Console.WriteLine(" hasdebugger = false,");
+ Console.WriteLine(" skipcompile = true,");
+ Console.WriteLine("}");
+ Console.WriteLine();
+
+ Console.WriteLine("-- This is an automatically generated Lua API definition generated for release-{0} of OpenRA.", Game.ModData.Manifest.Metadata.Version);
+ Console.WriteLine("-- https://github.com/OpenRA/OpenRA/wiki/Utility was used with the --zbstudio-lua-api parameter.");
+ Console.WriteLine("-- See https://github.com/OpenRA/OpenRA/wiki/Lua-API for human readable documentation.");
+ Console.WriteLine();
+ Console.WriteLine("local api = {");
+
+ var tables = Game.ModData.ObjectCreator.GetTypesImplementing().OrderBy(t => t.Name);
+ foreach (var t in tables)
+ {
+ var name = t.GetCustomAttributes(true).First().Name;
+ Console.WriteLine(" " + name + " = {");
+ Console.WriteLine(" type = \"class\",");
+ Console.WriteLine(" childs = {");
+
+ var members = ScriptMemberWrapper.WrappableMembers(t);
+ foreach (var member in members.OrderBy(m => m.Name))
+ {
+ Console.WriteLine(" " + member.Name + " = {");
+ var methodInfo = member as MethodInfo;
+ if (methodInfo != null)
+ Console.WriteLine(" type = \"function\",");
+
+ var propertyInfo = member as PropertyInfo;
+ if (propertyInfo != null)
+ Console.WriteLine(" type = \"value\",");
+
+ if (member.HasAttribute())
+ {
+ var desc = member.GetCustomAttributes(true).First().Lines.JoinWith("\n");
+ Console.WriteLine(" description = [[{0}]],", desc);
+ }
+
+ if (methodInfo != null)
+ {
+ var parameters = methodInfo.GetParameters().Select(pi => pi.LuaDocString());
+ Console.WriteLine(" args = \"({0})\",", parameters.JoinWith(", "));
+
+ var returnType = methodInfo.ReturnType.LuaDocString();
+ Console.WriteLine(" returns = \"({0})\",", returnType);
+ }
+
+ Console.WriteLine(" },");
+ }
+
+ Console.WriteLine(" }");
+ Console.WriteLine(" },");
+ }
+
+ var actorProperties = Game.ModData.ObjectCreator.GetTypesImplementing().SelectMany(cg => {
+ return ScriptMemberWrapper.WrappableMembers(cg);
+ });
+
+ var scriptProperties = Game.ModData.ObjectCreator.GetTypesImplementing().SelectMany(cg => {
+ return ScriptMemberWrapper.WrappableMembers(cg);
+ });
+
+ var properties = actorProperties.Concat(scriptProperties);
+ foreach (var property in properties.OrderBy(m => m.Name))
+ {
+ Console.WriteLine(" " + property.Name + " = {");
+
+ var methodInfo = property as MethodInfo;
+ if (methodInfo != null)
+ Console.WriteLine(" type = \"function\",");
+
+ var propertyInfo = property as PropertyInfo;
+ if (propertyInfo != null)
+ Console.WriteLine(" type = \"value\",");
+
+ if (property.HasAttribute())
+ {
+ var desc = property.GetCustomAttributes(true).First().Lines.JoinWith("\n");
+ Console.WriteLine(" description = [[{0}]],", desc);
+ }
+
+ if (methodInfo != null)
+ {
+ var parameters = methodInfo.GetParameters().Select(pi => pi.LuaDocString());
+ Console.WriteLine(" args = \"({0})\",", parameters.JoinWith(", "));
+
+ var returnType = methodInfo.ReturnType.LuaDocString();
+ Console.WriteLine(" returns = \"({0})\",", returnType);
+ }
+
+ Console.WriteLine(" },");
+ }
+
+ Console.WriteLine("}");
+ Console.WriteLine();
+ Console.WriteLine("return {");
+ Console.WriteLine(" name = \"OpenRA\",");
+ Console.WriteLine(" description = \"API description for auto-complete and tooltip support\",");
+ Console.WriteLine(" author = \"Matthias Mailänder\",");
+ Console.WriteLine(" version = {0},".F(Game.ModData.Manifest.Metadata.Version));
+ Console.WriteLine();
+ Console.WriteLine(" onRegister = function(self)");
+ Console.WriteLine(" ide:AddAPI(\"lua\", \"openra\", api)");
+ Console.WriteLine(" ide:AddInterpreter(\"openra\", interpreter)");
+ Console.WriteLine(" end,");
+ Console.WriteLine();
+ Console.WriteLine(" onUnRegister = function(self)");
+ Console.WriteLine(" ide:RemoveAPI(\"lua\", \"openra\")");
+ Console.WriteLine(" ide:RemoveInterpreter(\"openra\")");
+ Console.WriteLine(" end,");
+ Console.WriteLine("}");
+ }
+ }
+}