From 281bdf956a646fd8c9944e9a44f867c984792216 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Thu, 19 Mar 2015 11:42:54 -0400 Subject: Map editor can now edit properties for objects (breaks main game build) --- tools/mapedit/src/frame.cpp | 82 +++++++++++- tools/mapedit/src/frame.h | 1 + tools/mapedit/src/map.h | 18 +-- tools/mapedit/src/object.cpp | 304 ++++++++++++++++++++++++++++++++++++------- tools/mapedit/src/object.h | 91 +++++++++++-- tools/mapedit/src/undo.cpp | 74 ++++++++++- tools/mapedit/src/undo.h | 30 ++++- tools/mapedit/src/widget.cpp | 86 ++++++++---- tools/mapedit/src/widget.h | 7 +- tools/mapedit/src/world.cpp | 109 ++++++++++++---- 10 files changed, 666 insertions(+), 136 deletions(-) (limited to 'tools/mapedit') diff --git a/tools/mapedit/src/frame.cpp b/tools/mapedit/src/frame.cpp index 06102f7..aad3294 100644 --- a/tools/mapedit/src/frame.cpp +++ b/tools/mapedit/src/frame.cpp @@ -2,6 +2,7 @@ #include "mapselect_combo.h" #include #include +#include #include #include #include "widget.h" @@ -29,6 +30,7 @@ enum { TOOL_FILE_SAVE, TOOL_MAP_ADD_ROOT, TOOL_MAP_ADD_CHILD, + MAP_EDITOR_WIDGET, MAP_EDITOR_NOTEBOOK, MAP_EDITOR_TREE, MAP_TITLE_TEXTBOX, @@ -45,7 +47,10 @@ enum { UPMAP_TYPE_CHOICE, UPMAP_MAP_CHOICE, DOWNMAP_TYPE_CHOICE, - DOWNMAP_MAP_CHOICE + DOWNMAP_MAP_CHOICE, + ENTITY_EDITOR, + ENTITY_PROPERTY_EDITOR, + PROPERTY_EDITOR }; wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame) @@ -87,6 +92,7 @@ wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame) EVT_COMBOBOX_CLOSEUP(RIGHTMAP_MAP_CHOICE, MapeditFrame::OnSetRightmapMap) EVT_COMBOBOX_CLOSEUP(UPMAP_MAP_CHOICE, MapeditFrame::OnSetUpmapMap) EVT_COMBOBOX_CLOSEUP(DOWNMAP_MAP_CHOICE, MapeditFrame::OnSetDownmapMap) + EVT_COMMAND(wxID_ANY, EVT_MAP_SELECTED_ENTITY, MapeditFrame::OnSelectEntity) wxEND_EVENT_TABLE() MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") @@ -171,7 +177,7 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") tileEditor = new TileWidget(notebook, wxID_ANY, 6, 6, wxPoint(0,0), wxSize(TILE_WIDTH*6*6,TILE_HEIGHT*10*6)); notebook->AddPage(tileEditor, "Tile Chooser", false); - mapEditor = new MapeditWidget(layout3, wxID_ANY, currentMap, tileEditor, wxPoint(0,0), wxSize(GAME_WIDTH*2, GAME_HEIGHT*2)); + mapEditor = new MapeditWidget(layout3, MAP_EDITOR_WIDGET, currentMap, tileEditor, wxPoint(0,0), wxSize(GAME_WIDTH*2, GAME_HEIGHT*2)); mapEditor->frame = this; // Set up property editor @@ -181,7 +187,7 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:"); - startposLabel = new wxStaticText(propertyEditor, wxID_ANY, "Starting Position:"); + startposLabel = new wxStaticText(propertyEditor, PROPERTY_EDITOR, "Starting Position:"); setStartposButton = new wxButton(propertyEditor, SET_STARTPOS_BUTTON, "Set Starting Position"); cancelStartposButton = new wxButton(propertyEditor, CANCEL_STARTPOS_BUTTON, "Cancel"); @@ -264,7 +270,7 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") propertySizer->SetSizeHints(propertyEditor); // Set up entity editor - wxPanel* entityEditor = new wxPanel(notebook, wxID_ANY); + wxPanel* entityEditor = new wxPanel(notebook, ENTITY_EDITOR); notebook->AddPage(entityEditor, "Entity Manager", false); wxStaticText* entityHeader = new wxStaticText(entityEditor, wxID_ANY, "Add Entity"); @@ -275,9 +281,9 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") wxStaticText* entityTypeLabel = new wxStaticText(entityEditor, wxID_ANY, "Entity Type:"); entityTypeBox = new wxChoice(entityEditor, wxID_ANY); - for (auto entry : MapObject::getAllObjects()) + for (auto& entry : MapObject::getAllObjects()) { - entityTypeBox->Append(entry.second->getType(), entry.second.get()); + entityTypeBox->Append(entry.second.getName(), (void*) &entry.second); } addEntityButton = new wxButton(entityEditor, ADD_ENTITY_BUTTON, "Add Entity"); @@ -286,6 +292,8 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") wxStaticText* entityInfoLabel = new wxStaticText(entityEditor, wxID_ANY, "Click and drag an entity to move it.\nRight click an entity to delete it."); + wxPanel* entityPropertyEditor = new wxPanel(entityEditor, ENTITY_PROPERTY_EDITOR); + wxBoxSizer* entitySizer = new wxBoxSizer(wxVERTICAL); entitySizer->Add(entityHeader, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5); wxBoxSizer* entitySizer1 = new wxBoxSizer(wxHORIZONTAL); @@ -298,6 +306,12 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") entitySizer->Add(entitySizer2, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5); entitySizer->Add(new wxStaticLine(entityEditor), 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5); entitySizer->Add(entityInfoLabel, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5); + entitySizer->Add(entityPropertyEditor, 1, wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxALL, 5); + wxBoxSizer* entityPropertySizer = new wxBoxSizer(wxVERTICAL); + entityPropertySizer->Add(new wxStaticLine(entityPropertyEditor), 1, wxEXPAND, 0); + entityPropertyEditor->SetSizer(entityPropertySizer); + entityPropertySizer->SetSizeHints(entityPropertyEditor); + //entitySizer->Add(entityPropertySizer, 1, wxEXPAND, 0); entityEditor->SetSizer(entitySizer); entitySizer->SetSizeHints(entityEditor); @@ -840,6 +854,62 @@ void MapeditFrame::OnSetDownmapMap(wxCommandEvent&) })); } +void MapeditFrame::OnSelectEntity(wxCommandEvent& event) +{ + MapObjectEntry* entry = (MapObjectEntry*) event.GetClientData(); + wxPanel* entityPropertyEditor = (wxPanel*) wxWindow::FindWindowById(ENTITY_PROPERTY_EDITOR, this); + + if (entry == nullptr) + { + entityPropertyEditor->GetSizer()->Clear(); + entityPropertyEditor->DestroyChildren(); + } else { + wxSizer* sizer = entityPropertyEditor->GetSizer(); + for (auto input : entry->getObject().getInputs()) + { + wxStaticText* inputText = new wxStaticText(entityPropertyEditor, wxID_ANY, input.second.name + ":"); + sizer->Add(inputText, 0, wxEXPAND | wxALIGN_LEFT | wxBOTTOM, 0); + + MapObjectEntry::Item& item = entry->getItem(input.first); + + wxWindow* inputObject = nullptr; + switch (input.second.type) + { + case MapObject::Input::Type::Choice: + { + UndoableChoice* thechoice = new UndoableChoice(entityPropertyEditor, wxID_ANY, this, wxDefaultPosition, wxDefaultSize, 0, NULL, 0, VariableChoiceValidator(*world, item), input.second.name); + int selected = 0; + for (auto choice : input.second.choices) + { + thechoice->Append(choice.second, (void*) choice.first); + + if (item.intvalue == choice.first) + { + selected = thechoice->GetCount()-1; + } + } + + thechoice->SetSelection(selected); + inputObject = thechoice; + break; + } + + case MapObject::Input::Type::Slider: + { + if (item.intvalue < input.second.minvalue) item.intvalue = input.second.minvalue; + if (item.intvalue > input.second.maxvalue) item.intvalue = input.second.maxvalue; + inputObject = new UndoableSlider(entityPropertyEditor, wxID_ANY, this, item.intvalue, input.second.minvalue, input.second.maxvalue, wxDefaultPosition, wxDefaultSize, wxHORIZONTAL | wxSL_LABELS, SliderItemValidator(*world, item), input.second.name); + break; + } + } + + sizer->Add(inputObject, 0, wxEXPAND | wxALIGN_LEFT | wxBOTTOM, 10); + } + + entityPropertyEditor->Layout(); + } +} + void MapeditFrame::NewWorld() { LaunchWindow(new World()); diff --git a/tools/mapedit/src/frame.h b/tools/mapedit/src/frame.h index dd8424c..9f9ea1f 100644 --- a/tools/mapedit/src/frame.h +++ b/tools/mapedit/src/frame.h @@ -74,6 +74,7 @@ class MapeditFrame : public wxFrame { void OnSetUpmapMap(wxCommandEvent& event); void OnSetDownmapType(wxCommandEvent& event); void OnSetDownmapMap(wxCommandEvent& event); + void OnSelectEntity(wxCommandEvent& event); World* world; Map* currentMap; diff --git a/tools/mapedit/src/map.h b/tools/mapedit/src/map.h index c7f5b30..9c14218 100644 --- a/tools/mapedit/src/map.h +++ b/tools/mapedit/src/map.h @@ -8,10 +8,11 @@ #include #include #include +#include "object.h" -class MapObject; class World; class MapeditFrame; +class MapEntryObject; class MapLoadException: public std::exception { @@ -41,21 +42,6 @@ class MapWriteException: public std::exception std::string mapname; }; -struct MapObjectEntry { - MapObject* object; - std::pair position; - - bool operator==(MapObjectEntry& other) const - { - return (object == other.object) && (position == other.position); - } - - bool operator!=(MapObjectEntry& other) const - { - return (object != other.object) && (position != other.position); - } -}; - class Map { public: Map(int id, World* world); diff --git a/tools/mapedit/src/object.cpp b/tools/mapedit/src/object.cpp index a783691..8ed29af 100644 --- a/tools/mapedit/src/object.cpp +++ b/tools/mapedit/src/object.cpp @@ -2,29 +2,130 @@ #include #include #include +#include "world.h" -static std::map> allObjects; +static std::map allObjects; static bool objsInit = false; -const std::map> MapObject::getAllObjects() +const std::map& MapObject::getAllObjects() { if (!objsInit) { - DIR* dir = opendir("entities/"); - if (dir != NULL) + try { - struct dirent* ent; - while ((ent = readdir(dir)) != NULL) + xmlDocPtr doc = xmlParseFile("res/entities.xml"); + if (doc == nullptr) { - std::string path = ent->d_name; - if ((path.length() >= 4) && (path.substr(path.length() - 4, 4) == ".xml")) + throw MapObjectLoadException("can't open file"); + } + + xmlNodePtr top = xmlDocGetRootElement(doc); + if (top == nullptr) + { + throw MapObjectLoadException("missing root element"); + } + + if (xmlStrcmp(top->name, (const xmlChar*) "entities")) + { + throw MapObjectLoadException("root element is not entities"); + } + + for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next) + { + if (!xmlStrcmp(node->name, (const xmlChar*) "entity")) { - std::string name = path.substr(0, path.length() - 4); - auto obj = std::make_shared(name.c_str()); - - allObjects[name] = obj; + xmlChar* idKey = xmlGetProp(node, (xmlChar*) "id"); + if (idKey == 0) throw MapObjectLoadException("entity missing id"); + std::string theID = (char*) idKey; + xmlFree(idKey); + + allObjects.emplace(theID, theID); + MapObject& mapObject = allObjects.at(theID); + + xmlChar* nameKey = xmlGetProp(node, (xmlChar*) "name"); + if (nameKey == 0) throw MapObjectLoadException("entity missing name"); + mapObject.name = (char*) nameKey; + xmlFree(nameKey); + + xmlChar* spriteKey = xmlGetProp(node, (xmlChar*) "sprite"); + if (spriteKey == 0) throw MapObjectLoadException("entity missing sprite"); + mapObject.sprite = wxImage((char*) spriteKey); + xmlFree(spriteKey); + + xmlChar* widthKey = xmlGetProp(node, (xmlChar*) "width"); + if (widthKey == 0) throw MapObjectLoadException("entity missing width"); + mapObject.width = atoi((char*) widthKey); + xmlFree(widthKey); + + xmlChar* heightKey = xmlGetProp(node, (xmlChar*) "height"); + if (heightKey == 0) throw MapObjectLoadException("entity missing height"); + mapObject.height = atoi((char*) heightKey); + xmlFree(heightKey); + + for (xmlNodePtr entityNode = node->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next) + { + if (!xmlStrcmp(entityNode->name, (const xmlChar*) "input")) + { + xmlChar* key = xmlGetProp(entityNode, (xmlChar*) "id"); + if (key == 0) throw MapObjectLoadException("input missing id"); + std::string inputID = (char*) key; + xmlFree(key); + + Input& input = mapObject.inputs[inputID]; + + key = xmlGetProp(entityNode, (xmlChar*) "name"); + if (key == 0) throw MapObjectLoadException("input missing name"); + input.name = (char*) key; + xmlFree(key); + + key = xmlGetProp(entityNode, (xmlChar*) "type"); + if (key == 0) throw MapObjectLoadException("input missing type"); + std::string inputType = (char*) key; + xmlFree(key); + + if (inputType == "choice") + { + input.type = Input::Type::Choice; + + for (xmlNodePtr choiceNode = entityNode->xmlChildrenNode; choiceNode != NULL; choiceNode = choiceNode->next) + { + if (!xmlStrcmp(choiceNode->name, (xmlChar*) "value")) + { + key = xmlGetProp(choiceNode, (xmlChar*) "id"); + if (key == 0) throw MapObjectLoadException("input value missing id"); + int valueId = atoi((char*) key); + xmlFree(key); + + key = xmlNodeGetContent(choiceNode); + if (key == 0) throw MapObjectLoadException("input value missing content"); + std::string choiceText = (char*) key; + xmlFree(key); + + input.choices[valueId] = choiceText; + } + } + } else if (inputType == "slider") + { + input.type = Input::Type::Slider; + + key = xmlGetProp(entityNode, (xmlChar*) "minvalue"); + if (key == 0) throw MapObjectLoadException("integer input missing minvalue"); + input.minvalue = atoi((char*) key); + xmlFree(key); + + key = xmlGetProp(entityNode, (xmlChar*) "maxvalue"); + if (key == 0) throw MapObjectLoadException("integer input missing maxvalue"); + input.maxvalue = atoi((char*) key); + xmlFree(key); + } + } + } } } + } catch (std::exception& ex) + { + wxMessageBox(ex.what(), "Error loading objects", wxOK | wxCENTRE | wxICON_ERROR); + exit(3); } objsInit = true; @@ -33,67 +134,170 @@ const std::map> MapObject::getAllObjects return allObjects; } -MapObject::MapObject(const char* filename) +MapObject::MapObject(std::string id) : id(id) { - type = filename; - xmlDocPtr doc = xmlParseFile(("entities/" + std::string(filename) + ".xml").c_str()); - if (doc == nullptr) throw MapObjectLoadException(filename); +} - xmlNodePtr top = xmlDocGetRootElement(doc); - if (top == nullptr) throw MapObjectLoadException(filename); +std::string MapObject::getID() const +{ + return id; +} - if (xmlStrcmp(top->name, (const xmlChar*) "entity-def")) - { - throw MapObjectLoadException(filename); - } +std::string MapObject::getName() const +{ + return name; +} + +wxBitmap MapObject::getSprite() const +{ + return sprite; +} + +int MapObject::getWidth() const +{ + return width; +} + +int MapObject::getHeight() const +{ + return height; +} + +const std::map& MapObject::getInputs() const +{ + return inputs; +} + +const MapObject::Input& MapObject::getInput(std::string id) const +{ + return inputs.at(id); +} + +bool MapObject::operator==(const MapObject& other) const +{ + return id == other.id; +} + +bool MapObject::operator!=(const MapObject& other) const +{ + return id != other.id; +} + +MapObjectEntry::MapObjectEntry(const MapObject& object, int posx, int posy) : object(object) +{ + position = std::make_pair(posx, posy); +} + +const MapObject& MapObjectEntry::getObject() const +{ + return object; +} + +std::pair MapObjectEntry::getPosition() const +{ + return position; +} + +MapObjectEntry::Item& MapObjectEntry::getItem(std::string str) +{ + return items[str]; +} + +const std::map& MapObjectEntry::getItems() const +{ + return items; +} + +void MapObjectEntry::addItem(std::string id, Item& item) +{ + items[id] = item; +} + +void MapObjectEntry::setPosition(int x, int y) +{ + position = std::make_pair(x, y); +} + +bool MapObjectEntry::operator==(const MapObjectEntry& other) const +{ + return (object == other.object) && (position == other.position); +} + +bool MapObjectEntry::operator!=(const MapObjectEntry& other) const +{ + return (object != other.object) && (position != other.position); +} + +VariableChoiceValidator::VariableChoiceValidator(World& world, MapObjectEntry::Item& item) : world(world), item(item) +{ + +} - for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next) +wxObject* VariableChoiceValidator::Clone() const +{ + return new VariableChoiceValidator(world, item); +} + +bool VariableChoiceValidator::TransferFromWindow() +{ + wxChoice* choice = (wxChoice*) GetWindow(); + int sel = choice->GetSelection(); + int val = (intptr_t) choice->GetClientData(sel); + item.intvalue = val; + world.setDirty(true); + + return true; +} + +bool VariableChoiceValidator::TransferToWindow() +{ + wxChoice* choice = (wxChoice*) GetWindow(); + for (size_t i=0; iGetCount(); i++) { - if (!xmlStrcmp(node->name, (const xmlChar*) "sprite")) - { - xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - std::string spriteFile = (char*) key; - xmlFree(key); - - sprite = wxImage(spriteFile); - } else if (!xmlStrcmp(node->name, (const xmlChar*) "action")) + if ((intptr_t) choice->GetClientData(i) == item.intvalue) { - xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - action = (char*) key; - xmlFree(key); - } else if (!xmlStrcmp(node->name, (const xmlChar*) "size")) - { - xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - sscanf((char*) key, "%d,%d", &width, &height); - xmlFree(key); + choice->SetSelection(i); + return true; } } + + return false; +} - xmlFreeDoc(doc); +bool VariableChoiceValidator::Validate(wxWindow*) +{ + return true; } -wxBitmap MapObject::getSprite() const +SliderItemValidator::SliderItemValidator(World& world, MapObjectEntry::Item& item) : world(world), item(item) { - return sprite; + } -std::string MapObject::getAction() const +wxObject* SliderItemValidator::Clone() const { - return action; + return new SliderItemValidator(world, item); } -int MapObject::getWidth() const +bool SliderItemValidator::TransferFromWindow() { - return width; + wxSlider* slider = (wxSlider*) GetWindow(); + item.intvalue = slider->GetValue(); + world.setDirty(true); + + return true; } -int MapObject::getHeight() const +bool SliderItemValidator::TransferToWindow() { - return height; + wxSlider* slider = (wxSlider*) GetWindow(); + slider->SetValue(item.intvalue); + + return true; } -std::string MapObject::getType() const +bool SliderItemValidator::Validate(wxWindow*) { - return type; + return true; } diff --git a/tools/mapedit/src/object.h b/tools/mapedit/src/object.h index bfb493c..a870a2e 100644 --- a/tools/mapedit/src/object.h +++ b/tools/mapedit/src/object.h @@ -10,38 +10,111 @@ #include #include +class World; + class MapObjectLoadException: public std::exception { public: - MapObjectLoadException(std::string mapname) : mapname(mapname) {} + MapObjectLoadException(std::string stuff) : stuff(stuff) {} virtual const char* what() const throw() { - return ("An error occured loading map object " + mapname).c_str(); + return ("An error occured loading map objects: " + stuff).c_str(); } private: - std::string mapname; + std::string stuff; }; class MapObject { public: - MapObject(const char* filename); + MapObject(std::string id); + + static const std::map& getAllObjects(); - static const std::map> getAllObjects(); + struct Input { + enum class Type { + Slider, + Choice + }; + + std::string name; + Type type; + int minvalue; + int maxvalue; + std::map choices; + }; - std::string getType() const; + std::string getID() const; + std::string getName() const; wxBitmap getSprite() const; - std::string getAction() const; int getWidth() const; int getHeight() const; + const std::map& getInputs() const; + const Input& getInput(std::string id) const; + + bool operator==(const MapObject& other) const; + bool operator!=(const MapObject& other) const; private: - std::string type; + const std::string id; + std::string name; wxBitmap sprite; - std::string action; int width; int height; + std::map inputs; +}; + +class MapObjectEntry { + public: + MapObjectEntry(const MapObject& object, int posx, int posy); + + struct Item { + MapObject::Input::Type type; + int intvalue; + }; + + const MapObject& getObject() const; + std::pair getPosition() const; + Item& getItem(std::string str); + const std::map& getItems() const; + + void setPosition(int x, int y); + void addItem(std::string id, Item& item); + + bool operator==(const MapObjectEntry& other) const; + bool operator!=(const MapObjectEntry& other) const; + + private: + const MapObject& object; + std::pair position; + std::map items; +}; + +class VariableChoiceValidator : public wxValidator { + public: + VariableChoiceValidator(World& world, MapObjectEntry::Item& item); + wxObject* Clone() const; + bool TransferFromWindow(); + bool TransferToWindow(); + bool Validate(wxWindow* parent); + + private: + World& world; + MapObjectEntry::Item& item; +}; + +class SliderItemValidator : public wxValidator { + public: + SliderItemValidator(World& world, MapObjectEntry::Item& item); + wxObject* Clone() const; + bool TransferFromWindow(); + bool TransferToWindow(); + bool Validate(wxWindow* parent); + + private: + World& world; + MapObjectEntry::Item& item; }; #endif diff --git a/tools/mapedit/src/undo.cpp b/tools/mapedit/src/undo.cpp index bcf7b92..12cafc9 100644 --- a/tools/mapedit/src/undo.cpp +++ b/tools/mapedit/src/undo.cpp @@ -28,7 +28,7 @@ wxBEGIN_EVENT_TABLE(UndoableTextBox, wxTextCtrl) EVT_KILL_FOCUS(UndoableTextBox::OnKillFocus) wxEND_EVENT_TABLE() -UndoableTextBox::UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, wxPoint pos, wxSize size, long style) : wxTextCtrl(parent, id, startText, pos, size, style) +UndoableTextBox::UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, const wxPoint& pos, const wxSize& size, long style) : wxTextCtrl(parent, id, startText, pos, size, style) { this->undoText = undoType; this->realParent = realParent; @@ -93,3 +93,75 @@ void UndoableTextBox::Undo::endChanges() { parent.undo.reset(); } + +UndoableChoice::UndoableChoice() +{ + Init(); +} + +UndoableChoice::UndoableChoice(wxWindow* parent, wxWindowID id, MapeditFrame* realParent, const wxPoint& pos, const wxSize& size, int n, const wxString choices[], long style, const wxValidator& validator, const wxString& name) : wxChoice(parent, id, pos, size, n, choices, style, validator, name) +{ + this->realParent = realParent; + + Init(); +} + +void UndoableChoice::Init() +{ + Bind(wxEVT_CHOICE, &UndoableChoice::OnChoose, this); +} + +void UndoableChoice::OnChoose(wxCommandEvent& event) +{ + int new_selection = GetSelection(); + GetValidator()->TransferToWindow(); + int old_selection = GetSelection(); + + realParent->commitAction(std::make_shared(("Set " + GetName()).ToStdString(), [=] () { + SetSelection(new_selection); + GetValidator()->TransferFromWindow(); + }, [=] () { + SetSelection(old_selection); + GetValidator()->TransferFromWindow(); + })); + + event.Skip(); +} + +wxBEGIN_EVENT_TABLE(UndoableSlider, wxSlider) + EVT_SCROLL(UndoableSlider::OnSlide) +wxEND_EVENT_TABLE() + +UndoableSlider::UndoableSlider() +{ + Init(); +} + +UndoableSlider::UndoableSlider(wxWindow* parent, wxWindowID id, MapeditFrame* realParent, int value, int minvalue, int maxvalue, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) : wxSlider(parent, id, value, minvalue, maxvalue, pos, size, style, validator, name) +{ + this->realParent = realParent; + + Init(); +} + +void UndoableSlider::Init() +{ + +} + +void UndoableSlider::OnSlide(wxScrollEvent& event) +{ + int new_value = GetValue(); + GetValidator()->TransferToWindow(); + int old_value = GetValue(); + + realParent->commitAction(std::make_shared(("Set " + GetName()).ToStdString(), [=] () { + SetValue(new_value); + GetValidator()->TransferFromWindow(); + }, [=] () { + SetValue(old_value); + GetValidator()->TransferFromWindow(); + })); + + event.Skip(); +} diff --git a/tools/mapedit/src/undo.h b/tools/mapedit/src/undo.h index 794f993..621f42c 100644 --- a/tools/mapedit/src/undo.h +++ b/tools/mapedit/src/undo.h @@ -32,7 +32,7 @@ class Undoable { class UndoableTextBox : public wxTextCtrl { public: UndoableTextBox(); - UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize, long style = 0); + UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0); private: class Undo : public Undoable { @@ -60,4 +60,32 @@ class UndoableTextBox : public wxTextCtrl { wxDECLARE_EVENT_TABLE(); }; +class UndoableChoice : public wxChoice { + public: + UndoableChoice(); + UndoableChoice(wxWindow* parent, wxWindowID id, MapeditFrame* realParent, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, int n = 0, const wxString choices[] = NULL, long style = 0, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxChoiceNameStr); + + protected: + void Init(); + void OnChoose(wxCommandEvent& event); + + private: + MapeditFrame* realParent; +}; + +class UndoableSlider : public wxSlider { + public: + UndoableSlider(); + UndoableSlider(wxWindow* parent, wxWindowID id, MapeditFrame* realParent, int value, int minvalue, int maxvalue, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxSL_HORIZONTAL, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxSliderNameStr); + + protected: + void Init(); + void OnSlide(wxScrollEvent& event); + + private: + MapeditFrame* realParent; + + wxDECLARE_EVENT_TABLE(); +}; + #endif diff --git a/tools/mapedit/src/widget.cpp b/tools/mapedit/src/widget.cpp index fa0af39..8d74b39 100644 --- a/tools/mapedit/src/widget.cpp +++ b/tools/mapedit/src/widget.cpp @@ -16,6 +16,8 @@ BEGIN_EVENT_TABLE(MapeditWidget, wxScrolledCanvas) EVT_LEAVE_WINDOW(MapeditWidget::OnMouseOut) EVT_SCROLLWIN(MapeditWidget::OnScroll) END_EVENT_TABLE() + +wxDEFINE_EVENT(EVT_MAP_SELECTED_ENTITY, wxCommandEvent); MapeditWidget::MapeditWidget() { @@ -107,12 +109,12 @@ void MapeditWidget::OnPaint(wxPaintEvent&) { tiles_dc.SelectObject(wxNullBitmap); - wxBitmap sprite = object->object->getSprite(); + wxBitmap sprite = object->getObject().getSprite(); tiles_dc.SelectObject(sprite); - wxPoint pos {(object->position.first + EDITOR_SPACING_X)*scale-vX, (object->position.second + EDITOR_SPACING_Y)*scale-vY}; - wxSize size {object->object->getWidth()*scale, object->object->getHeight()*scale}; - dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, object->object->getWidth(), object->object->getHeight()); + wxPoint pos {(object->getPosition().first + EDITOR_SPACING_X)*scale-vX, (object->getPosition().second + EDITOR_SPACING_Y)*scale-vY}; + wxSize size {object->getObject().getWidth()*scale, object->getObject().getHeight()*scale}; + dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, object->getObject().getWidth(), object->getObject().getHeight()); if (editMode == EditEntities) { @@ -197,14 +199,14 @@ void MapeditWidget::OnPaint(wxPaintEvent&) dc.DrawRectangle(pos.x, pos.y, size.GetWidth(), size.GetHeight()); } else if ((editMode == EditEntities) && (movingEntity != nullptr)) { - wxBitmap sprite = movingEntity->object->getSprite(); + wxBitmap sprite = movingEntity->getObject().getSprite(); tiles_dc.SelectObject(wxNullBitmap); tiles_dc.SelectObject(sprite); - wxPoint pos {mousePos.x - movingEntity->object->getWidth()/2*scale, mousePos.y - movingEntity->object->getHeight()/2*scale}; - wxSize size {movingEntity->object->getWidth()*scale, movingEntity->object->getHeight()*scale}; + wxPoint pos {mousePos.x - movingEntity->getObject().getWidth()/2*scale, mousePos.y - movingEntity->getObject().getHeight()/2*scale}; + wxSize size {movingEntity->getObject().getWidth()*scale, movingEntity->getObject().getHeight()*scale}; - dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, movingEntity->object->getWidth(), movingEntity->object->getHeight()); + dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, movingEntity->getObject().getWidth(), movingEntity->getObject().getHeight()); wxPen pen(*wxGREEN, 2); dc.SetPen(pen); @@ -254,17 +256,23 @@ void MapeditWidget::OnClick(wxMouseEvent& event) int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale - (addingEntity->getWidth() / 2); int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale - (addingEntity->getHeight() / 2); - auto data = std::make_shared(); - data->object = addingEntity; - data->position = std::make_pair(x,y); - - frame->commitAction(std::make_shared("Add " + addingEntity->getType(), [=] () { + auto data = std::make_shared(*addingEntity, x, y); + frame->commitAction(std::make_shared("Add " + addingEntity->getName(), [=] () { map->addObject(data); Refresh(); }, [=] () { map->removeObject(data); + if (data == selectedEntity) + { + selectedEntity.reset(); + + wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY}; + sev.SetClientData(0); + ProcessEvent(sev); + } + Refresh(); })); @@ -272,17 +280,17 @@ void MapeditWidget::OnClick(wxMouseEvent& event) addingEntity = nullptr; } else if (movingEntity != nullptr) { - int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale - (movingEntity->object->getWidth() / 2); - int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale - (movingEntity->object->getHeight() / 2); - auto oldPos = movingEntity->position; + int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale - (movingEntity->getObject().getWidth() / 2); + int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale - (movingEntity->getObject().getHeight() / 2); + auto oldPos = movingEntity->getPosition(); MapObjectEntry* me = movingEntity; - frame->commitAction(std::make_shared("Move " + movingEntity->object->getType(), [=] () { - me->position = std::make_pair(x,y); + frame->commitAction(std::make_shared("Move " + movingEntity->getObject().getName(), [=] () { + me->setPosition(x, y); Refresh(); }, [=] () { - me->position = oldPos; + me->setPosition(oldPos.first, oldPos.second); Refresh(); })); @@ -295,24 +303,34 @@ void MapeditWidget::OnClick(wxMouseEvent& event) if (selectedEntity) { - if ((x > selectedEntity->position.first) && (x < selectedEntity->position.first + selectedEntity->object->getWidth()) - && (y > selectedEntity->position.second) && (y < selectedEntity->position.second + selectedEntity->object->getHeight())) + if ((x > selectedEntity->getPosition().first) + && (x < selectedEntity->getPosition().first + selectedEntity->getObject().getWidth()) + && (y > selectedEntity->getPosition().second) + && (y < selectedEntity->getPosition().second + selectedEntity->getObject().getHeight())) { movingEntity = selectedEntity.get(); frame->SetIsAddingEntity(true); } else { selectedEntity.reset(); + + wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY}; + sev.SetClientData(0); + ProcessEvent(sev); } Refresh(); } else { for (auto object : map->getObjects()) { - if ((x >= object->position.first) && (x <= object->position.first + object->object->getWidth()) - && (y >= object->position.second) && (y <= object->position.second + object->object->getHeight())) + if ((x >= object->getPosition().first) && (x <= object->getPosition().first + object->getObject().getWidth()) + && (y >= object->getPosition().second) && (y <= object->getPosition().second + object->getObject().getHeight())) { selectedEntity = object; + wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY}; + sev.SetClientData(object.get()); + ProcessEvent(sev); + Refresh(); break; @@ -364,12 +382,18 @@ void MapeditWidget::OnRightClick(wxMouseEvent& event) int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale; int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale; - if ((x > selectedEntity->position.first) && (x < selectedEntity->position.first + selectedEntity->object->getWidth()) - && (y > selectedEntity->position.second) && (y < selectedEntity->position.second + selectedEntity->object->getHeight())) + if ((x > selectedEntity->getPosition().first) + && (x < selectedEntity->getPosition().first + selectedEntity->getObject().getWidth()) + && (y > selectedEntity->getPosition().second) + && (y < selectedEntity->getPosition().second + selectedEntity->getObject().getHeight())) { map->removeObject(selectedEntity); selectedEntity.reset(); + wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY}; + sev.SetClientData(0); + ProcessEvent(sev); + Refresh(); } } @@ -494,6 +518,10 @@ void MapeditWidget::StartAddingEntity(MapObject* object) selectedEntity = nullptr; isSettingPos = false; frame->SetIsSettingStart(false); + + wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY}; + sev.SetClientData(0); + ProcessEvent(sev); } void MapeditWidget::CancelAddingEntity() @@ -511,6 +539,10 @@ void MapeditWidget::SetIsSettingStart(bool isSetting) frame->SetIsAddingEntity(false); addingEntity = nullptr; selectedEntity = nullptr; + + wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY}; + sev.SetClientData(0); + ProcessEvent(sev); } else { isSettingPos = false; } @@ -524,6 +556,10 @@ void MapeditWidget::SetMap(Map* map) movingEntity = nullptr; isSettingPos = false; + wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY}; + sev.SetClientData(0); + ProcessEvent(sev); + Refresh(); } diff --git a/tools/mapedit/src/widget.h b/tools/mapedit/src/widget.h index 864e299..937b699 100644 --- a/tools/mapedit/src/widget.h +++ b/tools/mapedit/src/widget.h @@ -16,7 +16,7 @@ class MapeditFrame; class TileWidget; class Map; class MapObject; -struct MapObjectEntry; +class MapObjectEntry; #include "consts.h" @@ -76,5 +76,10 @@ class MapeditWidget : public wxScrolledCanvas { DECLARE_DYNAMIC_CLASS(MapeditWidget) DECLARE_EVENT_TABLE() }; + +// sends when an entity is selected OR deselected. +// client data will be a pointer to the MapObjectEntry if selection. +// client data will be nullptr if deselection. +wxDECLARE_EVENT(EVT_MAP_SELECTED_ENTITY, wxCommandEvent); #endif diff --git a/tools/mapedit/src/world.cpp b/tools/mapedit/src/world.cpp index 01225cf..79f5a58 100644 --- a/tools/mapedit/src/world.cpp +++ b/tools/mapedit/src/world.cpp @@ -22,18 +22,18 @@ World::World(std::string filename) xmlDocPtr doc = xmlParseFile(filename.c_str()); if (doc == nullptr) { - throw MapLoadException(filename); + throw MapLoadException("file not found"); } xmlNodePtr top = xmlDocGetRootElement(doc); if (top == nullptr) { - throw MapLoadException(filename); + throw MapLoadException("no root element"); } if (xmlStrcmp(top->name, (const xmlChar*) "world")) { - throw MapLoadException(filename); + throw MapLoadException("no world element"); } xmlChar* nextmapKey = xmlGetProp(top, (xmlChar*) "nextmap"); @@ -51,17 +51,17 @@ World::World(std::string filename) xmlFree(lastmapKey); xmlChar* startxKey = xmlGetProp(top, (xmlChar*) "startx"); - if (startxKey == 0) throw MapLoadException(filename); + if (startxKey == 0) throw MapLoadException("world missing startx attribute"); startingPosition.first = atoi((char*) startxKey); xmlFree(startxKey); xmlChar* startyKey = xmlGetProp(top, (xmlChar*) "starty"); - if (startyKey == 0) throw MapLoadException(filename); + if (startyKey == 0) throw MapLoadException("world missing starty attribute"); startingPosition.second = atoi((char*) startyKey); xmlFree(startyKey); xmlChar* startmapKey = xmlGetProp(top, (xmlChar*) "startmap"); - if (startxKey == 0) throw MapLoadException(filename); + if (startxKey == 0) throw MapLoadException("world missing startmap attribute"); startingMap = atoi((char*) startmapKey); xmlFree(startmapKey); @@ -69,14 +69,14 @@ World::World(std::string filename) { if (!xmlStrcmp(node->name, (const xmlChar*) "root")) { - xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - if (key == 0) throw MapLoadException(filename); + xmlChar* key = xmlNodeGetContent(node); + if (key == 0) throw MapLoadException("root missing content"); rootChildren.push_back(atoi((char*) key)); xmlFree(key); } else if (!xmlStrcmp(node->name, (const xmlChar*) "map")) { xmlChar* idKey = xmlGetProp(node, (xmlChar*) "id"); - if (idKey == 0) throw MapLoadException(filename); + if (idKey == 0) throw MapLoadException("map missing id attribute"); int id = atoi((char*) idKey); xmlFree(idKey); @@ -90,7 +90,7 @@ World::World(std::string filename) xmlFree(expandKey); xmlChar* titleKey = xmlGetProp(node, (xmlChar*) "title"); - if (titleKey == 0) throw MapLoadException(filename); + if (titleKey == 0) throw MapLoadException("map missing title attribute"); map->setTitle((char*) titleKey, false); xmlFree(titleKey); @@ -98,7 +98,8 @@ World::World(std::string filename) { if (!xmlStrcmp(mapNode->name, (const xmlChar*) "environment")) { - xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1); + xmlChar* key = xmlNodeGetContent(mapNode); + if (key == 0) throw MapLoadException("map missing environment content"); int* mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int)); mapdata[0] = atoi(strtok((char*) key, ",\n")); for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++) @@ -109,24 +110,52 @@ World::World(std::string filename) xmlFree(key); } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "entity")) { - auto data = std::make_shared(); - xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type"); - if (typeKey == 0) throw MapLoadException(filename); - data->object = MapObject::getAllObjects().at((char*) typeKey).get(); + if (typeKey == 0) throw MapLoadException("entity missing type attribute"); + const MapObject& obj = MapObject::getAllObjects().at((char*) typeKey); xmlFree(typeKey); xmlChar* xKey = xmlGetProp(mapNode, (const xmlChar*) "x"); - if (xKey == 0) throw MapLoadException(filename); - data->position.first = atoi((char*) xKey); + if (xKey == 0) throw MapLoadException("entity missing x attribute"); + int xpos = atoi((char*) xKey); xmlFree(xKey); xmlChar* yKey = xmlGetProp(mapNode, (const xmlChar*) "y"); - if (yKey == 0) throw MapLoadException(filename); - data->position.second = atoi((char*) yKey); + if (yKey == 0) throw MapLoadException("entity missing y attribute"); + int ypos = atoi((char*) yKey); xmlFree(yKey); + auto data = std::make_shared(obj, xpos, ypos); + map->addObject(data, false); + + for (xmlNodePtr objectNode = mapNode->xmlChildrenNode; objectNode != NULL; objectNode = objectNode->next) + { + if (!xmlStrcmp(objectNode->name, (const xmlChar*) "item")) + { + xmlChar* key = xmlGetProp(objectNode, (const xmlChar*) "id"); + if (key == 0) throw MapLoadException("item missing id attribute"); + std::string itemID = (char*) key; + xmlFree(key); + + MapObjectEntry::Item item; + item.type = data->getObject().getInput(itemID).type; + + key = xmlNodeGetContent(objectNode); + if (key == 0) throw MapLoadException("item missing content"); + switch (item.type) + { + case MapObject::Input::Type::Choice: + case MapObject::Input::Type::Slider: + { + item.intvalue = atoi((char*) key); + break; + } + } + + data->addItem(itemID, item); + } + } } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "adjacent")) { Map::MoveDir direction; @@ -134,12 +163,12 @@ World::World(std::string filename) int mapId = 0; xmlChar* dirKey = xmlGetProp(mapNode, (const xmlChar*) "dir"); - if (dirKey == 0) throw MapLoadException(filename); + if (dirKey == 0) throw MapLoadException("adjacent missing dir attribute"); direction = Map::moveDirForShort((char*) dirKey); xmlFree(dirKey); xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type"); - if (typeKey == 0) throw MapLoadException(filename); + if (typeKey == 0) throw MapLoadException("adjacent missing type attribute"); moveType = Map::moveTypeForShort((char*) typeKey); xmlFree(typeKey); @@ -153,7 +182,7 @@ World::World(std::string filename) map->setAdjacent(direction, moveType, mapId, false); } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "child")) { - xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1); + xmlChar* key = xmlNodeGetContent(mapNode); if (key != 0) { map->addChild(atoi((char*) key)); @@ -281,7 +310,7 @@ void World::save(std::string name, wxTreeCtrl* mapTree) } // title= - rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "name", (xmlChar*) map.getTitle().c_str()); + rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "title", (xmlChar*) map.getTitle().c_str()); if (rc < 0) throw MapWriteException(name); // @@ -318,16 +347,42 @@ void World::save(std::string name, wxTreeCtrl* mapTree) if (rc < 0) throw MapWriteException(name); // type= - rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "type", (xmlChar*) object->object->getType().c_str()); + rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "type", (xmlChar*) object->getObject().getID().c_str()); if (rc < 0) throw MapWriteException(name); // x= - rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "x", "%d", object->position.first); + rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "x", "%d", object->getPosition().first); if (rc < 0) throw MapWriteException(name); // y= - rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "y", "%d", object->position.second); + rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "y", "%d", object->getPosition().second); if (rc < 0) throw MapWriteException(name); + + for (auto item : object->getItems()) + { + // + switch (item.second.type) + { + case MapObject::Input::Type::Slider: + case MapObject::Input::Type::Choice: + { + rc = xmlTextWriterWriteFormatString(writer, "%d", item.second.intvalue); + break; + } + } + + // + rc = xmlTextWriterEndElement(writer); + if (rc < 0) throw MapWriteException(name); + } // rc = xmlTextWriterEndElement(writer); -- cgit 1.4.1