summary refs log tree commit diff stats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-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