Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Mailaender
Created October 9, 2016 17:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mailaender/93e8041f2535cffaf2490c307bee4553 to your computer and use it in GitHub Desktop.
Save Mailaender/93e8041f2535cffaf2490c307bee4553 to your computer and use it in GitHub Desktop.
includes an .ini trigger to Lua converter
#region Copyright & License Information
/*
* Copyright 2007-2014 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;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Support;
using OpenRA.Traits;
namespace OpenRA
{
public class MapOptions
{
public bool? Cheats;
public bool? Crates;
public bool? Fog;
public bool? Shroud;
public bool? AllyBuildRadius;
public bool? FragileAlliances;
public int? StartingCash;
public string TechLevel;
public bool ConfigurableStartingUnits = true;
public string[] Difficulties = { };
public void setCrates(bool n)
{
this.Crates = n;
}
public void setFog(bool n)
{
this.Fog = n;
}
public void setShroud(bool n)
{
this.Shroud = n;
}
public void setAllyBuildRadius(bool n)
{
this.AllyBuildRadius = n;
}
public void setFragileAlliances(bool n)
{
this.FragileAlliances = n;
}
public void setStartingCash(int n)
{
this.StartingCash = n;
}
public void setConfigurableStartingUnits(bool n)
{
this.ConfigurableStartingUnits = n;
}
public void UpdateServerSettings(Session.Global settings)
{
if (Cheats.HasValue)
settings.AllowCheats = Cheats.Value;
if (Crates.HasValue)
settings.Crates = Crates.Value;
if (Fog.HasValue)
settings.Fog = Fog.Value;
if (Shroud.HasValue)
settings.Shroud = Shroud.Value;
if (AllyBuildRadius.HasValue)
settings.AllyBuildRadius = AllyBuildRadius.Value;
if (StartingCash.HasValue)
settings.StartingCash = StartingCash.Value;
if (FragileAlliances.HasValue)
settings.FragileAlliances = FragileAlliances.Value;
}
}
public class Map
{
public const int MaxTilesInCircleRange = 50;
[FieldLoader.Ignore] public IFolder Container;
public string Path { get; private set; }
// Yaml map data
public string Uid { get; private set; }
public int MapFormat;
public bool Selectable = true;
public bool UseAsShellmap;
public string RequiresMod;
public string Title;
public string Type = "Conquest";
public string PreviewVideo;
public string Description;
public string Author;
public string Player;
public string player;
public string enemy;
public string Tileset;
public bool AllowStartUnitConfig = true;
public Bitmap CustomPreview;
public bool InvalidCustomRules { get; private set; }
public readonly TileShape TileShape;
[FieldLoader.Ignore]
public readonly WVec[] SubCellOffsets;
public readonly SubCell DefaultSubCell;
public readonly SubCell LastSubCell;
public WVec OffsetOfSubCell(SubCell subCell) { return SubCellOffsets[(int)subCell]; }
[FieldLoader.LoadUsing("LoadOptions")]
public MapOptions Options;
static object LoadOptions(MiniYaml y)
{
var options = new MapOptions();
var nodesDict = y.ToDictionary();
if (nodesDict.ContainsKey("Options"))
FieldLoader.Load(options, nodesDict["Options"]);
return options;
}
[FieldLoader.Ignore]
public Lazy<Dictionary<string, ActorReference>> Actors;
public int PlayerCount { get { return Players.Count(p => p.Value.Playable); } }
public Rectangle Bounds;
// Yaml map data
[FieldLoader.Ignore]
public Dictionary<string, PlayerReference> Players = new Dictionary<string, PlayerReference>();
[FieldLoader.Ignore]
public Lazy<List<SmudgeReference>> Smudges;
[FieldLoader.Ignore]
public List<MiniYamlNode> RuleDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore]
public List<MiniYamlNode> SequenceDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore]
public List<MiniYamlNode> VoxelSequenceDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore]
public List<MiniYamlNode> WeaponDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore]
public List<MiniYamlNode> VoiceDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore]
public List<MiniYamlNode> NotificationDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore]
public List<MiniYamlNode> TranslationDefinitions = new List<MiniYamlNode>();
// Binary map data
[FieldLoader.Ignore]
public byte TileFormat = 1;
public int2 MapSize;
[FieldLoader.Ignore]
public Lazy<CellLayer<TerrainTile>> MapTiles;
[FieldLoader.Ignore]
public Lazy<CellLayer<ResourceTile>> MapResources;
[FieldLoader.Ignore]
public CellLayer<byte> CustomTerrain;
[FieldLoader.Ignore]
Lazy<TileSet> cachedTileSet;
[FieldLoader.Ignore]
Lazy<Ruleset> rules;
public Ruleset Rules { get { return rules != null ? rules.Value : null; } }
public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } }
[FieldLoader.Ignore]
public CellRegion Cells;
// Lua Data
public IniSection CellTriggers;
public IniSection Triggers;
public IniSection TeamTypes;
public IniSection Waypoints;
public IniSection Infantry;
public IniSection Units;
public IniSection Structures;
public static Map FromTileset(TileSet tileset)
{
var size = new Size(1, 1);
var tileShape = Game.modData.Manifest.TileShape;
var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0);
var makeMapTiles = Exts.Lazy(() =>
{
var ret = new CellLayer<TerrainTile>(tileShape, size);
ret.Clear(tileRef);
return ret;
});
var map = new Map()
{
Title = "Name your map here",
Description = "Describe your map here",
Author = "Your name here",
MapSize = new int2(size),
Tileset = tileset.Id,
Options = new MapOptions(),
MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(tileShape, size)),
MapTiles = makeMapTiles,
Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()),
Smudges = Exts.Lazy(() => new List<SmudgeReference>())
};
map.PostInit();
return map;
}
void AssertExists(string filename)
{
using (var s = Container.GetContent(filename))
if (s == null)
throw new InvalidOperationException("Required file {0} not present in this map".F(filename));
}
// Stub constructor that doesn't produce a valid map, but is
// sufficient for loading a mod to the content-install panel
public Map() { }
// The standard constructor for most purposes
public Map(string path) : this(path, null) { }
// Support upgrading format 5 maps to a more
// recent version by defining upgradeForMod.
public Map(string path, string upgradeForMod)
{
Path = path;
Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue);
AssertExists("map.yaml");
AssertExists("map.bin");
var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml")));
FieldLoader.Load(this, yaml);
// Support for formats 1-3 dropped 2011-02-11.
// Use release-20110207 to convert older maps to format 4
// Use release-20110511 to convert older maps to format 5
if (MapFormat < 5)
throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path));
// Format 5 -> 6 enforces the use of RequiresMod
if (MapFormat == 5)
{
if (upgradeForMod == null)
throw new InvalidDataException("Map format {0} is not supported, but can be upgraded.\n File: {1}".F(MapFormat, path));
Console.WriteLine("Upgrading {0} from Format 5 to Format 6", path);
// TODO: This isn't very nice, but there is no other consistent way
// of finding the mod early during the engine initialization.
RequiresMod = upgradeForMod;
}
var nd = yaml.ToDictionary();
// Load players
foreach (var my in nd["Players"].ToDictionary().Values)
{
var player = new PlayerReference(my);
Players.Add(player.Name, player);
}
Actors = Exts.Lazy(() =>
{
var ret = new Dictionary<string, ActorReference>();
foreach (var kv in nd["Actors"].ToDictionary())
// I don't have any idea what it makes
ret.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.ToDictionary()));
return ret;
});
// Smudges
Smudges = Exts.Lazy(() =>
{
var ret = new List<SmudgeReference>();
foreach (var name in nd["Smudges"].ToDictionary().Keys)
{
var vals = name.Split(' ');
var loc = vals[1].Split(',');
ret.Add(new SmudgeReference(vals[0], new int2(
Exts.ParseIntegerInvariant(loc[0]),
Exts.ParseIntegerInvariant(loc[1])),
Exts.ParseIntegerInvariant(vals[2])));
}
return ret;
});
RuleDefinitions = MiniYaml.NodesOrEmpty(yaml, "Rules");
SequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Sequences");
VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences");
WeaponDefinitions = MiniYaml.NodesOrEmpty(yaml, "Weapons");
VoiceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Voices");
NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications");
TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations");
MapTiles = Exts.Lazy(() => LoadMapTiles());
MapResources = Exts.Lazy(() => LoadResourceTiles());
TileShape = Game.modData.Manifest.TileShape;
SubCellOffsets = Game.modData.Manifest.SubCellOffsets;
LastSubCell = (SubCell)(SubCellOffsets.Length - 1);
DefaultSubCell = (SubCell)Game.modData.Manifest.SubCellDefaultIndex;
// The Uid is calculated from the data on-disk, so
// format changes must be flushed to disk.
// TODO: this isn't very nice
if (MapFormat < 6)
Save(path);
Uid = ComputeHash();
if (Container.Exists("map.png"))
using (var dataStream = Container.GetContent("map.png"))
CustomPreview = new Bitmap(dataStream);
PostInit();
}
void PostInit()
{
rules = Exts.Lazy(() =>
{
try
{
return Game.modData.RulesetCache.LoadMapRules(this);
}
catch (Exception e)
{
InvalidCustomRules = true;
Log.Write("debug", "Failed to load rules for {0} with error {1}", Title, e.Message);
}
return Game.modData.DefaultRules;
});
cachedTileSet = Exts.Lazy(() => Rules.TileSets[Tileset]);
var tl = Map.MapToCell(TileShape, new CPos(Bounds.Left, Bounds.Top));
var br = Map.MapToCell(TileShape, new CPos(Bounds.Right - 1, Bounds.Bottom - 1));
Cells = new CellRegion(TileShape, tl, br);
CustomTerrain = new CellLayer<byte>(this);
foreach (var cell in Cells)
CustomTerrain[cell] = byte.MaxValue;
}
public Ruleset PreloadRules()
{
return rules.Value;
}
public CPos[] GetSpawnPoints()
{
return Actors.Value.Values
.Where(a => a.Type == "mpspawn")
.Select(a => (CPos)a.InitDict.Get<LocationInit>().value)
.ToArray();
}
public IEnumerable<ActorReference> getActorNameByXY(int x, int y)
{
return Actors.Value.Values.Where(a => (CPos)a.InitDict.Get<LocationInit>().value == new CPos(x, y));
}
public void Save(string toPath)
{
MapFormat = 6;
var root = new List<MiniYamlNode>();
var fields = new[]
{
"Selectable",
"MapFormat",
"RequiresMod",
"Title",
"Description",
"Author",
"PreviewVideo",
"Tileset",
"MapSize",
"Bounds",
"UseAsShellmap",
"Type",
};
foreach (var field in fields)
{
var f = this.GetType().GetField(field);
if (f.GetValue(this) == null)
continue;
root.Add(new MiniYamlNode(field, FieldSaver.FormatValue(this, f)));
}
// MapOptions newOptions = new MapOptions();
Options.setCrates(false);
Options.setFog(true);
Options.setShroud(true);
Options.setAllyBuildRadius(false);
Options.setFragileAlliances(false);
Options.setStartingCash(0);
Options.setConfigurableStartingUnits(false);
root.Add(new MiniYamlNode("Options", FieldSaver.SaveDifferences(Options, new MapOptions())));
root.Add(new MiniYamlNode("Players", null,
Players.Select(p => new MiniYamlNode("PlayerReference@{0}".F(p.Key), FieldSaver.SaveDifferences(p.Value, new PlayerReference()))).ToList()));
root.Add(new MiniYamlNode("Actors", null,
Actors.Value.Select(x => new MiniYamlNode(x.Key, x.Value.Save())).ToList()));
root.Add(new MiniYamlNode("Smudges", MiniYaml.FromList<SmudgeReference>(Smudges.Value)));
root.Add(new MiniYamlNode("Rules", null, AddRules()));
root.Add(new MiniYamlNode("Sequences", null, SequenceDefinitions));
root.Add(new MiniYamlNode("VoxelSequences", null, VoxelSequenceDefinitions));
root.Add(new MiniYamlNode("Weapons", null, WeaponDefinitions));
root.Add(new MiniYamlNode("Voices", null, VoiceDefinitions));
root.Add(new MiniYamlNode("Notifications", null, NotificationDefinitions));
root.Add(new MiniYamlNode("Translations", null, TranslationDefinitions));
var entries = new Dictionary<string, byte[]>();
entries.Add("map.bin", SaveBinaryData());
var s = root.WriteToString();
entries.Add("map.yaml", Encoding.UTF8.GetBytes(s));
entries.Add("map.lua", Encoding.UTF8.GetBytes(SaveLuaData()));
// Add any custom assets
if (Container != null)
{
foreach (var file in Container.AllFileNames())
{
if (file == "map.bin" || file == "map.yaml")
continue;
entries.Add(file, Container.GetContent(file).ReadAllBytes());
}
}
// Saving the map to a new location
if (toPath != Path)
{
Path = toPath;
// Create a new map package
Container = GlobalFileSystem.CreatePackage(Path, int.MaxValue, entries);
}
// Update existing package
Container.Write(entries);
}
public List<MiniYamlNode> AddRules()
{
List<MiniYamlNode> RulesAdd = new List<MiniYamlNode>();
MiniYamlNode Player = new MiniYamlNode("Player", "");
MiniYamlNode conquest = new MiniYamlNode("\t-ConquestVictoryConditions", "");
MiniYamlNode missionO = new MiniYamlNode("\tMissionObjectives", "");
MiniYamlNode earlyGameOver = new MiniYamlNode("\t\tEarlyGameOver", "true");
RulesAdd.Add(Player);
RulesAdd.Add(conquest);
RulesAdd.Add(missionO);
RulesAdd.Add(earlyGameOver);
MiniYamlNode World = new MiniYamlNode("World", "");
MiniYamlNode crateSpawner = new MiniYamlNode("\t-CrateSpawner", "");
MiniYamlNode spawnMPUnits = new MiniYamlNode("\t-SpawnMPUnits", "");
MiniYamlNode mPStartLocations = new MiniYamlNode("\t-MPStartLocations", "");
MiniYamlNode objectivesPanel = new MiniYamlNode("\tObjectivesPanel", "");
MiniYamlNode panelName = new MiniYamlNode("\t\tPanelName", "MISSION_OBJECTIVES");
MiniYamlNode luaScript = new MiniYamlNode("\tLuaScript", "");
MiniYamlNode src = new MiniYamlNode("\t\tScripts", "map.lua");
MiniYamlNode vehicle = new MiniYamlNode("^Vehicle", "");
MiniYamlNode tank = new MiniYamlNode("^Tank", "");
MiniYamlNode infantry = new MiniYamlNode("^Infantry", "");
MiniYamlNode plane = new MiniYamlNode("^Plane", "");
MiniYamlNode ship = new MiniYamlNode("^Ship", "");
MiniYamlNode building = new MiniYamlNode("^Building", "");
MiniYamlNode tooltip = new MiniYamlNode("\tTooltip", "");
MiniYamlNode showOwnerRow = new MiniYamlNode("\t\tShowOwnerRow", "false");
RulesAdd.Add(World);
RulesAdd.Add(crateSpawner);
RulesAdd.Add(spawnMPUnits);
RulesAdd.Add(mPStartLocations);
RulesAdd.Add(objectivesPanel);
RulesAdd.Add(panelName);
RulesAdd.Add(luaScript);
RulesAdd.Add(src);
RulesAdd.Add(vehicle);
RulesAdd.Add(tooltip);
RulesAdd.Add(showOwnerRow);
RulesAdd.Add(tank);
RulesAdd.Add(tooltip);
RulesAdd.Add(showOwnerRow);
RulesAdd.Add(infantry);
RulesAdd.Add(tooltip);
RulesAdd.Add(showOwnerRow);
RulesAdd.Add(plane);
RulesAdd.Add(tooltip);
RulesAdd.Add(showOwnerRow);
RulesAdd.Add(ship);
RulesAdd.Add(tooltip);
RulesAdd.Add(showOwnerRow);
RulesAdd.Add(building);
RulesAdd.Add(tooltip);
RulesAdd.Add(showOwnerRow);
return RulesAdd;
}
public void generateLuaTriggers(StringBuilder sb)
{
foreach (var waypoint in Waypoints)
{
}
foreach (var trigger in Triggers)
{
List<string> triggerParams = trigger.Value.Split(',').ToList<string>();
// Array with units, which call triggers with name triggerName
generateUnitsTriggersArray(sb, trigger.Key, triggerParams[4]);
switch (triggerParams[0])
{
case "Time":
generateTimeTrigger(sb, trigger.Key, triggerParams);
break;
case "Destroyed":
generateDestroyedTrigger(sb, trigger.Key, triggerParams);
break;
case "Player Enters":
generatePlayerEntersTrigger(sb, trigger.Key, triggerParams);
break;
case "Attacked":
generateAttackedTrigger(sb, trigger.Key, triggerParams);
break;
case "Units Destr.":
break;
}
}
}
public void generateAttackedTrigger(StringBuilder sb, string triggerName, List<string> options)
{
switch (options[1])
{
case "Reinforce.":
// Create first Trigger.OnDamaged(Actor116, ATK5_TRIGGER)
List<string> actors = new List<string>();
foreach (var item in Structures)
{
List<string> infantryParams = item.Value.Split(',').ToList<string>();
if (infantryParams.Last() == triggerName)
{
var test = infantryParams[3];
var temp = recalculatePosition(Convert.ToInt32(test));
var tempArray = temp.Split(',');
IEnumerable<ActorReference> result = getActorNameByXY(Convert.ToInt32(tempArray[0]), Convert.ToInt32(tempArray[1]));
foreach (var x in result)
{
actors.Add(x.Name);
}
}
}
foreach (var item in Infantry)
{
List<string> infantryParams = item.Value.Split(',').ToList<string>();
if (infantryParams.Last() == triggerName)
{
var test = infantryParams[3];
var temp = recalculatePosition(Convert.ToInt32(test));
var tempArray = temp.Split(',');
IEnumerable<ActorReference> result = getActorNameByXY(Convert.ToInt32(tempArray[0]), Convert.ToInt32(tempArray[1]));
foreach (var x in result)
{
actors.Add(x.Name);
}
}
}
foreach (var item in Units)
{
List<string> infantryParams = item.Value.Split(',').ToList<string>();
if (infantryParams.Last() == triggerName)
{
var test = infantryParams[3];
var temp = recalculatePosition(Convert.ToInt32(test));
var tempArray = temp.Split(',');
IEnumerable<ActorReference> result = getActorNameByXY(Convert.ToInt32(tempArray[0]), Convert.ToInt32(tempArray[1]));
foreach (var x in result)
{
actors.Add(x.Name);
}
}
}
// Trigger.OnDamaged doesn't work...
if (actors.Count != 0)
{
foreach (var item in actors)
{
sb.AppendLine();
sb.AppendLine("--\tTrigger.OnDamaged(" + item.ToString() + ", " + triggerName.ToUpper() + "_TRIGGER)");
}
}
sb.AppendLine();
List<string> teamTypeOptions = generateUnitsArray(sb, triggerName, options[4]);
string pType = "";
if (Player == teamTypeOptions[0])
{
pType = enemy;
}
else
{
pType = player;
}
sb.AppendLine();
sb.AppendLine("\t" + triggerName.ToUpper() + "_SWITCH = true");
sb.AppendLine("\t" + triggerName.ToUpper() + "_TRIGGER = function()");
sb.AppendLine("\t\tif " + triggerName.ToUpper() + "_SWITCH == true then");
sb.AppendLine("\t\t\tReinforcements.Reinforce(" + pType + "," + triggerName.ToUpper() + "_UNITS, SPAWNPOINT, 15, function(unit)");
generateMoves(sb, teamTypeOptions);
sb.AppendLine("\t\t\tend)");
sb.AppendLine("\t\t\t" + triggerName.ToUpper() + "_SWITCH= false");
sb.AppendLine("\t\tend");
sb.AppendLine("\tend");
break;
}
}
public void generatePlayerEntersTrigger(StringBuilder sb, string triggerName, List<string> options)
{
switch (options[1])
{
case "Reinforce.":
break;
case "Create Team":
break;
case "Production":
break;
case "Ion Cannon":
break;
case "DZ at 'Z'":
break;
case "Allow Win":
break;
case "Win":
break;
case "All to Hunt":
break;
// Dstry Trig 'TRIGGER_NAME' case
default:
List<string> trigger = options[1].Split(' ').ToList<string>();
sb.AppendLine();
sb.AppendLine("\tTrigger.OnEnteredFootprint(" + triggerName.ToUpper() + "_CELLTRIGGERS, function(a, id)");
sb.AppendLine("\t\tif a.Owner == GoodGuy then");
sb.AppendLine("\t\t\tTrigger.RemoveFootprintTrigger(id)");
sb.AppendLine("\t\t\tTRIGGER_" + trigger[2].Trim(new[] { '\'' }) + " = function() end");
sb.AppendLine("\t\tend");
sb.AppendLine("\tend)");
break;
}
}
public void generateDestroyedTrigger(StringBuilder sb, string triggerName, List<string> options)
{
switch (options[1])
{
case "Reinforce.":
sb.AppendLine("\t" + triggerName.ToUpper() + "_TeamPath = {SPAWNPOINT.Location, waypoint1.Location}");
List<string> teamTypeOptions = generateUnitsArray(sb, triggerName, options[4]);
sb.AppendLine("\tTrigger.OnAnyKilled(" + triggerName.ToUpper() + "_ACTIVATE, function()");
string pType = "";
if (Player == teamTypeOptions[0])
{
pType = enemy;
}
else
{
pType = player;
}
sb.AppendLine("\t\tReinforcements.Reinforce(" + pType + "," + triggerName.ToUpper() + "_UNITS, " + triggerName.ToUpper() + "_TeamPath, 15, function(unit)");
generateMoves(sb, teamTypeOptions);
sb.AppendLine("\t\tend)");
sb.AppendLine("\tend)");
break;
}
}
public void generateUnitsTriggersArray(StringBuilder sb, string triggerName, string teamTypeName)
{
List<string> actors = new List<string>();
foreach (var item in Structures)
{
List<string> infantryParams = item.Value.Split(',').ToList<string>();
if (infantryParams.Last() == triggerName)
{
var test = infantryParams[3];
var temp = recalculatePosition(Convert.ToInt32(test));
var tempArray = temp.Split(',');
IEnumerable<ActorReference> result = getActorNameByXY(Convert.ToInt32(tempArray[0]), Convert.ToInt32(tempArray[1]));
foreach (var x in result)
{
actors.Add(x.Name);
}
}
}
foreach (var item in Infantry)
{
List<string> infantryParams = item.Value.Split(',').ToList<string>();
if (infantryParams.Last() == triggerName)
{
var test = infantryParams[3];
var temp = recalculatePosition(Convert.ToInt32(test));
var tempArray = temp.Split(',');
IEnumerable<ActorReference> result = getActorNameByXY(Convert.ToInt32(tempArray[0]), Convert.ToInt32(tempArray[1]));
foreach (var x in result)
{
actors.Add(x.Name);
}
}
}
foreach (var item in Units)
{
List<string> infantryParams = item.Value.Split(',').ToList<string>();
if (infantryParams.Last() == triggerName)
{
var test = infantryParams[3];
var temp = recalculatePosition(Convert.ToInt32(test));
var tempArray = temp.Split(',');
IEnumerable<ActorReference> result = getActorNameByXY(Convert.ToInt32(tempArray[0]), Convert.ToInt32(tempArray[1]));
foreach (var x in result)
{
actors.Add(x.Name);
}
}
}
if (actors.Count != 0)
{
sb.AppendLine();
sb.AppendLine("\t" + triggerName.ToUpper() + "_ACTIVATE = { " + string.Join(", ", actors.ToArray()) + " }");
}
}
public void generateTimeTrigger(StringBuilder sb, string triggerName, List<string> options)
{
switch (options[1])
{
case "Reinforce.":
List<string> teamTypeOptions = generateUnitsArray(sb, triggerName, options[4]);
sb.AppendLine("\tTRIGGER_" + triggerName.ToUpper() + "_TIME = " + options[2]);
sb.AppendLine("\tTrigger.AfterDelay(DateTime.Seconds(TRIGGER_" + triggerName.ToUpper() + "_TIME), function() TRIGGER_" + triggerName.ToUpper() + "() end)");
sb.AppendLine("");
generateTimeTriggerReinforceFunction(sb, triggerName, teamTypeOptions);
break;
}
}
public void generateTimeTriggerReinforceFunction(StringBuilder sb, string triggerName, List<string> teamTypeOptions)
{
string pType = "";
if (Player == teamTypeOptions[0])
{
pType = enemy;
}
else
{
pType = player;
}
sb.AppendLine("\tTRIGGER_" + triggerName.ToUpper() + " = function()");
sb.AppendLine("\t\tReinforcements.Reinforce(" + pType + ", " + triggerName.ToUpper() + "_UNITS, SPAWNPOINT, 15, function(unit)");
generateMoves(sb, teamTypeOptions);
sb.AppendLine("\t\tend)");
sb.AppendLine("\tend");
}
public void generateMoves(StringBuilder sb, List<string> teamTypeOptions)
{
// Unit moves
// With 10 starts units in TeamTypes field
int numberOfTypesOfUnits = Convert.ToInt32(teamTypeOptions[10]);
int getNumberOfMovesAtThisPoint = 11 + numberOfTypesOfUnits;
int numberOfMoves = Convert.ToInt32(teamTypeOptions[getNumberOfMovesAtThisPoint]);
int start = getNumberOfMovesAtThisPoint + 1;
int end = start + numberOfMoves;
// For moves like Attack Units and Attack Civil., because they don't have waypoints in INI
string lastMove = "0";
for (var i = start; i < end; i++)
{
List<string> moves = teamTypeOptions[i].Split(':').ToList<string>();
switch (moves[0])
{
case "Move":
sb.AppendLine("\t\t\tunit.Move(waypoint" + moves[1] + ".Location)");
lastMove = moves[1];
break;
case "Attack Units":
sb.AppendLine("\t\t\tunit.AttackMove(waypoint" + lastMove + ".Location)");
break;
case "Attack Civil.":
sb.AppendLine("\t\t\tunit.AttackMove(waypoint" + lastMove + ".Location)");
break;
default:
sb.AppendLine("\t\t\t-- Move " + moves[0] + " is not found");
break;
}
}
}
public List<string> generateUnitsArray(StringBuilder sb, string triggerName, string teamTypeName)
{
List<string> teamTypeOptions = new List<string>();
// Get options for searched team type
foreach (var teamType in TeamTypes)
{
if (teamType.Key == teamTypeName)
{
teamTypeOptions = teamType.Value.Split(',').ToList<string>();
}
}
// Start write team type array
sb.Append("\t" + triggerName.ToUpper() + "_UNITS = { ");
// With 10 starts units in TeamTypes field
int count = Convert.ToInt32(teamTypeOptions[10]);
// test is a number of types of units
int test = 11 + count;
for (var i = 11; i < test; i++)
{
// Parse unit types with it's number
List<string> temp = teamTypeOptions[i].Split(':').ToList<string>();
// temp[1] is a number of units
for (var j = 0; j < Convert.ToInt32(temp[1]); j++) {
// temp[0] is a name of unit
// Fall 1: Anzahl der Einhaiten ist 1 und anzahl der Truppen ist 1
if (count == 1 && Convert.ToInt32(temp[1]) == 1) {
sb.Append("\"" + temp[0].ToLower() + "\"");
// Fall 2: Anzahl der Einheiten ist 1
} else if (count == 1 && j == 0) {
sb.Append("\"" + temp[0].ToLower() + "\", ");
// Fall 3: Anzahl der Einheiten ist größer als 1
} else if (i < (test - 1)) {
sb.Append("\"" + temp[0].ToLower() + "\", ");
// Fall 4: Letzte Einheit wird ohne Komma ausgegeben!
} else if (i < test) {
sb.Append("\"" + temp[0].ToLower() + "\"");
}
}
}
sb.Append(" }");
sb.AppendLine("");
return teamTypeOptions;
}
public string SaveLuaData()
{
StringBuilder sb = new StringBuilder();
Dictionary<string, ArrayList> outputCells = new Dictionary<string, ArrayList>();
if (Player == "GoodGuy")
{
player = "GoodGuy";
enemy = "BadGuy";
}
else
{
player = "BadGuy";
enemy = "GoodGuy";
}
// Add default spawnpoint
sb.AppendLine("-- standard spawn point");
sb.AppendLine("SPAWNPOINT = { waypoint27.Location }");
// Generate cell triggers
sb.Append("-- Cell triggers arrays");
sb.AppendLine();
foreach (var cell in CellTriggers)
{
if (!outputCells.ContainsKey(cell.Value))
{
outputCells.Add(cell.Value, new ArrayList());
ArrayList l = outputCells[cell.Value];
l.Add(cell.Key);
}
else
{
ArrayList l = outputCells[cell.Value];
l.Add(cell.Key);
}
}
foreach (var temp in outputCells)
{
sb.Append(temp.Key.ToUpper() + "_CELLTRIGGERS = {");
int counter = 1;
foreach (var x in outputCells[temp.Key])
{
if (counter < outputCells[temp.Key].Count)
{
sb.Append("CPos.New(" + recalculatePosition(Convert.ToInt32(x)) + "), ");
counter++;
}
else
{
sb.Append("CPos.New(" + recalculatePosition(Convert.ToInt32(x)) + ")");
}
}
sb.Append("}");
sb.AppendLine();
}
sb.AppendLine();
// END generate cell trigger
// WORLDLOADED
sb.Append("WorldLoaded = function()\n");
sb.AppendLine("\t" + player + " = Player.GetPlayer(\"GoodGuy\")");
sb.AppendLine("\t" + enemy + " = Player.GetPlayer(\"BadGuy\")");
sb.AppendLine();
// GLOBAL TRIGGERS
// TODO Check which player is choosen and add objectives to the correct side
// now objects just added to nod
sb.AppendLine("\tTrigger.OnObjectiveAdded(" + enemy + ", function(p, id)");
sb.AppendLine("\t\tMedia.DisplayMessage(p.GetObjectiveDescription(id), \"New \" .. string.lower(p.GetObjectiveType(id)) .. \" objective\")");
sb.AppendLine("\tend)");
sb.AppendLine();
sb.AppendLine("\tTrigger.OnObjectiveCompleted(" + enemy + ", function(p, id)");
sb.AppendLine("\t\tMedia.DisplayMessage(p.GetObjectiveDescription(id), \"Objective completed\")");
sb.AppendLine("\tend)");
sb.AppendLine();
sb.AppendLine("\tTrigger.OnObjectiveFailed(" + enemy + ", function(p, id)");
sb.AppendLine("\t\tMedia.DisplayMessage(p.GetObjectiveDescription(id), \"Objective failed\")");
sb.AppendLine("\tend)");
sb.AppendLine();
sb.AppendLine("\tTrigger.OnPlayerWon(" + enemy + ", function()");
sb.AppendLine("\t\tMedia.PlaySpeechNotification(" + enemy + ", \"Win\")");
sb.AppendLine("\tend)");
sb.AppendLine();
sb.AppendLine("\tTrigger.OnPlayerLost(" + enemy + ", function()");
sb.AppendLine("\t\tMedia.PlaySpeechNotification(" + enemy + ", \"Lose\")");
sb.AppendLine("\tend)");
sb.AppendLine();
sb.AppendLine("\t" + player + "Objective = " + player + ".AddPrimaryObjective(\"Kill all enemies!\")");
sb.AppendLine("\t" + enemy + "Objective = " + enemy + ".AddPrimaryObjective(\"Kill all enemies!\")");
// Generate Triggers
generateLuaTriggers(sb);
// End Generate Triggers
sb.Append("end");
return sb.ToString();
}
public string recalculatePosition(int x)
{
StringBuilder result = new StringBuilder();
result.Append(x % 64);
result.Append(",");
result.Append(x / 64);
return result.ToString();
}
public CellLayer<TerrainTile> LoadMapTiles()
{
var tiles = new CellLayer<TerrainTile>(this);
using (var dataStream = Container.GetContent("map.bin"))
{
if (dataStream.ReadUInt8() != 1)
throw new InvalidDataException("Unknown binary map format");
// Load header info
var width = dataStream.ReadUInt16();
var height = dataStream.ReadUInt16();
if (width != MapSize.X || height != MapSize.Y)
throw new InvalidDataException("Invalid tile data");
// Load tile data
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3);
var d = 0;
for (var i = 0; i < MapSize.X; i++)
{
for (var j = 0; j < MapSize.Y; j++)
{
var tile = BitConverter.ToUInt16(data, d);
d += 2;
var index = data[d++];
if (index == byte.MaxValue)
index = (byte)(i % 4 + (j % 4) * 4);
tiles[i, j] = new TerrainTile(tile, index);
}
}
}
return tiles;
}
public CellLayer<ResourceTile> LoadResourceTiles()
{
var resources = new CellLayer<ResourceTile>(this);
using (var dataStream = Container.GetContent("map.bin"))
{
if (dataStream.ReadUInt8() != 1)
throw new InvalidDataException("Unknown binary map format");
// Load header info
var width = dataStream.ReadUInt16();
var height = dataStream.ReadUInt16();
if (width != MapSize.X || height != MapSize.Y)
throw new InvalidDataException("Invalid tile data");
// Skip past tile data
dataStream.Seek(3 * MapSize.X * MapSize.Y, SeekOrigin.Current);
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 2);
var d = 0;
// Load resource data
for (var i = 0; i < MapSize.X; i++)
for (var j = 0; j < MapSize.Y; j++)
resources[i, j] = new ResourceTile(data[d++], data[d++]);
}
return resources;
}
public byte[] SaveBinaryData()
{
var dataStream = new MemoryStream();
using (var writer = new BinaryWriter(dataStream))
{
// File header consists of a version byte, followed by 2 ushorts for width and height
writer.Write(TileFormat);
writer.Write((ushort)MapSize.X);
writer.Write((ushort)MapSize.Y);
// Tile data
for (var i = 0; i < MapSize.X; i++)
for (var j = 0; j < MapSize.Y; j++)
{
var tile = MapTiles.Value[i, j];
writer.Write(tile.Type);
writer.Write(tile.Index);
}
// Resource data
for (var i = 0; i < MapSize.X; i++)
{
for (var j = 0; j < MapSize.Y; j++)
{
var tile = MapResources.Value[i, j];
writer.Write(tile.Type);
writer.Write(tile.Index);
}
}
}
return dataStream.ToArray();
}
public bool Contains(CPos cell)
{
var uv = CellToMap(TileShape, cell);
return Bounds.Contains(uv.X, uv.Y);
}
public WPos CenterOfCell(CPos cell)
{
if (TileShape == TileShape.Rectangle)
return new WPos(1024 * cell.X + 512, 1024 * cell.Y + 512, 0);
// Convert from diamond cell position (x, y) to world position (u, v):
// (a) Consider the relationships:
// - Center of origin cell is (512, 512)
// - +x adds (512, 512) to world pos
// - +y adds (-512, 512) to world pos
// (b) Therefore:
// - ax + by adds (a - b) * 512 + 512 to u
// - ax + by adds (a + b) * 512 + 512 to v
return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), 0);
}
public WPos CenterOfSubCell(CPos cell, SubCell subCell)
{
var index = (int)subCell;
if (index >= 0 && index <= SubCellOffsets.Length)
return CenterOfCell(cell) + SubCellOffsets[index];
return CenterOfCell(cell);
}
public CPos CellContaining(WPos pos)
{
if (TileShape == TileShape.Rectangle)
return new CPos(pos.X / 1024, pos.Y / 1024);
// Convert from world position to diamond cell position:
// (a) Subtract (512, 512) to move the rotation center to the middle of the corner cell
// (b) Rotate axes by -pi/4
// (c) Add 512 to x (but not y) to realign the cell
// (d) Divide by 1024 to find final cell coords
var u = (pos.Y + pos.X - 512) / 1024;
var v = (pos.Y - pos.X) / 1024;
return new CPos(u, v);
}
public static CPos MapToCell(TileShape shape, CPos map)
{
if (shape == TileShape.Rectangle)
return map;
// Convert from rectangular map position to diamond cell position
// - The staggered rows make this fiddly (hint: draw a diagram!)
// (a) Consider the relationships:
// - +1u (even -> odd) adds (1, -1) to (x, y)
// - +1v (even -> odd) adds (1, 0) to (x, y)
// - +1v (odd -> even) adds (0, 1) to (x, y)
// (b) Therefore:
// - au + 2bv adds (a + b) to (x, y)
// - a correction factor is added if v is odd
var offset = (map.Y & 1) == 1 ? 1 : 0;
var y = (map.Y - offset) / 2 - map.X;
var x = map.Y - y;
return new CPos(x, y);
}
public static CPos CellToMap(TileShape shape, CPos cell)
{
if (shape == TileShape.Rectangle)
return cell;
// Convert from diamond cell (x, y) position to rectangular map position (u, v)
// - The staggered rows make this fiddly (hint: draw a diagram!)
// (a) Consider the relationships:
// - +1x (even -> odd) adds (0, 1) to (u, v)
// - +1x (odd -> even) adds (1, 1) to (u, v)
// - +1y (even -> odd) adds (-1, 1) to (u, v)
// - +1y (odd -> even) adds (0, 1) to (u, v)
// (b) Therefore:
// - ax + by adds (a - b)/2 to u (only even increments count)
// - ax + by adds a + b to v
var u = (cell.X - cell.Y) / 2;
var v = cell.X + cell.Y;
return new CPos(u, v);
}
public int FacingBetween(CPos cell, CPos towards, int fallbackfacing)
{
return Traits.Util.GetFacing(CenterOfCell(towards) - CenterOfCell(cell), fallbackfacing);
}
public void Resize(int width, int height) // editor magic.
{
var oldMapTiles = MapTiles.Value;
var oldMapResources = MapResources.Value;
var newSize = new Size(width, height);
MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[0, 0]));
MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[0, 0]));
MapSize = new int2(newSize);
}
public void ResizeCordon(int left, int top, int right, int bottom)
{
Bounds = Rectangle.FromLTRB(left, top, right, bottom);
var tl = Map.MapToCell(TileShape, new CPos(Bounds.Left, Bounds.Top));
var br = Map.MapToCell(TileShape, new CPos(Bounds.Right - 1, Bounds.Bottom - 1));
Cells = new CellRegion(TileShape, tl, br);
}
string ComputeHash()
{
// UID is calculated by taking an SHA1 of the yaml and binary data
using (var ms = new MemoryStream())
{
// Read the relevant data into the buffer
using (var s = Container.GetContent("map.yaml"))
s.CopyTo(ms);
using (var s = Container.GetContent("map.bin"))
s.CopyTo(ms);
// Take the SHA1
ms.Seek(0, SeekOrigin.Begin);
using (var csp = SHA1.Create())
return new string(csp.ComputeHash(ms).SelectMany(a => a.ToString("x2")).ToArray());
}
}
public void MakeDefaultPlayers()
{
var firstRace = Rules.Actors["world"].Traits
.WithInterface<CountryInfo>().First(c => c.Selectable).Race;
if (!Players.ContainsKey("Neutral"))
Players.Add("Neutral", new PlayerReference
{
Name = "Neutral",
Race = firstRace,
OwnsWorld = true,
NonCombatant = true
});
var numSpawns = GetSpawnPoints().Length;
for (var index = 0; index < numSpawns; index++)
{
if (Players.ContainsKey("Multi{0}".F(index)))
continue;
var p = new PlayerReference
{
Name = "Multi{0}".F(index),
Race = "Random",
Playable = true,
Enemies = new[] { "Creeps" }
};
Players.Add(p.Name, p);
}
Players.Add("Creeps", new PlayerReference
{
Name = "Creeps",
Race = firstRace,
NonCombatant = true,
Enemies = Players.Where(p => p.Value.Playable).Select(p => p.Key).ToArray()
});
}
public void FixOpenAreas(Ruleset rules)
{
var r = new Random();
var tileset = rules.TileSets[Tileset];
for (var j = Bounds.Top; j < Bounds.Bottom; j++)
{
for (var i = Bounds.Left; i < Bounds.Right; i++)
{
var type = MapTiles.Value[i, j].Type;
var index = MapTiles.Value[i, j].Index;
if (!tileset.Templates.ContainsKey(type))
{
Console.WriteLine("Unknown Tile ID {0}".F(type));
continue;
}
var template = tileset.Templates[type];
if (!template.PickAny)
continue;
index = (byte)r.Next(0, template.TilesCount);
MapTiles.Value[i, j] = new TerrainTile(type, index);
}
}
}
public byte GetTerrainIndex(CPos cell)
{
var custom = CustomTerrain[cell];
return custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[cell]);
}
public TerrainTypeInfo GetTerrainInfo(CPos cell)
{
return cachedTileSet.Value[GetTerrainIndex(cell)];
}
public CPos Clamp(CPos cell)
{
var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1);
return MapToCell(TileShape, CellToMap(TileShape, cell).Clamp(bounds));
}
public CPos ChooseRandomCell(MersenneTwister rand)
{
var x = rand.Next(Bounds.Left, Bounds.Right);
var y = rand.Next(Bounds.Top, Bounds.Bottom);
return MapToCell(TileShape, new CPos(x, y));
}
public CPos ChooseRandomEdgeCell(MersenneTwister rand)
{
var isX = rand.Next(2) == 0;
var edge = rand.Next(2) == 0;
var x = isX ? rand.Next(Bounds.Left, Bounds.Right) : (edge ? Bounds.Left : Bounds.Right);
var y = !isX ? rand.Next(Bounds.Top, Bounds.Bottom) : (edge ? Bounds.Top : Bounds.Bottom);
return MapToCell(TileShape, new CPos(x, y));
}
public WRange DistanceToEdge(WPos pos, WVec dir)
{
var tl = CenterOfCell(Cells.TopLeft) - new WVec(512, 512, 0);
var br = CenterOfCell(Cells.BottomRight) + new WVec(511, 511, 0);
var x = dir.X == 0 ? int.MaxValue : ((dir.X < 0 ? tl.X : br.X) - pos.X) / dir.X;
var y = dir.Y == 0 ? int.MaxValue : ((dir.Y < 0 ? tl.Y : br.Y) - pos.Y) / dir.Y;
return new WRange(Math.Min(x, y) * dir.Length);
}
static CVec[][] TilesByDistance = InitTilesByDistance(MaxTilesInCircleRange);
static CVec[][] InitTilesByDistance(int max)
{
var ts = new List<CVec>[max + 1];
for (var i = 0; i < max + 1; i++)
ts[i] = new List<CVec>();
for (var j = -max; j <= max; j++)
for (var i = -max; i <= max; i++)
if (max * max >= i * i + j * j)
ts[Exts.ISqrt(i * i + j * j, Exts.ISqrtRoundMode.Ceiling)].Add(new CVec(i, j));
// Sort each integer-distance group by the actual distance
foreach (var list in ts)
{
list.Sort((a, b) =>
{
var result = a.LengthSquared.CompareTo(b.LengthSquared);
if (result != 0)
return result;
// If the lengths are equal, use other means to sort them.
// Try the hashcode first because it gives more
// random-appearing results than X or Y that would always
// prefer the leftmost/topmost position.
result = a.GetHashCode().CompareTo(b.GetHashCode());
if (result != 0)
return result;
result = a.X.CompareTo(b.X);
if (result != 0)
return result;
return a.Y.CompareTo(b.Y);
});
}
return ts.Select(list => list.ToArray()).ToArray();
}
// Both ranges are inclusive because everything that calls it is designed for maxRange being inclusive:
// it rounds the actual distance up to the next integer so that this call
// will return any cells that intersect with the requested range circle.
// The returned positions are sorted by distance from the center.
public IEnumerable<CPos> FindTilesInAnnulus(CPos center, int minRange, int maxRange)
{
if (maxRange < minRange)
throw new ArgumentOutOfRangeException("maxRange", "Maximum range is less than the minimum range.");
if (maxRange > TilesByDistance.Length)
throw new ArgumentOutOfRangeException("maxRange", "The requested range ({0}) exceeds the maximum allowed ({1})".F(maxRange, MaxTilesInCircleRange));
for (var i = minRange; i <= maxRange; i++)
{
foreach (var offset in TilesByDistance[i])
{
var t = offset + center;
if (Contains(t))
yield return t;
}
}
}
public IEnumerable<CPos> FindTilesInCircle(CPos center, int maxRange)
{
return FindTilesInAnnulus(center, 0, maxRange);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment