summary refs log tree commit diff stats
path: root/tools/mapedit
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mapedit')
-rw-r--r--tools/mapedit/src/frame.cpp82
-rw-r--r--tools/mapedit/src/frame.h1
-rw-r--r--tools/mapedit/src/map.h18
-rw-r--r--tools/mapedit/src/object.cpp304
-rw-r--r--tools/mapedit/src/object.h91
-rw-r--r--tools/mapedit/src/undo.cpp74
-rw-r--r--tools/mapedit/src/undo.h30
-rw-r--r--tools/mapedit/src/widget.cpp86
-rw-r--r--tools/mapedit/src/widget.h7
-rw-r--r--tools/mapedit/src/world.cpp109
10 files changed, 666 insertions, 136 deletions
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 @@
2#include "mapselect_combo.h" 2#include "mapselect_combo.h"
3#include <wx/statline.h> 3#include <wx/statline.h>
4#include <list> 4#include <list>
5#include <wx/valgen.h>
5#include <exception> 6#include <exception>
6#include <sstream> 7#include <sstream>
7#include "widget.h" 8#include "widget.h"
@@ -29,6 +30,7 @@ enum {
29 TOOL_FILE_SAVE, 30 TOOL_FILE_SAVE,
30 TOOL_MAP_ADD_ROOT, 31 TOOL_MAP_ADD_ROOT,
31 TOOL_MAP_ADD_CHILD, 32 TOOL_MAP_ADD_CHILD,
33 MAP_EDITOR_WIDGET,
32 MAP_EDITOR_NOTEBOOK, 34 MAP_EDITOR_NOTEBOOK,
33 MAP_EDITOR_TREE, 35 MAP_EDITOR_TREE,
34 MAP_TITLE_TEXTBOX, 36 MAP_TITLE_TEXTBOX,
@@ -45,7 +47,10 @@ enum {
45 UPMAP_TYPE_CHOICE, 47 UPMAP_TYPE_CHOICE,
46 UPMAP_MAP_CHOICE, 48 UPMAP_MAP_CHOICE,
47 DOWNMAP_TYPE_CHOICE, 49 DOWNMAP_TYPE_CHOICE,
48 DOWNMAP_MAP_CHOICE 50 DOWNMAP_MAP_CHOICE,
51 ENTITY_EDITOR,
52 ENTITY_PROPERTY_EDITOR,
53 PROPERTY_EDITOR
49}; 54};
50 55
51wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame) 56wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame)
@@ -87,6 +92,7 @@ wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame)
87 EVT_COMBOBOX_CLOSEUP(RIGHTMAP_MAP_CHOICE, MapeditFrame::OnSetRightmapMap) 92 EVT_COMBOBOX_CLOSEUP(RIGHTMAP_MAP_CHOICE, MapeditFrame::OnSetRightmapMap)
88 EVT_COMBOBOX_CLOSEUP(UPMAP_MAP_CHOICE, MapeditFrame::OnSetUpmapMap) 93 EVT_COMBOBOX_CLOSEUP(UPMAP_MAP_CHOICE, MapeditFrame::OnSetUpmapMap)
89 EVT_COMBOBOX_CLOSEUP(DOWNMAP_MAP_CHOICE, MapeditFrame::OnSetDownmapMap) 94 EVT_COMBOBOX_CLOSEUP(DOWNMAP_MAP_CHOICE, MapeditFrame::OnSetDownmapMap)
95 EVT_COMMAND(wxID_ANY, EVT_MAP_SELECTED_ENTITY, MapeditFrame::OnSelectEntity)
90wxEND_EVENT_TABLE() 96wxEND_EVENT_TABLE()
91 97
92MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor") 98MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
@@ -171,7 +177,7 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
171 tileEditor = new TileWidget(notebook, wxID_ANY, 6, 6, wxPoint(0,0), wxSize(TILE_WIDTH*6*6,TILE_HEIGHT*10*6)); 177 tileEditor = new TileWidget(notebook, wxID_ANY, 6, 6, wxPoint(0,0), wxSize(TILE_WIDTH*6*6,TILE_HEIGHT*10*6));
172 notebook->AddPage(tileEditor, "Tile Chooser", false); 178 notebook->AddPage(tileEditor, "Tile Chooser", false);
173 179
174 mapEditor = new MapeditWidget(layout3, wxID_ANY, currentMap, tileEditor, wxPoint(0,0), wxSize(GAME_WIDTH*2, GAME_HEIGHT*2)); 180 mapEditor = new MapeditWidget(layout3, MAP_EDITOR_WIDGET, currentMap, tileEditor, wxPoint(0,0), wxSize(GAME_WIDTH*2, GAME_HEIGHT*2));
175 mapEditor->frame = this; 181 mapEditor->frame = this;
176 182
177 // Set up property editor 183 // Set up property editor
@@ -181,7 +187,7 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
181 187
182 wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:"); 188 wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:");
183 189
184 startposLabel = new wxStaticText(propertyEditor, wxID_ANY, "Starting Position:"); 190 startposLabel = new wxStaticText(propertyEditor, PROPERTY_EDITOR, "Starting Position:");
185 191
186 setStartposButton = new wxButton(propertyEditor, SET_STARTPOS_BUTTON, "Set Starting Position"); 192 setStartposButton = new wxButton(propertyEditor, SET_STARTPOS_BUTTON, "Set Starting Position");
187 cancelStartposButton = new wxButton(propertyEditor, CANCEL_STARTPOS_BUTTON, "Cancel"); 193 cancelStartposButton = new wxButton(propertyEditor, CANCEL_STARTPOS_BUTTON, "Cancel");
@@ -264,7 +270,7 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
264 propertySizer->SetSizeHints(propertyEditor); 270 propertySizer->SetSizeHints(propertyEditor);
265 271
266 // Set up entity editor 272 // Set up entity editor
267 wxPanel* entityEditor = new wxPanel(notebook, wxID_ANY); 273 wxPanel* entityEditor = new wxPanel(notebook, ENTITY_EDITOR);
268 notebook->AddPage(entityEditor, "Entity Manager", false); 274 notebook->AddPage(entityEditor, "Entity Manager", false);
269 275
270 wxStaticText* entityHeader = new wxStaticText(entityEditor, wxID_ANY, "Add Entity"); 276 wxStaticText* entityHeader = new wxStaticText(entityEditor, wxID_ANY, "Add Entity");
@@ -275,9 +281,9 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
275 wxStaticText* entityTypeLabel = new wxStaticText(entityEditor, wxID_ANY, "Entity Type:"); 281 wxStaticText* entityTypeLabel = new wxStaticText(entityEditor, wxID_ANY, "Entity Type:");
276 282
277 entityTypeBox = new wxChoice(entityEditor, wxID_ANY); 283 entityTypeBox = new wxChoice(entityEditor, wxID_ANY);
278 for (auto entry : MapObject::getAllObjects()) 284 for (auto& entry : MapObject::getAllObjects())
279 { 285 {
280 entityTypeBox->Append(entry.second->getType(), entry.second.get()); 286 entityTypeBox->Append(entry.second.getName(), (void*) &entry.second);
281 } 287 }
282 288
283 addEntityButton = new wxButton(entityEditor, ADD_ENTITY_BUTTON, "Add Entity"); 289 addEntityButton = new wxButton(entityEditor, ADD_ENTITY_BUTTON, "Add Entity");
@@ -286,6 +292,8 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
286 292
287 wxStaticText* entityInfoLabel = new wxStaticText(entityEditor, wxID_ANY, "Click and drag an entity to move it.\nRight click an entity to delete it."); 293 wxStaticText* entityInfoLabel = new wxStaticText(entityEditor, wxID_ANY, "Click and drag an entity to move it.\nRight click an entity to delete it.");
288 294
295 wxPanel* entityPropertyEditor = new wxPanel(entityEditor, ENTITY_PROPERTY_EDITOR);
296
289 wxBoxSizer* entitySizer = new wxBoxSizer(wxVERTICAL); 297 wxBoxSizer* entitySizer = new wxBoxSizer(wxVERTICAL);
290 entitySizer->Add(entityHeader, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5); 298 entitySizer->Add(entityHeader, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
291 wxBoxSizer* entitySizer1 = new wxBoxSizer(wxHORIZONTAL); 299 wxBoxSizer* entitySizer1 = new wxBoxSizer(wxHORIZONTAL);
@@ -298,6 +306,12 @@ MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
298 entitySizer->Add(entitySizer2, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5); 306 entitySizer->Add(entitySizer2, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
299 entitySizer->Add(new wxStaticLine(entityEditor), 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5); 307 entitySizer->Add(new wxStaticLine(entityEditor), 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
300 entitySizer->Add(entityInfoLabel, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5); 308 entitySizer->Add(entityInfoLabel, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
309 entitySizer->Add(entityPropertyEditor, 1, wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxALL, 5);
310 wxBoxSizer* entityPropertySizer = new wxBoxSizer(wxVERTICAL);
311 entityPropertySizer->Add(new wxStaticLine(entityPropertyEditor), 1, wxEXPAND, 0);
312 entityPropertyEditor->SetSizer(entityPropertySizer);
313 entityPropertySizer->SetSizeHints(entityPropertyEditor);
314 //entitySizer->Add(entityPropertySizer, 1, wxEXPAND, 0);
301 entityEditor->SetSizer(entitySizer); 315 entityEditor->SetSizer(entitySizer);
302 entitySizer->SetSizeHints(entityEditor); 316 entitySizer->SetSizeHints(entityEditor);
303 317
@@ -840,6 +854,62 @@ void MapeditFrame::OnSetDownmapMap(wxCommandEvent&)
840 })); 854 }));
841} 855}
842 856
857void MapeditFrame::OnSelectEntity(wxCommandEvent& event)
858{
859 MapObjectEntry* entry = (MapObjectEntry*) event.GetClientData();
860 wxPanel* entityPropertyEditor = (wxPanel*) wxWindow::FindWindowById(ENTITY_PROPERTY_EDITOR, this);
861
862 if (entry == nullptr)
863 {
864 entityPropertyEditor->GetSizer()->Clear();
865 entityPropertyEditor->DestroyChildren();
866 } else {
867 wxSizer* sizer = entityPropertyEditor->GetSizer();
868 for (auto input : entry->getObject().getInputs())
869 {
870 wxStaticText* inputText = new wxStaticText(entityPropertyEditor, wxID_ANY, input.second.name + ":");
871 sizer->Add(inputText, 0, wxEXPAND | wxALIGN_LEFT | wxBOTTOM, 0);
872
873 MapObjectEntry::Item& item = entry->getItem(input.first);
874
875 wxWindow* inputObject = nullptr;
876 switch (input.second.type)
877 {
878 case MapObject::Input::Type::Choice:
879 {
880 UndoableChoice* thechoice = new UndoableChoice(entityPropertyEditor, wxID_ANY, this, wxDefaultPosition, wxDefaultSize, 0, NULL, 0, VariableChoiceValidator(*world, item), input.second.name);
881 int selected = 0;
882 for (auto choice : input.second.choices)
883 {
884 thechoice->Append(choice.second, (void*) choice.first);
885
886 if (item.intvalue == choice.first)
887 {
888 selected = thechoice->GetCount()-1;
889 }
890 }
891
892 thechoice->SetSelection(selected);
893 inputObject = thechoice;
894 break;
895 }
896
897 case MapObject::Input::Type::Slider:
898 {
899 if (item.intvalue < input.second.minvalue) item.intvalue = input.second.minvalue;
900 if (item.intvalue > input.second.maxvalue) item.intvalue = input.second.maxvalue;
901 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);
902 break;
903 }
904 }
905
906 sizer->Add(inputObject, 0, wxEXPAND | wxALIGN_LEFT | wxBOTTOM, 10);
907 }
908
909 entityPropertyEditor->Layout();
910 }
911}
912
843void MapeditFrame::NewWorld() 913void MapeditFrame::NewWorld()
844{ 914{
845 LaunchWindow(new World()); 915 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 {
74 void OnSetUpmapMap(wxCommandEvent& event); 74 void OnSetUpmapMap(wxCommandEvent& event);
75 void OnSetDownmapType(wxCommandEvent& event); 75 void OnSetDownmapType(wxCommandEvent& event);
76 void OnSetDownmapMap(wxCommandEvent& event); 76 void OnSetDownmapMap(wxCommandEvent& event);
77 void OnSelectEntity(wxCommandEvent& event);
77 78
78 World* world; 79 World* world;
79 Map* currentMap; 80 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 @@
8#include <memory> 8#include <memory>
9#include <wx/treectrl.h> 9#include <wx/treectrl.h>
10#include <map> 10#include <map>
11#include "object.h"
11 12
12class MapObject;
13class World; 13class World;
14class MapeditFrame; 14class MapeditFrame;
15class MapEntryObject;
15 16
16class MapLoadException: public std::exception 17class MapLoadException: public std::exception
17{ 18{
@@ -41,21 +42,6 @@ class MapWriteException: public std::exception
41 std::string mapname; 42 std::string mapname;
42}; 43};
43 44
44struct MapObjectEntry {
45 MapObject* object;
46 std::pair<int, int> position;
47
48 bool operator==(MapObjectEntry& other) const
49 {
50 return (object == other.object) && (position == other.position);
51 }
52
53 bool operator!=(MapObjectEntry& other) const
54 {
55 return (object != other.object) && (position != other.position);
56 }
57};
58
59class Map { 45class Map {
60 public: 46 public:
61 Map(int id, World* world); 47 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 @@
2#include <dirent.h> 2#include <dirent.h>
3#include <libxml/parser.h> 3#include <libxml/parser.h>
4#include <memory> 4#include <memory>
5#include "world.h"
5 6
6static std::map<std::string, std::shared_ptr<MapObject>> allObjects; 7static std::map<std::string, MapObject> allObjects;
7static bool objsInit = false; 8static bool objsInit = false;
8 9
9const std::map<std::string, std::shared_ptr<MapObject>> MapObject::getAllObjects() 10const std::map<std::string, MapObject>& MapObject::getAllObjects()
10{ 11{
11 if (!objsInit) 12 if (!objsInit)
12 { 13 {
13 DIR* dir = opendir("entities/"); 14 try
14 if (dir != NULL)
15 { 15 {
16 struct dirent* ent; 16 xmlDocPtr doc = xmlParseFile("res/entities.xml");
17 while ((ent = readdir(dir)) != NULL) 17 if (doc == nullptr)
18 { 18 {
19 std::string path = ent->d_name; 19 throw MapObjectLoadException("can't open file");
20 if ((path.length() >= 4) && (path.substr(path.length() - 4, 4) == ".xml")) 20 }
21
22 xmlNodePtr top = xmlDocGetRootElement(doc);
23 if (top == nullptr)
24 {
25 throw MapObjectLoadException("missing root element");
26 }
27
28 if (xmlStrcmp(top->name, (const xmlChar*) "entities"))
29 {
30 throw MapObjectLoadException("root element is not entities");
31 }
32
33 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next)
34 {
35 if (!xmlStrcmp(node->name, (const xmlChar*) "entity"))
21 { 36 {
22 std::string name = path.substr(0, path.length() - 4); 37 xmlChar* idKey = xmlGetProp(node, (xmlChar*) "id");
23 auto obj = std::make_shared<MapObject>(name.c_str()); 38 if (idKey == 0) throw MapObjectLoadException("entity missing id");
24 39 std::string theID = (char*) idKey;
25 allObjects[name] = obj; 40 xmlFree(idKey);
41
42 allObjects.emplace(theID, theID);
43 MapObject& mapObject = allObjects.at(theID);
44
45 xmlChar* nameKey = xmlGetProp(node, (xmlChar*) "name");
46 if (nameKey == 0) throw MapObjectLoadException("entity missing name");
47 mapObject.name = (char*) nameKey;
48 xmlFree(nameKey);
49
50 xmlChar* spriteKey = xmlGetProp(node, (xmlChar*) "sprite");
51 if (spriteKey == 0) throw MapObjectLoadException("entity missing sprite");
52 mapObject.sprite = wxImage((char*) spriteKey);
53 xmlFree(spriteKey);
54
55 xmlChar* widthKey = xmlGetProp(node, (xmlChar*) "width");
56 if (widthKey == 0) throw MapObjectLoadException("entity missing width");
57 mapObject.width = atoi((char*) widthKey);
58 xmlFree(widthKey);
59
60 xmlChar* heightKey = xmlGetProp(node, (xmlChar*) "height");
61 if (heightKey == 0) throw MapObjectLoadException("entity missing height");
62 mapObject.height = atoi((char*) heightKey);
63 xmlFree(heightKey);
64
65 for (xmlNodePtr entityNode = node->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next)
66 {
67 if (!xmlStrcmp(entityNode->name, (const xmlChar*) "input"))
68 {
69 xmlChar* key = xmlGetProp(entityNode, (xmlChar*) "id");
70 if (key == 0) throw MapObjectLoadException("input missing id");
71 std::string inputID = (char*) key;
72 xmlFree(key);
73
74 Input& input = mapObject.inputs[inputID];
75
76 key = xmlGetProp(entityNode, (xmlChar*) "name");
77 if (key == 0) throw MapObjectLoadException("input missing name");
78 input.name = (char*) key;
79 xmlFree(key);
80
81 key = xmlGetProp(entityNode, (xmlChar*) "type");
82 if (key == 0) throw MapObjectLoadException("input missing type");
83 std::string inputType = (char*) key;
84 xmlFree(key);
85
86 if (inputType == "choice")
87 {
88 input.type = Input::Type::Choice;
89
90 for (xmlNodePtr choiceNode = entityNode->xmlChildrenNode; choiceNode != NULL; choiceNode = choiceNode->next)
91 {
92 if (!xmlStrcmp(choiceNode->name, (xmlChar*) "value"))
93 {
94 key = xmlGetProp(choiceNode, (xmlChar*) "id");
95 if (key == 0) throw MapObjectLoadException("input value missing id");
96 int valueId = atoi((char*) key);
97 xmlFree(key);
98
99 key = xmlNodeGetContent(choiceNode);
100 if (key == 0) throw MapObjectLoadException("input value missing content");
101 std::string choiceText = (char*) key;
102 xmlFree(key);
103
104 input.choices[valueId] = choiceText;
105 }
106 }
107 } else if (inputType == "slider")
108 {
109 input.type = Input::Type::Slider;
110
111 key = xmlGetProp(entityNode, (xmlChar*) "minvalue");
112 if (key == 0) throw MapObjectLoadException("integer input missing minvalue");
113 input.minvalue = atoi((char*) key);
114 xmlFree(key);
115
116 key = xmlGetProp(entityNode, (xmlChar*) "maxvalue");
117 if (key == 0) throw MapObjectLoadException("integer input missing maxvalue");
118 input.maxvalue = atoi((char*) key);
119 xmlFree(key);
120 }
121 }
122 }
26 } 123 }
27 } 124 }
125 } catch (std::exception& ex)
126 {
127 wxMessageBox(ex.what(), "Error loading objects", wxOK | wxCENTRE | wxICON_ERROR);
128 exit(3);
28 } 129 }
29 130
30 objsInit = true; 131 objsInit = true;
@@ -33,67 +134,170 @@ const std::map<std::string, std::shared_ptr<MapObject>> MapObject::getAllObjects
33 return allObjects; 134 return allObjects;
34} 135}
35 136
36MapObject::MapObject(const char* filename) 137MapObject::MapObject(std::string id) : id(id)
37{ 138{
38 type = filename;
39 139
40 xmlDocPtr doc = xmlParseFile(("entities/" + std::string(filename) + ".xml").c_str()); 140}
41 if (doc == nullptr) throw MapObjectLoadException(filename);
42 141
43 xmlNodePtr top = xmlDocGetRootElement(doc); 142std::string MapObject::getID() const
44 if (top == nullptr) throw MapObjectLoadException(filename); 143{
144 return id;
145}
45 146
46 if (xmlStrcmp(top->name, (const xmlChar*) "entity-def")) 147std::string MapObject::getName() const
47 { 148{
48 throw MapObjectLoadException(filename); 149 return name;
49 } 150}
151
152wxBitmap MapObject::getSprite() const
153{
154 return sprite;
155}
156
157int MapObject::getWidth() const
158{
159 return width;
160}
161
162int MapObject::getHeight() const
163{
164 return height;
165}
166
167const std::map<std::string, MapObject::Input>& MapObject::getInputs() const
168{
169 return inputs;
170}
171
172const MapObject::Input& MapObject::getInput(std::string id) const
173{
174 return inputs.at(id);
175}
176
177bool MapObject::operator==(const MapObject& other) const
178{
179 return id == other.id;
180}
181
182bool MapObject::operator!=(const MapObject& other) const
183{
184 return id != other.id;
185}
186
187MapObjectEntry::MapObjectEntry(const MapObject& object, int posx, int posy) : object(object)
188{
189 position = std::make_pair(posx, posy);
190}
191
192const MapObject& MapObjectEntry::getObject() const
193{
194 return object;
195}
196
197std::pair<int, int> MapObjectEntry::getPosition() const
198{
199 return position;
200}
201
202MapObjectEntry::Item& MapObjectEntry::getItem(std::string str)
203{
204 return items[str];
205}
206
207const std::map<std::string, MapObjectEntry::Item>& MapObjectEntry::getItems() const
208{
209 return items;
210}
211
212void MapObjectEntry::addItem(std::string id, Item& item)
213{
214 items[id] = item;
215}
216
217void MapObjectEntry::setPosition(int x, int y)
218{
219 position = std::make_pair(x, y);
220}
221
222bool MapObjectEntry::operator==(const MapObjectEntry& other) const
223{
224 return (object == other.object) && (position == other.position);
225}
226
227bool MapObjectEntry::operator!=(const MapObjectEntry& other) const
228{
229 return (object != other.object) && (position != other.position);
230}
231
232VariableChoiceValidator::VariableChoiceValidator(World& world, MapObjectEntry::Item& item) : world(world), item(item)
233{
234
235}
50 236
51 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next) 237wxObject* VariableChoiceValidator::Clone() const
238{
239 return new VariableChoiceValidator(world, item);
240}
241
242bool VariableChoiceValidator::TransferFromWindow()
243{
244 wxChoice* choice = (wxChoice*) GetWindow();
245 int sel = choice->GetSelection();
246 int val = (intptr_t) choice->GetClientData(sel);
247 item.intvalue = val;
248 world.setDirty(true);
249
250 return true;
251}
252
253bool VariableChoiceValidator::TransferToWindow()
254{
255 wxChoice* choice = (wxChoice*) GetWindow();
256 for (size_t i=0; i<choice->GetCount(); i++)
52 { 257 {
53 if (!xmlStrcmp(node->name, (const xmlChar*) "sprite")) 258 if ((intptr_t) choice->GetClientData(i) == item.intvalue)
54 {
55 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
56 std::string spriteFile = (char*) key;
57 xmlFree(key);
58
59 sprite = wxImage(spriteFile);
60 } else if (!xmlStrcmp(node->name, (const xmlChar*) "action"))
61 { 259 {
62 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); 260 choice->SetSelection(i);
63 action = (char*) key; 261 return true;
64 xmlFree(key);
65 } else if (!xmlStrcmp(node->name, (const xmlChar*) "size"))
66 {
67 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
68 sscanf((char*) key, "%d,%d", &width, &height);
69 xmlFree(key);
70 } 262 }
71 } 263 }
264
265 return false;
266}
72 267
73 xmlFreeDoc(doc); 268bool VariableChoiceValidator::Validate(wxWindow*)
269{
270 return true;
74} 271}
75 272
76wxBitmap MapObject::getSprite() const 273SliderItemValidator::SliderItemValidator(World& world, MapObjectEntry::Item& item) : world(world), item(item)
77{ 274{
78 return sprite; 275
79} 276}
80 277
81std::string MapObject::getAction() const 278wxObject* SliderItemValidator::Clone() const
82{ 279{
83 return action; 280 return new SliderItemValidator(world, item);
84} 281}
85 282
86int MapObject::getWidth() const 283bool SliderItemValidator::TransferFromWindow()
87{ 284{
88 return width; 285 wxSlider* slider = (wxSlider*) GetWindow();
286 item.intvalue = slider->GetValue();
287 world.setDirty(true);
288
289 return true;
89} 290}
90 291
91int MapObject::getHeight() const 292bool SliderItemValidator::TransferToWindow()
92{ 293{
93 return height; 294 wxSlider* slider = (wxSlider*) GetWindow();
295 slider->SetValue(item.intvalue);
296
297 return true;
94} 298}
95 299
96std::string MapObject::getType() const 300bool SliderItemValidator::Validate(wxWindow*)
97{ 301{
98 return type; 302 return true;
99} 303}
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 @@
10#include <string> 10#include <string>
11#include <map> 11#include <map>
12 12
13class World;
14
13class MapObjectLoadException: public std::exception 15class MapObjectLoadException: public std::exception
14{ 16{
15 public: 17 public:
16 MapObjectLoadException(std::string mapname) : mapname(mapname) {} 18 MapObjectLoadException(std::string stuff) : stuff(stuff) {}
17 19
18 virtual const char* what() const throw() 20 virtual const char* what() const throw()
19 { 21 {
20 return ("An error occured loading map object " + mapname).c_str(); 22 return ("An error occured loading map objects: " + stuff).c_str();
21 } 23 }
22 24
23 private: 25 private:
24 std::string mapname; 26 std::string stuff;
25}; 27};
26 28
27class MapObject { 29class MapObject {
28 public: 30 public:
29 MapObject(const char* filename); 31 MapObject(std::string id);
32
33 static const std::map<std::string, MapObject>& getAllObjects();
30 34
31 static const std::map<std::string, std::shared_ptr<MapObject>> getAllObjects(); 35 struct Input {
36 enum class Type {
37 Slider,
38 Choice
39 };
40
41 std::string name;
42 Type type;
43 int minvalue;
44 int maxvalue;
45 std::map<int, std::string> choices;
46 };
32 47
33 std::string getType() const; 48 std::string getID() const;
49 std::string getName() const;
34 wxBitmap getSprite() const; 50 wxBitmap getSprite() const;
35 std::string getAction() const;
36 int getWidth() const; 51 int getWidth() const;
37 int getHeight() const; 52 int getHeight() const;
53 const std::map<std::string, Input>& getInputs() const;
54 const Input& getInput(std::string id) const;
55
56 bool operator==(const MapObject& other) const;
57 bool operator!=(const MapObject& other) const;
38 58
39 private: 59 private:
40 std::string type; 60 const std::string id;
61 std::string name;
41 wxBitmap sprite; 62 wxBitmap sprite;
42 std::string action;
43 int width; 63 int width;
44 int height; 64 int height;
65 std::map<std::string, Input> inputs;
66};
67
68class MapObjectEntry {
69 public:
70 MapObjectEntry(const MapObject& object, int posx, int posy);
71
72 struct Item {
73 MapObject::Input::Type type;
74 int intvalue;
75 };
76
77 const MapObject& getObject() const;
78 std::pair<int, int> getPosition() const;
79 Item& getItem(std::string str);
80 const std::map<std::string, Item>& getItems() const;
81
82 void setPosition(int x, int y);
83 void addItem(std::string id, Item& item);
84
85 bool operator==(const MapObjectEntry& other) const;
86 bool operator!=(const MapObjectEntry& other) const;
87
88 private:
89 const MapObject& object;
90 std::pair<int, int> position;
91 std::map<std::string, Item> items;
92};
93
94class VariableChoiceValidator : public wxValidator {
95 public:
96 VariableChoiceValidator(World& world, MapObjectEntry::Item& item);
97 wxObject* Clone() const;
98 bool TransferFromWindow();
99 bool TransferToWindow();
100 bool Validate(wxWindow* parent);
101
102 private:
103 World& world;
104 MapObjectEntry::Item& item;
105};
106
107class SliderItemValidator : public wxValidator {
108 public:
109 SliderItemValidator(World& world, MapObjectEntry::Item& item);
110 wxObject* Clone() const;
111 bool TransferFromWindow();
112 bool TransferToWindow();
113 bool Validate(wxWindow* parent);
114
115 private:
116 World& world;
117 MapObjectEntry::Item& item;
45}; 118};
46 119
47#endif 120#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)
28 EVT_KILL_FOCUS(UndoableTextBox::OnKillFocus) 28 EVT_KILL_FOCUS(UndoableTextBox::OnKillFocus)
29wxEND_EVENT_TABLE() 29wxEND_EVENT_TABLE()
30 30
31UndoableTextBox::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) 31UndoableTextBox::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)
32{ 32{
33 this->undoText = undoType; 33 this->undoText = undoType;
34 this->realParent = realParent; 34 this->realParent = realParent;
@@ -93,3 +93,75 @@ void UndoableTextBox::Undo::endChanges()
93{ 93{
94 parent.undo.reset(); 94 parent.undo.reset();
95} 95}
96
97UndoableChoice::UndoableChoice()
98{
99 Init();
100}
101
102UndoableChoice::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)
103{
104 this->realParent = realParent;
105
106 Init();
107}
108
109void UndoableChoice::Init()
110{
111 Bind(wxEVT_CHOICE, &UndoableChoice::OnChoose, this);
112}
113
114void UndoableChoice::OnChoose(wxCommandEvent& event)
115{
116 int new_selection = GetSelection();
117 GetValidator()->TransferToWindow();
118 int old_selection = GetSelection();
119
120 realParent->commitAction(std::make_shared<Undoable>(("Set " + GetName()).ToStdString(), [=] () {
121 SetSelection(new_selection);
122 GetValidator()->TransferFromWindow();
123 }, [=] () {
124 SetSelection(old_selection);
125 GetValidator()->TransferFromWindow();
126 }));
127
128 event.Skip();
129}
130
131wxBEGIN_EVENT_TABLE(UndoableSlider, wxSlider)
132 EVT_SCROLL(UndoableSlider::OnSlide)
133wxEND_EVENT_TABLE()
134
135UndoableSlider::UndoableSlider()
136{
137 Init();
138}
139
140UndoableSlider::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)
141{
142 this->realParent = realParent;
143
144 Init();
145}
146
147void UndoableSlider::Init()
148{
149
150}
151
152void UndoableSlider::OnSlide(wxScrollEvent& event)
153{
154 int new_value = GetValue();
155 GetValidator()->TransferToWindow();
156 int old_value = GetValue();
157
158 realParent->commitAction(std::make_shared<Undoable>(("Set " + GetName()).ToStdString(), [=] () {
159 SetValue(new_value);
160 GetValidator()->TransferFromWindow();
161 }, [=] () {
162 SetValue(old_value);
163 GetValidator()->TransferFromWindow();
164 }));
165
166 event.Skip();
167}
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 {
32class UndoableTextBox : public wxTextCtrl { 32class UndoableTextBox : public wxTextCtrl {
33 public: 33 public:
34 UndoableTextBox(); 34 UndoableTextBox();
35 UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize, long style = 0); 35 UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0);
36 36
37 private: 37 private:
38 class Undo : public Undoable { 38 class Undo : public Undoable {
@@ -60,4 +60,32 @@ class UndoableTextBox : public wxTextCtrl {
60 wxDECLARE_EVENT_TABLE(); 60 wxDECLARE_EVENT_TABLE();
61}; 61};
62 62
63class UndoableChoice : public wxChoice {
64 public:
65 UndoableChoice();
66 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);
67
68 protected:
69 void Init();
70 void OnChoose(wxCommandEvent& event);
71
72 private:
73 MapeditFrame* realParent;
74};
75
76class UndoableSlider : public wxSlider {
77 public:
78 UndoableSlider();
79 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);
80
81 protected:
82 void Init();
83 void OnSlide(wxScrollEvent& event);
84
85 private:
86 MapeditFrame* realParent;
87
88 wxDECLARE_EVENT_TABLE();
89};
90
63#endif 91#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)
16 EVT_LEAVE_WINDOW(MapeditWidget::OnMouseOut) 16 EVT_LEAVE_WINDOW(MapeditWidget::OnMouseOut)
17 EVT_SCROLLWIN(MapeditWidget::OnScroll) 17 EVT_SCROLLWIN(MapeditWidget::OnScroll)
18END_EVENT_TABLE() 18END_EVENT_TABLE()
19
20wxDEFINE_EVENT(EVT_MAP_SELECTED_ENTITY, wxCommandEvent);
19 21
20MapeditWidget::MapeditWidget() 22MapeditWidget::MapeditWidget()
21{ 23{
@@ -107,12 +109,12 @@ void MapeditWidget::OnPaint(wxPaintEvent&)
107 { 109 {
108 tiles_dc.SelectObject(wxNullBitmap); 110 tiles_dc.SelectObject(wxNullBitmap);
109 111
110 wxBitmap sprite = object->object->getSprite(); 112 wxBitmap sprite = object->getObject().getSprite();
111 tiles_dc.SelectObject(sprite); 113 tiles_dc.SelectObject(sprite);
112 114
113 wxPoint pos {(object->position.first + EDITOR_SPACING_X)*scale-vX, (object->position.second + EDITOR_SPACING_Y)*scale-vY}; 115 wxPoint pos {(object->getPosition().first + EDITOR_SPACING_X)*scale-vX, (object->getPosition().second + EDITOR_SPACING_Y)*scale-vY};
114 wxSize size {object->object->getWidth()*scale, object->object->getHeight()*scale}; 116 wxSize size {object->getObject().getWidth()*scale, object->getObject().getHeight()*scale};
115 dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, object->object->getWidth(), object->object->getHeight()); 117 dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, object->getObject().getWidth(), object->getObject().getHeight());
116 118
117 if (editMode == EditEntities) 119 if (editMode == EditEntities)
118 { 120 {
@@ -197,14 +199,14 @@ void MapeditWidget::OnPaint(wxPaintEvent&)
197 dc.DrawRectangle(pos.x, pos.y, size.GetWidth(), size.GetHeight()); 199 dc.DrawRectangle(pos.x, pos.y, size.GetWidth(), size.GetHeight());
198 } else if ((editMode == EditEntities) && (movingEntity != nullptr)) 200 } else if ((editMode == EditEntities) && (movingEntity != nullptr))
199 { 201 {
200 wxBitmap sprite = movingEntity->object->getSprite(); 202 wxBitmap sprite = movingEntity->getObject().getSprite();
201 tiles_dc.SelectObject(wxNullBitmap); 203 tiles_dc.SelectObject(wxNullBitmap);
202 tiles_dc.SelectObject(sprite); 204 tiles_dc.SelectObject(sprite);
203 205
204 wxPoint pos {mousePos.x - movingEntity->object->getWidth()/2*scale, mousePos.y - movingEntity->object->getHeight()/2*scale}; 206 wxPoint pos {mousePos.x - movingEntity->getObject().getWidth()/2*scale, mousePos.y - movingEntity->getObject().getHeight()/2*scale};
205 wxSize size {movingEntity->object->getWidth()*scale, movingEntity->object->getHeight()*scale}; 207 wxSize size {movingEntity->getObject().getWidth()*scale, movingEntity->getObject().getHeight()*scale};
206 208
207 dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, movingEntity->object->getWidth(), movingEntity->object->getHeight()); 209 dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, movingEntity->getObject().getWidth(), movingEntity->getObject().getHeight());
208 210
209 wxPen pen(*wxGREEN, 2); 211 wxPen pen(*wxGREEN, 2);
210 dc.SetPen(pen); 212 dc.SetPen(pen);
@@ -254,17 +256,23 @@ void MapeditWidget::OnClick(wxMouseEvent& event)
254 int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale - (addingEntity->getWidth() / 2); 256 int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale - (addingEntity->getWidth() / 2);
255 int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale - (addingEntity->getHeight() / 2); 257 int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale - (addingEntity->getHeight() / 2);
256 258
257 auto data = std::make_shared<MapObjectEntry>(); 259 auto data = std::make_shared<MapObjectEntry>(*addingEntity, x, y);
258 data->object = addingEntity; 260 frame->commitAction(std::make_shared<Undoable>("Add " + addingEntity->getName(), [=] () {
259 data->position = std::make_pair(x,y);
260
261 frame->commitAction(std::make_shared<Undoable>("Add " + addingEntity->getType(), [=] () {
262 map->addObject(data); 261 map->addObject(data);
263 262
264 Refresh(); 263 Refresh();
265 }, [=] () { 264 }, [=] () {
266 map->removeObject(data); 265 map->removeObject(data);
267 266
267 if (data == selectedEntity)
268 {
269 selectedEntity.reset();
270
271 wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY};
272 sev.SetClientData(0);
273 ProcessEvent(sev);
274 }
275
268 Refresh(); 276 Refresh();
269 })); 277 }));
270 278
@@ -272,17 +280,17 @@ void MapeditWidget::OnClick(wxMouseEvent& event)
272 addingEntity = nullptr; 280 addingEntity = nullptr;
273 } else if (movingEntity != nullptr) 281 } else if (movingEntity != nullptr)
274 { 282 {
275 int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale - (movingEntity->object->getWidth() / 2); 283 int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale - (movingEntity->getObject().getWidth() / 2);
276 int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale - (movingEntity->object->getHeight() / 2); 284 int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale - (movingEntity->getObject().getHeight() / 2);
277 auto oldPos = movingEntity->position; 285 auto oldPos = movingEntity->getPosition();
278 MapObjectEntry* me = movingEntity; 286 MapObjectEntry* me = movingEntity;
279 287
280 frame->commitAction(std::make_shared<Undoable>("Move " + movingEntity->object->getType(), [=] () { 288 frame->commitAction(std::make_shared<Undoable>("Move " + movingEntity->getObject().getName(), [=] () {
281 me->position = std::make_pair(x,y); 289 me->setPosition(x, y);
282 290
283 Refresh(); 291 Refresh();
284 }, [=] () { 292 }, [=] () {
285 me->position = oldPos; 293 me->setPosition(oldPos.first, oldPos.second);
286 294
287 Refresh(); 295 Refresh();
288 })); 296 }));
@@ -295,24 +303,34 @@ void MapeditWidget::OnClick(wxMouseEvent& event)
295 303
296 if (selectedEntity) 304 if (selectedEntity)
297 { 305 {
298 if ((x > selectedEntity->position.first) && (x < selectedEntity->position.first + selectedEntity->object->getWidth()) 306 if ((x > selectedEntity->getPosition().first)
299 && (y > selectedEntity->position.second) && (y < selectedEntity->position.second + selectedEntity->object->getHeight())) 307 && (x < selectedEntity->getPosition().first + selectedEntity->getObject().getWidth())
308 && (y > selectedEntity->getPosition().second)
309 && (y < selectedEntity->getPosition().second + selectedEntity->getObject().getHeight()))
300 { 310 {
301 movingEntity = selectedEntity.get(); 311 movingEntity = selectedEntity.get();
302 frame->SetIsAddingEntity(true); 312 frame->SetIsAddingEntity(true);
303 } else { 313 } else {
304 selectedEntity.reset(); 314 selectedEntity.reset();
315
316 wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY};
317 sev.SetClientData(0);
318 ProcessEvent(sev);
305 } 319 }
306 320
307 Refresh(); 321 Refresh();
308 } else { 322 } else {
309 for (auto object : map->getObjects()) 323 for (auto object : map->getObjects())
310 { 324 {
311 if ((x >= object->position.first) && (x <= object->position.first + object->object->getWidth()) 325 if ((x >= object->getPosition().first) && (x <= object->getPosition().first + object->getObject().getWidth())
312 && (y >= object->position.second) && (y <= object->position.second + object->object->getHeight())) 326 && (y >= object->getPosition().second) && (y <= object->getPosition().second + object->getObject().getHeight()))
313 { 327 {
314 selectedEntity = object; 328 selectedEntity = object;
315 329
330 wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY};
331 sev.SetClientData(object.get());
332 ProcessEvent(sev);
333
316 Refresh(); 334 Refresh();
317 335
318 break; 336 break;
@@ -364,12 +382,18 @@ void MapeditWidget::OnRightClick(wxMouseEvent& event)
364 int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale; 382 int x = (event.GetPosition().x + vX - EDITOR_SPACING_X*scale) / scale;
365 int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale; 383 int y = (event.GetPosition().y + vY - EDITOR_SPACING_Y*scale) / scale;
366 384
367 if ((x > selectedEntity->position.first) && (x < selectedEntity->position.first + selectedEntity->object->getWidth()) 385 if ((x > selectedEntity->getPosition().first)
368 && (y > selectedEntity->position.second) && (y < selectedEntity->position.second + selectedEntity->object->getHeight())) 386 && (x < selectedEntity->getPosition().first + selectedEntity->getObject().getWidth())
387 && (y > selectedEntity->getPosition().second)
388 && (y < selectedEntity->getPosition().second + selectedEntity->getObject().getHeight()))
369 { 389 {
370 map->removeObject(selectedEntity); 390 map->removeObject(selectedEntity);
371 selectedEntity.reset(); 391 selectedEntity.reset();
372 392
393 wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY};
394 sev.SetClientData(0);
395 ProcessEvent(sev);
396
373 Refresh(); 397 Refresh();
374 } 398 }
375 } 399 }
@@ -494,6 +518,10 @@ void MapeditWidget::StartAddingEntity(MapObject* object)
494 selectedEntity = nullptr; 518 selectedEntity = nullptr;
495 isSettingPos = false; 519 isSettingPos = false;
496 frame->SetIsSettingStart(false); 520 frame->SetIsSettingStart(false);
521
522 wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY};
523 sev.SetClientData(0);
524 ProcessEvent(sev);
497} 525}
498 526
499void MapeditWidget::CancelAddingEntity() 527void MapeditWidget::CancelAddingEntity()
@@ -511,6 +539,10 @@ void MapeditWidget::SetIsSettingStart(bool isSetting)
511 frame->SetIsAddingEntity(false); 539 frame->SetIsAddingEntity(false);
512 addingEntity = nullptr; 540 addingEntity = nullptr;
513 selectedEntity = nullptr; 541 selectedEntity = nullptr;
542
543 wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY};
544 sev.SetClientData(0);
545 ProcessEvent(sev);
514 } else { 546 } else {
515 isSettingPos = false; 547 isSettingPos = false;
516 } 548 }
@@ -524,6 +556,10 @@ void MapeditWidget::SetMap(Map* map)
524 movingEntity = nullptr; 556 movingEntity = nullptr;
525 isSettingPos = false; 557 isSettingPos = false;
526 558
559 wxCommandEvent sev {EVT_MAP_SELECTED_ENTITY};
560 sev.SetClientData(0);
561 ProcessEvent(sev);
562
527 Refresh(); 563 Refresh();
528} 564}
529 565
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;
16class TileWidget; 16class TileWidget;
17class Map; 17class Map;
18class MapObject; 18class MapObject;
19struct MapObjectEntry; 19class MapObjectEntry;
20 20
21#include "consts.h" 21#include "consts.h"
22 22
@@ -76,5 +76,10 @@ class MapeditWidget : public wxScrolledCanvas {
76 DECLARE_DYNAMIC_CLASS(MapeditWidget) 76 DECLARE_DYNAMIC_CLASS(MapeditWidget)
77 DECLARE_EVENT_TABLE() 77 DECLARE_EVENT_TABLE()
78}; 78};
79
80// sends when an entity is selected OR deselected.
81// client data will be a pointer to the MapObjectEntry if selection.
82// client data will be nullptr if deselection.
83wxDECLARE_EVENT(EVT_MAP_SELECTED_ENTITY, wxCommandEvent);
79 84
80#endif 85#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)
22 xmlDocPtr doc = xmlParseFile(filename.c_str()); 22 xmlDocPtr doc = xmlParseFile(filename.c_str());
23 if (doc == nullptr) 23 if (doc == nullptr)
24 { 24 {
25 throw MapLoadException(filename); 25 throw MapLoadException("file not found");
26 } 26 }
27 27
28 xmlNodePtr top = xmlDocGetRootElement(doc); 28 xmlNodePtr top = xmlDocGetRootElement(doc);
29 if (top == nullptr) 29 if (top == nullptr)
30 { 30 {
31 throw MapLoadException(filename); 31 throw MapLoadException("no root element");
32 } 32 }
33 33
34 if (xmlStrcmp(top->name, (const xmlChar*) "world")) 34 if (xmlStrcmp(top->name, (const xmlChar*) "world"))
35 { 35 {
36 throw MapLoadException(filename); 36 throw MapLoadException("no world element");
37 } 37 }
38 38
39 xmlChar* nextmapKey = xmlGetProp(top, (xmlChar*) "nextmap"); 39 xmlChar* nextmapKey = xmlGetProp(top, (xmlChar*) "nextmap");
@@ -51,17 +51,17 @@ World::World(std::string filename)
51 xmlFree(lastmapKey); 51 xmlFree(lastmapKey);
52 52
53 xmlChar* startxKey = xmlGetProp(top, (xmlChar*) "startx"); 53 xmlChar* startxKey = xmlGetProp(top, (xmlChar*) "startx");
54 if (startxKey == 0) throw MapLoadException(filename); 54 if (startxKey == 0) throw MapLoadException("world missing startx attribute");
55 startingPosition.first = atoi((char*) startxKey); 55 startingPosition.first = atoi((char*) startxKey);
56 xmlFree(startxKey); 56 xmlFree(startxKey);
57 57
58 xmlChar* startyKey = xmlGetProp(top, (xmlChar*) "starty"); 58 xmlChar* startyKey = xmlGetProp(top, (xmlChar*) "starty");
59 if (startyKey == 0) throw MapLoadException(filename); 59 if (startyKey == 0) throw MapLoadException("world missing starty attribute");
60 startingPosition.second = atoi((char*) startyKey); 60 startingPosition.second = atoi((char*) startyKey);
61 xmlFree(startyKey); 61 xmlFree(startyKey);
62 62
63 xmlChar* startmapKey = xmlGetProp(top, (xmlChar*) "startmap"); 63 xmlChar* startmapKey = xmlGetProp(top, (xmlChar*) "startmap");
64 if (startxKey == 0) throw MapLoadException(filename); 64 if (startxKey == 0) throw MapLoadException("world missing startmap attribute");
65 startingMap = atoi((char*) startmapKey); 65 startingMap = atoi((char*) startmapKey);
66 xmlFree(startmapKey); 66 xmlFree(startmapKey);
67 67
@@ -69,14 +69,14 @@ World::World(std::string filename)
69 { 69 {
70 if (!xmlStrcmp(node->name, (const xmlChar*) "root")) 70 if (!xmlStrcmp(node->name, (const xmlChar*) "root"))
71 { 71 {
72 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); 72 xmlChar* key = xmlNodeGetContent(node);
73 if (key == 0) throw MapLoadException(filename); 73 if (key == 0) throw MapLoadException("root missing content");
74 rootChildren.push_back(atoi((char*) key)); 74 rootChildren.push_back(atoi((char*) key));
75 xmlFree(key); 75 xmlFree(key);
76 } else if (!xmlStrcmp(node->name, (const xmlChar*) "map")) 76 } else if (!xmlStrcmp(node->name, (const xmlChar*) "map"))
77 { 77 {
78 xmlChar* idKey = xmlGetProp(node, (xmlChar*) "id"); 78 xmlChar* idKey = xmlGetProp(node, (xmlChar*) "id");
79 if (idKey == 0) throw MapLoadException(filename); 79 if (idKey == 0) throw MapLoadException("map missing id attribute");
80 int id = atoi((char*) idKey); 80 int id = atoi((char*) idKey);
81 xmlFree(idKey); 81 xmlFree(idKey);
82 82
@@ -90,7 +90,7 @@ World::World(std::string filename)
90 xmlFree(expandKey); 90 xmlFree(expandKey);
91 91
92 xmlChar* titleKey = xmlGetProp(node, (xmlChar*) "title"); 92 xmlChar* titleKey = xmlGetProp(node, (xmlChar*) "title");
93 if (titleKey == 0) throw MapLoadException(filename); 93 if (titleKey == 0) throw MapLoadException("map missing title attribute");
94 map->setTitle((char*) titleKey, false); 94 map->setTitle((char*) titleKey, false);
95 xmlFree(titleKey); 95 xmlFree(titleKey);
96 96
@@ -98,7 +98,8 @@ World::World(std::string filename)
98 { 98 {
99 if (!xmlStrcmp(mapNode->name, (const xmlChar*) "environment")) 99 if (!xmlStrcmp(mapNode->name, (const xmlChar*) "environment"))
100 { 100 {
101 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1); 101 xmlChar* key = xmlNodeGetContent(mapNode);
102 if (key == 0) throw MapLoadException("map missing environment content");
102 int* mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int)); 103 int* mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int));
103 mapdata[0] = atoi(strtok((char*) key, ",\n")); 104 mapdata[0] = atoi(strtok((char*) key, ",\n"));
104 for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++) 105 for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++)
@@ -109,24 +110,52 @@ World::World(std::string filename)
109 xmlFree(key); 110 xmlFree(key);
110 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "entity")) 111 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "entity"))
111 { 112 {
112 auto data = std::make_shared<MapObjectEntry>();
113
114 xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type"); 113 xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type");
115 if (typeKey == 0) throw MapLoadException(filename); 114 if (typeKey == 0) throw MapLoadException("entity missing type attribute");
116 data->object = MapObject::getAllObjects().at((char*) typeKey).get(); 115 const MapObject& obj = MapObject::getAllObjects().at((char*) typeKey);
117 xmlFree(typeKey); 116 xmlFree(typeKey);
118 117
119 xmlChar* xKey = xmlGetProp(mapNode, (const xmlChar*) "x"); 118 xmlChar* xKey = xmlGetProp(mapNode, (const xmlChar*) "x");
120 if (xKey == 0) throw MapLoadException(filename); 119 if (xKey == 0) throw MapLoadException("entity missing x attribute");
121 data->position.first = atoi((char*) xKey); 120 int xpos = atoi((char*) xKey);
122 xmlFree(xKey); 121 xmlFree(xKey);
123 122
124 xmlChar* yKey = xmlGetProp(mapNode, (const xmlChar*) "y"); 123 xmlChar* yKey = xmlGetProp(mapNode, (const xmlChar*) "y");
125 if (yKey == 0) throw MapLoadException(filename); 124 if (yKey == 0) throw MapLoadException("entity missing y attribute");
126 data->position.second = atoi((char*) yKey); 125 int ypos = atoi((char*) yKey);
127 xmlFree(yKey); 126 xmlFree(yKey);
128 127
128 auto data = std::make_shared<MapObjectEntry>(obj, xpos, ypos);
129
129 map->addObject(data, false); 130 map->addObject(data, false);
131
132 for (xmlNodePtr objectNode = mapNode->xmlChildrenNode; objectNode != NULL; objectNode = objectNode->next)
133 {
134 if (!xmlStrcmp(objectNode->name, (const xmlChar*) "item"))
135 {
136 xmlChar* key = xmlGetProp(objectNode, (const xmlChar*) "id");
137 if (key == 0) throw MapLoadException("item missing id attribute");
138 std::string itemID = (char*) key;
139 xmlFree(key);
140
141 MapObjectEntry::Item item;
142 item.type = data->getObject().getInput(itemID).type;
143
144 key = xmlNodeGetContent(objectNode);
145 if (key == 0) throw MapLoadException("item missing content");
146 switch (item.type)
147 {
148 case MapObject::Input::Type::Choice:
149 case MapObject::Input::Type::Slider:
150 {
151 item.intvalue = atoi((char*) key);
152 break;
153 }
154 }
155
156 data->addItem(itemID, item);
157 }
158 }
130 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "adjacent")) 159 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "adjacent"))
131 { 160 {
132 Map::MoveDir direction; 161 Map::MoveDir direction;
@@ -134,12 +163,12 @@ World::World(std::string filename)
134 int mapId = 0; 163 int mapId = 0;
135 164
136 xmlChar* dirKey = xmlGetProp(mapNode, (const xmlChar*) "dir"); 165 xmlChar* dirKey = xmlGetProp(mapNode, (const xmlChar*) "dir");
137 if (dirKey == 0) throw MapLoadException(filename); 166 if (dirKey == 0) throw MapLoadException("adjacent missing dir attribute");
138 direction = Map::moveDirForShort((char*) dirKey); 167 direction = Map::moveDirForShort((char*) dirKey);
139 xmlFree(dirKey); 168 xmlFree(dirKey);
140 169
141 xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type"); 170 xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type");
142 if (typeKey == 0) throw MapLoadException(filename); 171 if (typeKey == 0) throw MapLoadException("adjacent missing type attribute");
143 moveType = Map::moveTypeForShort((char*) typeKey); 172 moveType = Map::moveTypeForShort((char*) typeKey);
144 xmlFree(typeKey); 173 xmlFree(typeKey);
145 174
@@ -153,7 +182,7 @@ World::World(std::string filename)
153 map->setAdjacent(direction, moveType, mapId, false); 182 map->setAdjacent(direction, moveType, mapId, false);
154 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "child")) 183 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "child"))
155 { 184 {
156 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1); 185 xmlChar* key = xmlNodeGetContent(mapNode);
157 if (key != 0) 186 if (key != 0)
158 { 187 {
159 map->addChild(atoi((char*) key)); 188 map->addChild(atoi((char*) key));
@@ -281,7 +310,7 @@ void World::save(std::string name, wxTreeCtrl* mapTree)
281 } 310 }
282 311
283 // title= 312 // title=
284 rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "name", (xmlChar*) map.getTitle().c_str()); 313 rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "title", (xmlChar*) map.getTitle().c_str());
285 if (rc < 0) throw MapWriteException(name); 314 if (rc < 0) throw MapWriteException(name);
286 315
287 // <environment 316 // <environment
@@ -304,7 +333,7 @@ void World::save(std::string name, wxTreeCtrl* mapTree)
304 mapdata_out << std::endl; 333 mapdata_out << std::endl;
305 } 334 }
306 335
307 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "environment", (xmlChar*) mapdata_out.str().c_str()); 336 rc = xmlTextWriterWriteString(writer, (xmlChar*) mapdata_out.str().c_str());
308 if (rc < 0) throw MapWriteException(name); 337 if (rc < 0) throw MapWriteException(name);
309 338
310 // </environment> 339 // </environment>
@@ -318,16 +347,42 @@ void World::save(std::string name, wxTreeCtrl* mapTree)
318 if (rc < 0) throw MapWriteException(name); 347 if (rc < 0) throw MapWriteException(name);
319 348
320 // type= 349 // type=
321 rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "type", (xmlChar*) object->object->getType().c_str()); 350 rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "type", (xmlChar*) object->getObject().getID().c_str());
322 if (rc < 0) throw MapWriteException(name); 351 if (rc < 0) throw MapWriteException(name);
323 352
324 // x= 353 // x=
325 rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "x", "%d", object->position.first); 354 rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "x", "%d", object->getPosition().first);
326 if (rc < 0) throw MapWriteException(name); 355 if (rc < 0) throw MapWriteException(name);
327 356
328 // y= 357 // y=
329 rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "y", "%d", object->position.second); 358 rc = xmlTextWriterWriteFormatAttribute(writer, (xmlChar*) "y", "%d", object->getPosition().second);
330 if (rc < 0) throw MapWriteException(name); 359 if (rc < 0) throw MapWriteException(name);
360
361 for (auto item : object->getItems())
362 {
363 // <item
364 rc = xmlTextWriterStartElement(writer, (xmlChar*) "item");
365 if (rc < 0) throw MapWriteException(name);
366
367 // id=
368 rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "id", (xmlChar*) item.first.c_str());
369 if (rc < 0) throw MapWriteException(name);
370
371 // >
372 switch (item.second.type)
373 {
374 case MapObject::Input::Type::Slider:
375 case MapObject::Input::Type::Choice:
376 {
377 rc = xmlTextWriterWriteFormatString(writer, "%d", item.second.intvalue);
378 break;
379 }
380 }
381
382 // </item>
383 rc = xmlTextWriterEndElement(writer);
384 if (rc < 0) throw MapWriteException(name);
385 }
331 386
332 // </entity> 387 // </entity>
333 rc = xmlTextWriterEndElement(writer); 388 rc = xmlTextWriterEndElement(writer);