diff --git a/build/premake/premake.lua b/build/premake/premake.lua index 301756393c..186838f3e5 100644 --- a/build/premake/premake.lua +++ b/build/premake/premake.lua @@ -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", diff --git a/source/graphics/LOSTexture.cpp b/source/graphics/LOSTexture.cpp index 18833c877c..e4e8deffdd 100644 --- a/source/graphics/LOSTexture.cpp +++ b/source/graphics/LOSTexture.cpp @@ -56,10 +56,13 @@ CLOSTexture::CLOSTexture(CSimulation2& simulation) : CLOSTexture::~CLOSTexture() { if (m_Texture) - { - glDeleteTextures(1, &m_Texture); - m_Texture = 0; - } + 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 cmpTerrain(m_Simulation, SYSTEM_ENTITY); + if (!cmpTerrain.null() && m_MapSize != cmpTerrain->GetVerticesPerSide()) + DeleteTexture(); + } + if (!m_Texture) ConstructTexture(unit); diff --git a/source/graphics/LOSTexture.h b/source/graphics/LOSTexture.h index a84c25e83d..ce572dbce6 100644 --- a/source/graphics/LOSTexture.h +++ b/source/graphics/LOSTexture.h @@ -71,6 +71,7 @@ public: const float* GetMinimapTextureMatrix(); private: + void DeleteTexture(); void ConstructTexture(int unit); void RecomputeTexture(int unit); diff --git a/source/simulation2/components/CCmpPathfinder.cpp b/source/simulation2/components/CCmpPathfinder.cpp index 417967c09c..fe511a9ad8 100644 --- a/source/simulation2/components/CCmpPathfinder.cpp +++ b/source/simulation2/components/CCmpPathfinder.cpp @@ -277,6 +277,14 @@ const Grid& 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) { diff --git a/source/simulation2/components/CCmpPathfinder_Tile.cpp b/source/simulation2/components/CCmpPathfinder_Tile.cpp index 4fe1983036..761d537c50 100644 --- a/source/simulation2/components/CCmpPathfinder_Tile.cpp +++ b/source/simulation2/components/CCmpPathfinder_Tile.cpp @@ -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)) diff --git a/source/simulation2/components/CCmpTerrain.cpp b/source/simulation2/components/CCmpTerrain.cpp index fb9fcafc89..9cadf75435 100644 --- a/source/simulation2/components/CCmpTerrain.cpp +++ b/source/simulation2/components/CCmpTerrain.cpp @@ -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) diff --git a/source/simulation2/components/ICmpTerrain.h b/source/simulation2/components/ICmpTerrain.h index f3f9f475ce..fd5b13e632 100644 --- a/source/simulation2/components/ICmpTerrain.h +++ b/source/simulation2/components/ICmpTerrain.h @@ -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; diff --git a/source/tools/atlas/AtlasObject/AtlasObject.h b/source/tools/atlas/AtlasObject/AtlasObject.h index 9f4a44f9b8..528f2048be 100644 --- a/source/tools/atlas/AtlasObject/AtlasObject.h +++ b/source/tools/atlas/AtlasObject/AtlasObject.h @@ -27,6 +27,8 @@ #include // for wchar_t #include +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 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); } diff --git a/source/tools/atlas/AtlasObject/AtlasObjectImpl.cpp b/source/tools/atlas/AtlasObject/AtlasObjectImpl.cpp index e33dd2660a..88e79e7497 100644 --- a/source/tools/atlas/AtlasObject/AtlasObjectImpl.cpp +++ b/source/tools/atlas/AtlasObject/AtlasObjectImpl.cpp @@ -19,6 +19,7 @@ #include "AtlasObjectImpl.h" #include +#include #define ATSMARTPTR_IMPL(T) \ template<> void AtSmartPtr::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) diff --git a/source/tools/atlas/AtlasObject/AtlasObjectImpl.h b/source/tools/atlas/AtlasObject/AtlasObjectImpl.h index 5885c28089..0ec0cdf4df 100644 --- a/source/tools/atlas/AtlasObject/AtlasObjectImpl.h +++ b/source/tools/atlas/AtlasObject/AtlasObjectImpl.h @@ -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 diff --git a/source/tools/atlas/AtlasObject/AtlasObjectJS.cpp b/source/tools/atlas/AtlasObject/AtlasObjectJS.cpp new file mode 100644 index 0000000000..38a5d572b9 --- /dev/null +++ b/source/tools/atlas/AtlasObject/AtlasObjectJS.cpp @@ -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 . + */ + +#include "AtlasObject.h" +#include "AtlasObjectImpl.h" + +#include "../AtlasScript/ScriptInterface.h" + +#include "wx/log.h" + +#include + +static AtSmartPtr 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(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 ConvertNode(JSContext* cx, jsval node) +{ + AtSmartPtr 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(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(new AtNode()) + )); + } + else if (JSVAL_IS_BOOLEAN(node)) + { + obj->children.insert(AtNode::child_pairtype( + "@boolean", AtSmartPtr(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(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>$i0... + // (This assumes arrays aren't nested) + if (JSVAL_IS_OBJECT(vp) && JS_IsArrayObject(cx, JSVAL_TO_OBJECT(vp))) + { + AtSmartPtr child(new AtNode()); + child->children.insert(AtNode::child_pairtype( + "@array", AtSmartPtr(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(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 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(buf), wxMBConvUTF16(), len*2); + std::string textStr(textWx.ToUTF8().data()); + + static_cast(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(); +} diff --git a/source/tools/atlas/AtlasScript/ScriptInterface.cpp b/source/tools/atlas/AtlasScript/ScriptInterface.cpp index 1ce19f81ce..1cc075397f 100644 --- a/source/tools/atlas/AtlasScript/ScriptInterface.cpp +++ b/source/tools/atlas/AtlasScript/ScriptInterface.cpp @@ -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); diff --git a/source/tools/atlas/AtlasScript/ScriptInterface.h b/source/tools/atlas/AtlasScript/ScriptInterface.h index e0df128a1b..7f5c49da13 100644 --- a/source/tools/atlas/AtlasScript/ScriptInterface.h +++ b/source/tools/atlas/AtlasScript/ScriptInterface.h @@ -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& args, jsval& ret); diff --git a/source/tools/atlas/AtlasUI/CustomControls/NewDialog/NewDialog.cpp b/source/tools/atlas/AtlasUI/CustomControls/NewDialog/NewDialog.cpp deleted file mode 100644 index 71c7e1f355..0000000000 --- a/source/tools/atlas/AtlasUI/CustomControls/NewDialog/NewDialog.cpp +++ /dev/null @@ -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 . - */ - -#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; -} diff --git a/source/tools/atlas/AtlasUI/CustomControls/NewDialog/NewDialog.h b/source/tools/atlas/AtlasUI/CustomControls/NewDialog/NewDialog.h deleted file mode 100644 index a9bdbc8628..0000000000 --- a/source/tools/atlas/AtlasUI/CustomControls/NewDialog/NewDialog.h +++ /dev/null @@ -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 . - */ - -#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 m_TilesArray; - -}; - -#endif // INCLUDED_NEWDIALOG diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index fc36e4f506..144334f774 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -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 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) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h index db9c92989c..bc84c4ee7b 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h @@ -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(); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp index 1f8f002737..44aab7bfa7 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp @@ -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) @@ -314,9 +316,8 @@ void SectionLayout::Build(ScenarioEditor& scenarioEditor) sidebar->GetBottomBar()->Show(false); \ 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(); +} diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.h b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.h index 96fa2f8354..cb8d128d4f 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.h @@ -39,6 +39,8 @@ public: void SelectPage(const wxString& classname); + void OnMapReload(); + private: SidebarBook* m_SidebarBook; wxWindow* m_Canvas; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h index d34d11f66c..95216ad624 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h @@ -30,6 +30,8 @@ public: wxWindow* GetBottomBar() { return m_BottomBar; } + virtual void OnMapReload() {} + protected: ScenarioEditor& m_ScenarioEditor; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp index a975fd6df3..91f2fe6d18 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp @@ -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(); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h index 9aed4a9e1a..f51a4babd3 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h @@ -26,6 +26,8 @@ class EnvironmentSidebar : public Sidebar public: EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer); + virtual void OnMapReload(); + protected: virtual void OnFirstDisplay(); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp index a2282e3e14..d11248b96f 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -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 m_MapSettingsKeywords; + + std::vector 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 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(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(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::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(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 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(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(); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h index 1dc82a64d3..de1996577a 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h @@ -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; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp index f524213719..7bb02f58bf 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp @@ -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; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp index 4b55b9954f..91c72d5ea4 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp @@ -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 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) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h index d9bb46cb4e..eae1d7aa2c 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h @@ -28,6 +28,7 @@ protected: private: void OnPassabilityChoice(wxCommandEvent& evt); void OnShowPriorities(wxCommandEvent& evt); + void OnResizeMap(wxCommandEvent& evt); wxChoice* m_PassabilityChoice; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp index ccbc8027e1..c7a9bb521c 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp @@ -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 diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h index 06d207b27a..cc7a6591b4 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h @@ -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 GetData() const; + void SetCircle(int size); + void SetSquare(int size); + float GetStrength() const; void SetStrength(float strength); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp index d9224f06f5..7327362e4d 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp @@ -53,6 +53,11 @@ ObservablePtr& 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) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h index 5e606d3522..59be5299b4 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h @@ -46,6 +46,7 @@ public: ToolManager(ScenarioEditor* scenarioEditor); ~ToolManager(); ObservablePtr& GetCurrentTool(); + wxString GetCurrentToolName(); void SetCurrentTool(const wxString& name, void* initData = NULL); private: ToolManagerImpl* m; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/ReplaceTerrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/ReplaceTerrain.cpp new file mode 100644 index 0000000000..02856fa7df --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/ReplaceTerrain.cpp @@ -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 . + */ + +#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 +{ + 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); diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp index bad580e492..e743d4f9b1 100644 --- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -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 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) + } diff --git a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp index d1bcdb764b..58fdceb43f 100644 --- a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp @@ -136,65 +136,91 @@ QUERYHANDLER(GetTerrainPassabilityClasses) ////////////////////////////////////////////////////////////////////////// +namespace { + +struct TerrainTile +{ + TerrainTile(CTerrainTextureEntry* t, ssize_t p) : tex(t), priority(p) {} + CTerrainTextureEntry* tex; + ssize_t priority; +}; + +class TerrainArray : public DeltaArray2D +{ +public: + void Init() + { + m_Terrain = g_Game->GetWorld()->GetTerrain(); + m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); + } + + void UpdatePriority(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priorityScale, ssize_t& priority) + { + CMiniPatch* tile = m_Terrain->GetTile(x, y); + if (!tile) + return; // tile was out-of-bounds + + // If this tile matches the current texture, we just want to match its + // priority; otherwise we want to exceed its priority + if (tile->GetTextureEntry() == tex) + priority = std::max(priority, tile->GetPriority()*priorityScale); + else + 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 + if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1)) + return; + + set(x,y, TerrainTile(tex, priority)); + } + + ssize_t GetTilesPerSide() + { + return m_VertsPerSide-1; + } + +protected: + TerrainTile getOld(ssize_t x, ssize_t y) + { + CMiniPatch* mp = m_Terrain->GetTile(x, y); + ENSURE(mp); + return TerrainTile(mp->Tex, mp->Priority); + } + void setNew(ssize_t x, ssize_t y, const TerrainTile& val) + { + CMiniPatch* mp = m_Terrain->GetTile(x, y); + ENSURE(mp); + mp->Tex = val.tex; + mp->Priority = val.priority; + } + + CTerrain* m_Terrain; + ssize_t m_VertsPerSide; +}; + +} + BEGIN_COMMAND(PaintTerrain) { - struct TerrainTile - { - TerrainTile(CTerrainTextureEntry* t, ssize_t p) : tex(t), priority(p) {} - CTerrainTextureEntry* tex; - ssize_t priority; - }; - class TerrainArray : public DeltaArray2D - { - public: - void Init() - { - m_Terrain = g_Game->GetWorld()->GetTerrain(); - m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); - } - - void UpdatePriority(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priorityScale, ssize_t& priority) - { - CMiniPatch* tile = m_Terrain->GetTile(x, y); - if (!tile) - return; // tile was out-of-bounds - - // If this tile matches the current texture, we just want to match its - // priority; otherwise we want to exceed its priority - if (tile->GetTextureEntry() == tex) - priority = std::max(priority, tile->GetPriority()*priorityScale); - else - priority = std::max(priority, tile->GetPriority()*priorityScale + 1); - } - - void PaintTile(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priority) - { - // Ignore out-of-bounds tiles - if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1)) - return; - - set(x,y, TerrainTile(tex, priority)); - } - - protected: - TerrainTile getOld(ssize_t x, ssize_t y) - { - CMiniPatch* mp = m_Terrain->GetTile(x, y); - ENSURE(mp); - return TerrainTile(mp->Tex, mp->Priority); - } - void setNew(ssize_t x, ssize_t y, const TerrainTile& val) - { - CMiniPatch* mp = m_Terrain->GetTile(x, y); - ENSURE(mp); - mp->Tex = val.tex; - mp->Priority = val.priority; - } - - CTerrain* m_Terrain; - ssize_t m_VertsPerSide; - }; - 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 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) + } diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index d57f56cbfc..d995ecc814 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -161,6 +161,10 @@ QUERY(GetRMSData, ((std::vector, 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,