# Converted Atlas's object-settings UI into JavaScript.
(Note: This breaks the Actor Viewer, hopefully temporarily.) This was SVN commit r6932.
This commit is contained in:
parent
9a977c9962
commit
34d0f012e8
114
binaries/data/tools/atlas/scripts/editorstate.js
Normal file
114
binaries/data/tools/atlas/scripts/editorstate.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// Define the default state settings.
|
||||||
|
// (It's done this way so that this script can be dynamically reloaded,
|
||||||
|
// and won't overwrite the previous runtime state but will still pick
|
||||||
|
// up any new properties)
|
||||||
|
var defaults = {
|
||||||
|
objectSettings: {
|
||||||
|
view: undefined,
|
||||||
|
selectedObjects: [],
|
||||||
|
playerID: 1,
|
||||||
|
actorSelections: [],
|
||||||
|
variantGroups: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defaults.objectSettings.toSObjectSettings = function () {
|
||||||
|
return {
|
||||||
|
player: this.playerID,
|
||||||
|
selections: this.actorSelections
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults.objectSettings.onSelectionChange = function () {
|
||||||
|
if (! this.selectedObjects.length)
|
||||||
|
return; // TODO: do something sensible here
|
||||||
|
|
||||||
|
// TODO: Support multiple selections
|
||||||
|
var selection = this.selectedObjects[0];
|
||||||
|
|
||||||
|
var settings = Atlas.Message.GetObjectSettings(this.view, selection).settings;
|
||||||
|
|
||||||
|
this.playerID = settings.player;
|
||||||
|
this.actorSelections = settings.selections;
|
||||||
|
this.variantGroups = settings.variantgroups;
|
||||||
|
|
||||||
|
this.notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current actor variation (as a list of chosen variants), based
|
||||||
|
* on its variant groups and the selection strings. This is equivalent to the
|
||||||
|
* variation rendered by the game.
|
||||||
|
*/
|
||||||
|
defaults.objectSettings.getActorVariation = function ()
|
||||||
|
{
|
||||||
|
var selectionMap = {};
|
||||||
|
for each (var s in this.actorSelections)
|
||||||
|
selectionMap[s] = 1;
|
||||||
|
|
||||||
|
var variation = [];
|
||||||
|
GROUP: for each (var group in this.variantGroups) {
|
||||||
|
for each (var variant in group) {
|
||||||
|
if (variant in selectionMap) {
|
||||||
|
variation.push(variant);
|
||||||
|
continue GROUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// None selected; default to first
|
||||||
|
variation.push(group[0]);
|
||||||
|
}
|
||||||
|
return variation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merges the 'defs' tree into 'obj', overwriting any old values with
|
||||||
|
// the same keys
|
||||||
|
function setDefaults(defs, obj)
|
||||||
|
{
|
||||||
|
for (var k in defs) {
|
||||||
|
if (k in obj)
|
||||||
|
setDefaults(defs[k], obj[k]);
|
||||||
|
else
|
||||||
|
obj[k] = defaults[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 postObjectSettingsToGame(objectSettings)
|
||||||
|
{
|
||||||
|
if (objectSettings.selectedObjects.length)
|
||||||
|
Atlas.Message.SetObjectSettings(objectSettings.view,
|
||||||
|
objectSettings.selectedObjects[0], objectSettings.toSObjectSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
function init()
|
||||||
|
{
|
||||||
|
setDefaults(defaults, Atlas.State);
|
||||||
|
|
||||||
|
makeObservable(Atlas.State.objectSettings);
|
||||||
|
Atlas.State.objectSettings.registerObserver(postObjectSettingsToGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deinit() {
|
||||||
|
Atlas.State.objectSettings.unregisterObserver(postObjectSettingsToGame);
|
||||||
|
}
|
||||||
|
|
@ -18,24 +18,56 @@ function loadScript(name /*, ...*/)
|
|||||||
var script = file.readAll(); // TODO: handle errors
|
var script = file.readAll(); // TODO: handle errors
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
var args = [];
|
|
||||||
for (var i = 1; i < arguments.length; ++i)
|
|
||||||
args.push(arguments[i])
|
|
||||||
|
|
||||||
var script = Atlas.LoadScript(name+'.js', script);
|
var script = Atlas.LoadScript(name+'.js', script);
|
||||||
scriptReloader.add(name, args, filename);
|
|
||||||
|
// Extract the arguments which the function will actually use
|
||||||
script.init.apply(null, args);
|
// (Can't use Array.slice since arguments isn't an Array)
|
||||||
|
// (Have to do this rather than use arguments.length, since we sometimes
|
||||||
|
// pass unused bottomWindows into init and then destroy the window when realising
|
||||||
|
// it wasn't used, and then we mustn't send the destroyed window again later)
|
||||||
|
var args = [];
|
||||||
|
if (script.init)
|
||||||
|
for (var i = 1; i < 1+script.init.length; ++i)
|
||||||
|
args.push(arguments[i]);
|
||||||
|
|
||||||
|
scriptReloader.add(name, args, filename, script);
|
||||||
|
|
||||||
|
if (script.init)
|
||||||
|
script.init.apply(null, args);
|
||||||
|
|
||||||
return script;
|
return script;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for C++ to easily set dot-separated names
|
||||||
|
*/
|
||||||
|
function setValue(name, value)
|
||||||
|
{
|
||||||
|
var obj = global;
|
||||||
|
var props = name.split(".");
|
||||||
|
for (var i = 0; i < props.length-1; ++i)
|
||||||
|
obj = obj[props[i]];
|
||||||
|
obj[props[props.length-1]] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for C++ to easily get dot-separated names
|
||||||
|
*/
|
||||||
|
function getValue(name)
|
||||||
|
{
|
||||||
|
var obj = global;
|
||||||
|
var props = name.split(".");
|
||||||
|
for (var i = 0; i < props.length; ++i)
|
||||||
|
obj = obj[props[i]];
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
function loadXML(name)
|
function loadXML(name)
|
||||||
{
|
{
|
||||||
var relativePath = 'tools/atlas/' + name + '.xml';
|
var relativePath = 'tools/atlas/' + name + '.xml';
|
||||||
var filename = new wxFileName(relativePath, wxPathFormat.UNIX);
|
var filename = new wxFileName(relativePath, wxPathFormat.UNIX);
|
||||||
filename.normalize(wxPathNormalize.DOTS | wxPathNormalize.ABSOLUTE | wxPathNormalize.TILDE,
|
filename.normalize(wxPathNormalize.DOTS | wxPathNormalize.ABSOLUTE | wxPathNormalize.TILDE,
|
||||||
Atlas.GetDataDirectory()); // equivalent to MakeAbsolute(dir);
|
Atlas.GetDataDirectory()); // equivalent to MakeAbsolute(dir);
|
||||||
|
|
||||||
var file = new wxFFile(filename.fullPath);
|
var file = new wxFFile(filename.fullPath);
|
||||||
var xml = file.readAll(); // TODO: handle errors
|
var xml = file.readAll(); // TODO: handle errors
|
||||||
@ -46,11 +78,10 @@ function loadXML(name)
|
|||||||
return new XML(xml);
|
return new XML(xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() { /* dummy function to make the script reloader happy */ }
|
// Automatically reload scripts from disk when they have been modified
|
||||||
|
|
||||||
var scriptReloader = {
|
var scriptReloader = {
|
||||||
timer: new wxTimer(),
|
timer: new wxTimer(),
|
||||||
scripts: [], // [ [filename,window], ... ]
|
scripts: [], // [ {name, args, filename, mtime, window, script}, ... ]
|
||||||
notify: function ()
|
notify: function ()
|
||||||
{
|
{
|
||||||
for each (var script in scriptReloader.scripts)
|
for each (var script in scriptReloader.scripts)
|
||||||
@ -59,13 +90,18 @@ var scriptReloader = {
|
|||||||
if (mtime - script.mtime != 0)
|
if (mtime - script.mtime != 0)
|
||||||
{
|
{
|
||||||
print('*** Modifications detected - reloading "' + script.name + '"...\n');
|
print('*** Modifications detected - reloading "' + script.name + '"...\n');
|
||||||
|
|
||||||
script.mtime = mtime;
|
script.mtime = mtime;
|
||||||
|
|
||||||
|
if (script.script && script.script.deinit)
|
||||||
|
script.script.deinit();
|
||||||
|
|
||||||
if (script.name == 'main')
|
if (script.name == 'main')
|
||||||
{
|
{
|
||||||
// Special case for this file to reload itself
|
// Special case for this file to reload itself
|
||||||
var obj = loadScript(script.name, null);
|
script.script = loadScript(script.name, null);
|
||||||
// Copy the important state into the new version of this file
|
// Copy the important state into the new version of this file
|
||||||
obj.scriptReloader.scripts = scriptReloader.scripts;
|
script.script.scriptReloader.scripts = scriptReloader.scripts;
|
||||||
// Stop this one
|
// Stop this one
|
||||||
scriptReloader.timer.stop();
|
scriptReloader.timer.stop();
|
||||||
}
|
}
|
||||||
@ -74,25 +110,30 @@ var scriptReloader = {
|
|||||||
// TODO: know which arguments are really windows that should be regenerated
|
// TODO: know which arguments are really windows that should be regenerated
|
||||||
for each (var window in script.args)
|
for each (var window in script.args)
|
||||||
window.destroyChildren();
|
window.destroyChildren();
|
||||||
loadScript.apply(null, [script.name].concat(script.args));
|
script.script = loadScript.apply(null, [script.name].concat(script.args));
|
||||||
for each (var window in script.args)
|
for each (var window in script.args)
|
||||||
window.layout();
|
window.layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
add: function (name, args, filename)
|
add: function (name, args, filename, script)
|
||||||
{
|
{
|
||||||
for each (var script in this.scripts)
|
for each (var s in this.scripts)
|
||||||
if (script.name == name)
|
if (s.name == name)
|
||||||
return; // stop if this is already loaded
|
return; // stop if this is already loaded
|
||||||
this.scripts.push({ name:name, args:args, filename:filename, mtime:filename.modificationTime });
|
|
||||||
|
this.scripts.push({ name:name, args:args, filename:filename, mtime:filename.modificationTime, script:script });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
scriptReloader.timer.onNotify = scriptReloader.notify;
|
scriptReloader.timer.onNotify = scriptReloader.notify;
|
||||||
scriptReloader.timer.start(1000);
|
scriptReloader.timer.start(1000);
|
||||||
scriptReloader.add('main', null, getScriptFilename('main'));
|
scriptReloader.add('main', null, getScriptFilename('main'));
|
||||||
|
|
||||||
|
loadScript('editorstate');
|
||||||
|
|
||||||
// Export global functions:
|
// Export global functions:
|
||||||
global.loadScript = loadScript;
|
global.loadScript = loadScript;
|
||||||
global.loadXML = loadXML;
|
global.loadXML = loadXML;
|
||||||
|
global.setValue = setValue;
|
||||||
|
global.getValue = getValue;
|
||||||
|
120
binaries/data/tools/atlas/scripts/section/object.js
Normal file
120
binaries/data/tools/atlas/scripts/section/object.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
function setObjectFilter(objectList, objects, type)
|
||||||
|
{
|
||||||
|
objectList.freeze();
|
||||||
|
objectList.clear();
|
||||||
|
var ids = [];
|
||||||
|
for each (var object in objects) {
|
||||||
|
if (object.type == type) {
|
||||||
|
ids.push(object.id);
|
||||||
|
objectList.append(object.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objectList.objectIDs = ids;
|
||||||
|
objectList.thaw();
|
||||||
|
}
|
||||||
|
|
||||||
|
var g_observer;
|
||||||
|
|
||||||
|
function init(window, bottomWindow)
|
||||||
|
{
|
||||||
|
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||||
|
|
||||||
|
var objects = Atlas.Message.GetObjectsList().objects;
|
||||||
|
|
||||||
|
var objectList = new wxListBox(window, -1, wxDefaultPosition, wxDefaultSize, [],
|
||||||
|
wxListBox.SINGLE | wxListBox.HSCROLL);
|
||||||
|
objectList.onListBox = function (evt) {
|
||||||
|
if (evt.selection == -1)
|
||||||
|
return;
|
||||||
|
var id = objectList.objectIDs[evt.selection];
|
||||||
|
Atlas.SetCurrentToolWith('PlaceObject', id);
|
||||||
|
};
|
||||||
|
setObjectFilter(objectList, objects, 0);
|
||||||
|
|
||||||
|
var groupSelector = new wxChoice(window, -1, wxDefaultPosition, wxDefaultSize,
|
||||||
|
["Entities", "Actors (all)"]
|
||||||
|
);
|
||||||
|
groupSelector.onChoice = function (evt) {
|
||||||
|
setObjectFilter(objectList, objects, evt.selection);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.sizer.add(groupSelector, 0, wxStretch.EXPAND);
|
||||||
|
window.sizer.add(objectList, 1, wxStretch.EXPAND);
|
||||||
|
|
||||||
|
|
||||||
|
bottomWindow.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||||
|
var playerSelector = new wxChoice(bottomWindow, -1, wxDefaultPosition, wxDefaultSize,
|
||||||
|
["Gaia", "Player 1", "Player 2", "Player 3", "Player 4", "Player 5", "Player 6", "Player 7", "Player 8"]
|
||||||
|
);
|
||||||
|
bottomWindow.sizer.add(playerSelector);
|
||||||
|
|
||||||
|
playerSelector.selection = Atlas.State.objectSettings.playerID;
|
||||||
|
playerSelector.onChoice = function (evt) {
|
||||||
|
Atlas.State.objectSettings.playerID = evt.selection;
|
||||||
|
Atlas.State.objectSettings.notifyObservers();
|
||||||
|
};
|
||||||
|
function updatePlayerSelector() {
|
||||||
|
playerSelector.selection = Atlas.State.objectSettings.playerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var variationControl = new wxScrolledWindow(bottomWindow, -1);
|
||||||
|
variationControl.setScrollRate(0, 5);
|
||||||
|
variationControl.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||||
|
var variationControlBox = new wxStaticBoxSizer(new wxStaticBox(bottomWindow, -1, "Actor Variation"), wxOrientation.VERTICAL);
|
||||||
|
variationControl.sizer.minSize = new wxSize(160, -1);
|
||||||
|
variationControlBox.add(variationControl, 1);
|
||||||
|
bottomWindow.sizer.add(variationControlBox, 1);
|
||||||
|
|
||||||
|
function onVariationSelect() {
|
||||||
|
// It's possible for a variant name to appear in multiple groups.
|
||||||
|
// If so, assume that all the names in each group are the same, so
|
||||||
|
// we don't have to worry about some impossible combinations (e.g.
|
||||||
|
// one group "a,b", a second "b,c", and a third "c,a", where's there's
|
||||||
|
// no set of selections that matches one (and only one) of each group).
|
||||||
|
//
|
||||||
|
// So... When a combo box is changed from 'a' to 'b', add 'b' to the new
|
||||||
|
// selections and make sure any other combo boxes containing both 'a' and
|
||||||
|
// 'b' no longer contain 'a'.
|
||||||
|
|
||||||
|
var sel = this.stringSelection;
|
||||||
|
var selections = [ sel ];
|
||||||
|
for each (var c in variationControl.children)
|
||||||
|
if (c.findString(sel) == wxNOT_FOUND)
|
||||||
|
selections.push(c.stringSelection);
|
||||||
|
|
||||||
|
Atlas.State.objectSettings.actorSelections = selections;
|
||||||
|
Atlas.State.objectSettings.notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVariationControl() {
|
||||||
|
variationControl.sizer.clear(true);
|
||||||
|
var settings = Atlas.State.objectSettings;
|
||||||
|
var variation = settings.getActorVariation();
|
||||||
|
for (var i = 0; i < settings.variantGroups.length; ++i) {
|
||||||
|
var choice = new wxChoice(variationControl, -1, wxDefaultPosition, new wxSize(80, -1),
|
||||||
|
settings.variantGroups[i]);
|
||||||
|
choice.onChoice = onVariationSelect;
|
||||||
|
choice.stringSelection = variation[i];
|
||||||
|
variationControl.sizer.add(choice, 0, wxStretch.EXPAND);
|
||||||
|
}
|
||||||
|
variationControlBox.layout();
|
||||||
|
variationControl.sizer.layout();
|
||||||
|
bottomWindow.sizer.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_observer = function() {
|
||||||
|
updatePlayerSelector();
|
||||||
|
updateVariationControl();
|
||||||
|
};
|
||||||
|
Atlas.State.objectSettings.registerObserver(g_observer);
|
||||||
|
|
||||||
|
// Initialise the controls
|
||||||
|
g_observer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function deinit()
|
||||||
|
{
|
||||||
|
Atlas.State.objectSettings.unregisterObserver(g_observer);
|
||||||
|
}
|
||||||
|
|
@ -94,6 +94,8 @@ TerrainPreviewPage.prototype = {
|
|||||||
itemSizer.cols = numCols;
|
itemSizer.cols = numCols;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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, w, h).previews;
|
var previews = Atlas.Message.GetTerrainGroupPreviews(this.name, w, h).previews;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var names = [];
|
var names = [];
|
||||||
@ -113,6 +115,8 @@ TerrainPreviewPage.prototype = {
|
|||||||
itemSizer.add(imgSizer, 0, wxAlignment.CENTRE | wxStretch.EXPAND);
|
itemSizer.add(imgSizer, 0, wxAlignment.CENTRE | wxStretch.EXPAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: fix keyboard navigation of the terrain previews
|
||||||
|
|
||||||
this.panel.layout();
|
this.panel.layout();
|
||||||
|
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
@ -124,10 +128,9 @@ function init(window, bottomWindow)
|
|||||||
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||||
|
|
||||||
var tools = [
|
var tools = [
|
||||||
/* text label; internal tool name; button */
|
{ label: 'Modify', name: 'AlterElevation' },
|
||||||
[ 'Modify', 'AlterElevation', undefined ],
|
{ label: 'Flatten', name: 'FlattenElevation' },
|
||||||
[ 'Flatten', 'FlattenElevation', undefined ],
|
{ label: 'Paint', name: 'PaintTerrain' },
|
||||||
[ 'Paint', 'PaintTerrain', undefined ]
|
|
||||||
];
|
];
|
||||||
var selectedTool = null; // null if none selected, else an element of 'tools'
|
var selectedTool = null; // null if none selected, else an element of 'tools'
|
||||||
|
|
||||||
@ -135,9 +138,9 @@ function init(window, bottomWindow)
|
|||||||
window.sizer.add(toolSizer, 0, wxStretch.EXPAND);
|
window.sizer.add(toolSizer, 0, wxStretch.EXPAND);
|
||||||
for each (var tool in tools)
|
for each (var tool in tools)
|
||||||
{
|
{
|
||||||
var button = new wxButton(window, -1, tool[0]);
|
var button = new wxButton(window, -1, tool.label);
|
||||||
toolSizer.add(button, 1);
|
toolSizer.add(button, 1);
|
||||||
tool[2] = button;
|
tool.button = button;
|
||||||
|
|
||||||
// Explicitly set the background to the default colour, so that the button
|
// Explicitly set the background to the default colour, so that the button
|
||||||
// is always owner-drawn (by the wxButton code), rather than initially using the
|
// is always owner-drawn (by the wxButton code), rather than initially using the
|
||||||
@ -157,15 +160,16 @@ function init(window, bottomWindow)
|
|||||||
{
|
{
|
||||||
// Disable the old tool
|
// Disable the old tool
|
||||||
if (selectedTool)
|
if (selectedTool)
|
||||||
selectedTool[2].backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
|
selectedTool.button.backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
|
||||||
// Enable the new one
|
// Enable the new one
|
||||||
selectedTool = tool;
|
selectedTool = tool;
|
||||||
this.backgroundColour = new wxColour(0xEE, 0xCC, 0x55);
|
this.backgroundColour = new wxColour(0xEE, 0xCC, 0x55);
|
||||||
Atlas.SetCurrentTool(tool[1]);
|
Atlas.SetCurrentTool(tool.name);
|
||||||
brush.send();
|
brush.send();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})(tool);
|
})(tool);
|
||||||
|
// TODO: Need to make this interact properly with Tools.cpp/RegisterToolButton so all the buttons are in sync
|
||||||
}
|
}
|
||||||
|
|
||||||
var brushSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Brush'), wxOrientation.VERTICAL);
|
var brushSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Brush'), wxOrientation.VERTICAL);
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
#include "ScriptInterface.h"
|
#include "ScriptInterface.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include <typeinfo>
|
||||||
|
# include <cxxabi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "js/jsapi.h"
|
#include "js/jsapi.h"
|
||||||
|
|
||||||
@ -37,30 +41,49 @@
|
|||||||
#include "GameInterface/Shareable.h"
|
#include "GameInterface/Shareable.h"
|
||||||
#include "GameInterface/Messages.h"
|
#include "GameInterface/Messages.h"
|
||||||
|
|
||||||
|
|
||||||
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
||||||
#include <boost/preprocessor/repetition/repeat.hpp>
|
#include <boost/preprocessor/repetition/repeat.hpp>
|
||||||
|
|
||||||
|
#include <valgrind/valgrind.h>
|
||||||
|
|
||||||
#define FAIL(msg) do { JS_ReportError(cx, msg); return false; } while (false)
|
#define FAIL(msg) do { JS_ReportError(cx, msg); return false; } while (false)
|
||||||
|
|
||||||
const int RUNTIME_SIZE = 1024*1024; // TODO: how much memory is needed?
|
const int RUNTIME_SIZE = 4*1024*1024; // TODO: how much memory is needed?
|
||||||
const int STACK_CHUNK_SIZE = 8192;
|
const int STACK_CHUNK_SIZE = 8192;
|
||||||
|
|
||||||
|
SubmitCommand g_SubmitCommand; // TODO: globals are ugly
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
template<typename T>
|
||||||
|
void ReportError(JSContext* cx, const char* title)
|
||||||
|
{
|
||||||
|
// TODO: SetPendingException turns the error into a JS-catchable exception,
|
||||||
|
// but the error report doesn't say anything useful like the line number,
|
||||||
|
// so I'm just using ReportError instead for now (and failures are uncatchable
|
||||||
|
// and will terminate the whole script)
|
||||||
|
//JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "%s: Unhandled type", title)));
|
||||||
|
#ifdef _WIN32
|
||||||
|
JS_ReportError(cx, "%s: Unhandled type", title);
|
||||||
|
#else
|
||||||
|
// Give a more informative message on GCC
|
||||||
|
int status;
|
||||||
|
char* name = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
|
||||||
|
JS_ReportError(cx, "%s: Unhandled type '%s'", title, name);
|
||||||
|
free(name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Use templated structs instead of functions, so that we can use partial specialisation:
|
// Use templated structs instead of functions, so that we can use partial specialisation:
|
||||||
|
|
||||||
template<typename T> struct FromJSVal
|
template<typename T> struct FromJSVal
|
||||||
{
|
{
|
||||||
static bool Convert(JSContext* cx, jsval WXUNUSED(v), T& WXUNUSED(out))
|
static bool Convert(JSContext* cx, jsval WXUNUSED(v), T& WXUNUSED(out))
|
||||||
{
|
{
|
||||||
JS_ReportError(cx, "Unrecognised argument type");
|
ReportError<T>(cx, "FromJSVal");
|
||||||
// TODO: SetPendingException turns the error into a JS-catchable exception,
|
|
||||||
// but the error report doesn't say anything useful like the line number,
|
|
||||||
// so I'm just using ReportError instead for now (and failures are uncatchable
|
|
||||||
// and will terminate the whole script)
|
|
||||||
//JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "Unrecognised argument type")));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -92,7 +115,18 @@ namespace
|
|||||||
static bool Convert(JSContext* cx, jsval v, int& out)
|
static bool Convert(JSContext* cx, jsval v, int& out)
|
||||||
{
|
{
|
||||||
int32 ret;
|
int32 ret;
|
||||||
if (! JS_ValueToInt32(cx, v, &ret)) return false;
|
if (! JS_ValueToECMAInt32(cx, v, &ret)) return false;
|
||||||
|
out = ret;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct FromJSVal<size_t>
|
||||||
|
{
|
||||||
|
static bool Convert(JSContext* cx, jsval v, size_t& out)
|
||||||
|
{
|
||||||
|
uint32 ret;
|
||||||
|
if (! JS_ValueToECMAUint32(cx, v, &ret)) return false;
|
||||||
out = ret;
|
out = ret;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -163,12 +197,13 @@ namespace
|
|||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
// Primitive types:
|
||||||
|
|
||||||
template<typename T> struct ToJSVal
|
template<typename T> struct ToJSVal
|
||||||
{
|
{
|
||||||
static jsval Convert(JSContext* cx, const T& WXUNUSED(val))
|
static jsval Convert(JSContext* cx, const T& WXUNUSED(val))
|
||||||
{
|
{
|
||||||
JS_ReportError(cx, "Unrecognised query return type");
|
ReportError<T>(cx, "ToJSVal");
|
||||||
return JSVAL_VOID;
|
return JSVAL_VOID;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -191,6 +226,14 @@ namespace
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<> struct ToJSVal<size_t>
|
||||||
|
{
|
||||||
|
static jsval Convert(JSContext* WXUNUSED(cx), const size_t& val)
|
||||||
|
{
|
||||||
|
return INT_TO_JSVAL(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<> struct ToJSVal<wxString>
|
template<> struct ToJSVal<wxString>
|
||||||
{
|
{
|
||||||
static jsval Convert(JSContext* cx, const wxString& val)
|
static jsval Convert(JSContext* cx, const wxString& val)
|
||||||
@ -221,6 +264,9 @@ namespace
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// Compound types:
|
||||||
|
|
||||||
template<typename T> struct ToJSVal<std::vector<T> >
|
template<typename T> struct ToJSVal<std::vector<T> >
|
||||||
{
|
{
|
||||||
static jsval Convert(JSContext* cx, const std::vector<T>& val)
|
static jsval Convert(JSContext* cx, const std::vector<T>& val)
|
||||||
@ -247,6 +293,7 @@ namespace
|
|||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
// AtlasMessage structures:
|
||||||
|
|
||||||
template<> struct ToJSVal<AtlasMessage::sTerrainGroupPreview>
|
template<> struct ToJSVal<AtlasMessage::sTerrainGroupPreview>
|
||||||
{
|
{
|
||||||
@ -269,6 +316,65 @@ namespace
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<> struct ToJSVal<AtlasMessage::sObjectsListItem>
|
||||||
|
{
|
||||||
|
static jsval Convert(JSContext* cx, const AtlasMessage::sObjectsListItem& val)
|
||||||
|
{
|
||||||
|
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
|
||||||
|
if (! obj) return JSVAL_VOID;
|
||||||
|
JS_AddRoot(cx, &obj);
|
||||||
|
JS_DefineProperty(cx, obj, "id", ToJSVal<std::wstring>::Convert(cx, *val.id), NULL, NULL, JSPROP_ENUMERATE);
|
||||||
|
JS_DefineProperty(cx, obj, "name", ToJSVal<std::wstring>::Convert(cx, *val.name), NULL, NULL, JSPROP_ENUMERATE);
|
||||||
|
JS_DefineProperty(cx, obj, "type", ToJSVal<int>::Convert(cx, val.type), NULL, NULL, JSPROP_ENUMERATE);
|
||||||
|
JS_RemoveRoot(cx, &obj);
|
||||||
|
return OBJECT_TO_JSVAL(obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct ToJSVal<AtlasMessage::sObjectSettings>
|
||||||
|
{
|
||||||
|
static jsval Convert(JSContext* cx, const AtlasMessage::sObjectSettings& val)
|
||||||
|
{
|
||||||
|
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
|
||||||
|
if (! obj) return JSVAL_VOID;
|
||||||
|
JS_AddRoot(cx, &obj);
|
||||||
|
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_RemoveRoot(cx, &obj);
|
||||||
|
return OBJECT_TO_JSVAL(obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct FromJSVal<AtlasMessage::sObjectSettings>
|
||||||
|
{
|
||||||
|
static bool Convert(JSContext* cx, jsval v, AtlasMessage::sObjectSettings& out)
|
||||||
|
{
|
||||||
|
JSObject* obj;
|
||||||
|
if (! JS_ValueToObject(cx, v, &obj) || obj == NULL)
|
||||||
|
FAIL("Argument must be an array");
|
||||||
|
jsval val;
|
||||||
|
|
||||||
|
int player;
|
||||||
|
if (! JS_GetProperty(cx, obj, "player", &val))
|
||||||
|
FAIL("Failed to get 'player'");
|
||||||
|
if (! ScriptInterface::FromJSVal(cx, val, player))
|
||||||
|
FAIL("Failed to convert 'player'");
|
||||||
|
out.player = player;
|
||||||
|
|
||||||
|
std::vector<std::wstring> selections;
|
||||||
|
if (! JS_GetProperty(cx, obj, "selections", &val))
|
||||||
|
FAIL("Failed to get 'selections'");
|
||||||
|
if (! ScriptInterface::FromJSVal(cx, val, selections))
|
||||||
|
FAIL("Failed to convert 'selections'");
|
||||||
|
out.selections = selections;
|
||||||
|
|
||||||
|
// variantgroups is only used in engine-to-editor, so we don't
|
||||||
|
// bother converting it here
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> bool ScriptInterface::FromJSVal(JSContext* cx, jsval v, T& out)
|
template<typename T> bool ScriptInterface::FromJSVal(JSContext* cx, jsval v, T& out)
|
||||||
@ -284,10 +390,10 @@ template<typename T> jsval ScriptInterface::ToJSVal(JSContext* cx, const T& v)
|
|||||||
// Explicit instantiation of functions that would otherwise be unused in this file
|
// Explicit instantiation of functions that would otherwise be unused in this file
|
||||||
// but are required for linking with other files
|
// but are required for linking with other files
|
||||||
template bool ScriptInterface::FromJSVal<wxString>(JSContext*, jsval, wxString&);
|
template bool ScriptInterface::FromJSVal<wxString>(JSContext*, jsval, wxString&);
|
||||||
|
|
||||||
template bool ScriptInterface::FromJSVal<float>(JSContext*, jsval, float&);
|
template bool ScriptInterface::FromJSVal<float>(JSContext*, jsval, float&);
|
||||||
|
|
||||||
template jsval ScriptInterface::ToJSVal<wxString>(JSContext*, wxString const&);
|
template jsval ScriptInterface::ToJSVal<wxString>(JSContext*, wxString const&);
|
||||||
|
template jsval ScriptInterface::ToJSVal<int>(JSContext*, int const&);
|
||||||
|
template jsval ScriptInterface::ToJSVal<std::vector<int> >(JSContext*, std::vector<int> const&);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -327,6 +433,7 @@ namespace
|
|||||||
wxLogWarning(_T("%s"), logMessage.c_str());
|
wxLogWarning(_T("%s"), logMessage.c_str());
|
||||||
else
|
else
|
||||||
wxLogError(_T("%s"), logMessage.c_str());
|
wxLogError(_T("%s"), logMessage.c_str());
|
||||||
|
VALGRIND_PRINTF_BACKTRACE("->");
|
||||||
wxPrintf(_T("wxJS %s: %s\n--------\n"), isWarning ? _T("warning") : _T("error"), logMessage.c_str());
|
wxPrintf(_T("wxJS %s: %s\n--------\n"), isWarning ? _T("warning") : _T("error"), logMessage.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,7 +484,8 @@ ScriptInterface_impl::ScriptInterface_impl()
|
|||||||
m_cx = JS_NewContext(m_rt, STACK_CHUNK_SIZE);
|
m_cx = JS_NewContext(m_rt, STACK_CHUNK_SIZE);
|
||||||
assert(m_cx);
|
assert(m_cx);
|
||||||
|
|
||||||
JS_BeginRequest(m_cx); // if you get linker errors, see the comment in the .h about JS_THREADSAFE
|
JS_BeginRequest(m_cx); // if you get linker errors, see the comment in ScriptInterface.h about JS_THREADSAFE
|
||||||
|
// (TODO: are we using requests correctly? (Probably not; how much does it matter?))
|
||||||
|
|
||||||
JS_SetContextPrivate(m_cx, NULL);
|
JS_SetContextPrivate(m_cx, NULL);
|
||||||
|
|
||||||
@ -400,9 +508,10 @@ ScriptInterface_impl::ScriptInterface_impl()
|
|||||||
|
|
||||||
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
|
|
||||||
m_atlas = JS_DefineObject(m_cx, m_glob, "Atlas", NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
|
m_atlas = JS_DefineObject(m_cx, m_glob, "Atlas", NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
JS_DefineFunction(m_cx, m_atlas, "ForceGC", ::ForceGC, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
JS_DefineFunction(m_cx, m_atlas, "ForceGC", ::ForceGC, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
JS_DefineFunction(m_cx, m_atlas, "LoadScript", ::LoadScript, 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
JS_DefineFunction(m_cx, m_atlas, "LoadScript", ::LoadScript, 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
|
JS_DefineObject(m_cx, m_atlas, "State", NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
|
|
||||||
RegisterMessages(m_atlas);
|
RegisterMessages(m_atlas);
|
||||||
}
|
}
|
||||||
@ -435,9 +544,10 @@ void ScriptInterface_impl::Register(const char* name, JSNative fptr, uintN nargs
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ScriptInterface::ScriptInterface()
|
ScriptInterface::ScriptInterface(SubmitCommand submitCommand)
|
||||||
: m(new ScriptInterface_impl)
|
: m(new ScriptInterface_impl())
|
||||||
{
|
{
|
||||||
|
g_SubmitCommand = submitCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptInterface::~ScriptInterface()
|
ScriptInterface::~ScriptInterface()
|
||||||
@ -459,6 +569,55 @@ void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs)
|
|||||||
m->Register(name, fptr, (uintN)nargs);
|
m->Register(name, fptr, (uintN)nargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSContext* ScriptInterface::GetContext()
|
||||||
|
{
|
||||||
|
return m->m_cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptInterface::AddRoot(void* ptr)
|
||||||
|
{
|
||||||
|
JS_AddRoot(m->m_cx, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptInterface::RemoveRoot(void* ptr)
|
||||||
|
{
|
||||||
|
JS_RemoveRoot(m->m_cx, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptInterface::SetValue_(const wxString& name, jsval val)
|
||||||
|
{
|
||||||
|
jsval jsName = ToJSVal(m->m_cx, name);
|
||||||
|
|
||||||
|
const uintN argc = 2;
|
||||||
|
jsval argv[argc] = { jsName, val };
|
||||||
|
jsval rval;
|
||||||
|
JSBool ok = JS_CallFunctionName(m->m_cx, m->m_glob, "setValue", argc, argv, &rval); // TODO: error checking
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptInterface::GetValue_(const wxString& name, jsval& ret)
|
||||||
|
{
|
||||||
|
jsval jsName = ToJSVal(m->m_cx, name);
|
||||||
|
|
||||||
|
const uintN argc = 1;
|
||||||
|
jsval argv[argc] = { jsName };
|
||||||
|
return JS_CallFunctionName(m->m_cx, m->m_glob, "getValue", argc, argv, &ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptInterface::Eval(const wxString& script)
|
||||||
|
{
|
||||||
|
jsval rval;
|
||||||
|
JSBool ok = JS_EvaluateScript(m->m_cx, m->m_glob,
|
||||||
|
script.mb_str(), script.length(), NULL, 0, &rval);
|
||||||
|
// TODO: error checking
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptInterface::Eval_(const wxString& script, jsval& rval)
|
||||||
|
{
|
||||||
|
JSBool ok = JS_EvaluateScript(m->m_cx, m->m_glob,
|
||||||
|
script.mb_str(), script.length(), NULL, 0, &rval);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptInterface::LoadScript(const wxString& filename, const wxString& code)
|
void ScriptInterface::LoadScript(const wxString& filename, const wxString& code)
|
||||||
{
|
{
|
||||||
size_t codeLength;
|
size_t codeLength;
|
||||||
@ -540,6 +699,15 @@ std::pair<wxPanel*, wxPanel*> ScriptInterface::LoadScriptAsSidebar(const wxStrin
|
|||||||
return JS_TRUE; \
|
return JS_TRUE; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define COMMAND(name, merge, vals) \
|
||||||
|
JSBool call_##name(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* argv, jsval* WXUNUSED(rval)) \
|
||||||
|
{ \
|
||||||
|
(void)cx; (void)argv; /* avoid 'unused parameter' warnings */ \
|
||||||
|
BOOST_PP_SEQ_FOR_EACH_I(CONVERT_ARGS, ~, vals) \
|
||||||
|
g_SubmitCommand(new AtlasMessage::m##name (AtlasMessage::d##name ( BOOST_PP_SEQ_FOR_EACH_I(ARG_LIST, ~, vals) ))); \
|
||||||
|
return JS_TRUE; \
|
||||||
|
}
|
||||||
|
|
||||||
#define QUERY(name, in_vals, out_vals) \
|
#define QUERY(name, in_vals, out_vals) \
|
||||||
JSBool call_##name(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* argv, jsval* rval) \
|
JSBool call_##name(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* argv, jsval* rval) \
|
||||||
{ \
|
{ \
|
||||||
@ -554,8 +722,6 @@ std::pair<wxPanel*, wxPanel*> ScriptInterface::LoadScriptAsSidebar(const wxStrin
|
|||||||
return JS_TRUE; \
|
return JS_TRUE; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define COMMAND(name, merge, vals)
|
|
||||||
|
|
||||||
#define MESSAGES_SKIP_SETUP
|
#define MESSAGES_SKIP_SETUP
|
||||||
#define MESSAGES_SKIP_STRUCTS
|
#define MESSAGES_SKIP_STRUCTS
|
||||||
|
|
||||||
@ -570,6 +736,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
#undef MESSAGE
|
#undef MESSAGE
|
||||||
|
#undef COMMAND
|
||||||
#undef QUERY
|
#undef QUERY
|
||||||
|
|
||||||
void ScriptInterface_impl::RegisterMessages(JSObject* parent)
|
void ScriptInterface_impl::RegisterMessages(JSObject* parent)
|
||||||
@ -583,13 +750,20 @@ void ScriptInterface_impl::RegisterMessages(JSObject* parent)
|
|||||||
ret = JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)vals)-1, \
|
ret = JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)vals)-1, \
|
||||||
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
|
|
||||||
|
#define COMMAND(name, merge, vals) \
|
||||||
|
ret = JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)vals)-1, \
|
||||||
|
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
|
|
||||||
#define QUERY(name, in_vals, out_vals) \
|
#define QUERY(name, in_vals, out_vals) \
|
||||||
ret = JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)in_vals)-1, \
|
ret = JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)in_vals)-1, \
|
||||||
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||||
|
|
||||||
// TODO: #define COMMAND(name, merge, vals) ...
|
|
||||||
|
|
||||||
#undef INCLUDED_MESSAGES
|
#undef INCLUDED_MESSAGES
|
||||||
|
|
||||||
#include "GameInterface/Messages.h"
|
#include "GameInterface/Messages.h"
|
||||||
|
|
||||||
|
#undef MESSAGE
|
||||||
|
#undef COMMAND
|
||||||
|
#undef QUERY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,15 +37,23 @@ class wxWindow;
|
|||||||
class wxString;
|
class wxString;
|
||||||
class wxPanel;
|
class wxPanel;
|
||||||
|
|
||||||
|
namespace AtlasMessage { struct mWorldCommand; }
|
||||||
|
typedef void (*SubmitCommand)(AtlasMessage::mWorldCommand* command);
|
||||||
|
|
||||||
struct ScriptInterface_impl;
|
struct ScriptInterface_impl;
|
||||||
class ScriptInterface
|
class ScriptInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ScriptInterface();
|
ScriptInterface(SubmitCommand submitCommand);
|
||||||
~ScriptInterface();
|
~ScriptInterface();
|
||||||
void SetCallbackData(void* cbdata);
|
void SetCallbackData(void* cbdata);
|
||||||
static void* GetCallbackData(JSContext* cx);
|
static void* GetCallbackData(JSContext* cx);
|
||||||
|
|
||||||
|
template <typename T> void SetValue(const wxString& name, const T& val);
|
||||||
|
template <typename T> bool GetValue(const wxString& name, T& ret);
|
||||||
|
void Eval(const wxString& script);
|
||||||
|
template <typename T> bool Eval(const wxString& script, T& ret);
|
||||||
|
|
||||||
// Defined elsewhere:
|
// Defined elsewhere:
|
||||||
// template <TR, T0..., TR (*fptr) (void* cbdata, T0...)>
|
// template <TR, T0..., TR (*fptr) (void* cbdata, T0...)>
|
||||||
// void RegisterFunction(const char* functionName);
|
// void RegisterFunction(const char* functionName);
|
||||||
@ -59,8 +67,43 @@ public:
|
|||||||
template <typename T> static bool FromJSVal(JSContext* cx, jsval val, T& ret);
|
template <typename T> static bool FromJSVal(JSContext* cx, jsval val, T& ret);
|
||||||
template <typename T> static jsval ToJSVal(JSContext* cx, const T& val);
|
template <typename T> static jsval ToJSVal(JSContext* cx, const T& val);
|
||||||
private:
|
private:
|
||||||
|
JSContext* GetContext();
|
||||||
|
void AddRoot(void* ptr);
|
||||||
|
void RemoveRoot(void* ptr);
|
||||||
|
void SetValue_(const wxString& name, jsval val);
|
||||||
|
bool GetValue_(const wxString& name, jsval& ret);
|
||||||
|
bool Eval_(const wxString& name, jsval& ret);
|
||||||
|
|
||||||
void Register(const char* name, JSNative fptr, size_t nargs);
|
void Register(const char* name, JSNative fptr, size_t nargs);
|
||||||
std::auto_ptr<ScriptInterface_impl> m;
|
std::auto_ptr<ScriptInterface_impl> m;
|
||||||
|
|
||||||
// The nasty macro/template bits are split into a separate file so you don't have to look at them
|
// The nasty macro/template bits are split into a separate file so you don't have to look at them
|
||||||
#include "NativeWrapper.inl"
|
#include "NativeWrapper.inl"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ScriptInterface::SetValue(const wxString& name, const T& val)
|
||||||
|
{
|
||||||
|
return SetValue_(name, ToJSVal(GetContext(), val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool ScriptInterface::GetValue(const wxString& name, T& ret)
|
||||||
|
{
|
||||||
|
jsval jsRet;
|
||||||
|
if (! GetValue_(name, jsRet)) return false;
|
||||||
|
AddRoot(&jsRet); // root it while we do some more work (TODO: is this really necessary?)
|
||||||
|
bool ok = FromJSVal(GetContext(), jsRet, ret);
|
||||||
|
RemoveRoot(&jsRet);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool ScriptInterface::Eval(const wxString& script, T& ret)
|
||||||
|
{
|
||||||
|
jsval jsRet;
|
||||||
|
if (! Eval_(script, jsRet)) return false;
|
||||||
|
AddRoot(&jsRet); // root it while we do some more work
|
||||||
|
bool ok = FromJSVal(GetContext(), jsRet, ret);
|
||||||
|
RemoveRoot(&jsRet);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include "ScenarioEditor/Tools/Common/Tools.h"
|
#include "ScenarioEditor/Tools/Common/Tools.h"
|
||||||
#include "ScenarioEditor/ScenarioEditor.h"
|
#include "ScenarioEditor/ScenarioEditor.h"
|
||||||
#include "ScenarioEditor/Sections/Environment/LightControl.h"
|
#include "ScenarioEditor/Sections/Environment/LightControl.h"
|
||||||
#include "ScenarioEditor/Sections/Object/VariationControl.h"
|
//#include "ScenarioEditor/Sections/Object/VariationControl.h"
|
||||||
|
|
||||||
#include "GameInterface/Messages.h"
|
#include "GameInterface/Messages.h"
|
||||||
|
|
||||||
@ -182,15 +182,20 @@ static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings)
|
|||||||
POST_COMMAND(SetEnvironmentSettings, (settings));
|
POST_COMMAND(SetEnvironmentSettings, (settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
ActorViewer::ActorViewer(wxWindow* parent)
|
ActorViewer::ActorViewer(wxWindow* parent, ScriptInterface& scriptInterface)
|
||||||
: wxFrame(parent, wxID_ANY, _("Actor Viewer"), wxDefaultPosition, wxSize(800, 600)),
|
: wxFrame(parent, wxID_ANY, _("Actor Viewer"), wxDefaultPosition, wxSize(800, 600)),
|
||||||
m_CurrentSpeed(0.f), m_BackgroundColour(wxColour(255, 255, 255)),
|
m_CurrentSpeed(0.f), m_BackgroundColour(wxColour(255, 255, 255)),
|
||||||
m_ToggledWalking(false), m_ToggledWireframe(false), m_ToggledGround(true),
|
m_ToggledWalking(false), m_ToggledWireframe(false), m_ToggledGround(true),
|
||||||
m_ToggledShadows(true), m_ToggledStats(false),
|
m_ToggledShadows(true), m_ToggledStats(false),
|
||||||
m_ObjectSettings(m_ObjectSelection, AtlasMessage::eRenderView::ACTOR)
|
m_ScriptInterface(scriptInterface),
|
||||||
|
m_ObjectSettings(m_ObjectSelection, m_ScriptInterface)
|
||||||
{
|
{
|
||||||
SetIcon(wxIcon(_T("ICON_ActorEditor")));
|
SetIcon(wxIcon(_T("ICON_ActorEditor")));
|
||||||
|
|
||||||
|
// XXX: need to init m_ScriptInterface
|
||||||
|
|
||||||
|
m_ObjectSettings.Init(AtlasMessage::eRenderView::ACTOR);
|
||||||
|
|
||||||
SnapSplitterWindow* splitter = new SnapSplitterWindow(this, 0);
|
SnapSplitterWindow* splitter = new SnapSplitterWindow(this, 0);
|
||||||
splitter->SetDefaultSashPosition(250);
|
splitter->SetDefaultSashPosition(250);
|
||||||
|
|
||||||
@ -301,7 +306,7 @@ ActorViewer::ActorViewer(wxWindow* parent)
|
|||||||
wxSizer* bottomRightSizer = new wxBoxSizer(wxVERTICAL);
|
wxSizer* bottomRightSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
wxSizer* playButtonSizer = new wxBoxSizer(wxHORIZONTAL);
|
wxSizer* playButtonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
wxSizer* optionButtonSizer = new wxBoxSizer(wxVERTICAL);
|
wxSizer* optionButtonSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
wxSizer* variationSizer = new wxStaticBoxSizer(wxVERTICAL, sidePanel, _("Variation"));
|
// wxSizer* variationSizer = new wxStaticBoxSizer(wxVERTICAL, sidePanel, _("Variation"));
|
||||||
|
|
||||||
playButtonSizer->Add(new wxButton(sidePanel, ID_Play, _("Play")), wxSizerFlags().Proportion(1));
|
playButtonSizer->Add(new wxButton(sidePanel, ID_Play, _("Play")), wxSizerFlags().Proportion(1));
|
||||||
playButtonSizer->Add(new wxButton(sidePanel, ID_Pause, _("Pause")), wxSizerFlags().Proportion(1));
|
playButtonSizer->Add(new wxButton(sidePanel, ID_Pause, _("Pause")), wxSizerFlags().Proportion(1));
|
||||||
@ -315,7 +320,7 @@ ActorViewer::ActorViewer(wxWindow* parent)
|
|||||||
optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleShadows, _("Shadows")), _("Toggle shadow rendering")), wxSizerFlags().Expand());
|
optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleShadows, _("Shadows")), _("Toggle shadow rendering")), wxSizerFlags().Expand());
|
||||||
optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleStats, _("Poly count")), _("Toggle polygon-count statistics - turn off ground and shadows for more useful data")), wxSizerFlags().Expand());
|
optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleStats, _("Poly count")), _("Toggle polygon-count statistics - turn off ground and shadows for more useful data")), wxSizerFlags().Expand());
|
||||||
|
|
||||||
variationSizer->Add(new VariationControl(sidePanel, m_ObjectSettings), wxSizerFlags().Expand().Proportion(1));
|
// variationSizer->Add(new VariationControl(sidePanel, m_ObjectSettings), wxSizerFlags().Expand().Proportion(1));
|
||||||
|
|
||||||
mainSizer->Add(m_TreeCtrl, wxSizerFlags().Expand().Proportion(1));
|
mainSizer->Add(m_TreeCtrl, wxSizerFlags().Expand().Proportion(1));
|
||||||
mainSizer->Add(bottomSizer, wxSizerFlags().Expand());
|
mainSizer->Add(bottomSizer, wxSizerFlags().Expand());
|
||||||
@ -328,7 +333,7 @@ ActorViewer::ActorViewer(wxWindow* parent)
|
|||||||
|
|
||||||
bottomRightSizer->Add(m_AnimationBox, wxSizerFlags().Expand());
|
bottomRightSizer->Add(m_AnimationBox, wxSizerFlags().Expand());
|
||||||
bottomRightSizer->Add(playButtonSizer, wxSizerFlags().Expand());
|
bottomRightSizer->Add(playButtonSizer, wxSizerFlags().Expand());
|
||||||
bottomRightSizer->Add(variationSizer, wxSizerFlags().Expand().Proportion(1));
|
// bottomRightSizer->Add(variationSizer, wxSizerFlags().Expand().Proportion(1));
|
||||||
|
|
||||||
sidePanel->SetSizer(mainSizer);
|
sidePanel->SetSizer(mainSizer);
|
||||||
|
|
||||||
|
@ -25,11 +25,12 @@
|
|||||||
|
|
||||||
class wxTreeCtrl;
|
class wxTreeCtrl;
|
||||||
class wxTreeEvent;
|
class wxTreeEvent;
|
||||||
|
class ScriptInterface;
|
||||||
|
|
||||||
class ActorViewer : public wxFrame
|
class ActorViewer : public wxFrame
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ActorViewer(wxWindow* parent);
|
ActorViewer(wxWindow* parent, ScriptInterface& scriptInterface);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetActorView(bool flushCache = false);
|
void SetActorView(bool flushCache = false);
|
||||||
@ -49,8 +50,10 @@ private:
|
|||||||
wxString m_CurrentActor;
|
wxString m_CurrentActor;
|
||||||
float m_CurrentSpeed;
|
float m_CurrentSpeed;
|
||||||
|
|
||||||
|
ScriptInterface& m_ScriptInterface;
|
||||||
|
|
||||||
Observable<std::vector<AtlasMessage::ObjectID> > m_ObjectSelection;
|
Observable<std::vector<AtlasMessage::ObjectID> > m_ObjectSelection;
|
||||||
Observable<ObjectSettings> m_ObjectSettings;
|
ObjectSettings m_ObjectSettings;
|
||||||
wxColour m_BackgroundColour;
|
wxColour m_BackgroundColour;
|
||||||
bool m_ToggledWireframe, m_ToggledWalking, m_ToggledGround, m_ToggledShadows, m_ToggledStats;
|
bool m_ToggledWireframe, m_ToggledWalking, m_ToggledGround, m_ToggledShadows, m_ToggledStats;
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ public:
|
|||||||
template <typename T1>
|
template <typename T1>
|
||||||
explicit Observable(const T1& a1) : T(a1) {}
|
explicit Observable(const T1& a1) : T(a1) {}
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
explicit Observable(T1& a1, T2 a2) : T(a1, a2) {}
|
explicit Observable(T1& a1, T2& a2) : T(a1, a2) {}
|
||||||
|
|
||||||
template<typename C> ObservableConnection RegisterObserver(int order, void (C::*callback) (const T&), C* obj)
|
template<typename C> ObservableConnection RegisterObserver(int order, void (C::*callback) (const T&), C* obj)
|
||||||
{
|
{
|
||||||
|
@ -155,6 +155,10 @@ ATLASDLLIMPEXP void Atlas_ReportError()
|
|||||||
///ReportError(); // janwas: disabled until ErrorReporter.cpp compiles
|
///ReportError(); // janwas: disabled until ErrorReporter.cpp compiles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScenarioEditorSubmitCommand(AtlasMessage::mWorldCommand* command)
|
||||||
|
{
|
||||||
|
ScenarioEditor::GetCommandProc().Submit(new WorldCommand(command));
|
||||||
|
}
|
||||||
|
|
||||||
class AtlasDLLApp : public wxApp
|
class AtlasDLLApp : public wxApp
|
||||||
{
|
{
|
||||||
@ -188,7 +192,6 @@ public:
|
|||||||
wxFrame* frame;
|
wxFrame* frame;
|
||||||
#define MAYBE(t) if (g_InitialWindowType == _T(#t)) frame = new t(NULL); else
|
#define MAYBE(t) if (g_InitialWindowType == _T(#t)) frame = new t(NULL); else
|
||||||
MAYBE(ActorEditor)
|
MAYBE(ActorEditor)
|
||||||
MAYBE(ActorViewer)
|
|
||||||
MAYBE(ColourTester)
|
MAYBE(ColourTester)
|
||||||
#ifdef USE_AOE3ED
|
#ifdef USE_AOE3ED
|
||||||
MAYBE(ArchiveViewer)
|
MAYBE(ArchiveViewer)
|
||||||
@ -198,9 +201,14 @@ public:
|
|||||||
// else
|
// else
|
||||||
if (g_InitialWindowType == _T("ScenarioEditor"))
|
if (g_InitialWindowType == _T("ScenarioEditor"))
|
||||||
{
|
{
|
||||||
m_ScriptInterface = new ScriptInterface();
|
m_ScriptInterface = new ScriptInterface(&ScenarioEditorSubmitCommand);
|
||||||
frame = new ScenarioEditor(NULL, *m_ScriptInterface);
|
frame = new ScenarioEditor(NULL, *m_ScriptInterface);
|
||||||
}
|
}
|
||||||
|
else if (g_InitialWindowType == _T("ActorViewer"))
|
||||||
|
{
|
||||||
|
m_ScriptInterface = new ScriptInterface(&ScenarioEditorSubmitCommand);
|
||||||
|
frame = new ActorViewer(NULL, *m_ScriptInterface);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wxFAIL_MSG(_("Internal error: invalid window type"));
|
wxFAIL_MSG(_("Internal error: invalid window type"));
|
||||||
|
@ -30,8 +30,8 @@
|
|||||||
#include "General/AtlasEventLoop.h"
|
#include "General/AtlasEventLoop.h"
|
||||||
#include "General/Datafile.h"
|
#include "General/Datafile.h"
|
||||||
|
|
||||||
#include "HighResTimer/HighResTimer.h"
|
#include "CustomControls/HighResTimer/HighResTimer.h"
|
||||||
#include "Buttons/ToolButton.h"
|
#include "CustomControls/Buttons/ToolButton.h"
|
||||||
#include "CustomControls/Canvas/Canvas.h"
|
#include "CustomControls/Canvas/Canvas.h"
|
||||||
|
|
||||||
#include "GameInterface/MessagePasser.h"
|
#include "GameInterface/MessagePasser.h"
|
||||||
@ -295,11 +295,18 @@ AtlasWindowCommandProc& ScenarioEditor::GetCommandProc() { return g_CommandProc;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// Wrapper function because SetCurrentTool takes an optional argument, which JS doesn't like
|
// Wrapper functions for scripts
|
||||||
void SetCurrentTool_script(void* cbdata, wxString name)
|
|
||||||
|
void SetCurrentTool_(void* cbdata, wxString name)
|
||||||
{
|
{
|
||||||
static_cast<ScenarioEditor*>(cbdata)->GetToolManager().SetCurrentTool(name);
|
static_cast<ScenarioEditor*>(cbdata)->GetToolManager().SetCurrentTool(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetCurrentToolWith(void* cbdata, wxString name, wxString arg)
|
||||||
|
{
|
||||||
|
static_cast<ScenarioEditor*>(cbdata)->GetToolManager().SetCurrentTool(name, &arg);
|
||||||
|
}
|
||||||
|
|
||||||
wxString GetDataDirectory(void*)
|
wxString GetDataDirectory(void*)
|
||||||
{
|
{
|
||||||
return Datafile::GetDataDirectory();
|
return Datafile::GetDataDirectory();
|
||||||
@ -319,7 +326,7 @@ namespace
|
|||||||
ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterface)
|
ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterface)
|
||||||
: wxFrame(parent, wxID_ANY, _T(""), wxDefaultPosition, wxSize(1024, 768))
|
: wxFrame(parent, wxID_ANY, _T(""), wxDefaultPosition, wxSize(1024, 768))
|
||||||
, m_FileHistory(_T("Scenario Editor")), m_ScriptInterface(scriptInterface)
|
, m_FileHistory(_T("Scenario Editor")), m_ScriptInterface(scriptInterface)
|
||||||
, m_ObjectSettings(g_SelectedObjects, AtlasMessage::eRenderView::GAME)
|
, m_ObjectSettings(g_SelectedObjects, m_ScriptInterface)
|
||||||
, m_ToolManager(this)
|
, m_ToolManager(this)
|
||||||
{
|
{
|
||||||
// Global application initialisation:
|
// Global application initialisation:
|
||||||
@ -338,7 +345,8 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
|
|||||||
// Script interface functions
|
// Script interface functions
|
||||||
GetScriptInterface().SetCallbackData(static_cast<void*>(this));
|
GetScriptInterface().SetCallbackData(static_cast<void*>(this));
|
||||||
GetScriptInterface().RegisterFunction<wxString, GetDataDirectory>("GetDataDirectory");
|
GetScriptInterface().RegisterFunction<wxString, GetDataDirectory>("GetDataDirectory");
|
||||||
GetScriptInterface().RegisterFunction<void, wxString, SetCurrentTool_script>("SetCurrentTool");
|
GetScriptInterface().RegisterFunction<void, wxString, SetCurrentTool_>("SetCurrentTool");
|
||||||
|
GetScriptInterface().RegisterFunction<void, wxString, wxString, SetCurrentToolWith>("SetCurrentToolWith");
|
||||||
GetScriptInterface().RegisterFunction<void, float, SetBrushStrength>("SetBrushStrength");
|
GetScriptInterface().RegisterFunction<void, float, SetBrushStrength>("SetBrushStrength");
|
||||||
GetScriptInterface().RegisterFunction<void, wxString, SetSelectedTexture>("SetSelectedTexture");
|
GetScriptInterface().RegisterFunction<void, wxString, SetSelectedTexture>("SetSelectedTexture");
|
||||||
|
|
||||||
@ -353,6 +361,9 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
|
|||||||
GetScriptInterface().LoadScript(filename.GetFullName(), script);
|
GetScriptInterface().LoadScript(filename.GetFullName(), script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialise things that rely on scripts
|
||||||
|
m_ObjectSettings.Init(AtlasMessage::eRenderView::GAME);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Menu
|
// Menu
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ public:
|
|||||||
static float GetSpeedModifier();
|
static float GetSpeedModifier();
|
||||||
|
|
||||||
ScriptInterface& GetScriptInterface() const { return m_ScriptInterface; }
|
ScriptInterface& GetScriptInterface() const { return m_ScriptInterface; }
|
||||||
Observable<ObjectSettings>& GetObjectSettings() { return m_ObjectSettings; }
|
ObjectSettings& GetObjectSettings() { return m_ObjectSettings; }
|
||||||
|
|
||||||
ToolManager& GetToolManager() { return m_ToolManager; }
|
ToolManager& GetToolManager() { return m_ToolManager; }
|
||||||
|
|
||||||
|
@ -22,13 +22,12 @@
|
|||||||
|
|
||||||
#include "SectionLayout.h"
|
#include "SectionLayout.h"
|
||||||
|
|
||||||
#include "SnapSplitterWindow/SnapSplitterWindow.h"
|
#include "CustomControls/SnapSplitterWindow/SnapSplitterWindow.h"
|
||||||
|
|
||||||
#include "ScenarioEditor.h"
|
#include "ScenarioEditor.h"
|
||||||
#include "AtlasScript/ScriptInterface.h"
|
#include "AtlasScript/ScriptInterface.h"
|
||||||
|
|
||||||
#include "Sections/Terrain/Terrain.h"
|
#include "Sections/Terrain/Terrain.h"
|
||||||
#include "Sections/Object/Object.h"
|
|
||||||
#include "Sections/Environment/Environment.h"
|
#include "Sections/Environment/Environment.h"
|
||||||
#include "Sections/Cinematic/Cinematic.h"
|
#include "Sections/Cinematic/Cinematic.h"
|
||||||
#include "Sections/Trigger/Trigger.h"
|
#include "Sections/Trigger/Trigger.h"
|
||||||
@ -317,7 +316,7 @@ void SectionLayout::Build(ScenarioEditor& scenarioEditor)
|
|||||||
ADD_SIDEBAR_SCRIPT(_T("map"), _T("map.png"), _("Map"));
|
ADD_SIDEBAR_SCRIPT(_T("map"), _T("map.png"), _("Map"));
|
||||||
ADD_SIDEBAR_SCRIPT(_T("terrain"), _T("terrain.png"), _("Terrain"));
|
ADD_SIDEBAR_SCRIPT(_T("terrain"), _T("terrain.png"), _("Terrain"));
|
||||||
//ADD_SIDEBAR(TerrainSidebar, _T("terrain.png"), _("Terrain"));
|
//ADD_SIDEBAR(TerrainSidebar, _T("terrain.png"), _("Terrain"));
|
||||||
ADD_SIDEBAR(ObjectSidebar, _T("object.png"), _("Object"));
|
ADD_SIDEBAR_SCRIPT(_T("object"), _T("object.png"), _("Object"));
|
||||||
ADD_SIDEBAR(EnvironmentSidebar, _T("environment.png"), _("Environment"));
|
ADD_SIDEBAR(EnvironmentSidebar, _T("environment.png"), _("Environment"));
|
||||||
|
|
||||||
#ifndef ATLAS_PUBLIC_RELEASE
|
#ifndef ATLAS_PUBLIC_RELEASE
|
||||||
|
@ -1,208 +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 "Object.h"
|
|
||||||
|
|
||||||
#include "Buttons/ToolButton.h"
|
|
||||||
#include "ScenarioEditor/ScenarioEditor.h"
|
|
||||||
#include "ScenarioEditor/Tools/Common/ObjectSettings.h"
|
|
||||||
#include "ScenarioEditor/Tools/Common/MiscState.h"
|
|
||||||
#include "VariationControl.h"
|
|
||||||
|
|
||||||
#include "GameInterface/Messages.h"
|
|
||||||
|
|
||||||
#include "wx/busyinfo.h"
|
|
||||||
|
|
||||||
class ObjectSelectListBox : public wxListBox
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ObjectSelectListBox(wxWindow* parent, ToolManager& toolManager)
|
|
||||||
: wxListBox(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE|wxLB_HSCROLL)
|
|
||||||
, m_ToolManager(toolManager)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnSelect(wxCommandEvent& evt)
|
|
||||||
{
|
|
||||||
// On selecting an object, enable the PlaceObject tool with this object
|
|
||||||
wxString id = static_cast<wxStringClientData*>(evt.GetClientObject())->GetData();
|
|
||||||
m_ToolManager.SetCurrentTool(_T("PlaceObject"), &id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ToolManager& m_ToolManager;
|
|
||||||
DECLARE_EVENT_TABLE();
|
|
||||||
};
|
|
||||||
BEGIN_EVENT_TABLE(ObjectSelectListBox, wxListBox)
|
|
||||||
EVT_LISTBOX(wxID_ANY, ObjectSelectListBox::OnSelect)
|
|
||||||
END_EVENT_TABLE();
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectChoiceCtrl : public wxChoice
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ObjectChoiceCtrl(wxWindow* parent, const wxArrayString& strings, ObjectSidebar& sidebar)
|
|
||||||
: wxChoice(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, strings),
|
|
||||||
m_Sidebar(sidebar)
|
|
||||||
{
|
|
||||||
SetSelection(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnSelect(wxCommandEvent& evt)
|
|
||||||
{
|
|
||||||
// Switch between displayed lists of objects (e.g. entities vs actors)
|
|
||||||
m_Sidebar.SetObjectFilter(evt.GetSelection());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ObjectSidebar& m_Sidebar;
|
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE();
|
|
||||||
};
|
|
||||||
BEGIN_EVENT_TABLE(ObjectChoiceCtrl, wxChoice)
|
|
||||||
EVT_CHOICE(wxID_ANY, ObjectChoiceCtrl::OnSelect)
|
|
||||||
END_EVENT_TABLE();
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectBottomBar : public wxPanel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ObjectBottomBar(wxWindow* parent, Observable<ObjectSettings>& objectSettings);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ObjectSidebarImpl
|
|
||||||
{
|
|
||||||
ObjectSidebarImpl() : m_BottomBar(NULL), m_ObjectListBox(NULL) { }
|
|
||||||
wxWindow* m_BottomBar;
|
|
||||||
wxListBox* m_ObjectListBox;
|
|
||||||
std::vector<AtlasMessage::sObjectsListItem> m_Objects;
|
|
||||||
};
|
|
||||||
|
|
||||||
ObjectSidebar::ObjectSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
|
||||||
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer), p(new ObjectSidebarImpl())
|
|
||||||
{
|
|
||||||
wxArrayString strings;
|
|
||||||
strings.Add(_("Entities"));
|
|
||||||
strings.Add(_("Actors (all)"));
|
|
||||||
m_MainSizer->Add(new ObjectChoiceCtrl(this, strings, *this), wxSizerFlags().Expand());
|
|
||||||
|
|
||||||
p->m_ObjectListBox = new ObjectSelectListBox(this, scenarioEditor.GetToolManager());
|
|
||||||
m_MainSizer->Add(p->m_ObjectListBox, wxSizerFlags().Proportion(1).Expand());
|
|
||||||
|
|
||||||
m_BottomBar = new ObjectBottomBar(bottomBarContainer, scenarioEditor.GetObjectSettings());
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectSidebar::~ObjectSidebar()
|
|
||||||
{
|
|
||||||
delete p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectSidebar::OnFirstDisplay()
|
|
||||||
{
|
|
||||||
wxBusyInfo busy (_("Loading list of objects"));
|
|
||||||
|
|
||||||
// Get the list of objects from the game
|
|
||||||
AtlasMessage::qGetObjectsList qry;
|
|
||||||
qry.Post();
|
|
||||||
p->m_Objects = *qry.objects;
|
|
||||||
// Display first group of objects
|
|
||||||
SetObjectFilter(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectSidebar::SetObjectFilter(int type)
|
|
||||||
{
|
|
||||||
p->m_ObjectListBox->Freeze();
|
|
||||||
p->m_ObjectListBox->Clear();
|
|
||||||
for (std::vector<AtlasMessage::sObjectsListItem>::iterator it = p->m_Objects.begin(); it != p->m_Objects.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->type == type)
|
|
||||||
{
|
|
||||||
wxString id = it->id.c_str();
|
|
||||||
wxString name = it->name.c_str();
|
|
||||||
p->m_ObjectListBox->Append(name, new wxStringClientData(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p->m_ObjectListBox->Thaw();
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class PlayerComboBox : public wxComboBox
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PlayerComboBox(wxWindow* parent, wxArrayString& choices, Observable<ObjectSettings>& objectSettings)
|
|
||||||
: wxComboBox(parent, -1, choices[objectSettings.GetPlayerID()], wxDefaultPosition, wxDefaultSize, choices, wxCB_READONLY)
|
|
||||||
, m_ObjectSettings(objectSettings)
|
|
||||||
{
|
|
||||||
m_Conn = m_ObjectSettings.RegisterObserver(1, &PlayerComboBox::OnObjectSettingsChange, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ObservableScopedConnection m_Conn;
|
|
||||||
Observable<ObjectSettings>& m_ObjectSettings;
|
|
||||||
|
|
||||||
void OnObjectSettingsChange(const ObjectSettings& settings)
|
|
||||||
{
|
|
||||||
SetSelection((long)settings.GetPlayerID());
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnSelect(wxCommandEvent& evt)
|
|
||||||
{
|
|
||||||
m_ObjectSettings.SetPlayerID(evt.GetInt());
|
|
||||||
m_ObjectSettings.NotifyObserversExcept(m_Conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE();
|
|
||||||
};
|
|
||||||
BEGIN_EVENT_TABLE(PlayerComboBox, wxComboBox)
|
|
||||||
EVT_COMBOBOX(wxID_ANY, PlayerComboBox::OnSelect)
|
|
||||||
END_EVENT_TABLE();
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ObjectBottomBar::ObjectBottomBar(wxWindow* parent, Observable<ObjectSettings>& objectSettings)
|
|
||||||
: wxPanel(parent, wxID_ANY)
|
|
||||||
{
|
|
||||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
|
|
||||||
wxArrayString players;
|
|
||||||
// TODO: get proper player names
|
|
||||||
players.Add(_("Gaia"));
|
|
||||||
players.Add(_("Player 1"));
|
|
||||||
players.Add(_("Player 2"));
|
|
||||||
players.Add(_("Player 3"));
|
|
||||||
players.Add(_("Player 4"));
|
|
||||||
players.Add(_("Player 5"));
|
|
||||||
players.Add(_("Player 6"));
|
|
||||||
players.Add(_("Player 7"));
|
|
||||||
players.Add(_("Player 8"));
|
|
||||||
wxComboBox* playerSelect = new PlayerComboBox(this, players, objectSettings);
|
|
||||||
sizer->Add(playerSelect);
|
|
||||||
|
|
||||||
wxWindow* variationSelect = new VariationControl(this, objectSettings);
|
|
||||||
variationSelect->SetMinSize(wxSize(160, -1));
|
|
||||||
wxSizer* variationSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Variation"));
|
|
||||||
variationSizer->Add(variationSelect, wxSizerFlags().Proportion(1).Expand());
|
|
||||||
sizer->Add(variationSizer, wxSizerFlags().Proportion(1));
|
|
||||||
|
|
||||||
SetSizer(sizer);
|
|
||||||
}
|
|
@ -1,33 +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 "../Common/Sidebar.h"
|
|
||||||
|
|
||||||
struct ObjectSidebarImpl;
|
|
||||||
class ObjectSidebar : public Sidebar
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ObjectSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
|
|
||||||
~ObjectSidebar();
|
|
||||||
void SetObjectFilter(int type);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void OnFirstDisplay();
|
|
||||||
|
|
||||||
private:
|
|
||||||
ObjectSidebarImpl* p;
|
|
||||||
};
|
|
@ -1,146 +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 "VariationControl.h"
|
|
||||||
|
|
||||||
#include "ScenarioEditor/Tools/Common/ObjectSettings.h"
|
|
||||||
|
|
||||||
VariationControl::VariationControl(wxWindow* parent, Observable<ObjectSettings>& objectSettings)
|
|
||||||
: wxScrolledWindow(parent, -1),
|
|
||||||
m_ObjectSettings(objectSettings)
|
|
||||||
{
|
|
||||||
m_Conn = m_ObjectSettings.RegisterObserver(1, &VariationControl::OnObjectSettingsChange, this);
|
|
||||||
|
|
||||||
SetScrollRate(0, 5);
|
|
||||||
|
|
||||||
m_Sizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
SetSizer(m_Sizer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event handler shared by all the combo boxes created by this window
|
|
||||||
void VariationControl::OnSelect(wxCommandEvent& evt)
|
|
||||||
{
|
|
||||||
std::set<wxString> selections;
|
|
||||||
|
|
||||||
// It's possible for a variant name to appear in multiple groups.
|
|
||||||
// If so, assume that all the names in each group are the same, so
|
|
||||||
// we don't have to worry about some impossible combinations (e.g.
|
|
||||||
// one group "a,b", a second "b,c", and a third "c,a", where's there's
|
|
||||||
// no set of selections that matches one (and only one) of each group).
|
|
||||||
//
|
|
||||||
// So... When a combo box is changed from 'a' to 'b', add 'b' to the new
|
|
||||||
// selections and make sure any other combo boxes containing both 'a' and
|
|
||||||
// 'b' no longer contain 'a'.
|
|
||||||
|
|
||||||
wxComboBox* thisComboBox = wxDynamicCast(evt.GetEventObject(), wxComboBox);
|
|
||||||
wxCHECK(thisComboBox != NULL, );
|
|
||||||
wxString newValue = thisComboBox->GetValue();
|
|
||||||
|
|
||||||
selections.insert(newValue);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_ComboBoxes.size(); ++i)
|
|
||||||
{
|
|
||||||
wxComboBox* comboBox = m_ComboBoxes[i];
|
|
||||||
// If our newly selected value is used in another combobox, we want
|
|
||||||
// that combobox to use the new value, so don't add its old value
|
|
||||||
// to the list of selections
|
|
||||||
if (comboBox->FindString(newValue) == wxNOT_FOUND)
|
|
||||||
selections.insert(comboBox->GetValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ObjectSettings.SetActorSelections(selections);
|
|
||||||
m_ObjectSettings.NotifyObserversExcept(m_Conn);
|
|
||||||
RefreshObjectSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VariationControl::OnObjectSettingsChange(const ObjectSettings& settings)
|
|
||||||
{
|
|
||||||
Freeze();
|
|
||||||
|
|
||||||
const std::vector<ObjectSettings::Group>& variation = settings.GetActorVariation();
|
|
||||||
|
|
||||||
// Creating combo boxes seems to be pretty expensive - so we create as
|
|
||||||
// few as possible, by never deleting any.
|
|
||||||
|
|
||||||
size_t oldCount = m_ComboBoxes.size();
|
|
||||||
size_t newCount = variation.size();
|
|
||||||
|
|
||||||
// If we have too many combo boxes, hide the excess ones
|
|
||||||
for (size_t i = newCount; i < oldCount; ++i)
|
|
||||||
{
|
|
||||||
m_ComboBoxes[i]->Show(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < variation.size(); ++i)
|
|
||||||
{
|
|
||||||
const ObjectSettings::Group& group = variation[i];
|
|
||||||
|
|
||||||
if (i < oldCount)
|
|
||||||
{
|
|
||||||
// Already got enough boxes available, so use an old one
|
|
||||||
wxComboBox* comboBox = m_ComboBoxes[i];
|
|
||||||
// Replace the contents of the old combobox with the new data
|
|
||||||
comboBox->Freeze();
|
|
||||||
comboBox->Clear();
|
|
||||||
comboBox->Append(group.variants);
|
|
||||||
comboBox->SetValue(group.chosen);
|
|
||||||
comboBox->Show(true);
|
|
||||||
comboBox->Thaw();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create an initially empty combobox, because we can fill it
|
|
||||||
// quicker than the default constructor can
|
|
||||||
wxComboBox* combo = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition,
|
|
||||||
wxSize(80, wxDefaultCoord), wxArrayString(), wxCB_READONLY);
|
|
||||||
// Freeze it before adding all the values
|
|
||||||
combo->Freeze();
|
|
||||||
combo->Append(group.variants);
|
|
||||||
combo->SetValue(group.chosen);
|
|
||||||
combo->Thaw();
|
|
||||||
// Add the on-select event handler
|
|
||||||
combo->Connect(wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED,
|
|
||||||
wxCommandEventHandler(VariationControl::OnSelect), NULL, this);
|
|
||||||
// Add box to sizer and list
|
|
||||||
m_Sizer->Add(combo, wxSizerFlags().Expand());
|
|
||||||
m_ComboBoxes.push_back(combo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout();
|
|
||||||
|
|
||||||
Thaw();
|
|
||||||
|
|
||||||
// Make the scrollbars appear when appropriate
|
|
||||||
FitInside();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VariationControl::RefreshObjectSettings()
|
|
||||||
{
|
|
||||||
const std::vector<ObjectSettings::Group>& variation = m_ObjectSettings.GetActorVariation();
|
|
||||||
|
|
||||||
// For each group, set the corresponding combobox's value to the chosen one
|
|
||||||
size_t i = 0;
|
|
||||||
for (std::vector<ObjectSettings::Group>::const_iterator group = variation.begin();
|
|
||||||
group != variation.end() && i < m_ComboBoxes.size();
|
|
||||||
++group, ++i)
|
|
||||||
{
|
|
||||||
m_ComboBoxes[i]->SetValue(group->chosen);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INCLUDED_VARIATIONCONTROL
|
|
||||||
#define INCLUDED_VARIATIONCONTROL
|
|
||||||
|
|
||||||
#include "General/Observable.h"
|
|
||||||
|
|
||||||
class ObjectSettings;
|
|
||||||
|
|
||||||
class VariationControl : public wxScrolledWindow
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VariationControl(wxWindow* parent, Observable<ObjectSettings>& objectSettings);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void OnSelect(wxCommandEvent& evt);
|
|
||||||
void OnObjectSettingsChange(const ObjectSettings& settings);
|
|
||||||
void RefreshObjectSettings();
|
|
||||||
|
|
||||||
ObservableScopedConnection m_Conn;
|
|
||||||
|
|
||||||
Observable<ObjectSettings>& m_ObjectSettings;
|
|
||||||
std::vector<wxComboBox*> m_ComboBoxes;
|
|
||||||
wxSizer* m_Sizer;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // INCLUDED_VARIATIONCONTROL
|
|
@ -21,133 +21,42 @@
|
|||||||
|
|
||||||
#include "GameInterface/Messages.h"
|
#include "GameInterface/Messages.h"
|
||||||
#include "ScenarioEditor/ScenarioEditor.h"
|
#include "ScenarioEditor/ScenarioEditor.h"
|
||||||
|
#include "AtlasScript/ScriptInterface.h"
|
||||||
|
|
||||||
ObjectSettings::ObjectSettings(Observable<std::vector<AtlasMessage::ObjectID> >& selectedObjects, int view)
|
ObjectSettings::ObjectSettings(Observable<std::vector<AtlasMessage::ObjectID> >& selectedObjects, ScriptInterface& scriptInterface)
|
||||||
: m_PlayerID(0), m_SelectedObjects(selectedObjects), m_View(view)
|
: m_ScriptInterface(scriptInterface)
|
||||||
{
|
{
|
||||||
m_Conn = m_SelectedObjects.RegisterObserver(0, &ObjectSettings::OnSelectionChange, this);
|
m_Conn = selectedObjects.RegisterObserver(0, &ObjectSettings::OnSelectionChange, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ObjectSettings::GetPlayerID() const
|
void ObjectSettings::Init(int view)
|
||||||
{
|
{
|
||||||
return m_PlayerID;
|
m_ScriptInterface.SetValue(_T("Atlas.State.objectSettings.view"), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectSettings::SetPlayerID(int playerID)
|
void ObjectSettings::SetPlayerID(int playerID)
|
||||||
{
|
{
|
||||||
m_PlayerID = playerID;
|
m_ScriptInterface.SetValue(_T("Atlas.State.objectSettings.playerID"), playerID);
|
||||||
PostToGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::set<wxString>& ObjectSettings::GetActorSelections() const
|
|
||||||
{
|
|
||||||
return m_ActorSelections;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectSettings::SetActorSelections(const std::set<wxString>& selections)
|
|
||||||
{
|
|
||||||
m_ActorSelections = selections;
|
|
||||||
PostToGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<ObjectSettings::Group> ObjectSettings::GetActorVariation() const
|
|
||||||
{
|
|
||||||
std::vector<Group> variation;
|
|
||||||
|
|
||||||
for (std::vector<wxArrayString>::const_iterator grp = m_VariantGroups.begin();
|
|
||||||
grp != m_VariantGroups.end();
|
|
||||||
++grp)
|
|
||||||
{
|
|
||||||
Group group;
|
|
||||||
group.variants = *grp;
|
|
||||||
|
|
||||||
// Variant choice method, as used by the game: Choose the first variant
|
|
||||||
// which matches any of the selections
|
|
||||||
|
|
||||||
size_t chosen = 0; // default to first
|
|
||||||
for (size_t i = 0; i < grp->GetCount(); ++i)
|
|
||||||
{
|
|
||||||
if (m_ActorSelections.find(grp->Item(i)) != m_ActorSelections.end())
|
|
||||||
{
|
|
||||||
chosen = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.chosen = grp->Item(chosen);
|
|
||||||
|
|
||||||
variation.push_back(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
return variation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AtlasMessage::sObjectSettings ObjectSettings::GetSettings() const
|
AtlasMessage::sObjectSettings ObjectSettings::GetSettings() const
|
||||||
{
|
{
|
||||||
AtlasMessage::sObjectSettings settings;
|
AtlasMessage::sObjectSettings settings;
|
||||||
|
bool ok = m_ScriptInterface.Eval(_T("Atlas.State.objectSettings.toSObjectSettings()"), settings);
|
||||||
settings.player = m_PlayerID;
|
wxASSERT(ok);
|
||||||
|
|
||||||
// Copy selections from set into vector
|
|
||||||
std::vector<std::wstring> selections;
|
|
||||||
for (std::set<wxString>::const_iterator it = m_ActorSelections.begin();
|
|
||||||
it != m_ActorSelections.end();
|
|
||||||
++it)
|
|
||||||
{
|
|
||||||
selections.push_back(it->c_str());
|
|
||||||
}
|
|
||||||
settings.selections = selections;
|
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectSettings::OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection)
|
void ObjectSettings::OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection)
|
||||||
{
|
{
|
||||||
// TODO: what would be the sensible action if nothing's selected?
|
// Convert to ints so they can be passed to JS
|
||||||
// and if multiple objects are selected?
|
std::vector<int> objs (selection.begin(), selection.end());
|
||||||
|
|
||||||
if (selection.empty())
|
m_ScriptInterface.SetValue(_T("Atlas.State.objectSettings.selectedObjects"), objs);
|
||||||
return;
|
m_ScriptInterface.Eval(_T("Atlas.State.objectSettings.onSelectionChange()"));
|
||||||
|
|
||||||
AtlasMessage::qGetObjectSettings qry (m_View, selection[0]);
|
|
||||||
qry.Post();
|
|
||||||
|
|
||||||
m_PlayerID = qry.settings->player;
|
|
||||||
|
|
||||||
m_ActorSelections.clear();
|
|
||||||
m_VariantGroups.clear();
|
|
||||||
|
|
||||||
std::vector<std::vector<std::wstring> > variation = *qry.settings->variantgroups;
|
|
||||||
for (std::vector<std::vector<std::wstring> >::iterator grp = variation.begin();
|
|
||||||
grp != variation.end();
|
|
||||||
++grp)
|
|
||||||
{
|
|
||||||
wxArrayString variants;
|
|
||||||
|
|
||||||
for (std::vector<std::wstring>::iterator it = grp->begin();
|
|
||||||
it != grp->end();
|
|
||||||
++it)
|
|
||||||
{
|
|
||||||
variants.Add(it->c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_VariantGroups.push_back(variants);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::wstring> selections = *qry.settings->selections;
|
|
||||||
for (std::vector<std::wstring>::iterator sel = selections.begin();
|
|
||||||
sel != selections.end();
|
|
||||||
++sel)
|
|
||||||
{
|
|
||||||
m_ActorSelections.insert(sel->c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static_cast<Observable<ObjectSettings>*>(this)->NotifyObservers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectSettings::PostToGame()
|
void ObjectSettings::NotifyObservers()
|
||||||
{
|
{
|
||||||
if (m_SelectedObjects.empty())
|
m_ScriptInterface.Eval(_T("Atlas.State.objectSettings.notifyObservers()"));
|
||||||
return;
|
|
||||||
|
|
||||||
POST_COMMAND(SetObjectSettings, (m_View, m_SelectedObjects[0], GetSettings()));
|
|
||||||
}
|
}
|
||||||
|
@ -19,62 +19,37 @@
|
|||||||
#define INCLUDED_OBJECTSETTINGS
|
#define INCLUDED_OBJECTSETTINGS
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "ScenarioEditor/Tools/Common/MiscState.h"
|
#include "ScenarioEditor/Tools/Common/MiscState.h"
|
||||||
|
|
||||||
|
class ScriptInterface;
|
||||||
|
|
||||||
namespace AtlasMessage
|
namespace AtlasMessage
|
||||||
{
|
{
|
||||||
struct sObjectSettings;
|
struct sObjectSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Various settings to be applied to newly created units, or to the currently
|
// This class is now just an interface to the JS Atlas.State.objectSettings,
|
||||||
// selected unit. If a unit is selected or being previewed, it should match
|
// for old C++ code that hasn't been ported to JS yet.
|
||||||
// these settings.
|
|
||||||
class ObjectSettings
|
class ObjectSettings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ObjectSettings(Observable<std::vector<AtlasMessage::ObjectID> >& selectedObjects, int view);
|
ObjectSettings(Observable<std::vector<AtlasMessage::ObjectID> >& selectedObjects, ScriptInterface& scriptInterface);
|
||||||
|
void Init(int view);
|
||||||
|
|
||||||
size_t GetPlayerID() const;
|
|
||||||
void SetPlayerID(int playerID);
|
void SetPlayerID(int playerID);
|
||||||
|
|
||||||
struct Group
|
|
||||||
{
|
|
||||||
wxArrayString variants;
|
|
||||||
wxString chosen;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::vector<Group> GetActorVariation() const;
|
|
||||||
|
|
||||||
const std::set<wxString>& GetActorSelections() const;
|
|
||||||
void SetActorSelections(const std::set<wxString>& selections);
|
|
||||||
|
|
||||||
// Constructs new sObjectSettings object from settings
|
// Constructs new sObjectSettings object from settings
|
||||||
AtlasMessage::sObjectSettings GetSettings() const;
|
AtlasMessage::sObjectSettings GetSettings() const;
|
||||||
|
|
||||||
|
void NotifyObservers();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Observable<std::vector<AtlasMessage::ObjectID> >& m_SelectedObjects;
|
ScriptInterface& m_ScriptInterface;
|
||||||
|
|
||||||
int m_View;
|
|
||||||
|
|
||||||
// 0 = gaia, 1..inf = normal players
|
|
||||||
size_t m_PlayerID;
|
|
||||||
|
|
||||||
// Set of user-chosen actor selections, potentially a superset of any single
|
|
||||||
// actor's possible variants (since it doesn't get reset if you select
|
|
||||||
// a new actor, and will accumulate variant names)
|
|
||||||
std::set<wxString> m_ActorSelections;
|
|
||||||
|
|
||||||
// List of actor variant groups (each a list of variant names)
|
|
||||||
std::vector<wxArrayString> m_VariantGroups;
|
|
||||||
|
|
||||||
// Observe changes to unit selection
|
// Observe changes to unit selection
|
||||||
ObservableScopedConnection m_Conn;
|
ObservableScopedConnection m_Conn;
|
||||||
void OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection);
|
void OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection);
|
||||||
|
|
||||||
// Transfer current settings to the currently selected unit (if any)
|
|
||||||
void PostToGame();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INCLUDED_OBJECTSETTINGS
|
#endif // INCLUDED_OBJECTSETTINGS
|
||||||
|
Loading…
Reference in New Issue
Block a user