summary refs log tree commit diff stats
path: root/tools/mapedit
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2015-03-16 16:53:05 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2015-03-16 16:53:05 -0400
commit0d30e9b57229905f78e7bd60fe5d3cde72851f28 (patch)
tree4ca2abff9fb1933685f570e97a8b9e88a976c95f /tools/mapedit
parent36536297aac5c07e3d5fb96abed74570fc7615e9 (diff)
downloadtherapy-0d30e9b57229905f78e7bd60fe5d3cde72851f28.tar.gz
therapy-0d30e9b57229905f78e7bd60fe5d3cde72851f28.tar.bz2
therapy-0d30e9b57229905f78e7bd60fe5d3cde72851f28.zip
Rewrote map editor so a single file contains all maps
Maps are viewed in a tree control on the left. They can be dragged and dropped. Maps are bolded when they are dirty. Saving saves expansion status and order of maps in tree.
Diffstat (limited to 'tools/mapedit')
-rw-r--r--tools/mapedit/CMakeLists.txt1
-rw-r--r--tools/mapedit/src/frame.cpp321
-rw-r--r--tools/mapedit/src/frame.h32
-rw-r--r--tools/mapedit/src/main.cpp2
-rw-r--r--tools/mapedit/src/map.cpp289
-rw-r--r--tools/mapedit/src/map.h45
-rw-r--r--tools/mapedit/src/widget.cpp7
-rw-r--r--tools/mapedit/src/widget.h3
-rw-r--r--tools/mapedit/src/world.cpp369
-rw-r--r--tools/mapedit/src/world.h45
10 files changed, 859 insertions, 255 deletions
diff --git a/tools/mapedit/CMakeLists.txt b/tools/mapedit/CMakeLists.txt index 9343f06..12af38c 100644 --- a/tools/mapedit/CMakeLists.txt +++ b/tools/mapedit/CMakeLists.txt
@@ -44,6 +44,7 @@ add_executable(AromatherapyMapEditor
44 src/widget.cpp 44 src/widget.cpp
45 src/tile_widget.cpp 45 src/tile_widget.cpp
46 src/object.cpp 46 src/object.cpp
47 src/world.cpp
47) 48)
48target_link_libraries(AromatherapyMapEditor ${ALL_LIBS}) 49target_link_libraries(AromatherapyMapEditor ${ALL_LIBS})
49install(TARGETS AromatherapyMapEditor RUNTIME DESTINATION ${BIN_DIR}) 50install(TARGETS AromatherapyMapEditor RUNTIME DESTINATION ${BIN_DIR})
diff --git a/tools/mapedit/src/frame.cpp b/tools/mapedit/src/frame.cpp index a38b861..9d489b8 100644 --- a/tools/mapedit/src/frame.cpp +++ b/tools/mapedit/src/frame.cpp
@@ -5,6 +5,7 @@
5#include <wx/statline.h> 5#include <wx/statline.h>
6#include "panel.h" 6#include "panel.h"
7#include <list> 7#include <list>
8#include <exception>
8 9
9static std::list<wxWindow*> openWindows; 10static std::list<wxWindow*> openWindows;
10 11
@@ -15,9 +16,18 @@ enum {
15 MENU_FILE_OPEN, 16 MENU_FILE_OPEN,
16 MENU_FILE_SAVE, 17 MENU_FILE_SAVE,
17 MENU_FILE_CLOSE, 18 MENU_FILE_CLOSE,
19 MENU_MAP_ADD_ROOT,
20 MENU_MAP_ADD_CHILD,
18 TOOL_FILE_NEW, 21 TOOL_FILE_NEW,
19 TOOL_FILE_OPEN, 22 TOOL_FILE_OPEN,
20 TOOL_FILE_SAVE 23 TOOL_FILE_SAVE,
24 TOOL_MAP_ADD_ROOT,
25 TOOL_MAP_ADD_CHILD,
26 MAP_EDITOR_NOTEBOOK,
27 MAP_EDITOR_TREE,
28 MAP_TITLE_TEXTBOX,
29 ADD_ENTITY_BUTTON,
30 CANCEL_ENTITY_BUTTON
21}; 31};
22 32
23wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame) 33wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame)
@@ -27,22 +37,41 @@ wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame)
27 EVT_MENU(MENU_FILE_NEW, MapeditFrame::OnNew) 37 EVT_MENU(MENU_FILE_NEW, MapeditFrame::OnNew)
28 EVT_MENU(MENU_FILE_OPEN, MapeditFrame::OnOpen) 38 EVT_MENU(MENU_FILE_OPEN, MapeditFrame::OnOpen)
29 EVT_MENU(MENU_FILE_SAVE, MapeditFrame::OnSave) 39 EVT_MENU(MENU_FILE_SAVE, MapeditFrame::OnSave)
40 EVT_MENU(MENU_FILE_CLOSE, MapeditFrame::OnClose)
41 EVT_MENU(MENU_MAP_ADD_ROOT, MapeditFrame::OnAddRoot)
42 EVT_MENU(MENU_MAP_ADD_CHILD, MapeditFrame::OnAddChild)
30 EVT_TOOL(TOOL_FILE_NEW, MapeditFrame::OnNew) 43 EVT_TOOL(TOOL_FILE_NEW, MapeditFrame::OnNew)
31 EVT_TOOL(TOOL_FILE_OPEN, MapeditFrame::OnOpen) 44 EVT_TOOL(TOOL_FILE_OPEN, MapeditFrame::OnOpen)
32 EVT_TOOL(TOOL_FILE_SAVE, MapeditFrame::OnSave) 45 EVT_TOOL(TOOL_FILE_SAVE, MapeditFrame::OnSave)
33 EVT_MENU(MENU_FILE_CLOSE, MapeditFrame::OnClose) 46 EVT_TOOL(TOOL_MAP_ADD_ROOT, MapeditFrame::OnAddRoot)
47 EVT_TOOL(TOOL_MAP_ADD_CHILD, MapeditFrame::OnAddChild)
34 EVT_CLOSE(MapeditFrame::OnExit) 48 EVT_CLOSE(MapeditFrame::OnExit)
49 EVT_NOTEBOOK_PAGE_CHANGED(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChange)
50 EVT_NOTEBOOK_PAGE_CHANGING(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChanging)
51 EVT_TREE_SEL_CHANGING(MAP_EDITOR_TREE, MapeditFrame::OnWillSelectMap)
52 EVT_TREE_SEL_CHANGED(MAP_EDITOR_TREE, MapeditFrame::OnDidSelectMap)
53 EVT_TREE_BEGIN_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnWillDragMap)
54 EVT_TREE_END_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnDidDragMap)
55 EVT_TEXT(MAP_TITLE_TEXTBOX, MapeditFrame::OnTitleChange)
56 EVT_BUTTON(ADD_ENTITY_BUTTON, MapeditFrame::OnAddEntity)
57 EVT_BUTTON(CANCEL_ENTITY_BUTTON, MapeditFrame::OnCancelAddEntity)
35wxEND_EVENT_TABLE() 58wxEND_EVENT_TABLE()
36 59
37MapeditFrame::MapeditFrame(Map map, std::string filename) : wxFrame(NULL, wxID_ANY, "Map Editor", wxDefaultPosition, wxSize(GAME_WIDTH*3, GAME_HEIGHT*2)), map(map), filename(filename) 60MapeditFrame::MapeditFrame(std::unique_ptr<World> world) : wxFrame(NULL, wxID_ANY, "Map Editor", wxDefaultPosition, wxSize(GAME_WIDTH*2+TILE_WIDTH*6*6+10+10+150, GAME_HEIGHT*3))
38{ 61{
39 this->map.frame = this; 62 this->world = std::move(world);
63 this->world->setParent(this);
64 currentMap = this->world->getLastMap();
40 65
41 wxMenu* menuFile = new wxMenu; 66 menuFile = new wxMenu;
42 menuFile->Append(MENU_FILE_NEW, "New\tCtrl-N"); 67 menuFile->Append(MENU_FILE_NEW, "New\tCtrl-N");
43 menuFile->Append(MENU_FILE_OPEN, "Open\tCtrl-O"); 68 menuFile->Append(MENU_FILE_OPEN, "Open\tCtrl-O");
44 menuFile->Append(MENU_FILE_SAVE, "Save\tCtrl-S"); 69 menuFile->Append(MENU_FILE_SAVE, "Save\tCtrl-S");
45 menuFile->Append(MENU_FILE_CLOSE, "Close\tCtrl-W"); 70 menuFile->Append(MENU_FILE_CLOSE, "Close\tCtrl-W");
71 menuFile->AppendSeparator();
72 menuFile->Append(MENU_MAP_ADD_ROOT, "New Map\tCtrl-Alt-N");
73 menuFile->Append(MENU_MAP_ADD_CHILD, "New Child Map\tCtrl-Alt-Shift-N");
74 menuFile->AppendSeparator();
46 menuFile->Append(wxID_EXIT); 75 menuFile->Append(wxID_EXIT);
47 76
48 wxMenu* menuView = new wxMenu; 77 wxMenu* menuView = new wxMenu;
@@ -59,22 +88,27 @@ MapeditFrame::MapeditFrame(Map map, std::string filename) : wxFrame(NULL, wxID_A
59 // Layout 2: Non-splitter between layout 3 and notebook 88 // Layout 2: Non-splitter between layout 3 and notebook
60 // Layout 3: Splitter between map editor and properties editor 89 // Layout 3: Splitter between map editor and properties editor
61 90
62 wxSplitterWindow* layout3 = new wxSplitterWindow(this, wxID_ANY); 91 wxSplitterWindow* layout1 = new wxSplitterWindow(this, wxID_ANY);
92 mapTree = new wxTreeCtrl(layout1, MAP_EDITOR_TREE, wxDefaultPosition, wxSize(200, 0), wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS);
93 wxTreeItemId mapTreeRoot = mapTree->AddRoot("root");
94 populateMapTree(mapTreeRoot, this->world->getRootMaps());
95
96 wxPanel* layout2 = new wxPanel(layout1, wxID_ANY);
97
98 wxSplitterWindow* layout3 = new wxSplitterWindow(layout2, wxID_ANY);
63 layout3->SetSashGravity(1.0); 99 layout3->SetSashGravity(1.0);
64 layout3->SetMinimumPaneSize(20);
65 100
66 notebook = new wxNotebook(this, wxID_ANY); 101 notebook = new wxNotebook(layout2, MAP_EDITOR_NOTEBOOK);
67 102
68 tileEditor = new TileWidget(notebook, wxID_ANY, 6, 6, wxPoint(0,0), wxSize(TILE_WIDTH*6*6,TILE_HEIGHT*10*6)); 103 tileEditor = new TileWidget(notebook, wxID_ANY, 6, 6, wxPoint(0,0), wxSize(TILE_WIDTH*6*6,TILE_HEIGHT*10*6));
69 notebook->AddPage(tileEditor, "Tile Chooser", true); 104 notebook->AddPage(tileEditor, "Tile Chooser", false);
70 105
71 mapEditor = new MapeditWidget(layout3, wxID_ANY, &this->map, tileEditor, wxPoint(0,0), wxSize(GAME_WIDTH*2, GAME_HEIGHT*2)); 106 mapEditor = new MapeditWidget(layout3, wxID_ANY, currentMap, tileEditor, wxPoint(0,0), wxSize(GAME_WIDTH*2, GAME_HEIGHT*2));
72 mapEditor->frame = this; 107 mapEditor->frame = this;
73 108
74 // Set up property editor 109 // Set up property editor
75 wxPanel* propertyEditor = new wxPanel(layout3, wxID_ANY); 110 wxPanel* propertyEditor = new wxPanel(layout3, wxID_ANY);
76 titleBox = new wxTextCtrl(propertyEditor, wxID_ANY, map.getTitle()); 111 titleBox = new wxTextCtrl(propertyEditor, MAP_TITLE_TEXTBOX, currentMap->getTitle());
77 titleBox->Bind(wxEVT_TEXT, &MapeditFrame::OnTitleChange, this);
78 112
79 wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:"); 113 wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:");
80 114
@@ -103,12 +137,9 @@ MapeditFrame::MapeditFrame(Map map, std::string filename) : wxFrame(NULL, wxID_A
103 entityTypeBox->Append(entry.second->getType(), entry.second.get()); 137 entityTypeBox->Append(entry.second->getType(), entry.second.get());
104 } 138 }
105 139
106 addEntityButton = new wxButton(entityEditor, wxID_ANY, "Add Entity"); 140 addEntityButton = new wxButton(entityEditor, ADD_ENTITY_BUTTON, "Add Entity");
107 addEntityButton->Bind(wxEVT_BUTTON, &MapeditFrame::OnAddEntity, this); 141 cancelEntityButton = new wxButton(entityEditor, CANCEL_ENTITY_BUTTON, "Cancel");
108
109 cancelEntityButton = new wxButton(entityEditor, wxID_ANY, "Cancel");
110 cancelEntityButton->Disable(); 142 cancelEntityButton->Disable();
111 cancelEntityButton->Bind(wxEVT_BUTTON, &MapeditFrame::OnCancelAddEntity, this);
112 143
113 wxStaticText* entityInfoLabel = new wxStaticText(entityEditor, wxID_ANY, "Click and drag an entity to move it.\nRight click an entity to delete it."); 144 wxStaticText* entityInfoLabel = new wxStaticText(entityEditor, wxID_ANY, "Click and drag an entity to move it.\nRight click an entity to delete it.");
114 145
@@ -129,51 +160,65 @@ MapeditFrame::MapeditFrame(Map map, std::string filename) : wxFrame(NULL, wxID_A
129 160
130 // Finish setting up the layouts 161 // Finish setting up the layouts
131 layout3->SplitHorizontally(mapEditor, propertyEditor); 162 layout3->SplitHorizontally(mapEditor, propertyEditor);
163 layout1->SplitVertically(mapTree, layout2);
132 164
133 notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &MapeditFrame::OnTabChange, this); 165 wxBoxSizer* sizer1 = new wxBoxSizer(wxHORIZONTAL);
134 notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGING, &MapeditFrame::OnTabChanging, this); 166 sizer1->Add(layout1, 1, wxEXPAND, 0);
167 sizer1->Add(mapTree, 0, wxALIGN_TOP | wxALIGN_LEFT, 0);
168 sizer1->Add(layout2, 1, wxEXPAND, 0);
169 layout1->SetSizer(sizer1);
170 sizer1->SetSizeHints(layout1);
135 171
136 wxBoxSizer* sizer2 = new wxBoxSizer(wxHORIZONTAL); 172 wxBoxSizer* sizer2 = new wxBoxSizer(wxHORIZONTAL);
137 sizer2->Add(layout3, 1, wxEXPAND, 0); 173 sizer2->Add(layout3, 1, wxEXPAND, 0);
138 sizer2->Add(notebook, 0, wxALIGN_TOP | wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxEXPAND, 2); 174 sizer2->Add(notebook, 0, wxALIGN_TOP | wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxEXPAND, 2);
139 this->SetSizer(sizer2); 175 layout2->SetSizer(sizer2);
140 sizer2->SetSizeHints(this); 176 sizer2->SetSizeHints(layout2);
141 177
142 wxBoxSizer* splitterSizer = new wxBoxSizer(wxVERTICAL); 178 wxBoxSizer* splitterSizer = new wxBoxSizer(wxVERTICAL);
143 splitterSizer->Add(layout3, 1, wxEXPAND, 0); 179 splitterSizer->Add(layout3, 1, wxEXPAND, 0);
144 splitterSizer->Add(mapEditor, 1, wxEXPAND, 0); 180 splitterSizer->Add(mapEditor, 1, wxEXPAND, 0);
145 splitterSizer->Add(propertyEditor, 0, wxALIGN_TOP, wxALIGN_LEFT, 0); 181 splitterSizer->Add(propertyEditor, 0, wxALIGN_TOP | wxALIGN_LEFT, 0);
146 layout3->SetSizer(splitterSizer); 182 layout3->SetSizer(splitterSizer);
147 splitterSizer->SetSizeHints(layout3); 183 splitterSizer->SetSizeHints(layout3);
148 184
149 // Toolbar time! 185 // Toolbar time!
150 toolbar = CreateToolBar(); 186 toolbar = CreateToolBar();
151 toolbar->AddTool(TOOL_FILE_NEW, "New", wxBitmap(wxImage("res/page_add.png"))); 187 toolbar->AddTool(TOOL_FILE_NEW, "New", wxBitmap(wxImage("res/page.png")));
152 toolbar->AddTool(TOOL_FILE_OPEN, "Open", wxBitmap(wxImage("res/folder_page.png"))); 188 toolbar->AddTool(TOOL_FILE_OPEN, "Open", wxBitmap(wxImage("res/folder_page.png")));
153 toolbar->AddTool(TOOL_FILE_SAVE, "Save", wxBitmap(wxImage("res/disk.png"))); 189 toolbar->AddTool(TOOL_FILE_SAVE, "Save", wxBitmap(wxImage("res/disk.png")));
154 toolbar->EnableTool(TOOL_FILE_SAVE, this->map.getDirty()); 190 toolbar->AddSeparator();
191 toolbar->AddTool(TOOL_MAP_ADD_ROOT, "Add Map", wxBitmap(wxImage("res/page_add.png")));
192 toolbar->AddTool(TOOL_MAP_ADD_CHILD, "Add Child Map", wxBitmap(wxImage("res/page_white_add.png")));
193 toolbar->EnableTool(TOOL_FILE_SAVE, this->world->getDirty());
155 toolbar->Realize(); 194 toolbar->Realize();
195
196 mapTree->SetFocusedItem(currentMap->getTreeItemId());
197 SelectMap(currentMap);
198
199 Maximize(true);
156} 200}
157 201
158void MapeditFrame::OnExit(wxCloseEvent& event) 202void MapeditFrame::OnExit(wxCloseEvent& event)
159{ 203{
160 if (event.CanVeto() && map.hasUnsavedChanges()) 204 if (event.CanVeto() && world->getDirty())
161 { 205 {
162 switch (wxMessageBox("Current map has unsaved changes. Save before closing?", "Please confirm", wxICON_QUESTION|wxYES_NO|wxCANCEL, this)) 206 switch (wxMessageBox("One or more maps have unsaved changes. Save before closing?", "Please confirm", wxICON_QUESTION|wxYES_NO|wxCANCEL, this))
163 { 207 {
164 case wxYES: 208 case wxYES:
165 if (filename == "") 209 if (world->getFilename() == "")
166 { 210 {
167 wxFileDialog saveFileDialog(this, "Save map", "", "", "XML files (*.xml)|*.xml", wxFD_SAVE); 211 wxFileDialog saveFileDialog(this, "Save world", "", "", "XML files (*.xml)|*.xml", wxFD_SAVE);
168 if (saveFileDialog.ShowModal() == wxID_CANCEL) 212 if (saveFileDialog.ShowModal() == wxID_CANCEL)
169 { 213 {
170 return; 214 return;
171 } 215 }
172 216
173 filename = saveFileDialog.GetPath().ToStdString(); 217 world->save(saveFileDialog.GetPath().ToStdString(), mapTree);
218 } else {
219 world->save(world->getFilename(), mapTree);
174 } 220 }
175 221
176 map.save(filename);
177 break; 222 break;
178 223
179 case wxCANCEL: 224 case wxCANCEL:
@@ -204,34 +249,34 @@ void MapeditFrame::ZoomOut(wxCommandEvent&)
204 249
205void MapeditFrame::OnNew(wxCommandEvent&) 250void MapeditFrame::OnNew(wxCommandEvent&)
206{ 251{
207 NewMap(); 252 NewWorld();
208} 253}
209 254
210void MapeditFrame::OnOpen(wxCommandEvent&) 255void MapeditFrame::OnOpen(wxCommandEvent&)
211{ 256{
212 wxFileDialog openFileDialog(this, "Open map", "", "", "XML files (*.xml)|*.xml", wxFD_OPEN|wxFD_FILE_MUST_EXIST); 257 wxFileDialog openFileDialog(this, "Open world", "", "", "XML files (*.xml)|*.xml", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
213 if (openFileDialog.ShowModal() == wxID_CANCEL) 258 if (openFileDialog.ShowModal() == wxID_CANCEL)
214 { 259 {
215 return; 260 return;
216 } 261 }
217 262
218 OpenMap(openFileDialog.GetPath().c_str()); 263 OpenWorld(openFileDialog.GetPath().ToStdString());
219} 264}
220 265
221void MapeditFrame::OnSave(wxCommandEvent&) 266void MapeditFrame::OnSave(wxCommandEvent&)
222{ 267{
223 if (filename == "") 268 if (world->getFilename() == "")
224 { 269 {
225 wxFileDialog saveFileDialog(this, "Save map", "", "", "XML files (*.xml)|*.xml", wxFD_SAVE); 270 wxFileDialog saveFileDialog(this, "Save world", "", "", "XML files (*.xml)|*.xml", wxFD_SAVE);
226 if (saveFileDialog.ShowModal() == wxID_CANCEL) 271 if (saveFileDialog.ShowModal() == wxID_CANCEL)
227 { 272 {
228 return; 273 return;
229 } 274 }
230 275
231 filename = saveFileDialog.GetPath().ToStdString(); 276 world->save(saveFileDialog.GetPath().ToStdString(), mapTree);
277 } else {
278 world->save(world->getFilename(), mapTree);
232 } 279 }
233
234 map.save(filename);
235} 280}
236 281
237void MapeditFrame::OnClose(wxCommandEvent&) 282void MapeditFrame::OnClose(wxCommandEvent&)
@@ -252,24 +297,8 @@ void MapeditFrame::OnQuit(wxCommandEvent&)
252 297
253void MapeditFrame::OnTitleChange(wxCommandEvent&) 298void MapeditFrame::OnTitleChange(wxCommandEvent&)
254{ 299{
255 map.setTitle(titleBox->GetLineText(0).ToStdString()); 300 currentMap->setTitle(titleBox->GetValue().ToStdString());
256} 301 mapTree->SetItemText(currentMap->getTreeItemId(), currentMap->getTitle());
257
258void MapeditFrame::NewMap()
259{
260 LaunchWindow(Map(), "");
261}
262
263void MapeditFrame::OpenMap(const char* filename)
264{
265 LaunchWindow(Map(filename), filename);
266}
267
268void MapeditFrame::LaunchWindow(Map map, const char* filename)
269{
270 MapeditFrame* frame = new MapeditFrame(map, filename);
271 frame->closer = openWindows.insert(end(openWindows), frame);
272 frame->Show(true);
273} 302}
274 303
275void MapeditFrame::OnTabChange(wxBookCtrlEvent& event) 304void MapeditFrame::OnTabChange(wxBookCtrlEvent& event)
@@ -317,11 +346,113 @@ void MapeditFrame::OnCancelAddEntity(wxCommandEvent&)
317 mapEditor->CancelAddingEntity(); 346 mapEditor->CancelAddingEntity();
318} 347}
319 348
349void MapeditFrame::OnAddRoot(wxCommandEvent&)
350{
351 auto map = world->newMap();
352 wxTreeItemId node = mapTree->AppendItem(mapTree->GetItemParent(mapTree->GetSelection()), map->getTitle());
353 map->setTreeItemId(node);
354 mapTree->SetItemData(node, new MapPtrCtr(map.get()));
355 mapTree->SetFocusedItem(node);
356 SelectMap(map.get());
357}
358
359void MapeditFrame::OnAddChild(wxCommandEvent&)
360{
361 auto map = world->newMap();
362 wxTreeItemId node = mapTree->AppendItem(mapTree->GetSelection(), map->getTitle());
363 map->setTreeItemId(node);
364 mapTree->SetItemData(node, new MapPtrCtr(map.get()));
365 mapTree->SetFocusedItem(node);
366 mapTree->Expand(mapTree->GetSelection());
367 SelectMap(map.get());
368}
369
370void MapeditFrame::OnDidSelectMap(wxTreeEvent& event)
371{
372 MapPtrCtr* data = (MapPtrCtr*) mapTree->GetItemData(event.GetItem());
373 SelectMap(data->map);
374}
375
376void MapeditFrame::OnWillSelectMap(wxTreeEvent& event)
377{
378 if (addingEntity)
379 {
380 event.Veto();
381 return;
382 }
383
384 event.Skip();
385}
386
387void MapeditFrame::OnWillDragMap(wxTreeEvent& event)
388{
389 if (!addingEntity)
390 {
391 event.Allow();
392 dragMap = event.GetItem();
393 }
394}
395
396void MapeditFrame::OnDidDragMap(wxTreeEvent& event)
397{
398 if (!dragMap.IsOk())
399 {
400 return;
401 }
402
403 wxTreeItemId newParent = event.GetItem();
404 if (!newParent.IsOk())
405 {
406 newParent = mapTree->GetRootItem();
407 }
408
409 wxTreeItemId newChild = MoveTreeNode(dragMap, newParent);
410 dragMap.Unset();
411 mapTree->SelectItem(newChild);
412}
413
414void MapeditFrame::NewWorld()
415{
416 LaunchWindow(std::unique_ptr<World>(new World()));
417}
418
419void MapeditFrame::OpenWorld(std::string filename)
420{
421 try
422 {
423 auto world = std::unique_ptr<World>(new World(filename));
424
425 LaunchWindow(std::move(world));
426 } catch (std::exception& ex)
427 {
428 wxMessageBox(ex.what(), "Error loading world", wxOK | wxCENTRE | wxICON_ERROR);
429 }
430}
431
432void MapeditFrame::LaunchWindow(std::unique_ptr<World> world)
433{
434 MapeditFrame* frame = new MapeditFrame(std::move(world));
435 frame->closer = openWindows.insert(end(openWindows), frame);
436 frame->Show(true);
437}
438
320void MapeditFrame::StartAddingEntity() 439void MapeditFrame::StartAddingEntity()
321{ 440{
322 addingEntity = true; 441 addingEntity = true;
323 addEntityButton->Disable(); 442 addEntityButton->Disable();
324 cancelEntityButton->Enable(); 443 cancelEntityButton->Enable();
444
445 toolbar->EnableTool(TOOL_FILE_NEW, false);
446 toolbar->EnableTool(TOOL_FILE_OPEN, false);
447 toolbar->EnableTool(TOOL_FILE_SAVE, false);
448 toolbar->EnableTool(TOOL_MAP_ADD_ROOT, false);
449 toolbar->EnableTool(TOOL_MAP_ADD_CHILD, false);
450
451 menuFile->Enable(MENU_FILE_NEW, false);
452 menuFile->Enable(MENU_FILE_OPEN, false);
453 menuFile->Enable(MENU_FILE_SAVE, false);
454 menuFile->Enable(MENU_MAP_ADD_ROOT, false);
455 menuFile->Enable(MENU_MAP_ADD_CHILD, false);
325} 456}
326 457
327void MapeditFrame::FinishAddingEntity() 458void MapeditFrame::FinishAddingEntity()
@@ -329,9 +460,87 @@ void MapeditFrame::FinishAddingEntity()
329 addingEntity = false; 460 addingEntity = false;
330 addEntityButton->Enable(); 461 addEntityButton->Enable();
331 cancelEntityButton->Disable(); 462 cancelEntityButton->Disable();
463 toolbar->Enable();
464
465 toolbar->EnableTool(TOOL_FILE_NEW, true);
466 toolbar->EnableTool(TOOL_FILE_OPEN, true);
467 toolbar->EnableTool(TOOL_FILE_SAVE, world->getDirty());
468 toolbar->EnableTool(TOOL_MAP_ADD_ROOT, true);
469 toolbar->EnableTool(TOOL_MAP_ADD_CHILD, true);
470
471 menuFile->Enable(MENU_FILE_NEW, true);
472 menuFile->Enable(MENU_FILE_OPEN, true);
473 menuFile->Enable(MENU_FILE_SAVE, world->getDirty());
474 menuFile->Enable(MENU_MAP_ADD_ROOT, true);
475 menuFile->Enable(MENU_MAP_ADD_CHILD, true);
332} 476}
333 477
334void MapeditFrame::MapDirtyDidChange(bool dirty) 478void MapeditFrame::MapDirtyDidChange(bool dirty)
335{ 479{
336 toolbar->EnableTool(TOOL_FILE_SAVE, dirty); 480 toolbar->EnableTool(TOOL_FILE_SAVE, dirty);
481 menuFile->Enable(MENU_FILE_SAVE, dirty);
482
483 if (dirty)
484 {
485 mapTree->SetItemBold(currentMap->getTreeItemId(), true);
486 } else {
487 for (auto map : world->getMaps())
488 {
489 mapTree->SetItemBold(map.second->getTreeItemId(), false);
490 }
491 }
492}
493
494void MapeditFrame::populateMapTree(wxTreeItemId node, std::list<std::shared_ptr<Map>> maps)
495{
496 for (auto map : maps)
497 {
498 wxTreeItemId childNode = mapTree->AppendItem(node, map->getTitle());
499 mapTree->SetItemData(childNode, new MapPtrCtr(map.get()));
500 map->setTreeItemId(childNode);
501
502 populateMapTree(childNode, map->getChildren());
503
504 if (map->getExpanded())
505 {
506 mapTree->Expand(childNode);
507 }
508 }
509}
510
511void MapeditFrame::SelectMap(Map* map)
512{
513 currentMap = map;
514 mapEditor->SetMap(map);
515 titleBox->ChangeValue(map->getTitle());
516}
517
518wxTreeItemId MapeditFrame::MoveTreeNode(wxTreeItemId toCopy, wxTreeItemId newParent)
519{
520 MapPtrCtr* ctl1 = (MapPtrCtr*) mapTree->GetItemData(toCopy);
521 MapPtrCtr* ctl2 = new MapPtrCtr(ctl1->map);
522
523 wxTreeItemId copied = mapTree->AppendItem(newParent, mapTree->GetItemText(toCopy), -1, -1, ctl2);
524 if (mapTree->IsBold(toCopy))
525 {
526 mapTree->SetItemBold(toCopy, true);
527 }
528
529 if (mapTree->ItemHasChildren(toCopy))
530 {
531 wxTreeItemIdValue cookie;
532 for (wxTreeItemId it = mapTree->GetFirstChild(toCopy, cookie); it.IsOk(); it = mapTree->GetNextChild(toCopy, cookie))
533 {
534 MoveTreeNode(it, copied);
535 }
536 }
537
538 if (mapTree->IsExpanded(toCopy))
539 {
540 mapTree->Expand(copied);
541 }
542
543 mapTree->Delete(toCopy);
544
545 return copied;
337} 546}
diff --git a/tools/mapedit/src/frame.h b/tools/mapedit/src/frame.h index 4aa44c5..f1e4efc 100644 --- a/tools/mapedit/src/frame.h +++ b/tools/mapedit/src/frame.h
@@ -12,24 +12,36 @@
12#include "tile_widget.h" 12#include "tile_widget.h"
13#include <list> 13#include <list>
14#include <wx/notebook.h> 14#include <wx/notebook.h>
15#include <memory>
16#include <wx/treectrl.h>
17
18class MapPtrCtr : public wxTreeItemData {
19 public:
20 Map* map;
21
22 MapPtrCtr(Map* map) : map(map) {}
23};
15 24
16class MapeditFrame : public wxFrame { 25class MapeditFrame : public wxFrame {
17 public: 26 public:
18 MapeditFrame() {} 27 MapeditFrame() {}
19 MapeditFrame(Map map, std::string filename); 28 MapeditFrame(std::unique_ptr<World> world);
20 29
21 MapeditWidget* GetMapEditor(); 30 MapeditWidget* GetMapEditor();
22 void StartAddingEntity(); 31 void StartAddingEntity();
23 void FinishAddingEntity(); 32 void FinishAddingEntity();
24 void MapDirtyDidChange(bool dirty); 33 void MapDirtyDidChange(bool dirty);
25 34
26 static void NewMap(); 35 static void NewWorld();
27 static void OpenMap(const char* filename); 36 static void OpenWorld(std::string filename);
28 37
29 std::list<wxWindow*>::iterator closer; 38 std::list<wxWindow*>::iterator closer;
30 39
31 private: 40 private:
32 static void LaunchWindow(Map map, const char* filename); 41 static void LaunchWindow(std::unique_ptr<World> world);
42 void populateMapTree(wxTreeItemId node, std::list<std::shared_ptr<Map>> maps);
43 void SelectMap(Map* map);
44 wxTreeItemId MoveTreeNode(wxTreeItemId toCopy, wxTreeItemId newParent);
33 45
34 void ZoomIn(wxCommandEvent& event); 46 void ZoomIn(wxCommandEvent& event);
35 void ZoomOut(wxCommandEvent& event); 47 void ZoomOut(wxCommandEvent& event);
@@ -44,8 +56,15 @@ class MapeditFrame : public wxFrame {
44 void OnTabChanging(wxBookCtrlEvent& event); 56 void OnTabChanging(wxBookCtrlEvent& event);
45 void OnAddEntity(wxCommandEvent& event); 57 void OnAddEntity(wxCommandEvent& event);
46 void OnCancelAddEntity(wxCommandEvent& event); 58 void OnCancelAddEntity(wxCommandEvent& event);
59 void OnAddRoot(wxCommandEvent& event);
60 void OnAddChild(wxCommandEvent& event);
61 void OnDidSelectMap(wxTreeEvent& event);
62 void OnWillSelectMap(wxTreeEvent& event);
63 void OnWillDragMap(wxTreeEvent& event);
64 void OnDidDragMap(wxTreeEvent& event);
47 65
48 Map map; 66 std::unique_ptr<World> world;
67 Map* currentMap;
49 MapeditWidget* mapEditor; 68 MapeditWidget* mapEditor;
50 TileWidget* tileEditor; 69 TileWidget* tileEditor;
51 wxTextCtrl* titleBox; 70 wxTextCtrl* titleBox;
@@ -55,6 +74,9 @@ class MapeditFrame : public wxFrame {
55 wxButton* addEntityButton; 74 wxButton* addEntityButton;
56 wxButton* cancelEntityButton; 75 wxButton* cancelEntityButton;
57 wxToolBar* toolbar; 76 wxToolBar* toolbar;
77 wxMenu* menuFile;
78 wxTreeCtrl* mapTree;
79 wxTreeItemId dragMap;
58 80
59 bool addingEntity = false; 81 bool addingEntity = false;
60 82
diff --git a/tools/mapedit/src/main.cpp b/tools/mapedit/src/main.cpp index 91dd98f..b09ee9a 100644 --- a/tools/mapedit/src/main.cpp +++ b/tools/mapedit/src/main.cpp
@@ -18,7 +18,7 @@ bool MapeditApp::OnInit()
18{ 18{
19 wxInitAllImageHandlers(); 19 wxInitAllImageHandlers();
20 20
21 MapeditFrame::NewMap(); 21 MapeditFrame::NewWorld();
22 22
23 return true; 23 return true;
24} 24}
diff --git a/tools/mapedit/src/map.cpp b/tools/mapedit/src/map.cpp index f9c07fc..32541e6 100644 --- a/tools/mapedit/src/map.cpp +++ b/tools/mapedit/src/map.cpp
@@ -1,106 +1,27 @@
1#include "map.h" 1#include "map.h"
2#include <libxml/parser.h>
3#include <libxml/xmlwriter.h>
4#include <sstream>
5#include "frame.h" 2#include "frame.h"
6 3
7Map::Map() 4Map::Map(int id, World* world) : id(id), world(world)
8{ 5{
9 mapdata = (int*) calloc(MAP_WIDTH * MAP_HEIGHT, sizeof(int)); 6 mapdata = (int*) calloc(MAP_WIDTH * MAP_HEIGHT, sizeof(int));
10} 7}
11 8
12Map::Map(std::string filename)
13{
14 xmlDocPtr doc = xmlParseFile(filename.c_str());
15 if (doc == nullptr)
16 {
17 throw MapLoadException(filename);
18 }
19
20 xmlNodePtr top = xmlDocGetRootElement(doc);
21 if (top == nullptr)
22 {
23 throw MapLoadException(filename);
24 }
25
26 if (xmlStrcmp(top->name, (const xmlChar*) "map-def"))
27 {
28 throw MapLoadException(filename);
29 }
30
31 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next)
32 {
33 if (!xmlStrcmp(node->name, (const xmlChar*) "name"))
34 {
35 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
36 title = (char*) key;
37 xmlFree(key);
38 } else if (!xmlStrcmp(node->name, (const xmlChar*) "environment"))
39 {
40 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
41 mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int));
42 mapdata[0] = atoi(strtok((char*) key, ",\n"));
43 for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++)
44 {
45 mapdata[i] = atoi(strtok(NULL, ",\n"));
46 }
47 xmlFree(key);
48 } else if (!xmlStrcmp(node->name, (const xmlChar*) "leftmap"))
49 {
50 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
51 leftmap = (char*) key;
52 xmlFree(key);
53 } else if (!xmlStrcmp(node->name, (const xmlChar*) "rightmap"))
54 {
55 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
56 rightmap = (char*) key;
57 xmlFree(key);
58 } else if (!xmlStrcmp(node->name, (const xmlChar*) "entities"))
59 {
60 for (xmlNodePtr entityNode = node->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next)
61 {
62 if (!xmlStrcmp(entityNode->name, (const xmlChar*) "entity"))
63 {
64 auto data = std::make_shared<MapObjectEntry>();
65
66 for (xmlNodePtr entityDataNode = entityNode->xmlChildrenNode; entityDataNode != NULL; entityDataNode = entityDataNode->next)
67 {
68 if (!xmlStrcmp(entityDataNode->name, (const xmlChar*) "entity-type"))
69 {
70 xmlChar* key = xmlNodeListGetString(doc, entityDataNode->xmlChildrenNode, 1);
71 data->object = MapObject::getAllObjects().at((char*) key).get();
72 xmlFree(key);
73 } else if (!xmlStrcmp(entityDataNode->name, (const xmlChar*) "entity-position"))
74 {
75 xmlChar* key = xmlNodeListGetString(doc, entityDataNode->xmlChildrenNode, 1);
76 sscanf((char*) key, "%lf,%lf", &data->position.first, &data->position.second);
77 xmlFree(key);
78 }
79 }
80
81 objects.push_back(data);
82 }
83 }
84 }
85 }
86
87 xmlFreeDoc(doc);
88}
89
90Map::Map(const Map& map) 9Map::Map(const Map& map)
91{ 10{
92 mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int)); 11 mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int));
93 memcpy(mapdata, map.mapdata, MAP_WIDTH*MAP_HEIGHT*sizeof(int)); 12 memcpy(mapdata, map.mapdata, MAP_WIDTH*MAP_HEIGHT*sizeof(int));
94 13
14 id = map.id;
95 title = map.title; 15 title = map.title;
96 leftmap = map.leftmap; 16 leftmap = map.leftmap;
97 rightmap = map.rightmap; 17 rightmap = map.rightmap;
98 dirty = map.dirty;
99 objects = map.objects; 18 objects = map.objects;
100 frame = map.frame; 19 world = map.world;
20 treeItemId = map.treeItemId;
21 children = map.children;
101} 22}
102 23
103Map::Map(Map&& map) : Map() 24Map::Map(Map&& map) : Map(-1, map.world)
104{ 25{
105 swap(*this, map); 26 swap(*this, map);
106} 27}
@@ -123,141 +44,157 @@ void swap(Map& first, Map& second)
123 std::swap(first.title, second.title); 44 std::swap(first.title, second.title);
124 std::swap(first.leftmap, second.leftmap); 45 std::swap(first.leftmap, second.leftmap);
125 std::swap(first.rightmap, second.rightmap); 46 std::swap(first.rightmap, second.rightmap);
126 std::swap(first.dirty, second.dirty);
127 std::swap(first.objects, second.objects); 47 std::swap(first.objects, second.objects);
128 std::swap(first.frame, second.frame); 48 std::swap(first.id, second.id);
49 std::swap(first.world, second.world);
50 std::swap(first.treeItemId, second.treeItemId);
51 std::swap(first.children, second.children);
129} 52}
130 53
131#define MY_ENCODING "ISO-8859-1" 54int Map::getID() const
55{
56 return id;
57}
132 58
133void Map::save(std::string name) 59std::string Map::getTitle() const
134{ 60{
135 if (!dirty) return; 61 return title;
136 62}
137 int rc;
138
139 xmlTextWriterPtr writer = xmlNewTextWriterFilename(name.c_str(), 0);
140 if (writer == NULL) throw MapWriteException(name);
141 63
142 rc = xmlTextWriterStartDocument(writer, NULL, MY_ENCODING, NULL); 64int Map::getTileAt(int x, int y) const
143 if (rc < 0) throw MapWriteException(name); 65{
144 66 return mapdata[x+y*MAP_WIDTH];
145 rc = xmlTextWriterStartElement(writer, (xmlChar*) "map-def"); 67}
146 if (rc < 0) throw MapWriteException(name); 68
147 69const std::list<std::shared_ptr<MapObjectEntry>>& Map::getObjects() const
148 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "name", (xmlChar*) title.c_str()); 70{
149 if (rc < 0) throw MapWriteException(name); 71 return objects;
150 72}
151 std::ostringstream mapdata_out; 73
152 for (int y=0; y<MAP_HEIGHT; y++) 74std::shared_ptr<Map> Map::getLeftmap() const
75{
76 if (leftmap == -1)
153 { 77 {
154 for (int x=0; x<MAP_WIDTH; x++) 78 return std::shared_ptr<Map>();
155 { 79 } else {
156 mapdata_out << mapdata[x+y*MAP_WIDTH] << ","; 80 return world->getMap(leftmap);
157 }
158
159 mapdata_out << std::endl;
160 } 81 }
161 82}
162 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "environment", (xmlChar*) mapdata_out.str().c_str());
163 if (rc < 0) throw MapWriteException(name);
164
165 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "leftmap", (xmlChar*) leftmap.c_str());
166 if (rc < 0) throw MapWriteException(name);
167 83
168 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "rightmap", (xmlChar*) rightmap.c_str()); 84std::shared_ptr<Map> Map::getRightmap() const
169 if (rc < 0) throw MapWriteException(name); 85{
170 86 if (rightmap == -1)
171 rc = xmlTextWriterStartElement(writer, (xmlChar*) "entities");
172 if (rc < 0) throw MapWriteException(name);
173
174 for (auto object : objects)
175 { 87 {
176 rc = xmlTextWriterStartElement(writer, (xmlChar*) "entity"); 88 return std::shared_ptr<Map>();
177 if (rc < 0) throw MapWriteException(name); 89 } else {
178 90 return world->getMap(rightmap);
179 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "entity-type", (xmlChar*) object->object->getType().c_str());
180 if (rc < 0) throw MapWriteException(name);
181
182 std::ostringstream entpos_out;
183 entpos_out << object->position.first << "," << object->position.second;
184
185 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "entity-position", (xmlChar*) entpos_out.str().c_str());
186 if (rc < 0) throw MapWriteException(name);
187
188 rc = xmlTextWriterEndElement(writer);
189 if (rc < 0) throw MapWriteException(name);
190 } 91 }
191
192 rc = xmlTextWriterEndElement(writer);
193 if (rc < 0) throw MapWriteException(name);
194
195 rc = xmlTextWriterEndElement(writer);
196 if (rc < 0) throw MapWriteException(name);
197
198 rc = xmlTextWriterEndDocument(writer);
199 if (rc < 0) throw MapWriteException(name);
200
201 xmlFreeTextWriter(writer);
202
203 setDirty(false);
204} 92}
205 93
206bool Map::hasUnsavedChanges() const 94wxTreeItemId Map::getTreeItemId() const
207{ 95{
208 return dirty; 96 return treeItemId;
209} 97}
210 98
211void Map::setTileAt(int x, int y, int tile) 99std::list<std::shared_ptr<Map>> Map::getChildren() const
212{ 100{
213 setDirty(true); 101 std::list<std::shared_ptr<Map>> ret;
214 mapdata[x+y*MAP_WIDTH] = tile; 102
103 for (auto id : children)
104 {
105 ret.push_back(world->getMap(id));
106 }
107
108 return ret;
215} 109}
216 110
217int Map::getTileAt(int x, int y) const 111bool Map::getExpanded() const
218{ 112{
219 return mapdata[x+y*MAP_WIDTH]; 113 return expanded;
220} 114}
221 115
222std::string Map::getTitle() const 116void Map::setTitle(std::string title, bool dirty)
223{ 117{
224 return title; 118 this->title = title;
119
120 if (dirty)
121 {
122 world->setDirty(true);
123 }
225} 124}
226 125
227void Map::setTitle(std::string title) 126void Map::setTileAt(int x, int y, int tile, bool dirty)
228{ 127{
229 setDirty(true); 128 mapdata[x+y*MAP_WIDTH] = tile;
230 this->title = title; 129
130 if (dirty)
131 {
132 world->setDirty(true);
133 }
231} 134}
232 135
233const std::list<std::shared_ptr<MapObjectEntry>>& Map::getObjects() const 136void Map::setMapdata(int* mapdata, bool dirty)
234{ 137{
235 return objects; 138 free(this->mapdata);
139 this->mapdata = mapdata;
140
141 if (dirty)
142 {
143 world->setDirty(true);
144 }
236} 145}
237 146
238void Map::addObject(std::shared_ptr<MapObjectEntry>& obj) 147void Map::addObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty)
239{ 148{
240 setDirty(true);
241 objects.push_back(obj); 149 objects.push_back(obj);
150
151 if (dirty)
152 {
153 world->setDirty(true);
154 }
242} 155}
243 156
244void Map::removeObject(std::shared_ptr<MapObjectEntry>& obj) 157void Map::removeObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty)
245{ 158{
246 setDirty(true);
247 objects.remove(obj); 159 objects.remove(obj);
160
161 if (dirty)
162 {
163 world->setDirty(true);
164 }
248} 165}
249 166
250bool Map::getDirty() const 167void Map::setLeftmap(int id, bool dirty)
251{ 168{
252 return dirty; 169 leftmap = id;
170
171 if (dirty)
172 {
173 world->setDirty(true);
174 }
253} 175}
254 176
255void Map::setDirty(bool dirty) 177void Map::setRightmap(int id, bool dirty)
256{ 178{
257 this->dirty = dirty; 179 rightmap = id;
258 180
259 if (frame != nullptr) 181 if (dirty)
260 { 182 {
261 frame->MapDirtyDidChange(dirty); 183 world->setDirty(true);
262 } 184 }
263} 185}
186
187void Map::setTreeItemId(wxTreeItemId id)
188{
189 this->treeItemId = id;
190}
191
192void Map::addChild(int id)
193{
194 children.push_back(id);
195}
196
197void Map::setExpanded(bool exp)
198{
199 expanded = exp;
200}
diff --git a/tools/mapedit/src/map.h b/tools/mapedit/src/map.h index a2b9cb8..5753cae 100644 --- a/tools/mapedit/src/map.h +++ b/tools/mapedit/src/map.h
@@ -1,12 +1,16 @@
1#ifndef MAP_H 1#ifndef MAP_H
2#define MAP_H 2#define MAP_H
3 3
4class Map;
5
4#include <string> 6#include <string>
5#include <exception> 7#include <exception>
6#include <utility> 8#include <utility>
7#include <list> 9#include <list>
8#include "object.h" 10#include "object.h"
9#include <memory> 11#include <memory>
12#include "world.h"
13#include <wx/treectrl.h>
10 14
11class MapeditFrame; 15class MapeditFrame;
12 16
@@ -57,36 +61,45 @@ struct MapObjectEntry {
57 61
58class Map { 62class Map {
59 public: 63 public:
60 Map(); 64 Map(int id, World* world);
61 Map(std::string name);
62 Map(const Map& map); 65 Map(const Map& map);
63 Map(Map&& map); 66 Map(Map&& map);
64 ~Map(); 67 ~Map();
65 Map& operator= (Map other); 68 Map& operator= (Map other);
66 friend void swap(Map& first, Map& second); 69 friend void swap(Map& first, Map& second);
67 70
71 int getID() const;
68 std::string getTitle() const; 72 std::string getTitle() const;
69 void setTitle(std::string title);
70 void save(std::string name);
71 bool hasUnsavedChanges() const;
72 void setTileAt(int x, int y, int tile);
73 int getTileAt(int x, int y) const; 73 int getTileAt(int x, int y) const;
74 const std::list<std::shared_ptr<MapObjectEntry>>& getObjects() const; 74 const std::list<std::shared_ptr<MapObjectEntry>>& getObjects() const;
75 void addObject(std::shared_ptr<MapObjectEntry>& obj); 75 std::shared_ptr<Map> getLeftmap() const;
76 void removeObject(std::shared_ptr<MapObjectEntry>& obj); 76 std::shared_ptr<Map> getRightmap() const;
77 bool getDirty() const; 77 wxTreeItemId getTreeItemId() const;
78 std::list<std::shared_ptr<Map>> getChildren() const;
79 bool getExpanded() const;
78 80
79 MapeditFrame* frame; 81 void setTitle(std::string title, bool dirty = true);
82 void setTileAt(int x, int y, int tile, bool dirty = true);
83 void setMapdata(int* mapdata, bool dirty = true);
84 void addObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty = true);
85 void removeObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty = true);
86 void setLeftmap(int id, bool dirty = true);
87 void setRightmap(int id, bool dirty = true);
88 void setTreeItemId(wxTreeItemId id);
89 void addChild(int id);
90 void setExpanded(bool exp);
80 91
81 private: 92 private:
82 void setDirty(bool dirty); 93 int id;
83 94 World* world;
84 std::list<std::shared_ptr<MapObjectEntry>> objects; 95 std::list<std::shared_ptr<MapObjectEntry>> objects;
85 int* mapdata; 96 int* mapdata;
86 std::string title; 97 std::string title {"Untitled Map"};
87 std::string leftmap; 98 std::list<int> children;
88 std::string rightmap; 99 int leftmap = -1;
89 bool dirty; 100 int rightmap = -1;
101 wxTreeItemId treeItemId;
102 bool expanded = false;
90}; 103};
91 104
92#endif 105#endif
diff --git a/tools/mapedit/src/widget.cpp b/tools/mapedit/src/widget.cpp index c1f044c..d50cf91 100644 --- a/tools/mapedit/src/widget.cpp +++ b/tools/mapedit/src/widget.cpp
@@ -297,3 +297,10 @@ void MapeditWidget::CancelAddingEntity()
297{ 297{
298 addingEntity = nullptr; 298 addingEntity = nullptr;
299} 299}
300
301void MapeditWidget::SetMap(Map* map)
302{
303 this->map = map;
304
305 Refresh();
306}
diff --git a/tools/mapedit/src/widget.h b/tools/mapedit/src/widget.h index a660f82..34627bc 100644 --- a/tools/mapedit/src/widget.h +++ b/tools/mapedit/src/widget.h
@@ -29,6 +29,7 @@ class MapeditWidget : public wxScrolledWindow {
29 void SetEditMode(EditMode editMode); 29 void SetEditMode(EditMode editMode);
30 void StartAddingEntity(MapObject* object); 30 void StartAddingEntity(MapObject* object);
31 void CancelAddingEntity(); 31 void CancelAddingEntity();
32 void SetMap(Map* map);
32 33
33 MapeditFrame* frame; 34 MapeditFrame* frame;
34 35
@@ -46,7 +47,7 @@ class MapeditWidget : public wxScrolledWindow {
46 void SetTile(wxPoint pos); 47 void SetTile(wxPoint pos);
47 void SetZoomSize(int zoom); 48 void SetZoomSize(int zoom);
48 49
49 Map* const map = nullptr; 50 Map* map = nullptr;
50 wxBitmap tiles; 51 wxBitmap tiles;
51 TileWidget* tileWidget; 52 TileWidget* tileWidget;
52 bool mouseIsDown = false; 53 bool mouseIsDown = false;
diff --git a/tools/mapedit/src/world.cpp b/tools/mapedit/src/world.cpp new file mode 100644 index 0000000..4c42593 --- /dev/null +++ b/tools/mapedit/src/world.cpp
@@ -0,0 +1,369 @@
1#include "world.h"
2#include <libxml/parser.h>
3#include <libxml/xmlwriter.h>
4#include "frame.h"
5#include <sstream>
6
7World::World()
8{
9 newMap();
10
11 rootChildren.push_back(0);
12}
13
14World::World(std::string filename)
15{
16 this->filename = filename;
17
18 xmlDocPtr doc = xmlParseFile(filename.c_str());
19 if (doc == nullptr)
20 {
21 throw MapLoadException(filename);
22 }
23
24 xmlNodePtr top = xmlDocGetRootElement(doc);
25 if (top == nullptr)
26 {
27 throw MapLoadException(filename);
28 }
29
30 if (xmlStrcmp(top->name, (const xmlChar*) "world"))
31 {
32 throw MapLoadException(filename);
33 }
34
35 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next)
36 {
37 if (!xmlStrcmp(node->name, (const xmlChar*) "nextmapid"))
38 {
39 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
40 if (key != 0)
41 {
42 nextMapID = atoi((char*) key);
43 }
44 xmlFree(key);
45 } else if (!xmlStrcmp(node->name, (const xmlChar*) "lastmap"))
46 {
47 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
48 if (key != 0)
49 {
50 lastmap = atoi((char*) key);
51 }
52 xmlFree(key);
53 } else if (!xmlStrcmp(node->name, (const xmlChar*) "root"))
54 {
55 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
56 if (key != 0)
57 {
58 rootChildren.push_back(atoi((char*) key));
59 }
60 xmlFree(key);
61 } else if (!xmlStrcmp(node->name, (const xmlChar*) "map"))
62 {
63 xmlChar* idKey = xmlGetProp(node, (xmlChar*) "id");
64 if (idKey == 0) throw MapLoadException(filename);
65 int id = atoi((char*) idKey);
66 xmlFree(idKey);
67
68 auto map = std::make_shared<Map>(id, this);
69
70 for (xmlNodePtr mapNode = node->xmlChildrenNode; mapNode != NULL; mapNode = mapNode->next)
71 {
72 if (!xmlStrcmp(mapNode->name, (const xmlChar*) "name"))
73 {
74 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1);
75 if (key != 0)
76 {
77 map->setTitle((char*) key, false);
78 }
79
80 xmlFree(key);
81 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "environment"))
82 {
83 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1);
84 int* mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int));
85 mapdata[0] = atoi(strtok((char*) key, ",\n"));
86 for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++)
87 {
88 mapdata[i] = atoi(strtok(NULL, ",\n"));
89 }
90 map->setMapdata(mapdata, false);
91 xmlFree(key);
92 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "leftmap"))
93 {
94 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1);
95 if (key != 0)
96 {
97 map->setLeftmap(atoi((char*) key), false);
98 }
99 xmlFree(key);
100 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "rightmap"))
101 {
102 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1);
103 if (key != 0)
104 {
105 map->setRightmap(atoi((char*) key));
106 }
107 xmlFree(key);
108 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "entities"))
109 {
110 for (xmlNodePtr entityNode = mapNode->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next)
111 {
112 if (!xmlStrcmp(entityNode->name, (const xmlChar*) "entity"))
113 {
114 auto data = std::make_shared<MapObjectEntry>();
115
116 for (xmlNodePtr entityDataNode = entityNode->xmlChildrenNode; entityDataNode != NULL; entityDataNode = entityDataNode->next)
117 {
118 if (!xmlStrcmp(entityDataNode->name, (const xmlChar*) "entity-type"))
119 {
120 xmlChar* key = xmlNodeListGetString(doc, entityDataNode->xmlChildrenNode, 1);
121 data->object = MapObject::getAllObjects().at((char*) key).get();
122 xmlFree(key);
123 } else if (!xmlStrcmp(entityDataNode->name, (const xmlChar*) "entity-position"))
124 {
125 xmlChar* key = xmlNodeListGetString(doc, entityDataNode->xmlChildrenNode, 1);
126 sscanf((char*) key, "%lf,%lf", &data->position.first, &data->position.second);
127 xmlFree(key);
128 }
129 }
130
131 map->addObject(data, false);
132 }
133 }
134 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "child"))
135 {
136 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1);
137 if (key != 0)
138 {
139 map->addChild(atoi((char*) key));
140 }
141 xmlFree(key);
142 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "expanded"))
143 {
144 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1);
145 if ((key != 0) && ((char) key[0] == '1'))
146 {
147 map->setExpanded(true);
148 }
149 }
150 }
151
152 maps[map->getID()] = map;
153 }
154 }
155
156 xmlFreeDoc(doc);
157}
158
159std::shared_ptr<Map> World::newMap()
160{
161 auto nm = std::make_shared<Map>(nextMapID++, this);
162 maps[nm->getID()] = nm;
163 return nm;
164}
165
166std::shared_ptr<Map> World::getMap(int id) const
167{
168 return maps.at(id);
169}
170
171void World::setDirty(bool dirty)
172{
173 this->dirty = dirty;
174 parent->MapDirtyDidChange(dirty);
175}
176
177bool World::getDirty() const
178{
179 return dirty;
180}
181
182std::string World::getFilename() const
183{
184 return filename;
185}
186
187void World::setParent(MapeditFrame* parent)
188{
189 this->parent = parent;
190}
191
192Map* World::getLastMap() const
193{
194 return getMap(lastmap).get();
195}
196
197#define MY_ENCODING "ISO-8859-1"
198
199void World::save(std::string name, wxTreeCtrl* mapTree)
200{
201 int rc;
202
203 xmlTextWriterPtr writer = xmlNewTextWriterFilename(name.c_str(), 0);
204 if (writer == NULL) throw MapWriteException(name);
205
206 rc = xmlTextWriterStartDocument(writer, NULL, MY_ENCODING, NULL);
207 if (rc < 0) throw MapWriteException(name);
208
209 // <world>
210 rc = xmlTextWriterStartElement(writer, (xmlChar*) "world");
211 if (rc < 0) throw MapWriteException(name);
212
213 // <nextmapid/>
214 std::ostringstream nextMap_out;
215 nextMap_out << nextMapID;
216 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "nextmapid", (xmlChar*) nextMap_out.str().c_str());
217 if (rc < 0) throw MapWriteException(name);
218
219 // <lastmap/>
220 std::ostringstream lastMap_out;
221 lastMap_out << lastmap;
222 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "lastmap", (xmlChar*) lastMap_out.str().c_str());
223 if (rc < 0) throw MapWriteException(name);
224
225 // ASSUMPTION: There will always be at least one child of the invisible root element. i.e. you cannot delete to zero maps.
226 wxTreeItemId root = mapTree->GetRootItem();
227 wxTreeItemIdValue cookie1;
228 for (wxTreeItemId it = mapTree->GetFirstChild(root, cookie1); it.IsOk(); it = mapTree->GetNextChild(root, cookie1))
229 {
230 // <root>
231 MapPtrCtr* ctl = (MapPtrCtr*) mapTree->GetItemData(it);
232 std::ostringstream rootid_out;
233 rootid_out << ctl->map->getID();
234 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "root", (xmlChar*) rootid_out.str().c_str());
235 if (rc < 0) throw MapWriteException(name);
236 }
237
238 for (auto mapPair : maps)
239 {
240 Map& map = *mapPair.second;
241
242 // <map>
243 rc = xmlTextWriterStartElement(writer, (xmlChar*) "map");
244 if (rc < 0) throw MapWriteException(name);
245
246 // id=
247 std::ostringstream id_out;
248 id_out << map.getID();
249 rc = xmlTextWriterWriteAttribute(writer, (xmlChar*) "id", (xmlChar*) id_out.str().c_str());
250 if (rc < 0) throw MapWriteException(name);
251
252 // <name/>
253 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "name", (xmlChar*) map.getTitle().c_str());
254 if (rc < 0) throw MapWriteException(name);
255
256 // <environment/>
257 std::ostringstream mapdata_out;
258 for (int y=0; y<MAP_HEIGHT; y++)
259 {
260 for (int x=0; x<MAP_WIDTH; x++)
261 {
262 mapdata_out << map.getTileAt(x,y) << ",";
263 }
264
265 mapdata_out << std::endl;
266 }
267
268 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "environment", (xmlChar*) mapdata_out.str().c_str());
269 if (rc < 0) throw MapWriteException(name);
270
271 // <leftmap/>
272 std::ostringstream leftmap_out;
273 if (map.getLeftmap())
274 {
275 leftmap_out << map.getLeftmap()->getID();
276 }
277 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "leftmap", (xmlChar*) leftmap_out.str().c_str());
278 if (rc < 0) throw MapWriteException(name);
279
280 // <rightmap/>
281 std::ostringstream rightmap_out;
282 if (map.getRightmap())
283 {
284 rightmap_out << map.getRightmap()->getID();
285 }
286 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "rightmap", (xmlChar*) rightmap_out.str().c_str());
287 if (rc < 0) throw MapWriteException(name);
288
289 // <entities>
290 rc = xmlTextWriterStartElement(writer, (xmlChar*) "entities");
291 if (rc < 0) throw MapWriteException(name);
292
293 for (auto object : map.getObjects())
294 {
295 // <entity>
296 rc = xmlTextWriterStartElement(writer, (xmlChar*) "entity");
297 if (rc < 0) throw MapWriteException(name);
298
299 // <entity-type/>
300 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "entity-type", (xmlChar*) object->object->getType().c_str());
301 if (rc < 0) throw MapWriteException(name);
302
303 // <entity-position/>
304 std::ostringstream entpos_out;
305 entpos_out << object->position.first << "," << object->position.second;
306 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "entity-position", (xmlChar*) entpos_out.str().c_str());
307 if (rc < 0) throw MapWriteException(name);
308
309 // </entity>
310 rc = xmlTextWriterEndElement(writer);
311 if (rc < 0) throw MapWriteException(name);
312 }
313
314 // </entities>
315 rc = xmlTextWriterEndElement(writer);
316 if (rc < 0) throw MapWriteException(name);
317
318 wxTreeItemId node = map.getTreeItemId();
319 if (mapTree->ItemHasChildren(node))
320 {
321 wxTreeItemIdValue cookie2;
322 for (wxTreeItemId it = mapTree->GetFirstChild(node, cookie2); it.IsOk(); it = mapTree->GetNextChild(node, cookie2))
323 {
324 // <child/>
325 MapPtrCtr* ctl = (MapPtrCtr*) mapTree->GetItemData(it);
326 std::ostringstream childid_out;
327 childid_out << ctl->map->getID();
328 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "child", (xmlChar*) childid_out.str().c_str());
329 if (rc < 0) throw MapWriteException(name);
330 }
331
332 if (mapTree->IsExpanded(node))
333 {
334 // <expanded/>
335 rc = xmlTextWriterWriteElement(writer, (xmlChar*) "expanded", (xmlChar*) "1");
336 if (rc < 0) throw MapWriteException(name);
337 }
338 }
339
340 // </map>
341 rc = xmlTextWriterEndElement(writer);
342 if (rc < 0) throw MapWriteException(name);
343 }
344
345 // </world>
346 rc = xmlTextWriterEndDocument(writer);
347 if (rc < 0) throw MapWriteException(name);
348
349 xmlFreeTextWriter(writer);
350
351 setDirty(false);
352}
353
354std::list<std::shared_ptr<Map>> World::getRootMaps() const
355{
356 std::list<std::shared_ptr<Map>> ret;
357
358 for (auto id : rootChildren)
359 {
360 ret.push_back(getMap(id));
361 }
362
363 return ret;
364}
365
366const std::map<int, std::shared_ptr<Map>> World::getMaps() const
367{
368 return maps;
369}
diff --git a/tools/mapedit/src/world.h b/tools/mapedit/src/world.h new file mode 100644 index 0000000..7d796cc --- /dev/null +++ b/tools/mapedit/src/world.h
@@ -0,0 +1,45 @@
1#ifndef WORLD_H
2#define WORLD_H
3
4class World;
5
6#include <wx/wxprec.h>
7
8#ifndef WX_PRECOMP
9#include <wx/wx.h>
10#endif
11
12#include <wx/treectrl.h>
13#include <map>
14#include <memory>
15#include "map.h"
16#include <list>
17
18class MapeditFrame;
19
20class World {
21 public:
22 World();
23 World(std::string filename);
24 std::shared_ptr<Map> newMap();
25 std::shared_ptr<Map> getMap(int id) const;
26 void setDirty(bool dirty);
27 bool getDirty() const;
28 std::string getFilename() const;
29 void setParent(MapeditFrame* parent);
30 void save(std::string filename, wxTreeCtrl* mapTree);
31 Map* getLastMap() const;
32 std::list<std::shared_ptr<Map>> getRootMaps() const;
33 const std::map<int, std::shared_ptr<Map>> getMaps() const;
34
35 private:
36 MapeditFrame* parent;
37 std::map<int, std::shared_ptr<Map>> maps;
38 int nextMapID = 0;
39 bool dirty = false;
40 std::string filename;
41 int lastmap = 0;
42 std::list<int> rootChildren;
43};
44
45#endif