forked from 0ad/0ad
Integrates random maps with Atlas. Fixes #460.
Adds various map controls to Atlas. Adds New Map dialog to Atlas. Removes a few references to old simulation header files. This was SVN commit r9247.
This commit is contained in:
parent
3b8e216622
commit
ad6f305359
@ -46,14 +46,24 @@
|
||||
|
||||
<playercolours>
|
||||
<colour name="Gaia" rgb="255 255 255"/>
|
||||
<colour name="Player 1" rgb="111 111 207" buttontext="255 255 255"/>
|
||||
<colour name="Player 2" rgb="207 111 111" buttontext="255 255 255"/>
|
||||
<colour name="Player 3" rgb="111 207 111" buttontext="255 255 255"/>
|
||||
<colour name="Player 4" rgb="207 207 111" buttontext="255 255 255"/>
|
||||
<colour name="Player 5" rgb="111 207 207" buttontext="255 255 255"/>
|
||||
<colour name="Player 6" rgb="207 111 207" buttontext="255 255 255"/>
|
||||
<colour name="Player 7" rgb="207 159 111" buttontext="255 255 255"/>
|
||||
<colour name="Player 8" rgb="159 159 159" buttontext="255 255 255"/>
|
||||
<colour name="Player 1" rgb="80 80 200" buttontext="255 255 255"/>
|
||||
<colour name="Player 2" rgb="150 20 20" buttontext="255 255 255"/>
|
||||
<colour name="Player 3" rgb="50 165 5" buttontext="255 255 255"/>
|
||||
<colour name="Player 4" rgb="230 230 75" buttontext="255 255 255"/>
|
||||
<colour name="Player 5" rgb="50 170 170" buttontext="255 255 255"/>
|
||||
<colour name="Player 6" rgb="160 80 200" buttontext="255 255 255"/>
|
||||
<colour name="Player 7" rgb="230 120 20" buttontext="255 255 255"/>
|
||||
<colour name="Player 8" rgb="64 64 64" buttontext="255 255 255"/>
|
||||
</playercolours>
|
||||
|
||||
<mapsizes>
|
||||
<size name="Tiny" patches="8"/>
|
||||
<size name="Small" patches="12"/>
|
||||
<size name="Medium" patches="16"/>
|
||||
<size name="Normal" patches="20"/>
|
||||
<size name="Large" patches="24"/>
|
||||
<size name="Very Large" patches="28"/>
|
||||
<size name="Giant" patches="32"/>
|
||||
</mapsizes>
|
||||
|
||||
</lists>
|
||||
|
139
binaries/data/tools/atlas/scripts/common/terrain.js
Normal file
139
binaries/data/tools/atlas/scripts/common/terrain.js
Normal file
@ -0,0 +1,139 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Terrain preview page
|
||||
//
|
||||
// Used by new dialog and terrain panel
|
||||
|
||||
global.TerrainPreviewPage = function(panel, name, width, height)
|
||||
{
|
||||
this.panel = panel;
|
||||
this.name = name;
|
||||
|
||||
// Size of texture preview images
|
||||
this.w = width ? width : 120
|
||||
this.h = height ? height : 40;
|
||||
|
||||
this.lastTerrainSelection = null; // button that was last selected, so we can undo its colouring
|
||||
this.previewReloadTimer = null;
|
||||
}
|
||||
|
||||
global.TerrainPreviewPage.prototype = {
|
||||
reloadPreviews: function()
|
||||
{
|
||||
this.panel.freeze();
|
||||
this.scrolled.destroyChildren();
|
||||
this.itemSizer.clear();
|
||||
|
||||
this.lastTerrainSelection = null; // clear any reference to deleted window
|
||||
|
||||
var previews = Atlas.Message.GetTerrainGroupPreviews(this.name, this.w, this.h).previews;
|
||||
var i = 0;
|
||||
var names = [];
|
||||
var allLoaded = true;
|
||||
for each (var p in previews)
|
||||
{
|
||||
if (!p.loaded)
|
||||
{
|
||||
allLoaded = false;
|
||||
}
|
||||
|
||||
// Create a wrapped-text label (replacing '_' with ' ' so there are more wrapping opportunities)
|
||||
var labelText = p.name.replace(/_/g, ' ');
|
||||
var label = new wxStaticText(this.scrolled, -1, labelText, wxDefaultPosition, wxDefaultSize, wxStaticText.ALIGN_CENTER);
|
||||
label.wrap(this.w);
|
||||
|
||||
var imgSizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
var button = new wxBitmapButton(this.scrolled, -1, p.imagedata);
|
||||
var self = this;
|
||||
button.terrainName = p.name;
|
||||
button.onClicked = function()
|
||||
{
|
||||
Atlas.SetSelectedTexture(this.terrainName);
|
||||
if (self.lastTerrainSelection)
|
||||
{
|
||||
self.lastTerrainSelection.backgroundColour = wxNullColour;
|
||||
}
|
||||
this.backgroundColour = new wxColour(255, 255, 0);
|
||||
self.lastTerrainSelection = this;
|
||||
};
|
||||
imgSizer.add(button, 0, wxAlignment.CENTRE);
|
||||
imgSizer.add(label, 1, wxAlignment.CENTRE);
|
||||
this.itemSizer.add(imgSizer, 0, wxAlignment.CENTRE | wxStretch.EXPAND);
|
||||
}
|
||||
this.itemSizer.layout();
|
||||
this.panel.layout();
|
||||
this.panel.thaw();
|
||||
|
||||
// If not all textures were loaded yet, run a timer to reload the previews
|
||||
// every so often until they've all finished
|
||||
if (allLoaded && this.previewReloadTimer)
|
||||
{
|
||||
this.previewReloadTimer.stop();
|
||||
this.previewReloadTimer = null;
|
||||
}
|
||||
else if (!allLoaded && !this.previewReloadTimer)
|
||||
{
|
||||
this.previewReloadTimer = new wxTimer();
|
||||
var self = this;
|
||||
this.previewReloadTimer.onNotify = function() { self.reloadPreviews(); };
|
||||
this.previewReloadTimer.start(2000);
|
||||
}
|
||||
},
|
||||
|
||||
display: function()
|
||||
{
|
||||
if (this.loaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.panel.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
var scrolled = new wxScrolledWindow(this.panel, -1, wxDefaultPosition, wxDefaultSize, wxWindow.VSCROLL);
|
||||
scrolled.setScrollRate(0, 10);
|
||||
scrolled.backgroundColour = new wxColour(255, 255, 255);
|
||||
this.panel.sizer.add(scrolled, 1, wxStretch.EXPAND);
|
||||
|
||||
var itemSizer = new wxGridSizer(6, 4, 0);
|
||||
scrolled.sizer = itemSizer;
|
||||
|
||||
// Adjust the number of columns to fit in the available area
|
||||
var w = this.w;
|
||||
scrolled.onSize = function (evt)
|
||||
{
|
||||
var numCols = Math.max(1, Math.floor(evt.size.width / (w+16)));
|
||||
if (itemSizer.cols != numCols)
|
||||
{
|
||||
itemSizer.cols = numCols;
|
||||
}
|
||||
};
|
||||
|
||||
this.scrolled = scrolled;
|
||||
this.itemSizer = itemSizer;
|
||||
this.reloadPreviews();
|
||||
|
||||
// TODO: fix keyboard navigation of the terrain previews
|
||||
|
||||
this.loaded = true;
|
||||
}
|
||||
};
|
||||
|
||||
global.TerrainPreviewNotebook = function(window)
|
||||
{
|
||||
var terrainGroups = Atlas.Message.GetTerrainGroups();
|
||||
var nb = new wxNotebook(window, -1);
|
||||
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
window.sizer.add(nb, 1, wxStretch.EXPAND);
|
||||
|
||||
var pages = [];
|
||||
nb.onPageChanged = function (evt)
|
||||
{
|
||||
pages[evt.selection].display()
|
||||
evt.skip = true;
|
||||
}
|
||||
for each (var groupName in terrainGroups.groupNames)
|
||||
{
|
||||
var panel = new wxPanel(nb, -1);
|
||||
var page = new global.TerrainPreviewPage(panel, groupName);
|
||||
pages.push(page);
|
||||
nb.addPage(panel, groupName); // TODO: use Titlecase letters
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ defaults.objectSettings.onSelectionChange = function () {
|
||||
if (settings.player != -1)
|
||||
this.playerID = settings.player;
|
||||
this.actorSelections = settings.selections;
|
||||
this.variantGroups = settings.variantgroups;
|
||||
this.variantGroups = settings.variantGroups;
|
||||
}
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
function setupSimTest(window)
|
||||
|
||||
function setupSimTest(window, rmsPanel)
|
||||
{
|
||||
var state = 'inactive'; // inactive, playing
|
||||
var speed = 0;
|
||||
@ -32,7 +33,19 @@ function setupSimTest(window)
|
||||
function updateEnableStatus()
|
||||
{
|
||||
for (var i in buttons)
|
||||
{
|
||||
buttons[i][3].enable = buttons[i][2]();
|
||||
}
|
||||
|
||||
if (state == 'playing')
|
||||
{
|
||||
// Disable RMS controls
|
||||
rmsPanel.enable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
rmsPanel.enable = true;
|
||||
}
|
||||
}
|
||||
|
||||
var speeds = [ 1/8, 1, 8 ];
|
||||
@ -56,67 +69,468 @@ function setupSimTest(window)
|
||||
}
|
||||
updateEnableStatus();
|
||||
window.sizer.add(sizer, 0, wxStretch.EXPAND | wxDirection.LEFT|wxDirection.RIGHT, 2);
|
||||
|
||||
var dumpDisplayButton = new wxButton(window, -1, 'Display sim state');
|
||||
dumpDisplayButton.onClicked = function ()
|
||||
{
|
||||
var state = Atlas.Message.SimStateDebugDump(false).dump;
|
||||
if (state.length > 10240) // avoid giant message boxes that crash
|
||||
state = state.substr(0, 10240)+"...";
|
||||
wxMessageBox(state);
|
||||
};
|
||||
window.sizer.add(dumpDisplayButton);
|
||||
|
||||
var dumpButton = new wxButton(window, -1, 'Dump sim state to disk');
|
||||
dumpButton.toolTip = 'Saves to sim-dump-$(TIMESTAMP).txt in working directory';
|
||||
dumpButton.onClicked = function () {
|
||||
var filename = 'sim-dump-' + (new Date().getTime()) + '.txt';
|
||||
var state = Atlas.Message.SimStateDebugDump(false).dump;
|
||||
var file = new wxFFile(filename, 'w'); // TODO: error check
|
||||
file.write(state);
|
||||
file.close();
|
||||
};
|
||||
window.sizer.add(dumpButton);
|
||||
|
||||
var dumpBinButton = new wxButton(window, -1, 'Dump binary sim state to disk');
|
||||
dumpBinButton.toolTip = 'Saves to sim-dump-$(TIMESTAMP).dat in working directory';
|
||||
dumpBinButton.onClicked = function () {
|
||||
var filename = 'sim-dump-' + (new Date().getTime()) + '.dat';
|
||||
var state = Atlas.Message.SimStateDebugDump(true).dump;
|
||||
var file = new wxFFile(filename, 'w'); // TODO: error check
|
||||
file.write(state);
|
||||
file.close();
|
||||
};
|
||||
window.sizer.add(dumpBinButton);
|
||||
|
||||
}
|
||||
|
||||
function generateRMS(name)
|
||||
//TODO: Would be nicer not to use an extra settings property, this is to avoid the observer data getting written to maps
|
||||
// perhaps that single element can be excluded
|
||||
var defaults = {
|
||||
mapSettings : {
|
||||
settings: {
|
||||
Name: 'Untitled',
|
||||
Description : '',
|
||||
PlayerData : []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var g_observer;
|
||||
|
||||
// Flag if we should only notify other scripts not this one (prevents recursion)
|
||||
var g_externalNotify;
|
||||
|
||||
// Merges the 'defs' tree into 'obj', overwriting any old values with
|
||||
// the same keys
|
||||
function setDefaults(defs, obj)
|
||||
{
|
||||
var cwd = wxFileName.cwd;
|
||||
wxFileName.cwd = Atlas.GetDataDirectory();
|
||||
var ret = wxExecute([ 'rmgen.exe', name, '_atlasrm' ], wxExecFlag.SYNC); // TODO: portability
|
||||
wxFileName.cwd = cwd;
|
||||
if (ret == 0)
|
||||
Atlas.Message.LoadMap('_atlasrm.pmp');
|
||||
else
|
||||
wxMessageBox('Failed to run rmgen (error code: '+ret+')');
|
||||
if (defs instanceof Object)
|
||||
{
|
||||
for (var k in defs) {
|
||||
if (obj instanceof Object && k in obj)
|
||||
setDefaults(defs[k], obj[k]);
|
||||
else
|
||||
obj[k] = defs[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read civ data and player defaults
|
||||
function getStartingData()
|
||||
{
|
||||
// Load civilization data
|
||||
if (!Atlas.State.CivData)
|
||||
{
|
||||
Atlas.State.CivData = [];
|
||||
Atlas.State.CivNames = [];
|
||||
Atlas.State.CivCodes = [];
|
||||
|
||||
var dataArray = Atlas.Message.GetCivData().data;
|
||||
if (dataArray)
|
||||
{ // parse JSON strings into objects
|
||||
for (var i = 0; i < dataArray.length; ++i)
|
||||
{
|
||||
var civ = JSON.parse(dataArray[i]);
|
||||
if (civ)
|
||||
{
|
||||
Atlas.State.CivData.push(civ);
|
||||
Atlas.State.CivNames.push(civ.Name);
|
||||
Atlas.State.CivCodes.push(civ.Code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load player default data (names, civs, colors, etc)
|
||||
if (!Atlas.State.PlayerDefaults)
|
||||
{
|
||||
var rawData = Atlas.Message.GetPlayerDefaults().defaults;
|
||||
if(rawData)
|
||||
{
|
||||
Atlas.State.PlayerDefaults = JSON.parse(rawData).PlayerData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMapSettings()
|
||||
{
|
||||
// Get map settings to populate GUI
|
||||
var settings = Atlas.Message.GetMapSettings().settings;
|
||||
if (settings)
|
||||
{
|
||||
Atlas.State.mapSettings.settings = JSON.parse(settings);
|
||||
}
|
||||
}
|
||||
|
||||
function makeObservable(obj)
|
||||
{
|
||||
// If the object has already been set up as observable, don't do any more work.
|
||||
// (In particular, don't delete any old observers, so they don't get lost when
|
||||
// this script is reloaded.)
|
||||
if (typeof obj._observers != 'undefined')
|
||||
return;
|
||||
|
||||
obj._observers = [];
|
||||
obj.registerObserver = function (callback) {
|
||||
this._observers.push(callback);
|
||||
}
|
||||
obj.unregisterObserver = function (callback) {
|
||||
this._observers = this._observers.filter(function (o) { return o !== callback });
|
||||
}
|
||||
obj.notifyObservers = function () {
|
||||
for each (var o in this._observers)
|
||||
o(this);
|
||||
};
|
||||
}
|
||||
|
||||
function init(window)
|
||||
{
|
||||
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
|
||||
var button = new wxButton(window, -1, 'Generate empty map');
|
||||
button.onClicked = function () { Atlas.Message.GenerateMap(9); };
|
||||
window.sizer.add(button, 0, wxDirection.ALL, 2);
|
||||
// Don't overwrite old data when script is hotloaded
|
||||
setDefaults(defaults, Atlas.State);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Map setting controls
|
||||
// Map name
|
||||
var settingsSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Map settings'), wxOrientation.VERTICAL);
|
||||
var boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
boxSizer.add(new wxStaticText(window, -1, 'Name'), 0, wxAlignment.CENTER_VERTICAL);
|
||||
boxSizer.add(10, 0);
|
||||
var mapNameCtrl = new wxTextCtrl(window, -1, 'Untitled');
|
||||
mapNameCtrl.toolTip = "Enter a name for the map here";
|
||||
mapNameCtrl.onText = function(evt)
|
||||
{
|
||||
Atlas.State.mapSettings.settings.Name = evt.string;
|
||||
};
|
||||
boxSizer.add(mapNameCtrl, 1);
|
||||
function updateMapName()
|
||||
{
|
||||
mapNameCtrl.value = Atlas.State.mapSettings.settings.Name ? Atlas.State.mapSettings.settings.Name : 'Untitled';
|
||||
}
|
||||
settingsSizer.add(boxSizer, 0, wxStretch.EXPAND);
|
||||
|
||||
var sizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
var rmsFilename = new wxTextCtrl(window, -1, 'cantabrian_highlands');
|
||||
var rmsButton = new wxButton(window, -1, 'Run RMS');
|
||||
rmsButton.onClicked = function () { generateRMS(rmsFilename.value) }
|
||||
sizer.add(rmsFilename, 1);
|
||||
sizer.add(rmsButton, 0);
|
||||
window.sizer.add(sizer, 0, wxStretch.EXPAND | wxDirection.ALL, 2);
|
||||
settingsSizer.add(5, 5, 0); // Add space
|
||||
|
||||
setupSimTest(window);
|
||||
// Map description
|
||||
boxSizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
boxSizer.add(new wxStaticText(window, -1, 'Description'), wxAlignment.RIGHT);
|
||||
var mapDescCtrl = new wxTextCtrl(window, -1, '', wxDefaultPosition, new wxSize(-1, 100), wxTextCtrl.MULTILINE);
|
||||
mapDescCtrl.toolTip = "Enter an interesting description for the map here";
|
||||
mapDescCtrl.onText = function(evt)
|
||||
{
|
||||
Atlas.State.mapSettings.settings.Description = evt.string;
|
||||
};
|
||||
boxSizer.add(mapDescCtrl, 0, wxStretch.EXPAND);
|
||||
function updateMapDesc()
|
||||
{
|
||||
mapDescCtrl.value = Atlas.State.mapSettings.settings.Description ? Atlas.State.mapSettings.settings.Description : '';
|
||||
}
|
||||
settingsSizer.add(boxSizer, 0, wxStretch.EXPAND);
|
||||
|
||||
settingsSizer.add(5, 5, 0); // Add space
|
||||
|
||||
// Reveal map
|
||||
boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
boxSizer.add(new wxStaticText(window, -1, 'Reveal map'), wxAlignment.LEFT);
|
||||
boxSizer.add(10, 0);
|
||||
var revealMapCtrl = new wxCheckBox(window, -1, '');
|
||||
revealMapCtrl.toolTip = 'If checked, players will see the whole map explored and their enemies';
|
||||
revealMapCtrl.onCheckBox = function(evt)
|
||||
{
|
||||
Atlas.State.mapSettings.settings.RevealMap = evt.checked;
|
||||
};
|
||||
boxSizer.add(revealMapCtrl);
|
||||
function updateRevealMap()
|
||||
{ // Defaults to false if setting doesn't exist
|
||||
revealMapCtrl.value = Atlas.State.mapSettings.settings.RevealMap;
|
||||
}
|
||||
settingsSizer.add(boxSizer, 0, wxStretch.EXPAND);
|
||||
|
||||
settingsSizer.add(5, 5, 0); // Add space
|
||||
|
||||
// Game type
|
||||
boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
boxSizer.add(new wxStaticText(window, -1, 'Game type'), 0, wxAlignment.CENTER_VERTICAL);
|
||||
boxSizer.add(10, 0);
|
||||
var gameTypes = ["conquest", "endless"];
|
||||
var gameTypeCtrl = new wxChoice(window, -1, wxDefaultPosition, new wxSize(100,-1), gameTypes);
|
||||
gameTypeCtrl.toolTip = 'Select the game type / victory conditions';
|
||||
gameTypeCtrl.onChoice = function (evt)
|
||||
{
|
||||
if (evt.selection != -1)
|
||||
{
|
||||
Atlas.State.mapSettings.settings.GameType = evt.string;
|
||||
}
|
||||
};
|
||||
boxSizer.add(gameTypeCtrl);
|
||||
function updateGameType()
|
||||
{
|
||||
var idx = Atlas.State.mapSettings.settings.GameType ? gameTypes.indexOf(Atlas.State.mapSettings.settings.GameType) : 0;
|
||||
gameTypeCtrl.selection = idx;
|
||||
}
|
||||
settingsSizer.add(boxSizer, 0, wxStretch.EXPAND);
|
||||
|
||||
settingsSizer.add(5, 5, 0); // Add space
|
||||
|
||||
// Number of players
|
||||
boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
boxSizer.add(new wxStaticText(window, -1, 'Number of Players'), 0, wxAlignment.CENTER_VERTICAL);
|
||||
boxSizer.add(10, 0);
|
||||
var numPlayers = new wxSpinCtrl(window, -1, '8', wxDefaultPosition, new wxSize(40, -1));
|
||||
numPlayers.toolTip = 'Select the number of players on this map';
|
||||
numPlayers.setRange(1, 8);
|
||||
numPlayers.onSpinCtrl = function(evt)
|
||||
{
|
||||
// Adjust player data accordingly
|
||||
if (!Atlas.State.mapSettings.settings.PlayerData)
|
||||
Atlas.State.mapSettings.settings.PlayerData = [];
|
||||
|
||||
Atlas.State.numPlayers = evt.position;
|
||||
var curPlayers = Atlas.State.mapSettings.settings.PlayerData.length;
|
||||
|
||||
if (curPlayers < evt.position)
|
||||
{ // Add extra players from default
|
||||
for (var i = curPlayers; i < evt.position; ++i)
|
||||
{
|
||||
Atlas.State.mapSettings.settings.PlayerData.push(Atlas.State.PlayerDefaults[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //Remove extra players
|
||||
Atlas.State.mapSettings.settings.PlayerData = Atlas.State.mapSettings.settings.PlayerData.slice(0, evt.position);
|
||||
}
|
||||
|
||||
g_externalNotify = true;
|
||||
Atlas.State.mapSettings.notifyObservers();
|
||||
g_externalNotify = false;
|
||||
};
|
||||
boxSizer.add(numPlayers);
|
||||
function updateNumPlayers()
|
||||
{
|
||||
numPlayers.value = Atlas.State.mapSettings.settings.PlayerData ? Atlas.State.mapSettings.settings.PlayerData.length : 8;
|
||||
}
|
||||
settingsSizer.add(boxSizer, 0, wxStretch.EXPAND);
|
||||
|
||||
window.sizer.add(settingsSizer, 0, wxStretch.EXPAND | wxDirection.LEFT|wxDirection.RIGHT, 2);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Keyword list
|
||||
|
||||
var keywordSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Keywords'), wxOrientation.VERTICAL);
|
||||
var keywords = ["demo", "hidden"];
|
||||
var keywordCtrl = new wxListBox(window, -1, wxDefaultPosition, wxDefaultSize, keywords, wxListBox.MULTIPLE);
|
||||
keywordCtrl.toolTip = 'Select keyword(s) to apply to this map';
|
||||
keywordCtrl.onListBox = function()
|
||||
{
|
||||
var kws = [];
|
||||
// Convert selections to keywords array
|
||||
var selections = this.selections;
|
||||
for (var i = 0; i < selections.length; ++i)
|
||||
{
|
||||
kws.push(keywords[selections[i]]);
|
||||
}
|
||||
Atlas.State.mapSettings.settings.Keywords = kws;
|
||||
};
|
||||
function updateKeywords()
|
||||
{
|
||||
var kws = Atlas.State.mapSettings.settings.Keywords;
|
||||
if (kws)
|
||||
{
|
||||
for (var i = 0; i < kws.length; ++i)
|
||||
{
|
||||
var idx = keywords.indexOf(kws[i]);
|
||||
if (idx != -1)
|
||||
{
|
||||
// TODO: There doesn't appear to be a way to set selections
|
||||
}
|
||||
else
|
||||
{ // New keyword, add to list
|
||||
keywordCtrl.insertItems([kws[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keywordSizer.add(keywordCtrl, 0, wxStretch.EXPAND);
|
||||
|
||||
window.sizer.add(keywordSizer, 0, wxStretch.EXPAND | wxDirection.LEFT|wxDirection.RIGHT, 2);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Random map controls
|
||||
var rmsPanel = new wxPanel(window, -1);
|
||||
var rmsSizer = new wxStaticBoxSizer(new wxStaticBox(rmsPanel, -1, 'Random map'), wxOrientation.VERTICAL);
|
||||
|
||||
var scriptNames = [];
|
||||
var scriptData = [];
|
||||
boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
var rmsChoice = new wxChoice(rmsPanel, -1, wxDefaultPosition, wxDefaultSize, scriptNames);
|
||||
rmsChoice.toolTip = "Select the random map script to run";
|
||||
function loadScriptChoices()
|
||||
{
|
||||
// Reload RMS data
|
||||
scriptNames = [];
|
||||
scriptData = [];
|
||||
|
||||
// Get array of RMS data
|
||||
var rawData = Atlas.Message.GetRMSData().data;
|
||||
for (var i = 0; i < rawData.length; ++i)
|
||||
{
|
||||
// Parse JSON strings to objects
|
||||
var data = JSON.parse(rawData[i]);
|
||||
if (data && data.settings)
|
||||
{
|
||||
scriptData.push(data);
|
||||
scriptNames.push(data.settings.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add script names to choice control
|
||||
rmsChoice.clear();
|
||||
rmsChoice.append(scriptNames);
|
||||
rmsChoice.selection = 0;
|
||||
}
|
||||
boxSizer.add(rmsChoice, 1, wxAlignment.CENTER_VERTICAL);
|
||||
boxSizer.add(5, 0);
|
||||
var rmsReload = new wxButton(rmsPanel, -1, 'R', wxDefaultPosition, new wxSize(20, -1));
|
||||
rmsReload.toolTip = 'Click to refresh random map list';
|
||||
rmsReload.onClicked = function()
|
||||
{
|
||||
loadScriptChoices();
|
||||
};
|
||||
boxSizer.add(rmsReload, 0, wxAlignment.CENTER_VERTICAL);
|
||||
rmsSizer.add(boxSizer, 0, wxStretch.EXPAND | wxDirection.ALL, 2);
|
||||
|
||||
boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
boxSizer.add(new wxStaticText(rmsPanel, -1, 'Map size'), 0, wxAlignment.CENTER_VERTICAL);
|
||||
boxSizer.add(10, 0);
|
||||
// TODO: Get this data from single location (currently specified here, Atlas\lists.xml, and game setup)
|
||||
var sizeNames = ["Tiny", "Small", "Medium", "Normal", "Large", "Very Large", "Giant"];
|
||||
var sizePatches = [8, 12, 16, 20, 24, 28, 32];
|
||||
var sizeChoice = new wxChoice(rmsPanel, -1, wxDefaultPosition, wxDefaultSize, sizeNames);
|
||||
var numChoices = sizeNames.length;
|
||||
sizeChoice.toolTip = 'Select the desired map size\n'+sizeNames[0]+' = '+sizePatches[0]+' patches, '+sizeNames[numChoices-1]+' = '+sizePatches[numChoices-1]+' patches';
|
||||
sizeChoice.selection = 0;
|
||||
boxSizer.add(sizeChoice, 1);
|
||||
rmsSizer.add(boxSizer, 0, wxStretch.EXPAND | wxDirection.ALL, 2);
|
||||
|
||||
boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
var generateBtn = new wxButton(rmsPanel, -1, 'Generate');
|
||||
generateBtn.toolTip = 'Generate random map using selected script';
|
||||
generateBtn.onClicked = function ()
|
||||
{
|
||||
var selection = rmsChoice.selection;
|
||||
if (selection != -1)
|
||||
{
|
||||
var RMSData = scriptData[selection].settings;
|
||||
if (RMSData)
|
||||
{
|
||||
if (useRandomCtrl.value)
|
||||
{ // Generate random seed
|
||||
generateRandomSeed();
|
||||
}
|
||||
|
||||
// Base terrains must be array
|
||||
var terrainArray = [];
|
||||
if (RMSData.BaseTerrain instanceof Array)
|
||||
{
|
||||
terrainArray = RMSData.BaseTerrain;
|
||||
}
|
||||
else
|
||||
{ // Add string to array
|
||||
terrainArray.push(RMSData.BaseTerrain);
|
||||
}
|
||||
|
||||
// Stringify player data
|
||||
var pDataStr = JSON.stringify(Atlas.State.mapSettings.settings.PlayerData);
|
||||
|
||||
// Generate map
|
||||
var ret = Atlas.Message.GenerateMap(
|
||||
RMSData.Script,
|
||||
sizePatches[sizeChoice.selection],
|
||||
Atlas.State.Seed ? Atlas.State.Seed : 0,
|
||||
terrainArray,
|
||||
RMSData.BaseHeight,
|
||||
pDataStr
|
||||
);
|
||||
|
||||
// Check for error
|
||||
if (ret.status < 0)
|
||||
{
|
||||
wxMessageBox("Random map script '"+RMSData.Script+"' failed. Loading blank map.");
|
||||
|
||||
Atlas.Message.LoadMap("_default");
|
||||
}
|
||||
Atlas.State.mapSettings.notifyObservers();
|
||||
}
|
||||
}
|
||||
};
|
||||
boxSizer.add(generateBtn, 0);
|
||||
boxSizer.add(5, 0);
|
||||
boxSizer.add(new wxStaticText(rmsPanel, -1, 'Random seed'), 0, wxAlignment.CENTER_VERTICAL);
|
||||
boxSizer.add(5, 0);
|
||||
var useRandomCtrl = new wxCheckBox(rmsPanel, -1, '');
|
||||
useRandomCtrl.toolTip = 'Always generate a new random seed';
|
||||
useRandomCtrl.value = true;
|
||||
boxSizer.add(useRandomCtrl, 0, wxAlignment.CENTER_VERTICAL);
|
||||
rmsSizer.add(boxSizer, 0, wxStretch.EXPAND | wxDirection.ALL, 2);
|
||||
|
||||
boxSizer = new wxBoxSizer(wxOrientation.HORIZONTAL);
|
||||
boxSizer.add(new wxStaticText(rmsPanel, -1, 'Seed'), 0, wxAlignment.CENTER_VERTICAL);
|
||||
boxSizer.add(10, 0);
|
||||
var seedCtrl = new wxTextCtrl(rmsPanel, -1, '', wxDefaultPosition, new wxSize(60,-1));
|
||||
seedCtrl.toolTip = 'Enter a numeric seed value here';
|
||||
seedCtrl.onText = function()
|
||||
{
|
||||
var num = parseInt(this.value, 10);
|
||||
if (!isNaN(num))
|
||||
{
|
||||
Atlas.State.Seed = num;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Can allow text by hashing it into a seed value - for fun?
|
||||
}
|
||||
};
|
||||
boxSizer.add(seedCtrl, 1);
|
||||
function generateRandomSeed()
|
||||
{
|
||||
seedCtrl.value = Math.floor(Math.random() * 2147483648)+ 1;
|
||||
}
|
||||
rmsSizer.add(boxSizer, 0, wxStretch.EXPAND | wxDirection.ALL, 2);
|
||||
|
||||
// TODO: Possibly have controls for base height + terrain to override RMS
|
||||
|
||||
rmsPanel.sizer = rmsSizer;
|
||||
window.sizer.add(rmsPanel, 0, wxStretch.EXPAND | wxDirection.LEFT|wxDirection.RIGHT, 2);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Simulation controls
|
||||
setupSimTest(window, rmsPanel);
|
||||
|
||||
|
||||
// Initial seed
|
||||
seedCtrl.value = 0;
|
||||
|
||||
g_observer = function()
|
||||
{
|
||||
if (!g_externalNotify)
|
||||
{
|
||||
// If we don't have civ data or player defaults yet, get those
|
||||
getStartingData();
|
||||
|
||||
// Load map settings from engine
|
||||
getMapSettings();
|
||||
|
||||
// Load RMS names from engine
|
||||
loadScriptChoices();
|
||||
|
||||
// Update UI controls
|
||||
updateMapName();
|
||||
updateMapDesc();
|
||||
updateRevealMap();
|
||||
updateGameType();
|
||||
updateNumPlayers();
|
||||
updateKeywords();
|
||||
}
|
||||
}
|
||||
|
||||
// Set up observers (so that this script can be notified when map data is loaded)
|
||||
makeObservable(Atlas.State.mapSettings);
|
||||
|
||||
Atlas.State.mapSettings.registerObserver(g_observer);
|
||||
}
|
||||
|
||||
function deinit()
|
||||
{
|
||||
Atlas.State.mapSettings.unregisterObserver(g_observer);
|
||||
}
|
||||
|
@ -55,111 +55,6 @@ var brush = {
|
||||
}
|
||||
};
|
||||
|
||||
var lastTerrainSelection = null; // button that was last selected, so we can undo its colouring
|
||||
function onTerrainSelect()
|
||||
{
|
||||
Atlas.SetSelectedTexture(this.terrainName);
|
||||
if (lastTerrainSelection)
|
||||
lastTerrainSelection.backgroundColour = wxNullColour;
|
||||
this.backgroundColour = new wxColour(255, 255, 0);
|
||||
lastTerrainSelection = this;
|
||||
}
|
||||
|
||||
function TerrainPreviewPage(panel, name)
|
||||
{
|
||||
this.panel = panel;
|
||||
this.name = name;
|
||||
|
||||
// Size of texture preview images
|
||||
this.w = 120
|
||||
this.h = 40;
|
||||
|
||||
this.previewReloadTimer = null;
|
||||
}
|
||||
|
||||
TerrainPreviewPage.prototype = {
|
||||
reloadPreviews: function() {
|
||||
this.panel.freeze();
|
||||
this.scrolled.destroyChildren();
|
||||
this.itemSizer.clear();
|
||||
|
||||
lastTerrainSelection = null; // clear any reference to deleted window
|
||||
|
||||
// TODO: Do something clever like load the preview images asynchronously,
|
||||
// to avoid the annoying freeze when switching tabs
|
||||
var previews = Atlas.Message.GetTerrainGroupPreviews(this.name, this.w, this.h).previews;
|
||||
var i = 0;
|
||||
var names = [];
|
||||
var allLoaded = true;
|
||||
for each (var p in previews)
|
||||
{
|
||||
if (!p.loaded)
|
||||
allLoaded = false;
|
||||
|
||||
// Create a wrapped-text label (replacing '_' with ' ' so there are more wrapping opportunities)
|
||||
var labelText = p.name.replace(/_/g, ' ');
|
||||
var label = new wxStaticText(this.scrolled, -1, labelText, wxDefaultPosition, wxDefaultSize, wxStaticText.ALIGN_CENTER);
|
||||
label.wrap(this.w);
|
||||
|
||||
var imgSizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
var button = new wxBitmapButton(this.scrolled, -1, p.imagedata);
|
||||
button.terrainName = p.name;
|
||||
button.onClicked = onTerrainSelect;
|
||||
imgSizer.add(button, 0, wxAlignment.CENTRE);
|
||||
imgSizer.add(label, 1, wxAlignment.CENTRE);
|
||||
this.itemSizer.add(imgSizer, 0, wxAlignment.CENTRE | wxStretch.EXPAND);
|
||||
}
|
||||
this.itemSizer.layout();
|
||||
this.panel.layout();
|
||||
this.panel.thaw();
|
||||
|
||||
// If not all textures were loaded yet, run a timer to reload the previews
|
||||
// every so often until they've all finished
|
||||
if (allLoaded && this.previewReloadTimer)
|
||||
{
|
||||
this.previewReloadTimer.stop();
|
||||
this.previewReloadTimer = null;
|
||||
}
|
||||
else if (!allLoaded && !this.previewReloadTimer)
|
||||
{
|
||||
this.previewReloadTimer = new wxTimer();
|
||||
var self = this;
|
||||
this.previewReloadTimer.onNotify = function() { self.reloadPreviews(); };
|
||||
this.previewReloadTimer.start(2000);
|
||||
}
|
||||
},
|
||||
|
||||
display: function() {
|
||||
if (this.loaded)
|
||||
return;
|
||||
|
||||
this.panel.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
var scrolled = new wxScrolledWindow(this.panel, -1, wxDefaultPosition, wxDefaultSize, wxWindow.VSCROLL);
|
||||
scrolled.setScrollRate(0, 10);
|
||||
scrolled.backgroundColour = new wxColour(255, 255, 255);
|
||||
this.panel.sizer.add(scrolled, 1, wxStretch.EXPAND);
|
||||
|
||||
var itemSizer = new wxGridSizer(6, 4, 0);
|
||||
scrolled.sizer = itemSizer;
|
||||
|
||||
// Adjust the number of columns to fit in the available area
|
||||
var w = this.w;
|
||||
scrolled.onSize = function (evt) {
|
||||
var numCols = Math.max(1, Math.floor(evt.size.width / (w+16)));
|
||||
if (itemSizer.cols != numCols)
|
||||
itemSizer.cols = numCols;
|
||||
};
|
||||
|
||||
this.scrolled = scrolled;
|
||||
this.itemSizer = itemSizer;
|
||||
this.reloadPreviews();
|
||||
|
||||
// TODO: fix keyboard navigation of the terrain previews
|
||||
|
||||
this.loaded = true;
|
||||
}
|
||||
};
|
||||
|
||||
function init(window, bottomWindow)
|
||||
{
|
||||
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
@ -256,7 +151,7 @@ function init(window, bottomWindow)
|
||||
visualiseSizer.add(visualiseSettingsSizer);
|
||||
|
||||
visualiseSettingsSizer.add(new wxStaticText(window, -1, 'Passability'), 0, wxAlignment.RIGHT);
|
||||
var passabilityClasses = Atlas.Message.GetTerrainPassabilityClasses().classnames;
|
||||
var passabilityClasses = Atlas.Message.GetTerrainPassabilityClasses().classNames;
|
||||
var passabilitySelector = new wxChoice(window, -1, wxDefaultPosition, wxDefaultSize,
|
||||
["(none)"].concat(passabilityClasses)
|
||||
);
|
||||
@ -287,11 +182,11 @@ function init(window, bottomWindow)
|
||||
pages[evt.selection].display()
|
||||
evt.skip = true;
|
||||
}
|
||||
for each (var groupname in terrainGroups.groupnames)
|
||||
for each (var groupName in terrainGroups.groupNames)
|
||||
{
|
||||
var panel = new wxPanel(nb, -1);
|
||||
var page = new TerrainPreviewPage(panel, groupname);
|
||||
var page = new global.TerrainPreviewPage(panel, groupName);
|
||||
pages.push(page);
|
||||
nb.addPage(panel, groupname); // TODO: use Titlecase letters
|
||||
nb.addPage(panel, groupName); // TODO: use Titlecase letters
|
||||
}
|
||||
}
|
||||
|
21
binaries/data/tools/atlas/scripts/terrainpreview.js
Normal file
21
binaries/data/tools/atlas/scripts/terrainpreview.js
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
function init(window)
|
||||
{
|
||||
var terrainGroups = Atlas.Message.GetTerrainGroups();
|
||||
var nb = new wxNotebook(window, -1);
|
||||
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
window.sizer.add(nb, 1, wxStretch.EXPAND);
|
||||
|
||||
var pages = [];
|
||||
nb.onPageChanged = function (evt) {
|
||||
pages[evt.selection].display()
|
||||
evt.skip = true;
|
||||
}
|
||||
for each (var groupName in terrainGroups.groupNames)
|
||||
{
|
||||
var panel = new wxPanel(nb, -1);
|
||||
var page = new global.TerrainPreviewPage(panel, groupName);
|
||||
pages.push(page);
|
||||
nb.addPage(panel, groupName); // TODO: use Titlecase letters
|
||||
}
|
||||
}
|
@ -811,6 +811,7 @@ function setup_atlas_packages()
|
||||
"CustomControls/EditableListCtrl",
|
||||
"CustomControls/FileHistory",
|
||||
"CustomControls/HighResTimer",
|
||||
"CustomControls/NewDialog",
|
||||
"CustomControls/SnapSplitterWindow",
|
||||
"CustomControls/VirtualDirTreeCtrl",
|
||||
"CustomControls/Windows",
|
||||
@ -822,9 +823,6 @@ function setup_atlas_packages()
|
||||
"ScenarioEditor/Sections/Common",
|
||||
"ScenarioEditor/Sections/Cinematic",
|
||||
"ScenarioEditor/Sections/Environment",
|
||||
"ScenarioEditor/Sections/Map",
|
||||
"ScenarioEditor/Sections/Object",
|
||||
"ScenarioEditor/Sections/Terrain",
|
||||
"ScenarioEditor/Sections/Trigger",
|
||||
"ScenarioEditor/Tools",
|
||||
"ScenarioEditor/Tools/Common",
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include "renderer/Renderer.h"
|
||||
#include "renderer/WaterManager.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpPosition.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
|
@ -517,3 +517,104 @@ std::string CSimulation2::GenerateSchema()
|
||||
{
|
||||
return m->m_ComponentManager.GenerateSchema();
|
||||
}
|
||||
|
||||
std::vector<std::string> CSimulation2::GetRMSData()
|
||||
{
|
||||
VfsPath path(L"maps/random/");
|
||||
VfsPaths pathnames;
|
||||
|
||||
std::vector<std::string> data;
|
||||
|
||||
// Find all ../maps/random/*.json
|
||||
LibError ret = fs_util::GetPathnames(g_VFS, path, L"*.json", pathnames);
|
||||
if (ret == INFO::OK)
|
||||
{
|
||||
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
|
||||
{
|
||||
// Load JSON file
|
||||
CVFSFile file;
|
||||
PSRETURN ret = file.Load(g_VFS, *it);
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.push_back(std::string(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some error reading directory
|
||||
wchar_t error[200];
|
||||
LOGERROR(L"Error reading directory '%ls': %hs", path.string().c_str(), error_description_r(ret, error, ARRAY_SIZE(error)));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<std::string> CSimulation2::GetCivData()
|
||||
{
|
||||
VfsPath path(L"civs/");
|
||||
VfsPaths pathnames;
|
||||
|
||||
std::vector<std::string> data;
|
||||
|
||||
// Load all JSON files in civs directory
|
||||
LibError ret = fs_util::GetPathnames(g_VFS, path, L"*.json", pathnames);
|
||||
if (ret == INFO::OK)
|
||||
{
|
||||
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
|
||||
{
|
||||
// Load JSON file
|
||||
CVFSFile file;
|
||||
PSRETURN ret = file.Load(g_VFS, *it);
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.push_back(std::string(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some error reading directory
|
||||
wchar_t error[200];
|
||||
LOGERROR(L"Error reading directory '%ls': %hs", path.string().c_str(), error_description_r(ret, error, ARRAY_SIZE(error)));
|
||||
}
|
||||
|
||||
// Convert from vector to array and stringify
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string CSimulation2::GetPlayerDefaults()
|
||||
{
|
||||
VfsPath path = VfsPath(L"simulation/data/player_defaults.json");
|
||||
|
||||
std::string data;
|
||||
|
||||
if (!VfsFileExists(g_VFS, path))
|
||||
{
|
||||
LOGERROR(L"File '%ls' does not exist", path.string().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load JSON file
|
||||
CVFSFile file;
|
||||
PSRETURN ret = file.Load(g_VFS, path);
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
data = std::string(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize());
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -202,6 +202,30 @@ public:
|
||||
|
||||
std::string GenerateSchema();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Some functions for Atlas UI to be able to access VFS data
|
||||
|
||||
/**
|
||||
* Get random map script data
|
||||
*
|
||||
* @return vector of strings containing JSON format data
|
||||
*/
|
||||
std::vector<std::string> GetRMSData();
|
||||
|
||||
/**
|
||||
* Get civilization data
|
||||
*
|
||||
* @return vector of strings containing JSON format data
|
||||
*/
|
||||
std::vector<std::string> GetCivData();
|
||||
|
||||
/**
|
||||
* Get player default data
|
||||
*
|
||||
* @return string containing JSON format data
|
||||
*/
|
||||
std::string GetPlayerDefaults();
|
||||
|
||||
private:
|
||||
CSimulation2Impl* m;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -259,9 +259,13 @@ namespace
|
||||
|
||||
template<> struct ToJSVal<size_t>
|
||||
{
|
||||
static jsval Convert(JSContext* WXUNUSED(cx), const size_t& val)
|
||||
static jsval Convert(JSContext* cx, const size_t& val)
|
||||
{
|
||||
return INT_TO_JSVAL(val);
|
||||
if (val <= JSVAL_INT_MAX)
|
||||
return INT_TO_JSVAL(val);
|
||||
jsval rval = JSVAL_VOID;
|
||||
JS_NewNumberValue(cx, val, &rval); // ignore return value
|
||||
return rval;
|
||||
}
|
||||
};
|
||||
|
||||
@ -295,6 +299,17 @@ namespace
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct ToJSVal<std::string>
|
||||
{
|
||||
static jsval Convert(JSContext* cx, const std::string& val)
|
||||
{
|
||||
JSString* str = JS_NewStringCopyN(cx, val.c_str(), val.length());
|
||||
if (str)
|
||||
return STRING_TO_JSVAL(str);
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// wxJS types:
|
||||
|
||||
@ -388,9 +403,9 @@ namespace
|
||||
|
||||
JS_DefineProperty(cx, obj, "loaded", ToJSVal<bool>::Convert(cx, val.loaded), NULL, NULL, JSPROP_ENUMERATE);
|
||||
|
||||
unsigned char* buf = (unsigned char*)(malloc(val.imagedata.GetSize()));
|
||||
memcpy(buf, val.imagedata.GetBuffer(), val.imagedata.GetSize());
|
||||
jsval bmp = wxjs::gui::Bitmap::CreateObject(cx, new wxBitmap (wxImage(val.imagewidth, val.imageheight, buf)));
|
||||
unsigned char* buf = (unsigned char*)(malloc(val.imageData.GetSize()));
|
||||
memcpy(buf, val.imageData.GetBuffer(), val.imageData.GetSize());
|
||||
jsval bmp = wxjs::gui::Bitmap::CreateObject(cx, new wxBitmap (wxImage(val.imageWidth, val.imageHeight, buf)));
|
||||
JS_DefineProperty(cx, obj, "imagedata", bmp, NULL, NULL, JSPROP_ENUMERATE);
|
||||
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
@ -418,7 +433,7 @@ namespace
|
||||
if (! obj) return JSVAL_VOID;
|
||||
JS_DefineProperty(cx, obj, "player", ToJSVal<size_t>::Convert(cx, val.player), NULL, NULL, JSPROP_ENUMERATE);
|
||||
JS_DefineProperty(cx, obj, "selections", ToJSVal<std::vector<std::wstring> >::Convert(cx, *val.selections), NULL, NULL, JSPROP_ENUMERATE);
|
||||
JS_DefineProperty(cx, obj, "variantgroups", ToJSVal<std::vector<std::vector<std::wstring> > >::Convert(cx, *val.variantgroups), NULL, NULL, JSPROP_ENUMERATE);
|
||||
JS_DefineProperty(cx, obj, "variantgroups", ToJSVal<std::vector<std::vector<std::wstring> > >::Convert(cx, *val.variantGroups), NULL, NULL, JSPROP_ENUMERATE);
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -33,6 +33,7 @@
|
||||
#include "CustomControls/HighResTimer/HighResTimer.h"
|
||||
#include "CustomControls/Buttons/ToolButton.h"
|
||||
#include "CustomControls/Canvas/Canvas.h"
|
||||
#include "CustomControls/NewDialog/NewDialog.h"
|
||||
|
||||
#include "GameInterface/MessagePasser.h"
|
||||
#include "GameInterface/Messages.h"
|
||||
@ -301,7 +302,7 @@ enum
|
||||
{
|
||||
ID_Quit = 1,
|
||||
|
||||
// ID_New,
|
||||
ID_New,
|
||||
ID_Open,
|
||||
ID_Save,
|
||||
ID_SaveAs,
|
||||
@ -313,6 +314,8 @@ enum
|
||||
ID_CameraReset,
|
||||
ID_RenderPathFixed,
|
||||
ID_RenderPathShader,
|
||||
ID_DumpState,
|
||||
ID_DumpBinaryState,
|
||||
|
||||
ID_Toolbar // must be last in the list
|
||||
};
|
||||
@ -321,7 +324,7 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
|
||||
EVT_CLOSE(ScenarioEditor::OnClose)
|
||||
EVT_TIMER(wxID_ANY, ScenarioEditor::OnTimer)
|
||||
|
||||
// EVT_MENU(ID_New, ScenarioEditor::OnNew)
|
||||
EVT_MENU(ID_New, ScenarioEditor::OnNew)
|
||||
EVT_MENU(ID_Open, ScenarioEditor::OnOpen)
|
||||
EVT_MENU(ID_Save, ScenarioEditor::OnSave)
|
||||
EVT_MENU(ID_SaveAs, ScenarioEditor::OnSaveAs)
|
||||
@ -336,6 +339,8 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
|
||||
EVT_MENU(ID_Screenshot, ScenarioEditor::OnScreenshot)
|
||||
EVT_MENU(ID_JavaScript, ScenarioEditor::OnJavaScript)
|
||||
EVT_MENU(ID_CameraReset, ScenarioEditor::OnCameraReset)
|
||||
EVT_MENU(ID_DumpState, ScenarioEditor::OnDumpState)
|
||||
EVT_MENU(ID_DumpBinaryState, ScenarioEditor::OnDumpState)
|
||||
EVT_MENU(ID_RenderPathFixed, ScenarioEditor::OnRenderPath)
|
||||
EVT_MENU(ID_RenderPathShader, ScenarioEditor::OnRenderPath)
|
||||
|
||||
@ -425,10 +430,36 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
|
||||
wxFFile file (filename.GetFullPath());
|
||||
wxString script;
|
||||
if (! file.ReadAll(&script))
|
||||
{
|
||||
wxLogError(_("Failed to read script"));
|
||||
}
|
||||
GetScriptInterface().LoadScript(filename.GetFullName(), script);
|
||||
}
|
||||
|
||||
// Load common scripts
|
||||
const wxString commonPath(_T("tools/atlas/scripts/common/"));
|
||||
wxFileName filename(commonPath, wxPATH_UNIX);
|
||||
filename.MakeAbsolute(Datafile::GetDataDirectory());
|
||||
wxArrayString filenames;
|
||||
if (wxDir::GetAllFiles(filename.GetFullPath(), &filenames, _T("*.js")) > 0)
|
||||
{
|
||||
for (size_t i = 0; i < filenames.GetCount(); ++i)
|
||||
{
|
||||
wxFFile file (filenames[i]);
|
||||
wxString script;
|
||||
if (! file.ReadAll(&script))
|
||||
{
|
||||
wxLogError(_("Failed to read script"));
|
||||
}
|
||||
GetScriptInterface().LoadScript(filename.GetFullName(), script);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError(_("Failed to read common scripts directory"));
|
||||
}
|
||||
|
||||
|
||||
// Initialise things that rely on scripts
|
||||
m_ObjectSettings.Init(AtlasMessage::eRenderView::GAME);
|
||||
|
||||
@ -452,7 +483,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
|
||||
wxMenu *menuFile = new wxMenu;
|
||||
menuBar->Append(menuFile, _("&File"));
|
||||
{
|
||||
// menuFile->Append(ID_New, _("&New"));
|
||||
menuFile->Append(ID_New, _("&New..."));
|
||||
menuFile->Append(ID_Open, _("&Open..."));
|
||||
menuFile->Append(ID_Save, _("&Save"));
|
||||
menuFile->Append(ID_SaveAs, _("Save &As..."));
|
||||
@ -485,6 +516,11 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
|
||||
menuMisc->Append(ID_JavaScript, _("&JS console"));
|
||||
menuMisc->Append(ID_CameraReset, _("&Reset camera"));
|
||||
|
||||
wxMenu *menuSS = new wxMenu;
|
||||
menuMisc->AppendSubMenu(menuSS, _("Si&mulation state"));
|
||||
menuSS->Append(ID_DumpState, _("&Dump to disk"));
|
||||
menuSS->Append(ID_DumpBinaryState, _("Dump &binary to disk"));
|
||||
|
||||
wxMenu *menuRP = new wxMenu;
|
||||
menuMisc->AppendSubMenu(menuRP, _("Render &path"));
|
||||
menuRP->Append(ID_RenderPathFixed, _("&Fixed function"));
|
||||
@ -554,7 +590,13 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
|
||||
|
||||
// Start with a blank map (so that the editor can assume there's always
|
||||
// a valid map loaded)
|
||||
POST_MESSAGE(GenerateMap, (9));
|
||||
POST_MESSAGE(LoadMap, (_T("_default")));
|
||||
|
||||
// Wait for blank map
|
||||
qry.Post();
|
||||
|
||||
// Notify UI scripts that map settings have been loaded
|
||||
m_ScriptInterface.Eval(_T("Atlas.State.mapSettings.notifyObservers()"));
|
||||
|
||||
POST_MESSAGE(RenderEnable, (eRenderView::GAME));
|
||||
|
||||
@ -640,6 +682,51 @@ void ScenarioEditor::OnRedo(wxCommandEvent&)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ScenarioEditor::OnNew(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
NewDialog dlg(NULL, _("Create new map"), wxSize(600, 400), *this);
|
||||
|
||||
if(dlg.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxBusyInfo busy(_("Creating blank map"));
|
||||
|
||||
// Generate new blank map
|
||||
size_t patches = dlg.GetSelectedSize();
|
||||
size_t height = dlg.GetBaseHeight();
|
||||
|
||||
// Get terrain texture
|
||||
// TODO: Support choosing multiple textures
|
||||
std::vector<std::wstring> textures;
|
||||
std::wstring baseTexture(g_SelectedTexture.wc_str());
|
||||
textures.push_back(baseTexture);
|
||||
|
||||
// Get player data
|
||||
std::string pData;
|
||||
m_ScriptInterface.Eval(_T("JSON.stringify(Atlas.State.mapSettings.settings.PlayerData)"), pData);
|
||||
|
||||
// Generate map
|
||||
// Script name, size (patches), seed, base terrain(s), base height, player data
|
||||
qGenerateMap qry(L"blank.js", patches, 0, textures, height, pData);
|
||||
|
||||
// Wait for map generation to finish
|
||||
qry.Post();
|
||||
|
||||
if (qry.status < 0)
|
||||
{ // Map generation failed
|
||||
wxMessageDialog msgDlg(NULL, _T("Random map script 'blank.js'. Loading blank map."), _T("Error"), wxICON_ERROR);
|
||||
|
||||
qPing pingQry;
|
||||
POST_MESSAGE(LoadMap, (_T("_default")));
|
||||
|
||||
// Wait for blank map
|
||||
pingQry.Post();
|
||||
}
|
||||
|
||||
// Notify UI scripts that map settings have been loaded
|
||||
m_ScriptInterface.Eval(_T("Atlas.State.mapSettings.notifyObservers()"));
|
||||
}
|
||||
}
|
||||
|
||||
void ScenarioEditor::OpenFile(const wxString& name)
|
||||
{
|
||||
wxBusyInfo busy(_("Loading map"));
|
||||
@ -661,6 +748,9 @@ void ScenarioEditor::OpenFile(const wxString& name)
|
||||
qPing qry;
|
||||
qry.Post();
|
||||
|
||||
// Notify UI scripts that map settings have been loaded
|
||||
m_ScriptInterface.Eval(_T("Atlas.State.mapSettings.notifyObservers()"));
|
||||
|
||||
// TODO: Make this a non-undoable command
|
||||
}
|
||||
|
||||
@ -687,15 +777,25 @@ void ScenarioEditor::OnOpen(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void ScenarioEditor::OnMRUFile(wxCommandEvent& event)
|
||||
{
|
||||
wxString file (m_FileHistory.GetHistoryFile(event.GetId() - wxID_FILE1));
|
||||
if (file.Len())
|
||||
OpenFile(file);
|
||||
wxString filename(m_FileHistory.GetHistoryFile(event.GetId() - wxID_FILE1));
|
||||
if (filename.Len())
|
||||
{
|
||||
OpenFile(filename);
|
||||
}
|
||||
else
|
||||
{ //Remove from MRU
|
||||
m_FileHistory.RemoveFileFromHistory(event.GetId() - wxID_FILE1);
|
||||
wxMessageDialog msgDlg(NULL, _T("File '/mods/*/maps/scenarios/")+filename + _T("' does not exist!"), _T("Error"), wxICON_ERROR);
|
||||
msgDlg.ShowModal();
|
||||
}
|
||||
}
|
||||
|
||||
void ScenarioEditor::OnSave(wxCommandEvent& event)
|
||||
{
|
||||
if (m_OpenFilename.IsEmpty())
|
||||
{
|
||||
OnSaveAs(event);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxBusyInfo busy(_("Saving map"));
|
||||
@ -705,11 +805,22 @@ void ScenarioEditor::OnSave(wxCommandEvent& event)
|
||||
// the preview units.)
|
||||
m_ToolManager.SetCurrentTool(_T(""));
|
||||
|
||||
qPing qry;
|
||||
|
||||
// Save map settings
|
||||
std::string settings;
|
||||
if (m_ScriptInterface.Eval(_T("JSON.stringify(Atlas.State.mapSettings.settings)"), settings))
|
||||
{
|
||||
POST_MESSAGE(SetMapSettings, (settings));
|
||||
|
||||
// Wait for it to finish saving
|
||||
qry.Post();
|
||||
}
|
||||
|
||||
std::wstring map = m_OpenFilename.c_str();
|
||||
POST_MESSAGE(SaveMap, (map));
|
||||
|
||||
// Wait for it to finish saving
|
||||
qPing qry;
|
||||
qry.Post();
|
||||
}
|
||||
}
|
||||
@ -727,6 +838,18 @@ void ScenarioEditor::OnSaveAs(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
m_ToolManager.SetCurrentTool(_T(""));
|
||||
|
||||
qPing qry;
|
||||
|
||||
// Save map settings
|
||||
std::string settings;
|
||||
if (m_ScriptInterface.Eval(_T("JSON.stringify(Atlas.State.mapSettings.settings)"), settings))
|
||||
{
|
||||
POST_MESSAGE(SetMapSettings, (settings));
|
||||
|
||||
// Wait for it to finish saving
|
||||
qry.Post();
|
||||
}
|
||||
|
||||
// TODO: Work when the map is not in .../maps/scenarios/
|
||||
std::wstring map = dlg.GetFilename().c_str();
|
||||
POST_MESSAGE(SaveMap, (map));
|
||||
@ -734,7 +857,6 @@ void ScenarioEditor::OnSaveAs(wxCommandEvent& WXUNUSED(event))
|
||||
SetOpenFilename(dlg.GetFilename());
|
||||
|
||||
// Wait for it to finish saving
|
||||
qPing qry;
|
||||
qry.Post();
|
||||
}
|
||||
}
|
||||
@ -793,6 +915,42 @@ void ScenarioEditor::OnRenderPath(wxCommandEvent& event)
|
||||
}
|
||||
}
|
||||
|
||||
void ScenarioEditor::OnDumpState(wxCommandEvent& event)
|
||||
{
|
||||
wxDateTime time = wxDateTime::Now();
|
||||
wxString filename;
|
||||
bool doBinary = false;
|
||||
|
||||
switch (event.GetId())
|
||||
{
|
||||
case ID_DumpState:
|
||||
filename = wxString::Format(_T("sim-dump-%d.txt"), time.GetTicks());
|
||||
break;
|
||||
case ID_DumpBinaryState:
|
||||
doBinary = true;
|
||||
filename = wxString::Format(_T("sim-dump-%d.dat"), time.GetTicks());
|
||||
break;
|
||||
}
|
||||
|
||||
qSimStateDebugDump q(doBinary);
|
||||
q.Post();
|
||||
|
||||
wxString state(std::wstring(*q.dump));
|
||||
|
||||
wxFFile file(filename.c_str(), _T("w"));
|
||||
if (file.IsOpened() && !file.Error())
|
||||
{
|
||||
file.Write(state);
|
||||
file.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageDialog msgDlg(NULL, _("Error writing to file: ") + filename, _("Error"), wxICON_ERROR);
|
||||
msgDlg.ShowModal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Position::Position(const wxPoint& pt)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -35,7 +35,7 @@ public:
|
||||
void OnTimer(wxTimerEvent& event);
|
||||
void OnIdle(wxIdleEvent& event);
|
||||
|
||||
// void OnNew(wxCommandEvent& event);
|
||||
void OnNew(wxCommandEvent& event);
|
||||
void OnOpen(wxCommandEvent& event);
|
||||
void OnSave(wxCommandEvent& event);
|
||||
void OnSaveAs(wxCommandEvent& event);
|
||||
@ -52,6 +52,7 @@ public:
|
||||
void OnJavaScript(wxCommandEvent& event);
|
||||
void OnCameraReset(wxCommandEvent& event);
|
||||
void OnRenderPath(wxCommandEvent& event);
|
||||
void OnDumpState(wxCommandEvent& event);
|
||||
|
||||
void OpenFile(const wxString& name);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -27,7 +27,6 @@
|
||||
#include "ScenarioEditor.h"
|
||||
#include "AtlasScript/ScriptInterface.h"
|
||||
|
||||
#include "Sections/Terrain/Terrain.h"
|
||||
#include "Sections/Environment/Environment.h"
|
||||
#include "Sections/Cinematic/Cinematic.h"
|
||||
#include "Sections/Trigger/Trigger.h"
|
||||
@ -315,7 +314,6 @@ void SectionLayout::Build(ScenarioEditor& scenarioEditor)
|
||||
|
||||
ADD_SIDEBAR_SCRIPT(_T("map"), _T("map.png"), _("Map"));
|
||||
ADD_SIDEBAR_SCRIPT(_T("terrain"), _T("terrain.png"), _("Terrain"));
|
||||
//ADD_SIDEBAR(TerrainSidebar, _T("terrain.png"), _("Terrain"));
|
||||
ADD_SIDEBAR_SCRIPT(_T("object"), _T("object.png"), _("Object"));
|
||||
ADD_SIDEBAR(EnvironmentSidebar, _T("environment.png"), _("Environment"));
|
||||
|
||||
|
@ -1,239 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Terrain.h"
|
||||
|
||||
#include "Buttons/ToolButton.h"
|
||||
#include "General/Datafile.h"
|
||||
#include "ScenarioEditor/ScenarioEditor.h"
|
||||
#include "ScenarioEditor/Tools/Common/Brushes.h"
|
||||
#include "ScenarioEditor/Tools/Common/MiscState.h"
|
||||
|
||||
#include "GameInterface/Messages.h"
|
||||
|
||||
#include "wx/spinctrl.h"
|
||||
#include "wx/listctrl.h"
|
||||
#include "wx/image.h"
|
||||
#include "wx/imaglist.h"
|
||||
#include "wx/busyinfo.h"
|
||||
#include "wx/notebook.h"
|
||||
|
||||
class TextureNotebook;
|
||||
|
||||
class TerrainBottomBar : public wxPanel
|
||||
{
|
||||
public:
|
||||
TerrainBottomBar(wxWindow* parent);
|
||||
void LoadTerrain();
|
||||
private:
|
||||
TextureNotebook* m_Textures;
|
||||
};
|
||||
|
||||
|
||||
TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
||||
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
|
||||
{
|
||||
// TODO: Less ugliness
|
||||
|
||||
m_MainSizer->Add(new wxStaticText(this, wxID_ANY, _T("TODO: Make this much less ugly\n")));
|
||||
|
||||
{
|
||||
wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools"));
|
||||
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Modify"), _T("AlterElevation"), wxSize(50,20)));
|
||||
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Smooth"), _T("SmoothElevation"), wxSize(50,20)));
|
||||
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Flatten"), _T("FlattenElevation"), wxSize(50,20)));
|
||||
// sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Smooth"), _T(""), wxSize(50,20)));
|
||||
// sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Sample"), _T(""), wxSize(50,20)));
|
||||
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Paint"), _T("PaintTerrain"), wxSize(50,20)));
|
||||
m_MainSizer->Add(sizer);
|
||||
}
|
||||
|
||||
{
|
||||
wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Brush"));
|
||||
g_Brush_Elevation.CreateUI(this, sizer);
|
||||
m_MainSizer->Add(sizer);
|
||||
}
|
||||
|
||||
m_BottomBar = new TerrainBottomBar(bottomBarContainer);
|
||||
}
|
||||
|
||||
void TerrainSidebar::OnFirstDisplay()
|
||||
{
|
||||
static_cast<TerrainBottomBar*>(m_BottomBar)->LoadTerrain();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TextureNotebookPage : public wxPanel
|
||||
{
|
||||
public:
|
||||
TextureNotebookPage(wxWindow* parent, const wxString& name)
|
||||
: wxPanel(parent, wxID_ANY), m_Name(name), m_ListCtrl(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void OnDisplay()
|
||||
{
|
||||
if (m_ListCtrl)
|
||||
{
|
||||
int sel = m_ListCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
if (sel != -1)
|
||||
SetSelection(m_ListCtrl->GetItemData(sel));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyInfo busy (_("Loading terrain previews"));
|
||||
|
||||
m_TextureNames.Clear();
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_ListCtrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_ICON | wxLC_SINGLE_SEL | wxLC_AUTOARRANGE);
|
||||
|
||||
const int imageWidth = 64;
|
||||
const int imageHeight = 32;
|
||||
wxImageList* imglist = new wxImageList(imageWidth, imageHeight, false, 0);
|
||||
|
||||
AtlasMessage::qGetTerrainGroupPreviews qry(m_Name.c_str(), imageWidth, imageHeight);
|
||||
qry.Post();
|
||||
|
||||
std::vector<AtlasMessage::sTerrainGroupPreview> previews = *qry.previews;
|
||||
|
||||
int i = 0;
|
||||
for (std::vector<AtlasMessage::sTerrainGroupPreview>::iterator it = previews.begin(); it != previews.end(); ++it)
|
||||
{
|
||||
unsigned char* buf = (unsigned char*)(malloc(it->imagedata.GetSize()));
|
||||
// it->imagedata.GetBuffer() gives a Shareable<unsigned char>*, which
|
||||
// is stored the same as a unsigned char*, so we can just copy it.
|
||||
memcpy(buf, it->imagedata.GetBuffer(), it->imagedata.GetSize());
|
||||
wxImage img (imageWidth, imageHeight, buf);
|
||||
imglist->Add(wxBitmap(img));
|
||||
|
||||
wxListItem item;
|
||||
|
||||
wxString name = it->name.c_str();
|
||||
m_TextureNames.Add(name);
|
||||
// Add spaces into the displayed name, so Windows doesn't just say
|
||||
// "grass_..." in the list ctrl for every terrain
|
||||
name.Replace(_T("_"), _T(" "));
|
||||
item.SetText(name);
|
||||
|
||||
item.SetData(i);
|
||||
item.SetId(i);
|
||||
item.SetImage(i);
|
||||
|
||||
m_ListCtrl->InsertItem(item);
|
||||
++i;
|
||||
}
|
||||
m_ListCtrl->AssignImageList(imglist, wxIMAGE_LIST_NORMAL);
|
||||
|
||||
sizer->Add(m_ListCtrl, wxSizerFlags().Expand().Proportion(1));
|
||||
SetSizer(sizer);
|
||||
Layout(); // required to make things size correctly
|
||||
}
|
||||
|
||||
void OnSelect(wxListEvent& evt)
|
||||
{
|
||||
SetSelection(evt.GetData());
|
||||
}
|
||||
|
||||
void SetSelection(int n)
|
||||
{
|
||||
if (n >= 0 && n < (int)m_TextureNames.GetCount())
|
||||
g_SelectedTexture = m_TextureNames[n];
|
||||
}
|
||||
|
||||
private:
|
||||
wxListCtrl* m_ListCtrl;
|
||||
wxString m_Name;
|
||||
wxArrayString m_TextureNames;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(TextureNotebookPage, wxPanel)
|
||||
EVT_LIST_ITEM_SELECTED(wxID_ANY, TextureNotebookPage::OnSelect)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
|
||||
class TextureNotebook : public wxNotebook
|
||||
{
|
||||
public:
|
||||
TextureNotebook(wxWindow *parent)
|
||||
: wxNotebook(parent, wxID_ANY/*, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH*/)
|
||||
{
|
||||
}
|
||||
|
||||
void LoadTerrain()
|
||||
{
|
||||
wxBusyInfo busy (_("Loading terrain groups"));
|
||||
|
||||
DeleteAllPages();
|
||||
m_TerrainGroups.Clear();
|
||||
|
||||
// Get the list of terrain groups from the engine
|
||||
AtlasMessage::qGetTerrainGroups qry;
|
||||
qry.Post();
|
||||
std::vector<std::wstring> groupnames = *qry.groupnames;
|
||||
for (std::vector<std::wstring>::iterator it = groupnames.begin(); it != groupnames.end(); ++it)
|
||||
m_TerrainGroups.Add(it->c_str());
|
||||
|
||||
for (size_t i = 0; i < m_TerrainGroups.GetCount(); ++i)
|
||||
{
|
||||
wxString visibleName = m_TerrainGroups[i];
|
||||
if (visibleName.Len())
|
||||
visibleName[0] = wxToupper(visibleName[0]);
|
||||
AddPage(new TextureNotebookPage(this, m_TerrainGroups[i]), visibleName);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnPageChanged(wxNotebookEvent& event)
|
||||
{
|
||||
if (event.GetSelection() >= 0 && event.GetSelection() < (int)GetPageCount())
|
||||
{
|
||||
static_cast<TextureNotebookPage*>(GetPage(event.GetSelection()))->OnDisplay();
|
||||
}
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
private:
|
||||
wxArrayString m_TerrainGroups;
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(TextureNotebook, wxNotebook)
|
||||
EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, TextureNotebook::OnPageChanged)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TerrainBottomBar::TerrainBottomBar(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
{
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_Textures = new TextureNotebook(this);
|
||||
sizer->Add(m_Textures, wxSizerFlags().Expand().Proportion(1));
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
void TerrainBottomBar::LoadTerrain()
|
||||
{
|
||||
m_Textures->LoadTerrain();
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -31,8 +31,6 @@
|
||||
#include "ps/World.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpPlayer.h"
|
||||
#include "simulation2/components/ICmpPlayerManager.h"
|
||||
@ -55,16 +53,8 @@ namespace
|
||||
g_Game->SetPlayerID(1);
|
||||
}
|
||||
|
||||
void StartGame(const CStrW& map)
|
||||
void StartGame(const CScriptValRooted& attrs)
|
||||
{
|
||||
CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any
|
||||
|
||||
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
|
||||
CScriptValRooted attrs;
|
||||
scriptInterface.Eval("({})", attrs);
|
||||
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario"), false);
|
||||
scriptInterface.SetProperty(attrs.get(), "map", std::wstring(mapBase), false);
|
||||
|
||||
g_Game->StartGame(attrs);
|
||||
|
||||
// TODO: Non progressive load can fail - need a decent way to handle this
|
||||
@ -82,22 +72,62 @@ namespace
|
||||
|
||||
namespace AtlasMessage {
|
||||
|
||||
MESSAGEHANDLER(GenerateMap)
|
||||
QUERYHANDLER(GenerateMap)
|
||||
{
|
||||
InitGame();
|
||||
|
||||
// Load the empty default map
|
||||
StartGame(L"_default");
|
||||
// Random map
|
||||
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
|
||||
|
||||
CScriptValRooted attrs;
|
||||
scriptInterface.Eval("({})", attrs);
|
||||
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("random"));
|
||||
scriptInterface.SetProperty(attrs.get(), "script", std::wstring(*msg->script));
|
||||
|
||||
// TODO: use msg->size somehow
|
||||
// (e.g. load the map then resize the terrain to match it)
|
||||
UNUSED2(msg);
|
||||
CScriptValRooted settings;
|
||||
scriptInterface.Eval("({})", settings);
|
||||
scriptInterface.SetProperty(settings.get(), "Size", (size_t)msg->size);
|
||||
scriptInterface.SetProperty(settings.get(), "Seed", (size_t)msg->seed);
|
||||
scriptInterface.SetProperty(settings.get(), "BaseTerrain", std::vector<std::wstring>(*msg->terrain));
|
||||
scriptInterface.SetProperty(settings.get(), "BaseHeight", (size_t)msg->height);
|
||||
scriptInterface.SetProperty(settings.get(), "CircularMap", true); // now default to circular map
|
||||
|
||||
CScriptValRooted pData = scriptInterface.ParseJSON(*msg->playerData);
|
||||
scriptInterface.SetProperty(settings.get(), "PlayerData", pData);
|
||||
|
||||
scriptInterface.SetProperty(attrs.get(), "settings", settings, false);
|
||||
|
||||
try
|
||||
{
|
||||
StartGame(attrs);
|
||||
|
||||
msg->status = 0;
|
||||
}
|
||||
catch (PSERROR_Game_World_MapLoadFailed e)
|
||||
{
|
||||
// Cancel loading
|
||||
LDR_Cancel();
|
||||
|
||||
msg->status = -1;
|
||||
}
|
||||
}
|
||||
|
||||
MESSAGEHANDLER(LoadMap)
|
||||
{
|
||||
InitGame();
|
||||
StartGame(*msg->filename);
|
||||
|
||||
// Scenario
|
||||
CStrW map = *msg->filename;
|
||||
CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any
|
||||
|
||||
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
|
||||
|
||||
CScriptValRooted attrs;
|
||||
scriptInterface.Eval("({})", attrs);
|
||||
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario"));
|
||||
scriptInterface.SetProperty(attrs.get(), "map", std::wstring(mapBase));
|
||||
|
||||
StartGame(attrs);
|
||||
}
|
||||
|
||||
MESSAGEHANDLER(SaveMap)
|
||||
@ -111,4 +141,19 @@ MESSAGEHANDLER(SaveMap)
|
||||
g_Game->GetSimulation2());
|
||||
}
|
||||
|
||||
QUERYHANDLER(GetMapSettings)
|
||||
{
|
||||
msg->settings = g_Game->GetSimulation2()->GetMapSettingsString();
|
||||
}
|
||||
|
||||
MESSAGEHANDLER(SetMapSettings)
|
||||
{
|
||||
g_Game->GetSimulation2()->SetMapSettings(*msg->settings);
|
||||
}
|
||||
|
||||
QUERYHANDLER(GetRMSData)
|
||||
{
|
||||
msg->data = g_Game->GetSimulation2()->GetRMSData();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -15,13 +15,24 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../Common/Sidebar.h"
|
||||
#include "precompiled.h"
|
||||
|
||||
class TerrainSidebar : public Sidebar
|
||||
#include "MessageHandler.h"
|
||||
|
||||
#include "ps/Game.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
|
||||
namespace AtlasMessage {
|
||||
|
||||
QUERYHANDLER(GetCivData)
|
||||
{
|
||||
public:
|
||||
TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
|
||||
msg->data = g_Game->GetSimulation2()->GetCivData();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void OnFirstDisplay();
|
||||
};
|
||||
QUERYHANDLER(GetPlayerDefaults)
|
||||
{
|
||||
msg->defaults = g_Game->GetSimulation2()->GetPlayerDefaults();
|
||||
}
|
||||
|
||||
}
|
@ -42,10 +42,10 @@ namespace AtlasMessage {
|
||||
QUERYHANDLER(GetTerrainGroups)
|
||||
{
|
||||
const CTerrainTextureManager::TerrainGroupMap &groups = g_TexMan.GetGroups();
|
||||
std::vector<std::wstring> groupnames;
|
||||
std::vector<std::wstring> groupNames;
|
||||
for (CTerrainTextureManager::TerrainGroupMap::const_iterator it = groups.begin(); it != groups.end(); ++it)
|
||||
groupnames.push_back(it->first.FromUTF8());
|
||||
msg->groupnames = groupnames;
|
||||
groupNames.push_back(it->first.FromUTF8());
|
||||
msg->groupNames = groupNames;
|
||||
}
|
||||
|
||||
static bool CompareTerrain(const sTerrainGroupPreview& a, const sTerrainGroupPreview& b)
|
||||
@ -57,13 +57,13 @@ QUERYHANDLER(GetTerrainGroupPreviews)
|
||||
{
|
||||
std::vector<sTerrainGroupPreview> previews;
|
||||
|
||||
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupname).ToUTF8());
|
||||
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8());
|
||||
for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
|
||||
{
|
||||
previews.push_back(sTerrainGroupPreview());
|
||||
previews.back().name = (*it)->GetTag().FromUTF8();
|
||||
|
||||
std::vector<unsigned char> buf (msg->imagewidth*msg->imageheight*3);
|
||||
std::vector<unsigned char> buf (msg->imageWidth*msg->imageHeight*3);
|
||||
|
||||
// It's not good to shrink the entire texture to fit the small preview
|
||||
// window, since it's the fine details in the texture that are
|
||||
@ -78,11 +78,11 @@ QUERYHANDLER(GetTerrainGroupPreviews)
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &w);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &h);
|
||||
|
||||
if (w < msg->imagewidth || h < msg->imageheight)
|
||||
if (w < msg->imageWidth || h < msg->imageHeight)
|
||||
{
|
||||
// Oops, too small to preview - just use a flat colour
|
||||
u32 c = (*it)->GetBaseColor();
|
||||
for (ssize_t i = 0; i < msg->imagewidth*msg->imageheight; ++i)
|
||||
for (ssize_t i = 0; i < msg->imageWidth*msg->imageHeight; ++i)
|
||||
{
|
||||
buf[i*3+0] = (c>>16) & 0xff;
|
||||
buf[i*3+1] = (c>>8) & 0xff;
|
||||
@ -97,12 +97,12 @@ QUERYHANDLER(GetTerrainGroupPreviews)
|
||||
|
||||
// Extract the middle section (as a representative preview),
|
||||
// and copy into buf
|
||||
unsigned char* texdata_ptr = texdata + (w*(h - msg->imageheight)/2 + (w - msg->imagewidth)/2) * 3;
|
||||
unsigned char* texdata_ptr = texdata + (w*(h - msg->imageHeight)/2 + (w - msg->imageWidth)/2) * 3;
|
||||
unsigned char* buf_ptr = &buf[0];
|
||||
for (ssize_t y = 0; y < msg->imageheight; ++y)
|
||||
for (ssize_t y = 0; y < msg->imageHeight; ++y)
|
||||
{
|
||||
memcpy(buf_ptr, texdata_ptr, msg->imagewidth*3);
|
||||
buf_ptr += msg->imagewidth*3;
|
||||
memcpy(buf_ptr, texdata_ptr, msg->imageWidth*3);
|
||||
buf_ptr += msg->imageWidth*3;
|
||||
texdata_ptr += w*3;
|
||||
}
|
||||
|
||||
@ -110,9 +110,9 @@ QUERYHANDLER(GetTerrainGroupPreviews)
|
||||
}
|
||||
|
||||
previews.back().loaded = (*it)->GetTexture()->IsLoaded();
|
||||
previews.back().imagewidth = msg->imagewidth;
|
||||
previews.back().imageheight = msg->imageheight;
|
||||
previews.back().imagedata = buf;
|
||||
previews.back().imageWidth = msg->imageWidth;
|
||||
previews.back().imageHeight = msg->imageHeight;
|
||||
previews.back().imageData = buf;
|
||||
}
|
||||
|
||||
// Sort the list alphabetically by name
|
||||
@ -127,10 +127,10 @@ QUERYHANDLER(GetTerrainPassabilityClasses)
|
||||
{
|
||||
std::map<std::string, ICmpPathfinder::pass_class_t> classes = cmpPathfinder->GetPassabilityClasses();
|
||||
|
||||
std::vector<std::wstring> classnames;
|
||||
std::vector<std::wstring> classNames;
|
||||
for (std::map<std::string, ICmpPathfinder::pass_class_t>::iterator it = classes.begin(); it != classes.end(); ++it)
|
||||
classnames.push_back(CStr(it->first).FromUTF8());
|
||||
msg->classnames = classnames;
|
||||
classNames.push_back(CStr(it->first).FromUTF8());
|
||||
msg->classNames = classNames;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -130,9 +130,17 @@ MESSAGE(ResizeScreen,
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Messages for map panel
|
||||
|
||||
MESSAGE(GenerateMap,
|
||||
((int, size)) // size in number of patches
|
||||
QUERY(GenerateMap,
|
||||
((std::wstring, script)) // name of script
|
||||
((size_t, size)) // size in number of patches
|
||||
((size_t, seed)) // seed for rng
|
||||
((std::vector<std::wstring>, terrain)) // base terrain(s)
|
||||
((size_t, height)) // base height
|
||||
((std::string, playerData)) // JSON player data
|
||||
,
|
||||
((int, status)) // Status code, 0 for success, or < 0 for failure
|
||||
);
|
||||
|
||||
MESSAGE(LoadMap,
|
||||
@ -143,6 +151,33 @@ MESSAGE(SaveMap,
|
||||
((std::wstring, filename))
|
||||
);
|
||||
|
||||
QUERY(GetMapSettings,
|
||||
,
|
||||
((std::string, settings))
|
||||
);
|
||||
|
||||
MESSAGE(SetMapSettings,
|
||||
((std::string, settings))
|
||||
);
|
||||
|
||||
QUERY(GetRMSData,
|
||||
,
|
||||
((std::vector<std::string>, data))
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Messages for player panel
|
||||
|
||||
QUERY(GetCivData,
|
||||
,
|
||||
((std::vector<std::string>, data))
|
||||
);
|
||||
|
||||
QUERY(GetPlayerDefaults,
|
||||
,
|
||||
((std::string, defaults))
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MESSAGE(RenderStyle,
|
||||
@ -193,7 +228,7 @@ MESSAGE(BrushPreview,
|
||||
|
||||
QUERY(GetTerrainGroups,
|
||||
, // no inputs
|
||||
((std::vector<std::wstring>, groupnames))
|
||||
((std::vector<std::wstring>, groupNames))
|
||||
);
|
||||
|
||||
#ifndef MESSAGES_SKIP_STRUCTS
|
||||
@ -201,24 +236,24 @@ struct sTerrainGroupPreview
|
||||
{
|
||||
Shareable<std::wstring> name;
|
||||
Shareable<bool> loaded;
|
||||
Shareable<int> imagewidth;
|
||||
Shareable<int> imageheight;
|
||||
Shareable<std::vector<unsigned char> > imagedata; // RGB*width*height
|
||||
Shareable<int> imageWidth;
|
||||
Shareable<int> imageHeight;
|
||||
Shareable<std::vector<unsigned char> > imageData; // RGB*width*height
|
||||
};
|
||||
SHAREABLE_STRUCT(sTerrainGroupPreview);
|
||||
#endif
|
||||
|
||||
QUERY(GetTerrainGroupPreviews,
|
||||
((std::wstring, groupname))
|
||||
((int, imagewidth))
|
||||
((int, imageheight))
|
||||
((std::wstring, groupName))
|
||||
((int, imageWidth))
|
||||
((int, imageHeight))
|
||||
,
|
||||
((std::vector<sTerrainGroupPreview>, previews))
|
||||
);
|
||||
|
||||
QUERY(GetTerrainPassabilityClasses,
|
||||
, // no inputs
|
||||
((std::vector<std::wstring>, classnames))
|
||||
((std::vector<std::wstring>, classNames))
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -247,7 +282,7 @@ struct sObjectSettings
|
||||
// Some settings are immutable and therefore are ignored (and should be left
|
||||
// empty) when passed from the editor to the game:
|
||||
|
||||
Shareable<std::vector<std::vector<std::wstring> > > variantgroups;
|
||||
Shareable<std::vector<std::vector<std::wstring> > > variantGroups;
|
||||
};
|
||||
SHAREABLE_STRUCT(sObjectSettings);
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -35,7 +35,6 @@
|
||||
#include "ps/GameSetup/GameSetup.h"
|
||||
#include "ps/World.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpObstructionManager.h"
|
||||
#include "simulation2/components/ICmpPathfinder.h"
|
||||
|
Loading…
Reference in New Issue
Block a user