1
1
forked from 0ad/0ad

Move Atlas map settings from JS to C++.

Replace New dialog box with separate tools for resizing maps and
replacing terrain textures, to provide more power and to simplify the
problem of initialising map settings.
Fix engine to cope with dynamic map resizing.
Add JSON support to AtObj, to let C++ interact with JSON more easily.

This was SVN commit r9566.
This commit is contained in:
Ykkrosh 2011-05-29 15:02:02 +00:00
parent 1618aa9c36
commit dc2035efc9
35 changed files with 1195 additions and 370 deletions

View File

@ -766,6 +766,7 @@ function setup_atlas_packages()
},{ -- include
},{ -- extern_libs
"libxml2",
"spidermonkey",
"wxwidgets"
},{ -- extra_params
})
@ -812,7 +813,6 @@ function setup_atlas_packages()
"CustomControls/EditableListCtrl",
"CustomControls/FileHistory",
"CustomControls/HighResTimer",
"CustomControls/NewDialog",
"CustomControls/SnapSplitterWindow",
"CustomControls/VirtualDirTreeCtrl",
"CustomControls/Windows",

View File

@ -56,10 +56,13 @@ CLOSTexture::CLOSTexture(CSimulation2& simulation) :
CLOSTexture::~CLOSTexture()
{
if (m_Texture)
{
DeleteTexture();
}
void CLOSTexture::DeleteTexture()
{
glDeleteTextures(1, &m_Texture);
m_Texture = 0;
}
}
void CLOSTexture::MakeDirty()
@ -149,6 +152,14 @@ void CLOSTexture::ConstructTexture(int unit)
void CLOSTexture::RecomputeTexture(int unit)
{
// If the map was resized, delete and regenerate the texture
if (m_Texture)
{
CmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);
if (!cmpTerrain.null() && m_MapSize != cmpTerrain->GetVerticesPerSide())
DeleteTexture();
}
if (!m_Texture)
ConstructTexture(unit);

View File

@ -71,6 +71,7 @@ public:
const float* GetMinimapTextureMatrix();
private:
void DeleteTexture();
void ConstructTexture(int unit);
void RecomputeTexture(int unit);

View File

@ -277,6 +277,14 @@ const Grid<u16>& CCmpPathfinder::GetPassabilityGrid()
void CCmpPathfinder::UpdateGrid()
{
// If the terrain was resized then delete the old grid data
if (m_Grid && m_MapSize != GetSimContext().GetTerrain().GetTilesPerSide())
{
SAFE_DELETE(m_Grid);
SAFE_DELETE(m_ObstructionGrid);
m_TerrainDirty = true;
}
// Initialise the terrain data when first needed
if (!m_Grid)
{

View File

@ -107,6 +107,11 @@ public:
{
}
virtual void StartRender()
{
m_Pathfinder.UpdateGrid();
}
virtual void ProcessTile(ssize_t i, ssize_t j)
{
if (m_Pathfinder.m_Grid && !IS_PASSABLE(m_Pathfinder.m_Grid->get(i, j), m_Pathfinder.m_DebugPassClass))

View File

@ -109,6 +109,8 @@ public:
entity_pos_t::FromInt(m_Terrain->GetTilesPerSide()*CELL_SIZE),
m_Terrain->GetVerticesPerSide());
}
MakeDirty(0, 0, m_Terrain->GetTilesPerSide()+1, m_Terrain->GetTilesPerSide()+1);
}
virtual void MakeDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)

View File

@ -47,8 +47,8 @@ public:
virtual void ReloadTerrain() = 0;
/**
* Indicate that the terrain within the given region (inclusive lower bound,
* exclusive upper bound) has been changed. CMessageTerrainChanged will be
* Indicate that terrain tiles within the given region (inclusive lower bound,
* exclusive upper bound) have been changed. CMessageTerrainChanged will be
* sent to any components that care about terrain changes.
*/
virtual void MakeDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1) = 0;

View File

@ -27,6 +27,8 @@
#include <wchar.h> // for wchar_t
#include <string>
typedef struct JSContext JSContext;
//////////////////////////////////////////////////////////////////////////
// Mostly-private bits:
@ -103,6 +105,8 @@ public:
bool defined() const;
// Return whether this iterator is pointing to a non-contentless AtObj
bool hasContent() const;
// Return the number of AtObjs that will be iterated over (including the current one)
size_t count() const;
// Return an iterator to the children matching 'key'. (That is, children
// of the AtObj currently pointed to by this iterator)
@ -148,7 +152,10 @@ public:
void add(const char* key, AtObj& data);
void set(const char* key, const wchar_t* value);
void set(const char* key, AtObj& data);
void setBool(const char* key, bool value);
void setInt(const char* key, int value);
void setString(const wchar_t* value);
void addOverlay(AtObj& data);
AtSmartPtr<const AtNode> p;
};
@ -160,10 +167,17 @@ namespace AtlasObject
// Returns AtObj() on failure - test with AtObj::defined()
AtObj LoadFromXML(const std::string& xml);
// Returns AtObj() on failure - test with AtObj::defined()
AtObj LoadFromJSON(JSContext* cx, const std::string& json);
// Returns UTF-8-encoded XML document string.
// Returns empty string on failure.
std::string SaveToXML(AtObj& obj);
// Returns UTF-8-encoded JSON string.
// Returns empty string on failure.
std::string SaveToJSON(JSContext* cx, AtObj& obj);
AtObj TrimEmptyChildren(AtObj& obj);
}

View File

@ -19,6 +19,7 @@
#include "AtlasObjectImpl.h"
#include <assert.h>
#include <sstream>
#define ATSMARTPTR_IMPL(T) \
template<> void AtSmartPtr<T>::inc_ref() \
@ -100,6 +101,14 @@ bool AtIter::hasContent() const
return p->iter->second->hasContent();
}
size_t AtIter::count() const
{
if (!p)
return 0;
return std::distance(p->iter, p->iter_upperbound);
}
//////////////////////////////////////////////////////////////////////////
const AtIter AtObj::operator [] (const char* key) const
@ -155,6 +164,30 @@ void AtObj::set(const char* key, const wchar_t* value)
p = p->setChild(key, AtNode::Ptr(o));
}
void AtObj::setBool(const char* key, bool value)
{
AtNode* o = new AtNode(value ? L"true" : L"false");
o->children.insert(AtNode::child_pairtype("@boolean", AtNode::Ptr(new AtNode())));
if (!p)
p = new AtNode();
p = p->setChild(key, AtNode::Ptr(o));
}
void AtObj::setInt(const char* key, int value)
{
std::wstringstream str;
str << value;
AtNode* o = new AtNode(str.str().c_str());
o->children.insert(AtNode::child_pairtype("@number", AtNode::Ptr(new AtNode())));
if (!p)
p = new AtNode();
p = p->setChild(key, AtNode::Ptr(o));
}
void AtObj::setString(const wchar_t* value)
{
if (!p)
@ -163,6 +196,14 @@ void AtObj::setString(const wchar_t* value)
p = p->setValue(value);
}
void AtObj::addOverlay(AtObj& data)
{
if (!p)
p = new AtNode();
p = p->addOverlay(data.p);
}
bool AtObj::hasContent() const
{
if (!p)
@ -222,6 +263,20 @@ const AtNode::Ptr AtNode::addChild(const char* key, const AtNode::Ptr &data) con
return AtNode::Ptr(newNode);
}
const AtNode::Ptr AtNode::addOverlay(const AtNode::Ptr &data) const
{
AtNode* newNode = new AtNode(this);
// Delete old childs that are also in the overlay
for (AtNode::child_maptype::const_iterator it = data->children.begin(); it != data->children.end(); ++it)
newNode->children.erase(it->first);
// Add the overlay childs back in
for (AtNode::child_maptype::const_iterator it = data->children.begin(); it != data->children.end(); ++it)
newNode->children.insert(*it);
return AtNode::Ptr(newNode);
}
//////////////////////////////////////////////////////////////////////////
AtObj AtlasObject::TrimEmptyChildren(AtObj& obj)

View File

@ -48,6 +48,7 @@ public:
const AtNode::Ptr setValue(const wchar_t* value) const;
const AtNode::Ptr addChild(const char* key, const AtNode::Ptr &data) const;
const AtNode::Ptr setChild(const char* key, const AtNode::Ptr &data) const;
const AtNode::Ptr addOverlay(const AtNode::Ptr &data) const;
const AtIter getChild(const char* key) const;
// Check recursively for any 'value' data

View File

@ -0,0 +1,268 @@
/* 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
* 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 "AtlasObject.h"
#include "AtlasObjectImpl.h"
#include "../AtlasScript/ScriptInterface.h"
#include "wx/log.h"
#include <sstream>
static AtSmartPtr<AtNode> ConvertNode(JSContext* cx, jsval node);
AtObj AtlasObject::LoadFromJSON(JSContext* cx, const std::string& json)
{
// Convert UTF8 to UTF16
wxString jsonW(json.c_str(), wxConvUTF8);
size_t json16len;
wxCharBuffer json16 = wxMBConvUTF16().cWC2MB(jsonW.c_str(), jsonW.Length(), &json16len);
jsval vp = JSVAL_NULL;
JSONParser* parser = JS_BeginJSONParse(cx, &vp);
if (!parser)
{
wxLogError(_T("ParseJSON failed to begin"));
return AtObj();
}
if (!JS_ConsumeJSONText(cx, parser, reinterpret_cast<const jschar*>(json16.data()), (uint32)(json16len/2)))
{
wxLogError(_T("ParseJSON failed to consume"));
return AtObj();
}
if (!JS_FinishJSONParse(cx, parser, JSVAL_NULL))
{
wxLogError(_T("ParseJSON failed to finish"));
return AtObj();
}
AtObj obj;
obj.p = ConvertNode(cx, vp);
return obj;
}
// Convert from a jsval to an AtNode
static AtSmartPtr<AtNode> ConvertNode(JSContext* cx, jsval node)
{
AtSmartPtr<AtNode> obj (new AtNode());
// Non-objects get converted into strings
if (!JSVAL_IS_OBJECT(node))
{
JSString* str = JS_ValueToString(cx, node);
if (!str)
return obj; // error
size_t valueLen;
const jschar* valueChars = JS_GetStringCharsAndLength(str, &valueLen);
if (!valueChars)
return obj; // error
wxString valueWx(reinterpret_cast<const char*>(valueChars), wxMBConvUTF16(), valueLen*2);
obj->value = valueWx.c_str();
// Annotate numbers/booleans specially, to allow round-tripping
if (JSVAL_IS_NUMBER(node))
{
obj->children.insert(AtNode::child_pairtype(
"@number", AtSmartPtr<AtNode>(new AtNode())
));
}
else if (JSVAL_IS_BOOLEAN(node))
{
obj->children.insert(AtNode::child_pairtype(
"@boolean", AtSmartPtr<AtNode>(new AtNode())
));
}
return obj;
}
JSObject* it = JS_NewPropertyIterator(cx, JSVAL_TO_OBJECT(node));
if (!it)
return obj; // error
while (true)
{
jsid idp;
jsval val;
if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &val))
return obj; // error
if (val == JSVAL_VOID)
break; // end of iteration
if (! JSVAL_IS_STRING(val))
continue; // ignore integer properties
JSString* name = JSVAL_TO_STRING(val);
size_t len = JS_GetStringLength(name);
jschar* chars = JS_GetStringChars(name);
wxString nameWx(reinterpret_cast<char*>(chars), wxMBConvUTF16(), len*2);
std::string nameStr(nameWx.ToUTF8().data());
jsval vp;
if (!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(node), idp, &vp))
return obj; // error
// Unwrap arrays into a special format like <$name><item>$i0</item><item>...
// (This assumes arrays aren't nested)
if (JSVAL_IS_OBJECT(vp) && JS_IsArrayObject(cx, JSVAL_TO_OBJECT(vp)))
{
AtSmartPtr<AtNode> child(new AtNode());
child->children.insert(AtNode::child_pairtype(
"@array", AtSmartPtr<AtNode>(new AtNode())
));
jsuint arrayLength;
if (!JS_GetArrayLength(cx, JSVAL_TO_OBJECT(vp), &arrayLength))
return obj; // error
for (jsuint i = 0; i < arrayLength; ++i)
{
jsval val;
if (!JS_GetElement(cx, JSVAL_TO_OBJECT(vp), i, &val))
return obj; // error
child->children.insert(AtNode::child_pairtype(
"item", ConvertNode(cx, val)
));
}
obj->children.insert(AtNode::child_pairtype(
nameStr, child
));
}
else
{
obj->children.insert(AtNode::child_pairtype(
nameStr, ConvertNode(cx, vp)
));
}
}
return obj;
}
jsval BuildJSVal(JSContext* cx, AtNode::Ptr p)
{
if (!p)
return JSVAL_VOID;
// Special case for numbers/booleans to allow round-tripping
if (p->children.count("@number"))
{
// Convert to double
std::wstringstream str;
str << p->value;
double val = 0;
str >> val;
jsval rval;
if (!JS_NewNumberValue(cx, val, &rval))
return JSVAL_VOID; // error
return rval;
}
else if (p->children.count("@boolean"))
{
bool val = false;
if (p->value == L"true")
val = true;
return BOOLEAN_TO_JSVAL(val);
}
// If no children, then use the value string instead
if (p->children.empty())
{
size_t val16len;
wxCharBuffer val16 = wxMBConvUTF16().cWC2MB(p->value.c_str(), p->value.length(), &val16len);
JSString* str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar*>(val16.data()), (uint32)(val16len/2));
if (!str)
return JSVAL_VOID; // error
return STRING_TO_JSVAL(str);
}
if (p->children.find("@array") != p->children.end())
{
JSObject* obj = JS_NewArrayObject(cx, 0, NULL);
if (!obj)
return JSVAL_VOID; // error
// Find the <item> children
AtNode::child_maptype::const_iterator lower = p->children.lower_bound("item");
AtNode::child_maptype::const_iterator upper = p->children.upper_bound("item");
jsint idx = 0;
for (AtNode::child_maptype::const_iterator it = lower; it != upper; ++it)
{
jsval val = BuildJSVal(cx, it->second);
if (!JS_SetElement(cx, obj, idx, &val))
return JSVAL_VOID; // error
++idx;
}
return OBJECT_TO_JSVAL(obj);
}
else
{
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
if (!obj)
return JSVAL_VOID; // error
for (AtNode::child_maptype::const_iterator it = p->children.begin(); it != p->children.end(); ++it)
{
jsval val = BuildJSVal(cx, it->second);
if (!JS_SetProperty(cx, obj, it->first.c_str(), &val))
return JSVAL_VOID; // error
}
return OBJECT_TO_JSVAL(obj);
}
}
struct Stringifier
{
static JSBool callback(const jschar* buf, uint32 len, void* data)
{
wxString textWx(reinterpret_cast<const char*>(buf), wxMBConvUTF16(), len*2);
std::string textStr(textWx.ToUTF8().data());
static_cast<Stringifier*>(data)->stream << textStr;
return JS_TRUE;
}
std::stringstream stream;
};
std::string AtlasObject::SaveToJSON(JSContext* cx, AtObj& obj)
{
jsval root = BuildJSVal(cx, obj.p);
Stringifier str;
if (!JS_Stringify(cx, &root, NULL, JSVAL_VOID, &Stringifier::callback, &str))
{
wxLogError(_T("SaveToJSON failed"));
return "";
}
return str.stream.str();
}

View File

@ -609,6 +609,8 @@ AtlasScriptInterface_impl::AtlasScriptInterface_impl()
| JSOPTION_XML // "ECMAScript for XML support: parse <!-- --> as a token"
);
JS_SetVersion(m_cx, JSVERSION_LATEST);
m_glob = JS_NewGlobalObject(m_cx, &global_class);
JS_InitStandardClasses(m_cx, m_glob);

View File

@ -80,8 +80,9 @@ public:
bool AddRoot(jsval* ptr);
bool RemoveRoot(jsval* ptr);
private:
JSContext* GetContext();
private:
bool SetValue_(const wxString& name, jsval val);
bool GetValue_(const wxString& name, jsval& ret);
bool CallFunction_(jsval val, const char* name, std::vector<jsval>& args, jsval& ret);

View File

@ -1,121 +0,0 @@
/* 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
* 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 "NewDialog.h"
#include "AtlasScript/ScriptInterface.h"
#include "General/Datafile.h"
enum {
ID_CH_SIZE = wxID_HIGHEST+1,
ID_SP_HEIGHT
};
IMPLEMENT_CLASS(NewDialog, wxDialog)
BEGIN_EVENT_TABLE(NewDialog, wxDialog)
EVT_CHOICE(ID_CH_SIZE, NewDialog::OnSizeChange)
EVT_SPINCTRL(ID_SP_HEIGHT, NewDialog::OnHeightChange)
END_EVENT_TABLE()
NewDialog::NewDialog(wxWindow* parent, const wxString& title, const wxSize& size, ScenarioEditor& scenarioEditor)
: wxDialog(parent, -1, title, wxDefaultPosition, size,
wxCAPTION | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCLOSE_BOX)
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
SetSizer(mainSizer);
m_SelectedSize = 0;
m_BaseHeight = 0;
m_Panel = new wxPanel(this);
mainSizer->Add(m_Panel, wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT, 5));
// Get available map sizes
AtObj sizes(Datafile::ReadList("mapsizes"));
for (AtIter s = sizes["size"]; s.defined(); ++s)
{
if (s["@name"].defined() && s["@tiles"].defined())
{
m_SizeArray.Add(wxString(s["@name"]));
size_t size;
std::wstringstream stream;
stream << (std::wstring)s["@tiles"];
stream >> size;
m_TilesArray.push_back(size);
}
}
// Map size
wxBoxSizer* mapSizeSizer = new wxBoxSizer(wxHORIZONTAL);
mainSizer->Add(mapSizeSizer, wxSizerFlags().Expand().Align(wxALIGN_TOP|wxALIGN_RIGHT).Border(wxALL, 5));
mapSizeSizer->Add(new wxStaticText(this, wxID_ANY, _("Choose map size")), wxSizerFlags().Border(wxRIGHT, 25));
wxChoice* sizeCtrl = new wxChoice(this, ID_CH_SIZE, wxDefaultPosition, wxDefaultSize, m_SizeArray);
sizeCtrl->SetSelection(m_SelectedSize);
mapSizeSizer->Add(sizeCtrl, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
// Base height
wxBoxSizer* baseHeightSizer = new wxBoxSizer(wxHORIZONTAL);
mainSizer->Add(baseHeightSizer, wxSizerFlags().Expand().Align(wxALIGN_TOP|wxALIGN_RIGHT).Border(wxALL, 5));
baseHeightSizer->Add(new wxStaticText(this, wxID_ANY, _("Choose base height")), wxSizerFlags().Border(wxRIGHT, 25));
wxSpinCtrl* heightCtrl = new wxSpinCtrl(this, ID_SP_HEIGHT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 65535);
baseHeightSizer->Add(heightCtrl, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
// Map base terrain
wxBoxSizer* mapTerrainSizer = new wxBoxSizer(wxVERTICAL);
mainSizer->Add(mapTerrainSizer, wxSizerFlags().Proportion(1).Expand().Border(wxALL, 5));
mapTerrainSizer->Add(new wxStaticText(this, wxID_ANY, _("Choose map terrain")), wxSizerFlags().Border(wxRIGHT, 25));
wxPanel* terrainPanel = scenarioEditor.GetScriptInterface().LoadScriptAsPanel(_T("terrainpreview"), this);
mapTerrainSizer->Add(terrainPanel, wxSizerFlags().Proportion(1).Expand().Align(wxALIGN_BOTTOM|wxALIGN_RIGHT).Border(wxALL, 5));
// OK/Cancel buttons
wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
mainSizer->Add(buttonSizer, wxSizerFlags().Expand().Align(wxALIGN_RIGHT).Border(wxALL, 5));
buttonSizer->Add(new wxButton(this, wxID_OK, _("OK")), wxSizerFlags().Border(wxRIGHT, 25));
buttonSizer->Add(new wxButton(this, wxID_CANCEL, _("Cancel")), wxSizerFlags().Border(wxRIGHT, 5));
}
void NewDialog::OnSizeChange(wxCommandEvent& event)
{
m_SelectedSize = (size_t)event.GetSelection();
}
void NewDialog::OnHeightChange(wxSpinEvent& event)
{
m_BaseHeight = (size_t)event.GetSelection();
}
size_t NewDialog::GetSelectedSize()
{
return m_TilesArray[m_SelectedSize];
}
size_t NewDialog::GetBaseHeight()
{
return m_BaseHeight;
}

View File

@ -1,53 +0,0 @@
/* 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
* 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_NEWDIALOG
#define INCLUDED_NEWDIALOG
#include "ScenarioEditor/ScenarioEditor.h"
#include "wx/dialog.h"
#include "wx/spinctrl.h"
class NewDialog : public wxDialog
{
DECLARE_CLASS(NewDialog)
DECLARE_EVENT_TABLE()
public:
NewDialog(wxWindow* parent, const wxString& title, const wxSize& size, ScenarioEditor& scenarioEditor);
virtual ~NewDialog() {}
size_t GetSelectedSize();
size_t GetBaseHeight();
protected:
wxPanel* m_Panel;
private:
void OnSizeChange(wxCommandEvent& event);
void OnHeightChange(wxSpinEvent& event);
size_t m_SelectedSize;
size_t m_BaseHeight;
wxArrayString m_SizeArray;
std::vector<size_t> m_TilesArray;
};
#endif // INCLUDED_NEWDIALOG

View File

@ -34,7 +34,6 @@
#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"
@ -590,12 +589,12 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
// a valid map loaded)
POST_MESSAGE(LoadMap, (_T("_default")));
// Select the initial sidebar (after the map has loaded)
m_SectionLayout.SelectPage(_T("MapSidebar"));
// 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));
// Set up a timer to make sure tool-updates happen frequently (in addition
@ -682,54 +681,11 @@ 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 tiles = dlg.GetSelectedSize();
size_t height = dlg.GetBaseHeight();
// Get terrain texture
// TODO: Support choosing multiple textures
std::vector<wxString> textures;
textures.push_back(g_SelectedTexture);
// TODO: This seems like a nasty way to do this
std::string settings;
m_ScriptInterface.SetValue(_T("Atlas.State.mapSettings.settings.Size"), tiles);
m_ScriptInterface.SetValue(_T("Atlas.State.mapSettings.settings.Seed"), 0);
m_ScriptInterface.SetValue(_T("Atlas.State.mapSettings.settings.BaseTerrain"), textures);
m_ScriptInterface.SetValue(_T("Atlas.State.mapSettings.settings.BaseHeight"), height);
m_ScriptInterface.Eval(_T("JSON.stringify(Atlas.State.mapSettings.settings)"), settings);
// Generate map
qGenerateMap qry(L"blank.js", settings);
// 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()"));
}
if (wxMessageBox(_("Discard current map and start blank new map?"), _("New map"), wxOK|wxCANCEL|wxICON_QUESTION, this) == wxOK)
OpenFile(_T("_default"), _T(""));
}
void ScenarioEditor::OpenFile(const wxString& name)
void ScenarioEditor::OpenFile(const wxString& name, const wxString& filename)
{
wxBusyInfo busy(_("Loading map"));
wxBusyCursor busyc;
@ -744,14 +700,13 @@ void ScenarioEditor::OpenFile(const wxString& name)
POST_MESSAGE(LoadMap, (map));
SetOpenFilename(name);
SetOpenFilename(filename);
// Wait for it to load, while the wxBusyInfo is telling the user that we're doing that
qPing qry;
qry.Post();
// Notify UI scripts that map settings have been loaded
m_ScriptInterface.Eval(_T("Atlas.State.mapSettings.notifyObservers()"));
NotifyOnMapReload();
// TODO: Make this a non-undoable command
}
@ -768,7 +723,7 @@ void ScenarioEditor::OnOpen(wxCommandEvent& WXUNUSED(event))
wxString cwd = wxFileName::GetCwd();
if (dlg.ShowModal() == wxID_OK)
OpenFile(dlg.GetFilename());
OpenFile(dlg.GetFilename(), dlg.GetFilename());
wxCHECK_RET(cwd == wxFileName::GetCwd(), _T("cwd changed"));
// paranoia - MSDN says OFN_NOCHANGEDIR (used when we don't give wxCHANGE_DIR)
@ -782,7 +737,7 @@ void ScenarioEditor::OnMRUFile(wxCommandEvent& event)
wxString filename(m_FileHistory.GetHistoryFile(event.GetId() - wxID_FILE1));
if (filename.Len())
{
OpenFile(filename);
OpenFile(filename, filename);
}
else
{ //Remove from MRU
@ -807,22 +762,11 @@ 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();
}
}
@ -840,18 +784,6 @@ 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));
@ -859,6 +791,7 @@ void ScenarioEditor::OnSaveAs(wxCommandEvent& WXUNUSED(event))
SetOpenFilename(dlg.GetFilename());
// Wait for it to finish saving
qPing qry;
qry.Post();
}
}
@ -874,6 +807,11 @@ void ScenarioEditor::SetOpenFilename(const wxString& filename)
m_FileHistory.AddFileToHistory(filename);
}
void ScenarioEditor::NotifyOnMapReload()
{
m_SectionLayout.OnMapReload();
}
//////////////////////////////////////////////////////////////////////////
void ScenarioEditor::OnWireframe(wxCommandEvent& event)

View File

@ -54,7 +54,9 @@ public:
void OnRenderPath(wxCommandEvent& event);
void OnDumpState(wxCommandEvent& event);
void OpenFile(const wxString& name);
void OpenFile(const wxString& name, const wxString& filename);
void NotifyOnMapReload();
static AtlasWindowCommandProc& GetCommandProc();

View File

@ -131,10 +131,6 @@ public:
sidebar->Show(false);
// If this is the first page, make it selected by default
if (m_Pages.size() == 1)
SetSelection(0);
return true;
}
@ -186,6 +182,12 @@ public:
}
}
void OnMapReload()
{
for (size_t i = 0; i < m_Pages.size(); ++i)
m_Pages[i].bar->OnMapReload();
}
protected:
void OnPageChanged(SidebarPage oldPage, SidebarPage newPage)
@ -315,8 +317,7 @@ void SectionLayout::Build(ScenarioEditor& scenarioEditor)
m_SidebarBook->AddPage(sidebar, icon, tooltip); \
m_PageMappings.insert(std::make_pair(name, (int)m_SidebarBook->GetPageCount()-1));
ADD_SIDEBAR_SCRIPT(_T("map"), _T("map.png"), _("Map"));
// ADD_SIDEBAR(MapSidebar, _T("map.png"), _("Map"));
ADD_SIDEBAR(MapSidebar, _T("map.png"), _("Map"));
ADD_SIDEBAR(TerrainSidebar, _T("terrain.png"), _("Terrain"));
ADD_SIDEBAR(ObjectSidebar, _T("object.png"), _("Object"));
ADD_SIDEBAR(EnvironmentSidebar, _T("environment.png"), _("Environment"));
@ -339,3 +340,8 @@ void SectionLayout::SelectPage(const wxString& classname)
if (it != m_PageMappings.end())
m_SidebarBook->SetSelection(it->second);
}
void SectionLayout::OnMapReload()
{
m_SidebarBook->OnMapReload();
}

View File

@ -39,6 +39,8 @@ public:
void SelectPage(const wxString& classname);
void OnMapReload();
private:
SidebarBook* m_SidebarBook;
wxWindow* m_Canvas;

View File

@ -30,6 +30,8 @@ public:
wxWindow* GetBottomBar() { return m_BottomBar; }
virtual void OnMapReload() {}
protected:
ScenarioEditor& m_ScenarioEditor;

View File

@ -258,5 +258,13 @@ void EnvironmentSidebar::OnFirstDisplay()
g_EnvironmentSettings.NotifyObservers();
// TODO: reupdate everything when loading a new map...
}
void EnvironmentSidebar::OnMapReload()
{
AtlasMessage::qGetEnvironmentSettings qry_env;
qry_env.Post();
g_EnvironmentSettings = qry_env.settings;
g_EnvironmentSettings.NotifyObservers();
}

View File

@ -26,6 +26,8 @@ class EnvironmentSidebar : public Sidebar
public:
EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
virtual void OnMapReload();
protected:
virtual void OnFirstDisplay();

View File

@ -21,15 +21,28 @@
#include "General/Datafile.h"
#include "ScenarioEditor/Tools/Common/Tools.h"
#include "ScenarioEditor/ScenarioEditor.h"
#include "AtlasScript/ScriptInterface.h"
#include "GameInterface/Messages.h"
#include "wx/busyinfo.h"
#include "wx/filename.h"
enum
{
ID_GenerateMap,
ID_GenerateRMS,
ID_MapName,
ID_MapDescription,
ID_MapReveal,
ID_MapType,
ID_MapNumPlayers,
ID_MapKW_Demo,
ID_MapKW_Hidden,
ID_RandomScript,
ID_RandomSize,
ID_RandomSeed,
ID_RandomReseed,
ID_RandomGenerate,
ID_SimPlay,
ID_SimFast,
ID_SimSlow,
@ -47,20 +60,303 @@ enum
};
bool IsPlaying(int s) { return (s == SimPlaying || s == SimPlayingFast || s == SimPlayingSlow); }
// Helper function for adding tooltips
static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
{
window->SetToolTip(tip);
return window;
}
// Helper class for storing AtObjs
class AtObjClientData : public wxClientData
{
public:
AtObjClientData(const AtObj& obj) : obj(obj) {}
virtual ~AtObjClientData() {}
AtObj GetValue() { return obj; }
private:
AtObj obj;
};
// TODO: Some of these helper things should be moved out of this file
// and into shared locations
class MapSettings : public wxPanel
{
public:
MapSettings(wxWindow* parent, ScenarioEditor& scenarioEditor);
void CreateWidgets();
void ReadFromEngine();
AtObj UpdateSettingsObject();
private:
void SendToEngine();
void OnEdit(wxCommandEvent& WXUNUSED(evt))
{
SendToEngine();
}
void OnEditSpin(wxSpinEvent& WXUNUSED(evt))
{
SendToEngine();
}
static const size_t MAX_NUM_PLAYERS = 8;
AtObj m_MapSettings;
std::set<std::wstring> m_MapSettingsKeywords;
std::vector<wxChoice*> m_PlayerCivChoices;
ScenarioEditor& m_ScenarioEditor;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(MapSettings, wxPanel)
EVT_TEXT(ID_MapName, MapSettings::OnEdit)
EVT_TEXT(ID_MapDescription, MapSettings::OnEdit)
EVT_CHECKBOX(wxID_ANY, MapSettings::OnEdit)
EVT_CHOICE(wxID_ANY, MapSettings::OnEdit)
EVT_SPINCTRL(ID_MapNumPlayers, MapSettings::OnEditSpin)
END_EVENT_TABLE();
MapSettings::MapSettings(wxWindow* parent, ScenarioEditor& scenarioEditor)
: wxPanel(parent, wxID_ANY), m_ScenarioEditor(scenarioEditor)
{
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Map settings"));
SetSizer(sizer);
}
void MapSettings::CreateWidgets()
{
wxSizer* sizer = GetSizer();
wxBoxSizer* nameSizer = new wxBoxSizer(wxHORIZONTAL);
nameSizer->Add(new wxStaticText(this, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
nameSizer->Add(8, 0);
nameSizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapName),
_("Displayed name of the map")), wxSizerFlags().Proportion(1));
sizer->Add(nameSizer, wxSizerFlags().Expand());
sizer->Add(0, 2);
sizer->Add(new wxStaticText(this, wxID_ANY, _("Description")));
sizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapDescription, wxEmptyString, wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE),
_("Short description used on the map selection screen")), wxSizerFlags().Expand());
wxArrayString gameTypes;
gameTypes.Add(_T("conquest"));
gameTypes.Add(_T("endless"));
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2);
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Reveal map")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(new wxCheckBox(this, ID_MapReveal, wxEmptyString));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Game type")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(new wxChoice(this, ID_MapType, wxDefaultPosition, wxDefaultSize, gameTypes));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Num players")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
wxSpinCtrl* numPlayersSpin = new wxSpinCtrl(this, ID_MapNumPlayers, wxEmptyString, wxDefaultPosition, wxSize(40, -1));
numPlayersSpin->SetRange(1, MAX_NUM_PLAYERS);
numPlayersSpin->SetValue(MAX_NUM_PLAYERS);
gridSizer->Add(numPlayersSpin);
sizer->Add(gridSizer);
wxArrayString civNames;
wxArrayString civCodes;
AtlasMessage::qGetCivData qryCiv;
qryCiv.Post();
std::vector<std::string> civData = *qryCiv.data;
for (size_t i = 0; i < civData.size(); ++i)
{
AtObj civ = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), civData[i]);
civNames.Add(wxString(civ["Name"]));
civCodes.Add(wxString(civ["Code"]));
}
wxCollapsiblePane* playersPane = new wxCollapsiblePane(this, wxID_ANY, _("Player settings"), wxDefaultPosition, wxDefaultSize, wxCP_NO_TLW_RESIZE);
wxFlexGridSizer* playersPaneSizer = new wxFlexGridSizer(2);
playersPaneSizer->Add(new wxStaticText(playersPane->GetPane(), wxID_ANY, _T("")));
playersPaneSizer->Add(new wxStaticText(playersPane->GetPane(), wxID_ANY, _("Civ")));
for (size_t i = 0; i < MAX_NUM_PLAYERS; ++i)
{
wxString idStr;
idStr << (i+1);
playersPaneSizer->Add(new wxStaticText(playersPane->GetPane(), wxID_ANY, idStr), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
wxChoice* civChoice = new wxChoice(playersPane->GetPane(), wxID_ANY);
for (size_t j = 0; j < civNames.Count(); ++j)
civChoice->Append(civNames[j], new wxStringClientData(civCodes[j]));
m_PlayerCivChoices.push_back(civChoice);
playersPaneSizer->Add(civChoice);
// TODO: Team
// TODO: Resources?
}
playersPane->GetPane()->SetSizer(playersPaneSizer);
sizer->Add(playersPane, wxSizerFlags().Expand());
wxStaticBoxSizer* keywordsSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Keywords"));
wxFlexGridSizer* kwGridSizer = new wxFlexGridSizer(2);
kwGridSizer->Add(new wxStaticText(this, wxID_ANY, _("Demo")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
kwGridSizer->Add(new wxCheckBox(this, ID_MapKW_Demo, wxEmptyString));
kwGridSizer->Add(new wxStaticText(this, wxID_ANY, _("Hidden")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
kwGridSizer->Add(new wxCheckBox(this, ID_MapKW_Hidden, wxEmptyString));
keywordsSizer->Add(kwGridSizer);
sizer->Add(keywordsSizer, wxSizerFlags().Expand());
}
void MapSettings::ReadFromEngine()
{
AtlasMessage::qGetMapSettings qry;
qry.Post();
m_MapSettings = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qry.settings);
m_MapSettingsKeywords.clear();
for (AtIter keyword = m_MapSettings["Keywords"]["item"]; keyword.defined(); ++keyword)
m_MapSettingsKeywords.insert(std::wstring(keyword));
wxDynamicCast(FindWindow(ID_MapName), wxTextCtrl)->ChangeValue(wxString(m_MapSettings["Name"]));
wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->ChangeValue(wxString(m_MapSettings["Description"]));
wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->SetValue(wxString(m_MapSettings["RevealMap"]) == L"true");
wxDynamicCast(FindWindow(ID_MapType), wxChoice)->SetStringSelection(wxString(m_MapSettings["GameType"]));
size_t numPlayers = m_MapSettings["PlayerData"]["item"].count();
wxDynamicCast(FindWindow(ID_MapNumPlayers), wxSpinCtrl)->SetValue(numPlayers);
AtIter player = m_MapSettings["PlayerData"]["item"];
for (size_t i = 0; i < numPlayers && i < MAX_NUM_PLAYERS; ++i, ++player)
{
wxChoice* choice = m_PlayerCivChoices[i];
choice->Enable(true);
wxString civCode(player["Civ"]);
for (size_t j = 0; j < choice->GetCount(); ++j)
{
wxStringClientData* str = dynamic_cast<wxStringClientData*>(choice->GetClientObject(j));
if (str->GetData() == civCode)
{
choice->SetSelection(j);
break;
}
}
}
for (size_t i = numPlayers; i < MAX_NUM_PLAYERS; ++i)
{
wxChoice* choice = m_PlayerCivChoices[i];
choice->SetSelection(0);
choice->Enable(false);
}
wxDynamicCast(FindWindow(ID_MapKW_Demo), wxCheckBox)->SetValue(m_MapSettingsKeywords.count(L"demo") != 0);
wxDynamicCast(FindWindow(ID_MapKW_Hidden), wxCheckBox)->SetValue(m_MapSettingsKeywords.count(L"hidden") != 0);
}
AtObj MapSettings::UpdateSettingsObject()
{
m_MapSettings.set("Name", wxDynamicCast(FindWindow(ID_MapName), wxTextCtrl)->GetValue());
m_MapSettings.set("Description", wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->GetValue());
m_MapSettings.setBool("RevealMap", wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->GetValue());
m_MapSettings.set("MapType", wxDynamicCast(FindWindow(ID_MapType), wxChoice)->GetStringSelection());
AtIter oldPlayer = m_MapSettings["PlayerData"]["item"];
AtObj players;
players.set("@array", L"");
size_t numPlayers = (size_t)wxDynamicCast(FindWindow(ID_MapNumPlayers), wxSpinCtrl)->GetValue();
for (size_t i = 0; i < numPlayers && i < MAX_NUM_PLAYERS; ++i)
{
wxChoice* choice = m_PlayerCivChoices[i];
choice->Enable(true);
AtObj player = *oldPlayer;
if (choice->GetSelection() >= 0)
{
wxStringClientData* str = dynamic_cast<wxStringClientData*>(choice->GetClientObject(choice->GetSelection()));
player.set("Civ", str->GetData());
}
players.add("item", player);
if (oldPlayer.defined())
++oldPlayer;
}
for (size_t i = numPlayers; i < MAX_NUM_PLAYERS; ++i)
{
wxChoice* choice = m_PlayerCivChoices[i];
choice->Enable(false);
}
m_MapSettings.set("PlayerData", players);
if (wxDynamicCast(FindWindow(ID_MapKW_Demo), wxCheckBox)->GetValue())
m_MapSettingsKeywords.insert(L"demo");
else
m_MapSettingsKeywords.erase(L"demo");
if (wxDynamicCast(FindWindow(ID_MapKW_Hidden), wxCheckBox)->GetValue())
m_MapSettingsKeywords.insert(L"hidden");
else
m_MapSettingsKeywords.erase(L"hidden");
AtObj keywords;
keywords.set("@array", L"");
for (std::set<std::wstring>::iterator it = m_MapSettingsKeywords.begin(); it != m_MapSettingsKeywords.end(); ++it)
keywords.add("item", it->c_str());
m_MapSettings.set("Keywords", keywords);
return m_MapSettings;
}
void MapSettings::SendToEngine()
{
UpdateSettingsObject();
std::string json = AtlasObject::SaveToJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), m_MapSettings);
// TODO: would be nice if we supported undo for settings changes
POST_MESSAGE(SetMapSettings, (json));
}
MapSidebar::MapSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer), m_SimState(SimInactive)
{
// TODO: Less ugliness
// TODO: Intercept arrow keys and send them to the GL window
m_MainSizer->Add(new wxButton(this, ID_GenerateMap, _("Generate empty map")));
m_MapSettings = new MapSettings(this, m_ScenarioEditor);
m_MainSizer->Add(m_MapSettings, wxSizerFlags().Expand());
{
wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
m_RMSText = new wxTextCtrl(this, wxID_ANY, _T("cantabrian_highlands"));
sizer->Add(m_RMSText);
sizer->Add(new wxButton(this, ID_GenerateRMS, _("Generate RMS")));
m_MainSizer->Add(sizer);
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Random map"));
sizer->Add(new wxChoice(this, ID_RandomScript), wxSizerFlags().Expand());
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2);
gridSizer->AddGrowableCol(1);
wxChoice* sizeChoice = new wxChoice(this, ID_RandomSize);
AtObj sizes(Datafile::ReadList("mapsizes"));
for (AtIter s = sizes["size"]; s.defined(); ++s)
{
long tiles = 0;
wxString(s["@tiles"]).ToLong(&tiles);
sizeChoice->Append(wxString(s["@name"]), (void*)(intptr_t)tiles);
}
sizeChoice->SetSelection(0);
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Map size")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(sizeChoice, wxSizerFlags().Expand());
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Random seed")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
wxBoxSizer* seedSizer = new wxBoxSizer(wxHORIZONTAL);
seedSizer->Add(new wxTextCtrl(this, ID_RandomSeed, _T("0")), wxSizerFlags(1).Expand());
seedSizer->Add(new wxButton(this, ID_RandomReseed, _("R"), wxDefaultPosition, wxSize(24, -1)));
gridSizer->Add(seedSizer, wxSizerFlags().Expand());
sizer->Add(gridSizer, wxSizerFlags().Expand());
sizer->Add(new wxButton(this, ID_RandomGenerate, _("Generate map")), wxSizerFlags().Expand());
m_MainSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 16));
}
{
@ -75,25 +371,38 @@ MapSidebar::MapSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContaine
}
}
void MapSidebar::GenerateMap(wxCommandEvent& WXUNUSED(event))
void MapSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
{
// qGenerateMap qry();
// qry.Post();
// POST_MESSAGE(GenerateMap, (9));
// Toggling the collapsing doesn't seem to update the sidebar layout
// automatically, so do it explicitly here
Layout();
}
void MapSidebar::GenerateRMS(wxCommandEvent& WXUNUSED(event))
void MapSidebar::OnFirstDisplay()
{
wxChar* argv[] = { _T("rmgen.exe"), 0, _T("_atlasrm"), 0 };
wxString scriptName = m_RMSText->GetValue();
argv[1] = const_cast<wxChar*>(scriptName.c_str());
m_MapSettings->CreateWidgets();
m_MapSettings->ReadFromEngine();
wxString cwd = wxFileName::GetCwd();
wxFileName::SetCwd(Datafile::GetDataDirectory());
wxExecute(argv, wxEXEC_SYNC);
wxFileName::SetCwd(cwd);
// Load the RMS script list:
POST_MESSAGE(LoadMap, (L"_atlasrm.pmp"));
AtlasMessage::qGetRMSData qry;
qry.Post();
std::vector<std::string> scripts = *qry.data;
wxChoice* scriptChoice = wxDynamicCast(FindWindow(ID_RandomScript), wxChoice);
scriptChoice->Clear();
for (size_t i = 0; i < scripts.size(); ++i)
{
AtObj data = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), scripts[i]);
wxString name(data["settings"]["Name"]);
scriptChoice->Append(name, new AtObjClientData(*data["settings"]));
}
scriptChoice->SetSelection(0);
}
void MapSidebar::OnMapReload()
{
m_MapSettings->ReadFromEngine();
}
void MapSidebar::UpdateSimButtons()
@ -139,6 +448,7 @@ void MapSidebar::OnSimPlay(wxCommandEvent& event)
if (m_SimState == SimInactive)
{
POST_MESSAGE(SimStateSave, (L"default"));
POST_MESSAGE(GuiSwitchPage, (L"page_session.xml"));
POST_MESSAGE(SimPlay, (speed));
m_SimState = newState;
}
@ -166,22 +476,77 @@ void MapSidebar::OnSimReset(wxCommandEvent& WXUNUSED(event))
{
POST_MESSAGE(SimPlay, (0.f));
POST_MESSAGE(SimStateRestore, (L"default"));
POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
m_SimState = SimInactive;
}
else if (m_SimState == SimPaused)
{
POST_MESSAGE(SimStateRestore, (L"default"));
POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
m_SimState = SimInactive;
}
UpdateSimButtons();
}
void MapSidebar::OnRandomReseed(wxCommandEvent& WXUNUSED(evt))
{
// Pick a shortish randomish value
wxString seed;
seed << (int)floor((rand() / (float)RAND_MAX) * 10000.f);
wxDynamicCast(FindWindow(ID_RandomSeed), wxTextCtrl)->SetValue(seed);
}
void MapSidebar::OnRandomGenerate(wxCommandEvent& WXUNUSED(evt))
{
wxChoice* scriptChoice = wxDynamicCast(FindWindow(ID_RandomScript), wxChoice);
if (scriptChoice->GetSelection() < 0)
return;
// TODO: this settings thing seems a bit of a mess,
// since it's mixing data from three different sources
AtObj settings = m_MapSettings->UpdateSettingsObject();
AtObj scriptSettings = dynamic_cast<AtObjClientData*>(scriptChoice->GetClientObject(scriptChoice->GetSelection()))->GetValue();
settings.addOverlay(scriptSettings);
wxChoice* sizeChoice = wxDynamicCast(FindWindow(ID_RandomSize), wxChoice);
wxString size;
size << (intptr_t)sizeChoice->GetClientData(sizeChoice->GetSelection());
AtObj sizeObj;
sizeObj.setString(size);
sizeObj.set("@number", L"");
settings.set("Size", sizeObj);
AtObj seedObj;
seedObj.setString(wxDynamicCast(FindWindow(ID_RandomSeed), wxTextCtrl)->GetValue());
seedObj.set("@number", L"");
settings.set("Seed", seedObj);
std::string json = AtlasObject::SaveToJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), settings);
wxBusyInfo(_("Generating map"));
wxString scriptName(settings["Script"]);
AtlasMessage::qGenerateMap qry(scriptName.c_str(), json);
qry.Post();
if (qry.status < 0)
wxLogError(_("Random map script '%ls' failed"), scriptName.c_str());
m_ScenarioEditor.NotifyOnMapReload();
}
BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
EVT_BUTTON(ID_GenerateMap, MapSidebar::GenerateMap)
EVT_BUTTON(ID_GenerateRMS, MapSidebar::GenerateRMS)
EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, MapSidebar::OnCollapse)
EVT_BUTTON(ID_SimPlay, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimFast, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimSlow, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimPause, MapSidebar::OnSimPause)
EVT_BUTTON(ID_SimReset, MapSidebar::OnSimReset)
EVT_BUTTON(ID_RandomReseed, MapSidebar::OnRandomReseed)
EVT_BUTTON(ID_RandomGenerate, MapSidebar::OnRandomGenerate)
END_EVENT_TABLE();

View File

@ -17,20 +17,29 @@
#include "../Common/Sidebar.h"
#include "wx/collpane.h"
class MapSettings;
class MapSidebar : public Sidebar
{
public:
MapSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
virtual void OnMapReload();
protected:
virtual void OnFirstDisplay();
private:
void GenerateMap(wxCommandEvent& event);
void GenerateRMS(wxCommandEvent& event);
MapSettings* m_MapSettings;
wxTextCtrl* m_RMSText;
void OnSimPlay(wxCommandEvent& event);
void OnSimPause(wxCommandEvent& event);
void OnSimReset(wxCommandEvent& event);
void OnCollapse(wxCollapsiblePaneEvent& evt);
void OnSimPlay(wxCommandEvent& evt);
void OnSimPause(wxCommandEvent& evt);
void OnSimReset(wxCommandEvent& evt);
void OnRandomReseed(wxCommandEvent& evt);
void OnRandomGenerate(wxCommandEvent& evt);
void UpdateSimButtons();
int m_SimState;

View File

@ -46,7 +46,7 @@ enum
};
// Helper function for adding tooltips
wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
{
window->SetToolTip(tip);
return window;

View File

@ -49,18 +49,29 @@ private:
enum
{
ID_Passability = 1,
ID_ShowPriorities
ID_ShowPriorities,
ID_ResizeMap
};
TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer) :
Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
{
{
wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools"));
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Modify"), _T("AlterElevation")), wxSizerFlags().Proportion(1));
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Smooth"), _T("SmoothElevation")), wxSizerFlags().Proportion(1));
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Flatten"), _T("FlattenElevation")), wxSizerFlags().Proportion(1));
sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Paint"), _T("PaintTerrain")), wxSizerFlags().Proportion(1));
wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Elevation tools"));
wxSizer* gridSizer = new wxGridSizer(3);
gridSizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Modify"), _T("AlterElevation")), wxSizerFlags().Expand());
gridSizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Smooth"), _T("SmoothElevation")), wxSizerFlags().Expand());
gridSizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Flatten"), _T("FlattenElevation")), wxSizerFlags().Expand());
sizer->Add(gridSizer, wxSizerFlags().Expand());
m_MainSizer->Add(sizer, wxSizerFlags().Expand());
}
{
wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Texture tools"));
wxSizer* gridSizer = new wxGridSizer(3);
gridSizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Paint"), _T("PaintTerrain")), wxSizerFlags().Expand());
gridSizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Replace"), _T("ReplaceTerrain")), wxSizerFlags().Expand());
sizer->Add(gridSizer, wxSizerFlags().Expand());
m_MainSizer->Add(sizer, wxSizerFlags().Expand());
}
@ -88,6 +99,12 @@ TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebar
visSizer->Add(new wxCheckBox(this, ID_ShowPriorities, _("")));
}
{
wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Misc tools"));
sizer->Add(new wxButton(this, ID_ResizeMap, _("Resize map")), wxSizerFlags().Expand());
m_MainSizer->Add(sizer, wxSizerFlags().Expand());
}
m_BottomBar = new TerrainBottomBar(scenarioEditor, bottomBarContainer);
}
@ -115,9 +132,36 @@ void TerrainSidebar::OnShowPriorities(wxCommandEvent& evt)
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::GAME, L"priorities", evt.IsChecked()));
}
void TerrainSidebar::OnResizeMap(wxCommandEvent& WXUNUSED(evt))
{
wxArrayString sizeNames;
std::vector<size_t> sizeTiles;
AtObj sizes(Datafile::ReadList("mapsizes"));
for (AtIter s = sizes["size"]; s.defined(); ++s)
{
long tiles = 0;
wxString(s["@tiles"]).ToLong(&tiles);
sizeNames.Add(wxString(s["@name"]));
sizeTiles.push_back((size_t)tiles);
}
// TODO: set default based on current map size
wxSingleChoiceDialog dlg(this, _("Select new map size. WARNING: This probably only works reliably on blank maps, and cannot be undone."),
_("Resize map"), sizeNames);
if (dlg.ShowModal() != wxID_OK)
return;
size_t tiles = sizeTiles.at(dlg.GetSelection());
POST_COMMAND(ResizeMap, (tiles));
}
BEGIN_EVENT_TABLE(TerrainSidebar, Sidebar)
EVT_CHOICE(ID_Passability, TerrainSidebar::OnPassabilityChoice)
EVT_CHECKBOX(ID_ShowPriorities, TerrainSidebar::OnShowPriorities)
EVT_BUTTON(ID_ResizeMap, TerrainSidebar::OnResizeMap)
END_EVENT_TABLE();
//////////////////////////////////////////////////////////////////////////
@ -232,7 +276,11 @@ public:
button->SetBackgroundColour(wxColour(255, 255, 0));
m_LastTerrainSelection = button;
m_ScenarioEditor.GetToolManager().SetCurrentTool(L"PaintTerrain");
// Slight hack: Default to Paint mode because that's probably what the user wanted
// when they selected a terrain; unless already explicitly in Replace mode, because
// then the user probably wanted that instead
if (m_ScenarioEditor.GetToolManager().GetCurrentToolName() != _T("ReplaceTerrain"))
m_ScenarioEditor.GetToolManager().SetCurrentTool(_T("PaintTerrain"));
}
void OnSize(wxSizeEvent& evt)

View File

@ -28,6 +28,7 @@ protected:
private:
void OnPassabilityChoice(wxCommandEvent& evt);
void OnShowPriorities(wxCommandEvent& evt);
void OnResizeMap(wxCommandEvent& evt);
wxChoice* m_PassabilityChoice;

View File

@ -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
@ -114,6 +114,18 @@ void Brush::SetStrength(float strength)
m_Strength = strength;
}
void Brush::SetCircle(int size)
{
m_Shape = CIRCLE;
m_Size = size;
}
void Brush::SetSquare(int size)
{
m_Shape = SQUARE;
m_Size = size;
}
//////////////////////////////////////////////////////////////////////////
class BrushShapeCtrl : public wxRadioBox

View File

@ -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
@ -37,6 +37,9 @@ public:
int GetHeight() const;
std::vector<float> GetData() const;
void SetCircle(int size);
void SetSquare(int size);
float GetStrength() const;
void SetStrength(float strength);

View File

@ -53,6 +53,11 @@ ObservablePtr<ITool>& ToolManager::GetCurrentTool()
return m->CurrentTool;
}
wxString ToolManager::GetCurrentToolName()
{
return m->CurrentTool->GetClassInfo()->GetClassName();
}
void SetActive(bool active, const wxString& name);
void ToolManager::SetCurrentTool(const wxString& name, void* initData)

View File

@ -46,6 +46,7 @@ public:
ToolManager(ScenarioEditor* scenarioEditor);
~ToolManager();
ObservablePtr<ITool>& GetCurrentTool();
wxString GetCurrentToolName();
void SetCurrentTool(const wxString& name, void* initData = NULL);
private:
ToolManagerImpl* m;

View File

@ -0,0 +1,77 @@
/* 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
* 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 "ScenarioEditor/ScenarioEditor.h"
#include "Common/Tools.h"
#include "Common/Brushes.h"
#include "Common/MiscState.h"
#include "GameInterface/Messages.h"
using AtlasMessage::Position;
class ReplaceTerrain : public StateDrivenTool<ReplaceTerrain>
{
DECLARE_DYNAMIC_CLASS(ReplaceTerrain);
Position m_Pos;
Brush m_Brush;
public:
ReplaceTerrain()
{
m_Brush.SetSquare(2);
SetState(&Waiting);
}
void OnEnable()
{
m_Brush.MakeActive();
}
void OnDisable()
{
POST_MESSAGE(BrushPreview, (false, Position()));
}
struct sWaiting : public State
{
bool OnMouse(ReplaceTerrain* WXUNUSED(obj), wxMouseEvent& evt)
{
if (evt.LeftDown())
{
Position pos(evt.GetPosition());
POST_MESSAGE(BrushPreview, (true, pos));
POST_COMMAND(ReplaceTerrain, (pos, g_SelectedTexture.c_str()));
return true;
}
else if (evt.Moving())
{
POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition())));
return true;
}
else
{
return false;
}
}
}
Waiting;
};
IMPLEMENT_DYNAMIC_CLASS(ReplaceTerrain, StateDrivenTool<ReplaceTerrain>);

View File

@ -19,8 +19,10 @@
#include "MessageHandler.h"
#include "../GameLoop.h"
#include "../CommandProc.h"
#include "graphics/GameView.h"
#include "graphics/LOSTexture.h"
#include "graphics/MapWriter.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
@ -36,6 +38,7 @@
#include "simulation2/components/ICmpPlayerManager.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTerrain.h"
namespace
{
@ -146,4 +149,43 @@ QUERYHANDLER(GetRMSData)
msg->data = g_Game->GetSimulation2()->GetRMSData();
}
BEGIN_COMMAND(ResizeMap)
{
cResizeMap()
{
}
void MakeDirty()
{
CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
if (!cmpTerrain.null())
cmpTerrain->ReloadTerrain();
// The LOS texture won't normally get updated when running Atlas
// (since there's no simulation updates), so explicitly dirty it
g_Game->GetView()->GetLOSTexture().MakeDirty();
}
void Do()
{
Redo();
}
void Undo()
{
// TODO
debug_warn(L"Can't undo ResizeMap");
}
void Redo()
{
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
terrain->Resize(msg->tiles / PATCH_SIZE);
MakeDirty();
}
};
END_COMMAND(ResizeMap)
}

View File

@ -136,17 +136,18 @@ QUERYHANDLER(GetTerrainPassabilityClasses)
//////////////////////////////////////////////////////////////////////////
BEGIN_COMMAND(PaintTerrain)
namespace {
struct TerrainTile
{
struct TerrainTile
{
TerrainTile(CTerrainTextureEntry* t, ssize_t p) : tex(t), priority(p) {}
CTerrainTextureEntry* tex;
ssize_t priority;
};
class TerrainArray : public DeltaArray2D<TerrainTile>
{
public:
};
class TerrainArray : public DeltaArray2D<TerrainTile>
{
public:
void Init()
{
m_Terrain = g_Game->GetWorld()->GetTerrain();
@ -167,6 +168,22 @@ BEGIN_COMMAND(PaintTerrain)
priority = std::max(priority, tile->GetPriority()*priorityScale + 1);
}
CTerrainTextureEntry* GetTexEntry(ssize_t x, ssize_t y)
{
if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
return NULL;
return get(x, y).tex;
}
ssize_t GetPriority(ssize_t x, ssize_t y)
{
if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
return 0;
return get(x, y).priority;
}
void PaintTile(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priority)
{
// Ignore out-of-bounds tiles
@ -176,7 +193,12 @@ BEGIN_COMMAND(PaintTerrain)
set(x,y, TerrainTile(tex, priority));
}
protected:
ssize_t GetTilesPerSide()
{
return m_VertsPerSide-1;
}
protected:
TerrainTile getOld(ssize_t x, ssize_t y)
{
CMiniPatch* mp = m_Terrain->GetTile(x, y);
@ -193,8 +215,12 @@ BEGIN_COMMAND(PaintTerrain)
CTerrain* m_Terrain;
ssize_t m_VertsPerSide;
};
};
}
BEGIN_COMMAND(PaintTerrain)
{
TerrainArray m_TerrainDelta;
ssize_t m_i0, m_j0, m_i1, m_j1;
@ -280,5 +306,78 @@ BEGIN_COMMAND(PaintTerrain)
};
END_COMMAND(PaintTerrain)
//////////////////////////////////////////////////////////////////////////
BEGIN_COMMAND(ReplaceTerrain)
{
TerrainArray m_TerrainDelta;
ssize_t m_i0, m_j0, m_i1, m_j1;
cReplaceTerrain()
{
m_TerrainDelta.Init();
}
void MakeDirty()
{
g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_INDICES);
CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
if (!cmpTerrain.null())
cmpTerrain->MakeDirty(m_i0, m_j0, m_i1, m_j1);
}
void Do()
{
g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace();
ssize_t x0, y0;
g_CurrentBrush.GetBottomLeft(x0, y0);
m_i0 = m_i1 = x0;
m_j0 = m_j1 = y0;
CTerrainTextureEntry* texentry = g_TexMan.FindTexture(CStrW(*msg->texture).ToUTF8());
if (! texentry)
{
debug_warn(L"Can't find texentry"); // TODO: nicer error handling
return;
}
CTerrainTextureEntry* replacedTex = m_TerrainDelta.GetTexEntry(x0, y0);
ssize_t tiles = m_TerrainDelta.GetTilesPerSide();
for (ssize_t j = 0; j < tiles; ++j)
{
for (ssize_t i = 0; i < tiles; ++i)
{
if (m_TerrainDelta.GetTexEntry(i, j) == replacedTex)
{
m_i0 = std::min(m_i0, i);
m_j0 = std::min(m_j0, j);
m_i1 = std::max(m_i1, i+1);
m_j1 = std::max(m_j1, j+1);
m_TerrainDelta.PaintTile(i, j, texentry, m_TerrainDelta.GetPriority(i, j));
}
}
}
MakeDirty();
}
void Undo()
{
m_TerrainDelta.Undo();
MakeDirty();
}
void Redo()
{
m_TerrainDelta.Redo();
MakeDirty();
}
};
END_COMMAND(ReplaceTerrain)
}

View File

@ -161,6 +161,10 @@ QUERY(GetRMSData,
((std::vector<std::string>, data))
);
COMMAND(ResizeMap, NOMERGE,
((int, tiles))
);
//////////////////////////////////////////////////////////////////////////
// Messages for player panel
@ -431,6 +435,11 @@ COMMAND(PaintTerrain, MERGE,
((int, priority)) // ePaintTerrainPriority
);
COMMAND(ReplaceTerrain, NOMERGE,
((Position, pos))
((std::wstring, texture))
);
//////////////////////////////////////////////////////////////////////////
QUERY(PickObject,