Map Editor

RD Core / Commit #3

Commit Map Editor

Alex Alex committed 18/10/2026 23:40 main Patch
436 files +250,791 -0
A RD_Map_Editor/source/extension.cpp +87-0 View file
@@ -0,0 +1,87 @@
1+//////////////////////////////////////////////////////////////////////
2+// This file is part of Remere's Map Editor
3+//////////////////////////////////////////////////////////////////////
4+// Remere's Map Editor is free software: you can redistribute it and/or modify
5+// it under the terms of the GNU General Public License as published by
6+// the Free Software Foundation, either version 3 of the License, or
7+// (at your option) any later version.
8+//
9+// Remere's Map Editor is distributed in the hope that it will be useful,
10+// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+// GNU General Public License for more details.
13+//
14+// You should have received a copy of the GNU General Public License
15+// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+//////////////////////////////////////////////////////////////////////
17+
18+#include "main.h"
19+
20+#include "extension.h"
21+
22+MaterialsExtension::MaterialsExtension(std::string name, std::string author, std::string description) :
23+ name(name),
24+ author(author),
25+ description(description),
26+ for_all_versions(false) {
27+ ////
28+}
29+
30+MaterialsExtension::~MaterialsExtension() {
31+ ////
32+}
33+
34+void MaterialsExtension::addVersion(const std::string& versionString) {
35+ if (versionString == "all") {
36+ for_all_versions = true;
37+ } else {
38+ ClientVersion* client = ClientVersion::get(versionString);
39+ if (client) {
40+ ClientVersionList supported_versions = ClientVersion::getAllVersionsSupportedForClientVersion(client);
41+ version_list.insert(version_list.end(), supported_versions.begin(), supported_versions.end());
42+ }
43+ }
44+}
45+
46+bool MaterialsExtension::isForVersion(uint16_t versionId) {
47+ if (for_all_versions) {
48+ return true;
49+ }
50+
51+ for (ClientVersion* version : version_list) {
52+ if (version->getID() == versionId) {
53+ return true;
54+ }
55+ }
56+ return false;
57+}
58+
59+std::string MaterialsExtension::getVersionString() {
60+ if (for_all_versions) {
61+ return "All";
62+ }
63+
64+ std::string versions;
65+ std::string last;
66+ for (ClientVersion* version : version_list) {
67+ if (!last.empty()) {
68+ if (!versions.empty()) {
69+ versions += ", " + last;
70+ } else {
71+ versions = last;
72+ }
73+ }
74+ last = version->getName();
75+ }
76+
77+ if (!last.empty()) {
78+ if (!versions.empty()) {
79+ versions += " and " + last;
80+ } else {
81+ versions = last;
82+ }
83+ } else {
84+ return "None";
85+ }
86+ return versions;
87+}
A RD_Map_Editor/source/extension.h +48-0 View file
@@ -0,0 +1,48 @@
1+//////////////////////////////////////////////////////////////////////
2+// This file is part of Remere's Map Editor
3+//////////////////////////////////////////////////////////////////////
4+// Remere's Map Editor is free software: you can redistribute it and/or modify
5+// it under the terms of the GNU General Public License as published by
6+// the Free Software Foundation, either version 3 of the License, or
7+// (at your option) any later version.
8+//
9+// Remere's Map Editor is distributed in the hope that it will be useful,
10+// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+// GNU General Public License for more details.
13+//
14+// You should have received a copy of the GNU General Public License
15+// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+//////////////////////////////////////////////////////////////////////
17+
18+#ifndef RME_EXTENSION_H_
19+#define RME_EXTENSION_H_
20+
21+#include "tileset.h"
22+#include "client_version.h"
23+
24+class MaterialsExtension {
25+public:
26+ MaterialsExtension(std::string name, std::string author, std::string description);
27+ ~MaterialsExtension();
28+
29+ void addVersion(const std::string& versionString);
30+ bool isForVersion(uint16_t versionId);
31+ std::string getVersionString();
32+
33+ std::string name;
34+ std::string url;
35+ std::string author;
36+ std::string author_url;
37+ std::string description;
38+ bool for_all_versions;
39+ ClientVersionList version_list;
40+
41+private:
42+ MaterialsExtension(const MaterialsExtension&);
43+ MaterialsExtension& operator=(const MaterialsExtension&);
44+};
45+
46+typedef std::vector<MaterialsExtension*> MaterialsExtensionList;
47+
48+#endif
A RD_Map_Editor/source/extension_window.cpp +135-0 View file
@@ -0,0 +1,135 @@
1+//////////////////////////////////////////////////////////////////////
2+// This file is part of Remere's Map Editor
3+//////////////////////////////////////////////////////////////////////
4+// Remere's Map Editor is free software: you can redistribute it and/or modify
5+// it under the terms of the GNU General Public License as published by
6+// the Free Software Foundation, either version 3 of the License, or
7+// (at your option) any later version.
8+//
9+// Remere's Map Editor is distributed in the hope that it will be useful,
10+// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+// GNU General Public License for more details.
13+//
14+// You should have received a copy of the GNU General Public License
15+// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+//////////////////////////////////////////////////////////////////////
17+
18+#include "main.h"
19+
20+#include "extension_window.h"
21+
22+#include "gui.h"
23+#include "materials.h"
24+
25+extern Materials g_materials;
26+
27+BEGIN_EVENT_TABLE(ExtensionsDialog, wxDialog)
28+EVT_HTML_LINK_CLICKED(wxID_ANY, ExtensionsDialog::OnClickLink)
29+EVT_BUTTON(wxID_OK, ExtensionsDialog::OnClickOK)
30+EVT_BUTTON(EXTENSIONS_OPEN_FOLDER_BUTTON, ExtensionsDialog::OnClickOpenFolder)
31+END_EVENT_TABLE()
32+
33+ExtensionsDialog::ExtensionsDialog(wxWindow* parent) :
34+ wxDialog(parent, wxID_ANY, "Extensions", wxDefaultPosition, wxSize(600, 500), wxRESIZE_BORDER | wxCAPTION) {
35+ wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
36+
37+ wxHtmlWindow* htmlWindow = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(550, 400));
38+ htmlWindow->SetPage(HTML());
39+ topSizer->Add(htmlWindow, wxSizerFlags(1).DoubleBorder().Expand());
40+
41+ wxSizer* buttonSizer = newd wxBoxSizer(wxHORIZONTAL);
42+ buttonSizer->Add(newd wxButton(this, wxID_OK, "OK"), wxSizerFlags(1).Center());
43+ buttonSizer->Add(newd wxButton(this, EXTENSIONS_OPEN_FOLDER_BUTTON, "Open Extensions Folder"), wxSizerFlags(1).Center());
44+ topSizer->Add(buttonSizer, 0, wxCENTER | wxLEFT | wxRIGHT | wxBOTTOM, 20);
45+
46+ SetSizerAndFit(topSizer);
47+ Centre(wxBOTH);
48+}
49+
50+ExtensionsDialog::~ExtensionsDialog() {
51+ ////
52+}
53+
54+void ExtensionsDialog::OnClickLink(wxHtmlLinkEvent& evt) {
55+ ::wxLaunchDefaultBrowser(evt.GetLinkInfo().GetHref(), wxBROWSER_NEW_WINDOW);
56+}
57+
58+void ExtensionsDialog::OnClickOK(wxCommandEvent& evt) {
59+ EndModal(0);
60+}
61+
62+void ExtensionsDialog::OnClickOpenFolder(wxCommandEvent& evt) {
63+ wxString cmd, extensionsDir = g_gui.GetExtensionsDirectory();
64+#if defined __WINDOWS__
65+ cmd << "explorer";
66+#elif defined __APPLE__
67+ cmd << "open";
68+ extensionsDir.Replace(" ", "\\ "); // Escape spaces
69+#elif defined __linux
70+ cmd << "xdg-open";
71+#else
72+ #error "NOT IMPLEMENTED"
73+#endif
74+
75+ cmd << " " << extensionsDir;
76+
77+ wxExecute(cmd);
78+}
79+
80+wxString ExtensionsDialog::HTML() const {
81+ wxString markup;
82+ for (MaterialsExtensionList::const_iterator me = g_materials.getExtensions().begin(); me != g_materials.getExtensions().end(); ++me) {
83+ markup << HTMLForExtension(*me);
84+ markup << "<hr>";
85+ }
86+ return markup;
87+}
88+
89+wxString ExtensionsDialog::HTMLForExtension(MaterialsExtension* me) const {
90+ wxString markup;
91+ markup
92+ << "<table>"
93+
94+ << "< background='#ff0000'>"
95+ << "<td width='100px'><b>Extension</b></td>"
96+ << "<td>";
97+
98+ if (me->url.empty()) {
99+ markup << me->name;
100+ } else {
101+ markup << "<a href='" << me->url << "'>" << me->name << "</a>";
102+ }
103+
104+ markup
105+ << "</td>"
106+ << "</tr>"
107+
108+ << "<tr>"
109+ << "<td width='100px'><b>Author</b></td>"
110+ << "<td>";
111+
112+ if (me->author_url.empty()) {
113+ markup << me->author;
114+ } else {
115+ markup << "<a href='" << me->author_url << "'>" << me->author << "</a>";
116+ }
117+
118+ markup
119+ << "</td>"
120+ << "</tr>"
121+
122+ << "<tr>"
123+ << "<td width='100px'><b>Description</b></td>"
124+ << "<td>" << me->description << "</td>"
125+ << "</tr>"
126+
127+ << "<tr>"
128+ << "<td width='100px'><b>Clients</b></td>"
129+ << "<td>" << me->getVersionString() << "</td>"
130+ << "</tr>"
131+
132+ << "</table>";
133+
134+ return markup;
135+}
A RD_Map_Editor/source/extension_window.h +39-0 View file
@@ -0,0 +1,39 @@
1+//////////////////////////////////////////////////////////////////////
2+// This file is part of Remere's Map Editor
3+//////////////////////////////////////////////////////////////////////
4+// Remere's Map Editor is free software: you can redistribute it and/or modify
5+// it under the terms of the GNU General Public License as published by
6+// the Free Software Foundation, either version 3 of the License, or
7+// (at your option) any later version.
8+//
9+// Remere's Map Editor is distributed in the hope that it will be useful,
10+// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+// GNU General Public License for more details.
13+//
14+// You should have received a copy of the GNU General Public License
15+// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+//////////////////////////////////////////////////////////////////////
17+
18+#ifndef RME_EXTENSION_WINDOW_H
19+#define RME_EXTENSION_WINDOW_H
20+
21+class MaterialsExtension;
22+
23+class ExtensionsDialog : public wxDialog {
24+public:
25+ ExtensionsDialog(wxWindow* parent);
26+ virtual ~ExtensionsDialog();
27+
28+ void OnClickOK(wxCommandEvent& evt);
29+ void OnClickOpenFolder(wxCommandEvent& evt);
30+ void OnClickLink(wxHtmlLinkEvent& evt);
31+
32+ DECLARE_EVENT_TABLE();
33+
34+private:
35+ wxString HTML() const;
36+ wxString HTMLForExtension(MaterialsExtension* me) const;
37+};
38+
39+#endif
A RD_Map_Editor/source/filehandle.h +389-0 View file
@@ -0,0 +1,389 @@
1+//////////////////////////////////////////////////////////////////////
2+// This file is part of Remere's Map Editor
3+//////////////////////////////////////////////////////////////////////
4+// Remere's Map Editor is free software: you can redistribute it and/or modify
5+// it under the terms of the GNU General Public License as published by
6+// the Free Software Foundation, either version 3 of the License, or
7+// (at your option) any later version.
8+//
9+// Remere's Map Editor is distributed in the hope that it will be useful,
10+// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+// GNU General Public License for more details.
13+//
14+// You should have received a copy of the GNU General Public License
15+// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+//////////////////////////////////////////////////////////////////////
17+
18+#ifndef RME_FILEHANDLE_H_
19+#define RME_FILEHANDLE_H_
20+
21+#include "definitions.h"
22+
23+#include <stdexcept>
24+#include <string>
25+#include <stack>
26+#include <stdio.h>
27+
28+#ifndef FORCEINLINE
29+ #ifdef _MSV_VER
30+ #define FORCEINLINE __forceinline
31+ #else
32+ #define FORCEINLINE inline
33+ #endif
34+#endif
35+
36+enum FileHandleError {
37+ FILE_NO_ERROR,
38+ FILE_COULD_NOT_OPEN,
39+ FILE_INVALID_IDENTIFIER,
40+ FILE_STRING_TOO_LONG,
41+ FILE_READ_ERROR,
42+ FILE_WRITE_ERROR,
43+ FILE_SYNTAX_ERROR,
44+ FILE_PREMATURE_END,
45+};
46+
47+enum NodeType {
48+ NODE_START = 0xfe,
49+ NODE_END = 0xff,
50+ ESCAPE_CHAR = 0xfd,
51+};
52+
53+class FileHandle : boost::noncopyable {
54+public:
55+ FileHandle() :
56+ error_code(FILE_NO_ERROR), file(nullptr) { }
57+ virtual ~FileHandle() {
58+ close();
59+ }
60+
61+ virtual void close();
62+ virtual bool isOpen() {
63+ return file != nullptr;
64+ }
65+ virtual bool isOk() {
66+ return isOpen() && error_code == FILE_NO_ERROR && ferror(file) == 0;
67+ }
68+ std::string getErrorMessage();
69+
70+public:
71+ FileHandleError error_code;
72+ FILE* file;
73+};
74+
75+class FileReadHandle : public FileHandle {
76+public:
77+ explicit FileReadHandle(const std::string& name);
78+ virtual ~FileReadHandle();
79+
80+ FORCEINLINE bool getU8(uint8_t& u8) {
81+ return getType(u8);
82+ }
83+ FORCEINLINE bool getByte(uint8_t& u8) {
84+ return getType(u8);
85+ }
86+ FORCEINLINE bool getSByte(int8_t& i8) {
87+ return getType(i8);
88+ }
89+ FORCEINLINE bool getU16(uint16_t& u16) {
90+ return getType(u16);
91+ }
92+ FORCEINLINE bool getU32(uint32_t& u32) {
93+ return getType(u32);
94+ }
95+ FORCEINLINE bool get32(int32_t& i32) {
96+ return getType(i32);
97+ }
98+ bool getRAW(uint8_t* ptr, size_t sz);
99+ bool getRAW(std::string& str, size_t sz);
100+ bool getString(std::string& str);
101+ bool getLongString(std::string& str);
102+
103+ virtual void close();
104+ bool seek(size_t offset);
105+ bool seekRelative(size_t offset);
106+ FORCEINLINE void skip(size_t offset) {
107+ seekRelative(offset);
108+ }
109+ size_t size() {
110+ return file_size;
111+ }
112+ size_t tell() {
113+ if (file) {
114+ return ftell(file);
115+ }
116+ return 0;
117+ }
118+
119+protected:
120+ size_t file_size;
121+
122+ template <class T>
123+ bool getType(T& ref) {
124+ fread(&ref, sizeof(ref), 1, file);
125+ return ferror(file) == 0;
126+ }
127+};
128+
129+class NodeFileReadHandle;
130+class DiskNodeFileReadHandle;
131+class MemoryNodeFileReadHandle;
132+
133+class BinaryNode {
134+public:
135+ BinaryNode(NodeFileReadHandle* file, BinaryNode* parent);
136+ ~BinaryNode();
137+
138+ FORCEINLINE bool getU8(uint8_t& u8) {
139+ return getType(u8);
140+ }
141+ FORCEINLINE bool getByte(uint8_t& u8) {
142+ return getType(u8);
143+ }
144+ FORCEINLINE bool getU16(uint16_t& u16) {
145+ return getType(u16);
146+ }
147+ FORCEINLINE bool getU32(uint32_t& u32) {
148+ return getType(u32);
149+ }
150+ FORCEINLINE bool getU64(uint64_t& u64) {
151+ return getType(u64);
152+ }
153+ FORCEINLINE bool skip(size_t sz) {
154+ if (read_offset + sz > data.size()) {
155+ read_offset = data.size();
156+ return false;
157+ }
158+ read_offset += sz;
159+ return true;
160+ }
161+ bool getRAW(uint8_t* ptr, size_t sz);
162+ bool getRAW(std::string& str, size_t sz);
163+ bool getString(std::string& str);
164+ bool getLongString(std::string& str);
165+
166+ BinaryNode* getChild();
167+ // Returns this on success, nullptr on failure
168+ BinaryNode* advance();
169+
170+protected:
171+ template <class T>
172+ bool getType(T& ref) {
173+ if (read_offset + sizeof(ref) > data.size()) {
174+ read_offset = data.size();
175+ return false;
176+ }
177+ ref = *(T*)(data.data() + read_offset);
178+
179+ read_offset += sizeof(ref);
180+ return true;
181+ }
182+
183+ void load();
184+ std::string data;
185+ size_t read_offset;
186+ NodeFileReadHandle* file;
187+ BinaryNode* parent;
188+ BinaryNode* child;
189+
190+ friend class DiskNodeFileReadHandle;
191+ friend class MemoryNodeFileReadHandle;
192+};
193+
194+class NodeFileReadHandle : public FileHandle {
195+public:
196+ NodeFileReadHandle();
197+ virtual ~NodeFileReadHandle();
198+
199+ virtual BinaryNode* getRootNode() = 0;
200+
201+ virtual size_t size() = 0;
202+ virtual size_t tell() = 0;
203+
204+protected:
205+ BinaryNode* getNode(BinaryNode* parent);
206+ void freeNode(BinaryNode* node);
207+ // Returns false when end-of-file is reached
208+ virtual bool renewCache() = 0;
209+
210+ bool last_was_start;
211+ uint8_t* cache;
212+ size_t cache_size;
213+ size_t cache_length;
214+ size_t local_read_index;
215+
216+ BinaryNode* root_node;
217+
218+ std::stack<void*> unused;
219+
220+ friend class BinaryNode;
221+};
222+
223+class DiskNodeFileReadHandle : public NodeFileReadHandle {
224+public:
225+ DiskNodeFileReadHandle(const std::string& name, const std::vector<std::string>& acceptable_identifiers);
226+ virtual ~DiskNodeFileReadHandle();
227+
228+ virtual void close();
229+ virtual BinaryNode* getRootNode();
230+
231+ virtual size_t size() {
232+ return file_size;
233+ }
234+ virtual size_t tell() {
235+ if (file) {
236+ return ftell(file);
237+ }
238+ return 0;
239+ }
240+
241+protected:
242+ virtual bool renewCache();
243+
244+ size_t file_size;
245+};
246+
247+class MemoryNodeFileReadHandle : public NodeFileReadHandle {
248+public:
249+ // Does NOT claim ownership of the memory it is given.
250+ MemoryNodeFileReadHandle(const uint8_t* data, size_t size);
251+ virtual ~MemoryNodeFileReadHandle();
252+
253+ void assign(const uint8_t* data, size_t size);
254+
255+ virtual void close();
256+ virtual BinaryNode* getRootNode();
257+
258+ virtual size_t size() {
259+ return cache_size;
260+ }
261+ virtual size_t tell() {
262+ return local_read_index;
263+ }
264+ virtual bool isOk() {
265+ return true;
266+ }
267+
268+protected:
269+ virtual bool renewCache();
270+
271+ uint8_t* index;
272+};
273+
274+class FileWriteHandle : public FileHandle {
275+public:
276+ explicit FileWriteHandle(const std::string& name);
277+ virtual ~FileWriteHandle();
278+
279+ FORCEINLINE bool addU8(uint8_t u8) {
280+ return addType(u8);
281+ }
282+ FORCEINLINE bool addByte(uint8_t u8) {
283+ return addType(u8);
284+ }
285+ FORCEINLINE bool addU16(uint16_t u16) {
286+ return addType(u16);
287+ }
288+ FORCEINLINE bool addU32(uint32_t u32) {
289+ return addType(u32);
290+ }
291+ FORCEINLINE bool addU64(uint64_t u64) {
292+ return addType(u64);
293+ }
294+ bool addString(const std::string& str);
295+ bool addString(const char* str);
296+ bool addLongString(const std::string& str);
297+ bool addRAW(const std::string& str);
298+ bool addRAW(const uint8_t* ptr, size_t sz);
299+ bool addRAW(const char* c) {
300+ return addRAW(reinterpret_cast<const uint8_t*>(c), strlen(c));
301+ }
302+
303+protected:
304+ template <class T>
305+ bool addType(T ref) {
306+ fwrite(&ref, sizeof(ref), 1, file);
307+ return ferror(file) == 0;
308+ }
309+};
310+
311+class NodeFileWriteHandle : public FileHandle {
312+public:
313+ NodeFileWriteHandle();
314+ virtual ~NodeFileWriteHandle();
315+
316+ bool addNode(uint8_t nodetype);
317+ bool endNode();
318+
319+ bool addU8(uint8_t u8);
320+ bool addByte(uint8_t u8);
321+ bool addU16(uint16_t u16);
322+ bool addU32(uint32_t u32);
323+ bool addU64(uint64_t u64);
324+ bool addString(const std::string& str);
325+ bool addLongString(const std::string& str);
326+ bool addRAW(std::string& str);
327+ bool addRAW(const uint8_t* ptr, size_t sz);
328+ bool addRAW(const char* c) {
329+ return addRAW(reinterpret_cast<const uint8_t*>(c), strlen(c));
330+ }
331+
332+protected:
333+ virtual void renewCache() = 0;
334+
335+ static uint8_t NODE_START;
336+ static uint8_t NODE_END;
337+ static uint8_t ESCAPE_CHAR;
338+
339+ uint8_t* cache;
340+ size_t cache_size;
341+ size_t local_write_index;
342+
343+ FORCEINLINE void writeBytes(const uint8_t* ptr, size_t sz) {
344+ if (sz) {
345+ do {
346+ if (*ptr == NODE_START || *ptr == NODE_END || *ptr == ESCAPE_CHAR) {
347+ cache[local_write_index++] = ESCAPE_CHAR;
348+ if (local_write_index >= cache_size) {
349+ renewCache();
350+ }
351+ }
352+ cache[local_write_index++] = *ptr;
353+ if (local_write_index >= cache_size) {
354+ renewCache();
355+ }
356+ ++ptr;
357+ --sz;
358+ } while (sz != 0);
359+ }
360+ }
361+};
362+
363+class DiskNodeFileWriteHandle : public NodeFileWriteHandle {
364+public:
365+ DiskNodeFileWriteHandle(const std::string& name, const std::string& identifier);
366+ virtual ~DiskNodeFileWriteHandle();
367+
368+ virtual void close();
369+
370+protected:
371+ virtual void renewCache();
372+};
373+
374+class MemoryNodeFileWriteHandle : public NodeFileWriteHandle {
375+public:
376+ MemoryNodeFileWriteHandle();
377+ virtual ~MemoryNodeFileWriteHandle();
378+
379+ void reset();
380+ virtual void close();
381+
382+ uint8_t* getMemory();
383+ size_t getSize();
384+
385+protected:
386+ virtual void renewCache();
387+};
388+
389+#endif
A RD_Map_Editor/source/find_creature_window.cpp +283-0 View file
@@ -0,0 +1,283 @@
1+//////////////////////////////////////////////////////////////////////
2+// This file is part of Remere's Map Editor
3+//////////////////////////////////////////////////////////////////////
4+// Remere's Map Editor is free software: you can redistribute it and/or modify
5+// it under the terms of the GNU General Public License as published by
6+// the Free Software Foundation, either version 3 of the License, or
7+// (at your option) any later version.
8+//
9+// Remere's Map Editor is distributed in the hope that it will be useful,
10+// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+// GNU General Public License for more details.
13+//
14+// You should have received a copy of the GNU General Public License
15+// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+//////////////////////////////////////////////////////////////////////
17+
18+#include "main.h"
19+#include "find_creature_window.h"
20+#include "common_windows.h"
21+#include "gui.h"
22+#include "creatures.h"
23+#include "creature_brush.h"
24+#include "spawn_brush.h"
25+#include "spawn.h"
26+#include "map.h"
27+#include "editor.h"
28+#include "result_window.h"
29+
30+// ============================================================================
31+// FindCreatureListBox
32+
33+class FindCreatureListBox : public wxListBox {
34+public:
35+ FindCreatureListBox(wxWindow* parent, wxWindowID id) : wxListBox(parent, id, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SINGLE | wxLB_NEEDED_SB) {
36+ // ...
37+ }
38+
39+ ~FindCreatureListBox() {
40+ // ...
41+ }
42+};
43+
44+// ============================================================================
45+// FindCreatureDialog
46+
47+BEGIN_EVENT_TABLE(FindCreatureDialog, wxDialog)
48+ EVT_TEXT(wxID_ANY, FindCreatureDialog::OnText)
49+ EVT_TIMER(wxID_ANY, FindCreatureDialog::OnInputTimer)
50+ EVT_BUTTON(wxID_OK, FindCreatureDialog::OnClickOK)
51+ EVT_BUTTON(wxID_CANCEL, FindCreatureDialog::OnClickCancel)
52+ EVT_BUTTON(wxID_REFRESH, FindCreatureDialog::OnRefreshClick)
53+END_EVENT_TABLE()
54+
55+FindCreatureDialog::FindCreatureDialog(wxWindow* parent, const wxString& title) :
56+ wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
57+ input_timer(this) {
58+
59+ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
60+
61+ wxBoxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL);
62+ wxBoxSizer* options_sizer = new wxBoxSizer(wxVERTICAL);
63+
64+ // Name search controls
65+ wxStaticBoxSizer* name_box_sizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, "Creature Name"), wxVERTICAL);
66+ name_text_input = new wxTextCtrl(name_box_sizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
67+ name_box_sizer->Add(name_text_input, 0, wxALL | wxEXPAND, 5);
68+ options_sizer->Add(name_box_sizer, 0, wxALL | wxEXPAND, 5);
69+
70+ // Type selection
71+ wxStaticBoxSizer* type_box_sizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, "Type"), wxVERTICAL);
72+ search_monsters = new wxCheckBox(type_box_sizer->GetStaticBox(), wxID_ANY, "Monsters", wxDefaultPosition, wxDefaultSize, 0);
73+ search_monsters->SetValue(true);
74+ type_box_sizer->Add(search_monsters, 0, wxALL, 5);
75+
76+ search_npcs = new wxCheckBox(type_box_sizer->GetStaticBox(), wxID_ANY, "NPCs", wxDefaultPosition, wxDefaultSize, 0);
77+ search_npcs->SetValue(true);
78+ type_box_sizer->Add(search_npcs, 0, wxALL, 5);
79+
80+ options_sizer->Add(type_box_sizer, 0, wxALL | wxEXPAND, 5);
81+
82+ // Auto-refresh option
83+ wxStaticBoxSizer* refresh_box_sizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, "Options"), wxVERTICAL);
84+ auto_refresh = new wxCheckBox(refresh_box_sizer->GetStaticBox(), wxID_ANY, "Auto Refresh", wxDefaultPosition, wxDefaultSize, 0);
85+ auto_refresh->SetValue(true);
86+ refresh_box_sizer->Add(auto_refresh, 0, wxALL, 5);
87+
88+ options_sizer->Add(refresh_box_sizer, 0, wxALL | wxEXPAND, 5);
89+
90+ // Add spacer
91+ options_sizer->Add(0, 0, 1, wxEXPAND, 5);
92+
93+ // Add buttons at the bottom
94+ buttons_box_sizer = new wxStdDialogButtonSizer();
95+ ok_button = new wxButton(this, wxID_OK);
96+ buttons_box_sizer->AddButton(ok_button);
97+ cancel_button = new wxButton(this, wxID_CANCEL);
98+ buttons_box_sizer->AddButton(cancel_button);
99+ refresh_button = new wxButton(this, wxID_REFRESH, "Refresh");
100+ buttons_box_sizer->Add(refresh_button);
101+ buttons_box_sizer->Realize();
102+ options_sizer->Add(buttons_box_sizer, 0, wxALIGN_CENTER | wxALL, 5);
103+
104+ main_sizer->Add(options_sizer, 1, wxALL | wxEXPAND, 5);
105+
106+ // Creature list
107+ wxStaticBoxSizer* creature_list_sizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, "Creatures"), wxVERTICAL);
108+ creatures_list = new FindCreatureListBox(this, wxID_ANY);
109+ creature_list_sizer->Add(creatures_list, 1, wxALL | wxEXPAND, 5);
110+
111+ main_sizer->Add(creature_list_sizer, 1, wxALL | wxEXPAND, 5);
112+
113+ SetSizer(main_sizer);
114+ Layout();
115+ Centre(wxBOTH);
116+
117+ RefreshContentsInternal();
118+}
119+
120+FindCreatureDialog::~FindCreatureDialog() {
121+ // Nothing to do
122+}
123+
124+void FindCreatureDialog::OnText(wxCommandEvent& event) {
125+ input_timer.Start(300, true);
126+}
127+
128+void FindCreatureDialog::OnInputTimer(wxTimerEvent& event) {
129+ if (auto_refresh->GetValue()) {
130+ RefreshContentsInternal();
131+ }
132+}
133+
134+void FindCreatureDialog::RefreshContentsInternal() {
135+ creatures_list->Clear();
136+
137+ bool include_monsters = search_monsters->GetValue();
138+ bool include_npcs = search_npcs->GetValue();
139+ wxString search_text = name_text_input->GetValue().Lower();
140+
141+ // Add matching creatures from the database to the list
142+ for (CreatureMap::iterator iter = g_creatures.begin(); iter != g_creatures.end(); ++iter) {
143+ CreatureType* creature_type = iter->second;
144+ if (!creature_type) continue;
145+
146+ // Filter by type
147+ if ((creature_type->isNpc && !include_npcs) ||
148+ (!creature_type->isNpc && !include_monsters)) {
149+ continue;
150+ }
151+
152+ // Filter by name
153+ wxString creature_name = wxString(creature_type->name.c_str(), wxConvUTF8).Lower();
154+ if (!search_text.IsEmpty() && creature_name.Find(search_text) == wxNOT_FOUND) {
155+ continue;
156+ }
157+
158+ // Add to list
159+ int index = creatures_list->Append(wxString(creature_type->name.c_str(), wxConvUTF8));
160+ creatures_list->SetClientData(index, creature_type);
161+ }
162+
163+ if (creatures_list->GetCount() > 0) {
164+ creatures_list->SetSelection(0);
165+ }
166+}
167+
168+void FindCreatureDialog::OnClickOK(wxCommandEvent& event) {
169+ int selection = creatures_list->GetSelection();
170+ if (selection != wxNOT_FOUND) {
171+ CreatureType* creature_type = reinterpret_cast<CreatureType*>(creatures_list->GetClientData(selection));
172+ if (creature_type) {
173+ result_name = wxString(creature_type->name.c_str(), wxConvUTF8);
174+
175+ // Find all instances of this creature in spawns across the map
176+ Editor* editor = g_gui.GetCurrentEditor();
177+ if (editor) {
178+ Map& map = editor->getMap();
179+ // Create a results window to display the found spawns
180+ SearchResultWindow* result_window = g_gui.ShowSearchWindow();
181+ result_window->Clear();
182+
183+ // Iterate through all tiles to find spawns with matching creatures
184+ size_t creature_found_count = 0;
185+
186+ g_gui.CreateLoadBar("Searching for creatures...");
187+
188+ const std::string creature_name = creature_type->name;
189+ uint64_t tile_count = 0;
190+ for (int z = 0; z < 16; ++z) {
191+ for (int x = 0; x < map.getWidth(); ++x) {
192+ for (int y = 0; y < map.getHeight(); ++y) {
193+ ++tile_count;
194+ if (tile_count % 5000 == 0) {
195+ g_gui.SetLoadDone(int(tile_count * 100.0 / map.getTileCount()));
196+ }
197+
198+ Tile* tile = map.getTile(x, y, z);
199+ if (!tile) continue;
200+
201+ // Check for spawns
202+ if (tile->spawn) {
203+ // Found a spawn, now check for creatures nearby with matching name
204+ bool found_matching_creature = false;
205+ Position spawn_pos = tile->getPosition();
206+ int spawn_radius = tile->spawn->getSize();
207+
208+ // Check if any creatures of this type exist within spawn radius
209+ for (int sx = -spawn_radius; sx <= spawn_radius && !found_matching_creature; ++sx) {
210+ for (int sy = -spawn_radius; sy <= spawn_radius && !found_matching_creature; ++sy) {
211+ Tile* creature_tile = map.getTile(spawn_pos.x + sx, spawn_pos.y + sy, spawn_pos.z);
212+ if (!creature_tile || !creature_tile->creature) continue;
213+
214+ // Check if this creature matches our search
215+ if (creature_tile->creature && creature_tile->creature->getName() == creature_name) {
216+ found_matching_creature = true;
217+ wxString description = wxString::Format("%s at (%d,%d,%d)",
218+ wxString(creature_name.c_str(), wxConvUTF8),
219+ creature_tile->getPosition().x,
220+ creature_tile->getPosition().y,
221+ creature_tile->getPosition().z);
222+ result_window->AddPosition(description, creature_tile->getPosition());
223+ ++creature_found_count;
224+ }
225+ }
226+ }
227+
228+ // If no creature of this type found, still show the spawn position
229+ if (!found_matching_creature) {
230+ wxString description = wxString::Format("Spawn for %s at (%d,%d,%d)",
231+ wxString(creature_name.c_str(), wxConvUTF8),
232+ spawn_pos.x, spawn_pos.y, spawn_pos.z);
233+ result_window->AddPosition(description, spawn_pos);
234+ ++creature_found_count;
235+ }
236+ }
237+
238+ // Check for loose creatures (not in spawns)
239+ if (tile->creature) {
240+ // Check if this loose creature matches our search
241+ if (tile->creature->getName() == creature_name) {
242+ wxString description = wxString::Format("%s (loose) at (%d,%d,%d)",
243+ wxString(creature_name.c_str(), wxConvUTF8),
244+ tile->getPosition().x,
245+ tile->getPosition().y,
246+ tile->getPosition().z);
247+ result_window->AddPosition(description, tile->getPosition());
248+ ++creature_found_count;
249+ }
250+ }
251+ }
252+ }
253+ }
254+
255+ g_gui.DestroyLoadBar();
256+
257+ wxString result_message;
258+ if (creature_found_count == 0) {
259+ result_message = wxString::Format("No %s found on the map.", wxString(creature_name.c_str(), wxConvUTF8));
260+ g_gui.PopupDialog("Search completed", result_message, wxOK);
261+ } else {
262+ result_message = wxString::Format("Found %d instances of %s on the map.",
263+ creature_found_count, wxString(creature_name.c_str(), wxConvUTF8));
264+ g_gui.SetStatusText(result_message);
265+ }
266+ }
267+
268+ EndModal(wxID_OK);
269+ }
270+ }
271+}
272+
273+void FindCreatureDialog::OnClickCancel(wxCommandEvent& event) {
274+ EndModal(wxID_CANCEL);
275+}
276+
277+void FindCreatureDialog::OnRefreshClick(wxCommandEvent& event) {
278+ RefreshContentsInternal();
279+}
280+
281+void FindCreatureDialog::OnClose(wxCloseEvent& event) {
282+ EndModal(wxID_CANCEL);
283+}
Top