diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js
index c683ba6da4..ac55aa33c9 100644
--- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js
+++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js
@@ -21,6 +21,9 @@ var g_MaxPlayers = 8;
var g_ChatMessages = [];
+// Cache of output from Engine.LoadMapData
+var g_MapData = {};
+
function init(attribs)
{
switch (attribs.type)
@@ -162,6 +165,14 @@ function initMapNameList(object)
object.selected = selected;
}
+function loadMapData(name)
+{
+ if (!(name in g_MapData))
+ g_MapData[name] = Engine.LoadMapData(name);
+
+ return g_MapData[name];
+}
+
// Called when the user selects a map from the list
function selectMap(name)
{
@@ -193,16 +204,18 @@ function onGameAttributesChange()
getGUIObjectByName("mapInfoName").caption = mapName;
- var description = "Sorry, no description available.";
+ var mapData = loadMapData(mapName);
+ var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
- // TODO: we ought to load map descriptions from the map itself, somehow.
- // Just hardcode it now for testing.
- if (mapName == "Latium")
- {
- description = "2 players. A fertile coastal region which was the birthplace of the Roman Empire. Plentiful natural resources let you build up a city and experiment with the game’s features in relative peace. Some more description could go here if you want as long as it’s not too long and still fits on the screen.";
- }
+ // Load the description from the map file, if there is one
+ var description = mapSettings.Description || "Sorry, no description available.";
+
+ // Describe the number of players
+ var playerString = "";
+ if (mapSettings.NumPlayers)
+ playerString = mapSettings.NumPlayers + " " + (mapSettings.NumPlayers == 1 ? "player" : "players") + ". ";
- getGUIObjectByName("mapInfoDescription").caption = description;
+ getGUIObjectByName("mapInfoDescription").caption = playerString + description;
g_IsInGuiUpdate = false;
}
diff --git a/binaries/data/mods/public/maps/scenarios/Latium.xml b/binaries/data/mods/public/maps/scenarios/Latium.xml
index 9a41a3611c..63b12f247c 100644
--- a/binaries/data/mods/public/maps/scenarios/Latium.xml
+++ b/binaries/data/mods/public/maps/scenarios/Latium.xml
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:df816f8db2c260eb8044564123725fdc74ce7dd41774392b4562fa2ff2481eaa
-size 198221
+oid sha256:0f96bd1738cdd3bcc80197d27a9256273df0079d7a4aafdafab52a3f2c659a8c
+size 198493
diff --git a/binaries/data/mods/public/maps/scenarios/techdemo3.xml b/binaries/data/mods/public/maps/scenarios/techdemo3.xml
index ccb367d4ac..9f6a2812f0 100644
--- a/binaries/data/mods/public/maps/scenarios/techdemo3.xml
+++ b/binaries/data/mods/public/maps/scenarios/techdemo3.xml
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3fee2e242e51ebf2947360d1c15dcf421a4503d1c4301cdeb5330f944ce57e2d
-size 18064
+oid sha256:e105ac2e9cc3ce892479cafcd4edc4ad667ec76f139989aadc5ce21ce404100c
+size 18197
diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js
index fcb88abd97..7ca26853bc 100644
--- a/binaries/data/mods/public/simulation/components/UnitAI.js
+++ b/binaries/data/mods/public/simulation/components/UnitAI.js
@@ -5,6 +5,17 @@ UnitAI.prototype.Schema =
"" +
"";
+// Very basic stance support (currently just for test maps where we don't want
+// everyone killing each other immediately after loading)
+var g_Stances = {
+ "aggressive": {
+ attackOnSight: true,
+ },
+ "holdfire": {
+ attackOnSight: false,
+ },
+};
+
var UnitFsmSpec = {
"INDIVIDUAL": {
@@ -41,7 +52,7 @@ var UnitFsmSpec = {
{
var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var ents = rangeMan.ResetActiveQuery(this.losRangeQuery);
- if (this.AttackVisibleEntity(ents))
+ if (this.GetStance().attackOnSight && this.AttackVisibleEntity(ents))
return true;
}
@@ -56,10 +67,11 @@ var UnitFsmSpec = {
},
"LosRangeUpdate": function(msg) {
- // TODO: implement stances (ignore this message if hold-fire stance)
-
- // Start attacking one of the newly-seen enemy (if any)
- this.AttackVisibleEntity(msg.data.added);
+ if (this.GetStance().attackOnSight)
+ {
+ // Start attacking one of the newly-seen enemy (if any)
+ this.AttackVisibleEntity(msg.data.added);
+ }
},
},
@@ -373,6 +385,8 @@ UnitAI.prototype.Init = function()
{
this.orderQueue = []; // current order is at the front of the list
this.order = undefined; // always == this.orderQueue[0]
+
+ this.SetStance("aggressive");
};
UnitAI.prototype.OnCreate = function()
@@ -732,6 +746,19 @@ UnitAI.prototype.Repair = function(target, queued)
this.AddOrder("Repair", { "target": target }, queued);
};
+UnitAI.prototype.SetStance = function(stance)
+{
+ if (g_Stances[stance])
+ this.stance = stance;
+ else
+ error("UnitAI: Setting to invalid stance '"+stance+"'");
+};
+
+UnitAI.prototype.GetStance = function()
+{
+ return g_Stances[this.stance];
+};
+
//// Helper functions ////
UnitAI.prototype.CanAttack = function(target)
diff --git a/binaries/data/mods/public/simulation/helpers/Setup.js b/binaries/data/mods/public/simulation/helpers/Setup.js
new file mode 100644
index 0000000000..95177b4d5a
--- /dev/null
+++ b/binaries/data/mods/public/simulation/helpers/Setup.js
@@ -0,0 +1,17 @@
+function LoadMapSettings(settings)
+{
+ // Default settings for old maps
+ if (!settings)
+ settings = {};
+
+ if (settings.DefaultStance)
+ {
+ for each (var ent in Engine.GetEntitiesWithInterface(IID_UnitAI))
+ {
+ var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ cmpUnitAI.SetStance(settings.DefaultStance);
+ }
+ }
+}
+
+Engine.RegisterGlobal("LoadMapSettings", LoadMapSettings);
diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp
index 82d4737086..ff08cc039e 100644
--- a/source/graphics/MapReader.cpp
+++ b/source/graphics/MapReader.cpp
@@ -33,6 +33,7 @@
#include "ps/XML/Xeromyces.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
+#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpPlayer.h"
@@ -247,6 +248,50 @@ int CMapReader::ApplyData()
+PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname)
+{
+ VfsPath filename_xml = fs::change_extension(pathname, L".xml");
+
+ CXeromyces xmb_file;
+ if (xmb_file.Load(g_VFS, filename_xml) != PSRETURN_OK)
+ return PSRETURN_File_ReadFailed;
+
+ // Define all the relevant elements used in the XML file
+ #define EL(x) int el_##x = xmb_file.GetElementID(#x)
+ #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
+ EL(scenario);
+ EL(scriptsettings);
+ #undef AT
+ #undef EL
+
+ XMBElement root = xmb_file.GetRoot();
+ debug_assert(root.GetNodeName() == el_scenario);
+
+ XERO_ITER_EL(root, child)
+ {
+ int child_name = child.GetNodeName();
+ if (child_name == el_scriptsettings)
+ {
+ m_ScriptSettings = child.GetText();
+ }
+ }
+
+ return PSRETURN_OK;
+}
+
+CScriptValRooted CMapSummaryReader::GetScriptData(ScriptInterface& scriptInterface)
+{
+ CScriptValRooted data;
+ scriptInterface.Eval("({})", data);
+ if (!m_ScriptSettings.empty())
+ scriptInterface.SetProperty(data.get(), "settings", scriptInterface.ParseJSON(m_ScriptSettings), false);
+ return data;
+}
+
+
+
+
+
// Holds various state data while reading maps, so that loading can be
// interrupted (e.g. to update the progress display) then later resumed.
class CXMLReader
@@ -1115,16 +1160,25 @@ int CXMLReader::ReadOldEntities(XMBElement parent, double end_time)
entity_id_t ent = m_MapReader.pSimulation2->AddEntity(TemplateName);
if (ent != INVALID_ENTITY)
{
- CmpPtr cmpPos(*m_MapReader.pSimulation2, ent);
- if (!cmpPos.null())
+ CmpPtr cmpPosition(*m_MapReader.pSimulation2, ent);
+ if (!cmpPosition.null())
{
- cmpPos->JumpTo(Position.X, Position.Z);
- cmpPos->SetYRotation(Orientation);
+ cmpPosition->JumpTo(Position.X, Position.Z);
+ cmpPosition->SetYRotation(Orientation);
}
CmpPtr cmpOwner(*m_MapReader.pSimulation2, ent);
if (!cmpOwner.null())
cmpOwner->SetOwner(PlayerID);
+
+ if (m_MapReader.m_CameraStartupTarget == INVALID_ENTITY && !cmpPosition.null())
+ {
+ // Special-case civil centre files to initialise the camera.
+ if (PlayerID == m_MapReader.m_PlayerID && boost::algorithm::ends_with(TemplateName, L"civil_centre"))
+ {
+ m_MapReader.m_CameraStartupTarget = ent;
+ }
+ }
}
completed_jobs++;
@@ -1223,6 +1277,10 @@ int CXMLReader::ProgressiveRead()
{
ReadCamera(node);
}
+ else if (name == "ScriptSettings")
+ {
+ m_MapReader.pSimulation2->SetMapSettings(node.GetText());
+ }
else if (m_MapReader.file_format_version <= 4 && name == "Entities")
{
ret = ReadOldEntities(node, end_time);
diff --git a/source/graphics/MapReader.h b/source/graphics/MapReader.h
index 44c42cdf16..6606ff6ccd 100644
--- a/source/graphics/MapReader.h
+++ b/source/graphics/MapReader.h
@@ -35,6 +35,8 @@ class CCinemaManager;
class CTriggerManager;
class CSimulation2;
class CTextureEntry;
+class CScriptValRooted;
+class ScriptInterface;
class CXMLReader;
@@ -104,4 +106,31 @@ private:
CXMLReader* xml_reader;
};
+/**
+ * A restricted map reader that returns various summary information
+ * for use by scripts (particularly the GUI).
+ */
+class CMapSummaryReader
+{
+public:
+ /**
+ * Try to load a map file.
+ * @param pathname Path to .pmp or .xml file
+ */
+ PSRETURN LoadMap(const VfsPath& pathname);
+
+ /**
+ * Returns a value of the form:
+ * @code
+ * {
+ * "settings": { ... contents of the map's ... }
+ * }
+ * @endcode
+ */
+ CScriptValRooted GetScriptData(ScriptInterface& scriptInterface);
+
+private:
+ utf16string m_ScriptSettings;
+};
+
#endif
diff --git a/source/graphics/MapWriter.cpp b/source/graphics/MapWriter.cpp
index e88178b5ad..5cf4af3409 100644
--- a/source/graphics/MapWriter.cpp
+++ b/source/graphics/MapWriter.cpp
@@ -167,6 +167,7 @@ void CMapWriter::PackTerrain(CFilePacker& packer, CTerrain* pTerrain)
// pack tile data
packer.PackRaw(&tiles[0],sizeof(STileDesc)*tiles.size());
}
+
void CMapWriter::WriteXML(const VfsPath& filename,
WaterManager* pWaterMan, SkyManager* pSkyMan,
CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,
@@ -275,6 +276,16 @@ void CMapWriter::WriteXML(const VfsPath& filename,
}
}
+ if (pSimulation2)
+ {
+ std::string settings = pSimulation2->GetMapSettings();
+ if (!settings.empty())
+ {
+ XML_Element("ScriptSettings");
+ XML_CDATA(("\n" + settings + "\n").c_str());
+ }
+ }
+
{
XML_Element("Entities");
@@ -310,13 +321,13 @@ void CMapWriter::WriteXML(const VfsPath& filename,
CFixedVector3D rot = cmpPosition->GetRotation();
{
XML_Element("Position");
- XML_Attribute("x", pos.X.ToDouble());
- XML_Attribute("z", pos.Z.ToDouble());
+ XML_Attribute("x", pos.X);
+ XML_Attribute("z", pos.Z);
// TODO: height offset etc
}
{
XML_Element("Orientation");
- XML_Attribute("y", rot.Y.ToDouble());
+ XML_Attribute("y", rot.Y);
// TODO: X, Z maybe
}
}
diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp
index e828f27486..016c920864 100644
--- a/source/gui/scripting/ScriptFunctions.cpp
+++ b/source/gui/scripting/ScriptFunctions.cpp
@@ -21,6 +21,7 @@
#include "graphics/Camera.h"
#include "graphics/GameView.h"
+#include "graphics/MapReader.h"
#include "gui/GUIManager.h"
#include "lib/sysdep/sysdep.h"
#include "maths/FixedVector3D.h"
@@ -291,6 +292,18 @@ bool AtlasIsAvailable(void* UNUSED(cbdata))
return ATLAS_IsAvailable();
}
+CScriptVal LoadMapData(void* cbdata, std::wstring name)
+{
+ CGUIManager* guiManager = static_cast (cbdata);
+
+ CMapSummaryReader reader;
+ PSRETURN err = reader.LoadMap(VfsPath(L"maps/scenarios/") / (name + L".xml"));
+ if (err)
+ return CScriptVal();
+
+ return reader.GetScriptData(guiManager->GetScriptInterface()).get();
+}
+
} // namespace
void GuiScriptingInit(ScriptInterface& scriptInterface)
@@ -329,4 +342,5 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction("OpenURL");
scriptInterface.RegisterFunction("RestartInAtlas");
scriptInterface.RegisterFunction("AtlasIsAvailable");
+ scriptInterface.RegisterFunction("LoadMapData");
}
diff --git a/source/maths/Fixed.cpp b/source/maths/Fixed.cpp
index 445985e926..c19d16249c 100644
--- a/source/maths/Fixed.cpp
+++ b/source/maths/Fixed.cpp
@@ -90,6 +90,57 @@ CFixed_15_16 CFixed_15_16::FromString(const CStrW& s)
return FromString(CStr8(s));
}
+template<>
+CStr8 CFixed_15_16::ToString() const
+{
+ std::stringstream r;
+
+ u32 posvalue = abs(value);
+ if (value < 0)
+ r << "-";
+
+ r << (posvalue >> fract_bits);
+
+ u16 fraction = posvalue & ((1 << fract_bits) - 1);
+ if (fraction)
+ {
+ r << ".";
+
+ u32 frac = 0;
+ u32 div = 1;
+
+ // Do the inverse of FromString: Keep adding digits until (frac<<16)/div == expected fraction
+ while (true)
+ {
+ frac *= 10;
+ div *= 10;
+
+ // Low estimate of d such that ((frac+d)<<16)/div == fraction
+ u32 digit = (((u64)fraction*div) >> 16) - frac;
+ frac += digit;
+
+ // If this gives the exact target, then add the digit and stop
+ if (((u64)frac << 16) / div == fraction)
+ {
+ r << digit;
+ break;
+ }
+
+ // If the next higher digit gives the exact target, then add that digit and stop
+ if (digit <= 8 && (((u64)frac+1) << 16) / div == fraction)
+ {
+ r << digit+1;
+ break;
+ }
+
+ // Otherwise add the digit and continue
+ r << digit;
+ }
+ }
+
+ return r.str();
+}
+
// Based on http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization
CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x)
{
diff --git a/source/maths/Fixed.h b/source/maths/Fixed.h
index 51141649cd..96c8ff1127 100644
--- a/source/maths/Fixed.h
+++ b/source/maths/Fixed.h
@@ -166,6 +166,9 @@ public:
return (value + fract_pow2 - 1) >> fract_bits;
}
+ /// Returns the shortest string such that FromString will parse to the correct value.
+ CStr8 ToString() const;
+
/// Returns true if the number is precisely 0.
bool IsZero() const { return value == 0; }
diff --git a/source/maths/tests/test_Fixed.h b/source/maths/tests/test_Fixed.h
index d78a628ccc..4e8481cbbd 100644
--- a/source/maths/tests/test_Fixed.h
+++ b/source/maths/tests/test_Fixed.h
@@ -116,6 +116,31 @@ public:
// TODO: could test more large/small numbers, errors, etc
}
+ void test_ToString()
+ {
+ #define T(n, str) { fixed f = fixed::FromDouble(n); TS_ASSERT_STR_EQUALS(f.ToString(), str); TS_ASSERT_EQUALS(fixed::FromString(f.ToString()), f); }
+
+ T(1.0, "1");
+ T(-1.0, "-1");
+ T(10000.0, "10000");
+ T(1.25, "1.25");
+ T(-1.25, "-1.25");
+ T(0.5, "0.5");
+ T(1.0/65536.0, "0.00002");
+ T(2.0/65536.0, "0.00004");
+ T(250367.0/65536.0, "3.8203");
+ T(32768.0 - 1.0/65536.0, "32767.99999");
+ T(-32768.0 + 1.0/65536.0, "-32767.99999");
+
+ #undef T
+
+ for (int i = 0; i < 65536; ++i)
+ {
+ fixed f = fixed::FromDouble(i / 65536.0);
+ TS_ASSERT_EQUALS(fixed::FromString(f.ToString()), f);
+ }
+ }
+
void test_RoundToZero()
{
TS_ASSERT_EQUALS(fixed::FromFloat(10.f).ToInt_RoundToZero(), 10);
diff --git a/source/ps/XML/XMLWriter.cpp b/source/ps/XML/XMLWriter.cpp
index f5e7877eba..c0d232c42c 100644
--- a/source/ps/XML/XMLWriter.cpp
+++ b/source/ps/XML/XMLWriter.cpp
@@ -23,6 +23,7 @@
#include "ps/Filesystem.h"
#include "lib/utf8.h"
#include "lib/sysdep/cpu.h"
+#include "maths/Fixed.h"
#define LOG_CATEGORY L"xml"
@@ -273,6 +274,11 @@ template <> void XMLWriter_File::ElementAttribute(const char* name
ElementAttribute(name, utf8_from_wstring(value).c_str(), newelement);
}
+template <> void XMLWriter_File::ElementAttribute(const char* name, const fixed& value, bool newelement)
+{
+ ElementAttribute(name, value.ToString().c_str(), newelement);
+}
+
// Use CStr's conversion for most types:
#define TYPE2(ID_T, ARG_T) \
template <> void XMLWriter_File::ElementAttribute(const char* name, ARG_T value, bool newelement) \
diff --git a/source/scriptinterface/ScriptInterface.cpp b/source/scriptinterface/ScriptInterface.cpp
index 91785b7f39..bb7c550c82 100644
--- a/source/scriptinterface/ScriptInterface.cpp
+++ b/source/scriptinterface/ScriptInterface.cpp
@@ -22,6 +22,7 @@
#include "AutoRooters.h"
#include "lib/debug.h"
+#include "lib/utf8.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "ps/utf16string.h"
@@ -628,6 +629,58 @@ std::wstring ScriptInterface::ToString(jsval obj)
return source;
}
+CScriptValRooted ScriptInterface::ParseJSON(const utf16string& string)
+{
+ jsval vp;
+ JSONParser* parser = JS_BeginJSONParse(m->m_cx, &vp);
+ if (!parser)
+ {
+ LOGERROR(L"ParseJSON failed to begin");
+ return CScriptValRooted();
+ }
+
+ if (!JS_ConsumeJSONText(m->m_cx, parser, string.c_str(), string.size()))
+ {
+ LOGERROR(L"ParseJSON failed to consume");
+ return CScriptValRooted();
+ }
+
+ if (!JS_FinishJSONParse(m->m_cx, parser, JSVAL_NULL))
+ {
+ LOGERROR(L"ParseJSON failed to finish");
+ return CScriptValRooted();
+ }
+
+ return CScriptValRooted(m->m_cx, vp);
+}
+
+struct Stringifier
+{
+ static JSBool callback(const jschar *buf, uint32 len, void *data)
+ {
+ utf16string str(buf, buf+len);
+ std::wstring strw(str.begin(), str.end());
+
+ LibError err; // ignore Unicode errors
+ static_cast(data)->stream << utf8_from_wstring(strw, &err);
+ return JS_TRUE;
+ }
+
+ std::stringstream stream;
+};
+
+std::string ScriptInterface::StringifyJSON(jsval obj)
+{
+ Stringifier str;
+ if (!JS_Stringify(m->m_cx, &obj, NULL, INT_TO_JSVAL(2), &Stringifier::callback, &str))
+ {
+ LOGERROR(L"StringifyJSON failed");
+ return "";
+ }
+
+ return str.stream.str();
+}
+
void ScriptInterface::ReportError(const char* msg)
{
// JS_ReportError by itself doesn't seem to set a JS-style exception, and so
diff --git a/source/scriptinterface/ScriptInterface.h b/source/scriptinterface/ScriptInterface.h
index c84c6e6a39..599361de72 100644
--- a/source/scriptinterface/ScriptInterface.h
+++ b/source/scriptinterface/ScriptInterface.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -29,6 +29,8 @@
#include "ScriptTypes.h"
#include "ScriptVal.h"
+#include "ps/utf16string.h"
+
class AutoGCRooter;
namespace boost { class rand48; }
@@ -154,6 +156,16 @@ public:
std::wstring ToString(jsval obj);
+ /**
+ * Parse a JSON string. Returns the undefined value on error.
+ */
+ CScriptValRooted ParseJSON(const utf16string& string);
+
+ /**
+ * Stringify to a JSON string, UTF-8 encoded. Returns an empty string on error.
+ */
+ std::string StringifyJSON(jsval obj);
+
/**
* Report the given error message through the JS error reporting mechanism,
* and throw a JS exception. (Callers can check IsPendingException, and must
diff --git a/source/scriptinterface/tests/test_ScriptInterface.h b/source/scriptinterface/tests/test_ScriptInterface.h
index 6df1f2ef2c..84d7ac605e 100644
--- a/source/scriptinterface/tests/test_ScriptInterface.h
+++ b/source/scriptinterface/tests/test_ScriptInterface.h
@@ -19,6 +19,7 @@
#include "scriptinterface/ScriptInterface.h"
+#include "lib/utf8.h"
#include "ps/CLogger.h"
#include
@@ -125,4 +126,20 @@ public:
TS_ASSERT(script.Eval("Math.random()", d2));
TS_ASSERT_EQUALS(d1, d2);
}
+
+ void test_json()
+ {
+ ScriptInterface script("Test");
+
+ std::string input = "({'x':1,'z':[2,'3\\u263A\\ud800'],\"y\":true})";
+ CScriptValRooted val;
+ TS_ASSERT(script.Eval(input.c_str(), val));
+
+ std::string stringified = script.StringifyJSON(val.get());
+ TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\":1,\n \"z\":[2,\n \"3\xE2\x98\xBA\xEF\xBF\xBD\"\n ],\n \"y\":true\n}");
+
+ std::wstring stringifiedw = wstring_from_utf8(stringified.c_str());
+ val = script.ParseJSON(utf16string(stringifiedw.begin(), stringifiedw.end()));
+ TS_ASSERT_WSTR_EQUALS(script.ToString(val.get()), L"({x:1, z:[2, \"3\\u263A\\uFFFD\"], y:true})");
+ }
};
diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp
index 14108e8b79..aa8e84c199 100644
--- a/source/simulation2/Simulation2.cpp
+++ b/source/simulation2/Simulation2.cpp
@@ -135,6 +135,7 @@ public:
double m_DeltaTime;
std::wstring m_StartupScript;
+ CScriptValRooted m_MapSettings;
std::set m_LoadedScripts;
@@ -189,8 +190,15 @@ bool CSimulation2Impl::Update(int turnLength, const std::vector cmpCommandQueue(m_SimContext, SYSTEM_ENTITY);
if (!cmpCommandQueue.null())
@@ -370,6 +378,16 @@ const std::wstring& CSimulation2::GetStartupScript()
return m->m_StartupScript;
}
+void CSimulation2::SetMapSettings(const utf16string& settings)
+{
+ m->m_MapSettings = m->m_ComponentManager.GetScriptInterface().ParseJSON(settings);
+}
+
+std::string CSimulation2::GetMapSettings()
+{
+ return m->m_ComponentManager.GetScriptInterface().StringifyJSON(m->m_MapSettings.get());
+}
+
LibError CSimulation2::ReloadChangedFile(const VfsPath& path)
{
return m->ReloadChangedFile(path);
diff --git a/source/simulation2/Simulation2.h b/source/simulation2/Simulation2.h
index 4b31787b13..e83edca059 100644
--- a/source/simulation2/Simulation2.h
+++ b/source/simulation2/Simulation2.h
@@ -24,6 +24,7 @@
#include "scriptinterface/ScriptVal.h"
#include "lib/file/vfs/vfs_path.h"
+#include "ps/utf16string.h"
#include