tibia-rme/source/palette_house.cpp
Pedro Henrique Alves Cruz 5e2fb466d5 refactor: gui
2024-06-17 00:23:05 -03:00

554 lines
18 KiB
C++

//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
// Remere's Map Editor is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Remere's Map Editor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////
#include "main.h"
#include "palette_house.h"
#include "settings.h"
#include "brush.h"
#include "editor.h"
#include "map.h"
#include "application.h"
#include "map_display.h"
#include "house_brush.h"
#include "house_exit_brush.h"
#include "spawn_monster_brush.h"
#include "spawn_npc_brush.h"
// ============================================================================
// House palette
BEGIN_EVENT_TABLE(HousePalettePanel, PalettePanel)
EVT_TIMER(PALETTE_LAYOUT_FIX_TIMER, HousePalettePanel::OnLayoutFixTimer)
EVT_CHOICE(PALETTE_HOUSE_TOWN_CHOICE, HousePalettePanel::OnTownChange)
EVT_LISTBOX(PALETTE_HOUSE_LISTBOX, HousePalettePanel::OnListBoxChange)
EVT_LISTBOX_DCLICK(PALETTE_HOUSE_LISTBOX, HousePalettePanel::OnListBoxDoubleClick)
EVT_BUTTON(PALETTE_HOUSE_ADD_HOUSE, HousePalettePanel::OnClickAddHouse)
EVT_BUTTON(PALETTE_HOUSE_EDIT_HOUSE, HousePalettePanel::OnClickEditHouse)
EVT_BUTTON(PALETTE_HOUSE_REMOVE_HOUSE, HousePalettePanel::OnClickRemoveHouse)
EVT_TOGGLEBUTTON(PALETTE_HOUSE_BRUSH_BUTTON, HousePalettePanel::OnClickHouseBrushButton)
EVT_TOGGLEBUTTON(PALETTE_HOUSE_SELECT_EXIT_BUTTON, HousePalettePanel::OnClickSelectExitButton)
END_EVENT_TABLE()
HousePalettePanel::HousePalettePanel(wxWindow* parent, wxWindowID id) :
PalettePanel(parent, id),
map(nullptr),
do_resize_on_display(true),
fix_size_timer(this, PALETTE_LAYOUT_FIX_TIMER) {
wxSizer* topsizer = newd wxBoxSizer(wxVERTICAL);
wxSizer* tmpsizer;
wxSizer* sidesizer = newd wxStaticBoxSizer(wxVERTICAL, this, "Houses");
town_choice = newd wxChoice(this, PALETTE_HOUSE_TOWN_CHOICE, wxDefaultPosition, wxDefaultSize, (int)0, (const wxString*)nullptr);
sidesizer->Add(town_choice, 0, wxEXPAND);
house_list = newd SortableListBox(this, PALETTE_HOUSE_LISTBOX);
#ifdef __APPLE__
// Used for detecting a deselect
house_list->Bind(wxEVT_LEFT_UP, &HousePalettePanel::OnListBoxClick, this);
#endif
sidesizer->Add(house_list, 1, wxEXPAND);
tmpsizer = newd wxBoxSizer(wxHORIZONTAL);
wxSizerFlags sizerFlags(1);
tmpsizer->Add(add_house_button = newd wxButton(this, PALETTE_HOUSE_ADD_HOUSE, "Add", wxDefaultPosition, wxSize(50, -1)), sizerFlags);
tmpsizer->Add(edit_house_button = newd wxButton(this, PALETTE_HOUSE_EDIT_HOUSE, "Edit", wxDefaultPosition, wxSize(50, -1)), sizerFlags);
tmpsizer->Add(remove_house_button = newd wxButton(this, PALETTE_HOUSE_REMOVE_HOUSE, "Remove", wxDefaultPosition, wxSize(70, -1)), sizerFlags);
sidesizer->Add(tmpsizer, wxSizerFlags(0).Right());
topsizer->Add(sidesizer, 1, wxEXPAND);
// Temple position
sidesizer = newd wxStaticBoxSizer(newd wxStaticBox(this, wxID_ANY, "Brushes", wxDefaultPosition, wxSize(150, 200)), wxVERTICAL);
// sidesizer->Add(180, 1, wxEXPAND);
tmpsizer = newd wxBoxSizer(wxHORIZONTAL);
house_brush_button = newd wxToggleButton(this, PALETTE_HOUSE_BRUSH_BUTTON, "House tiles");
tmpsizer->Add(house_brush_button);
sidesizer->Add(tmpsizer, wxSizerFlags(1).Center());
tmpsizer = newd wxBoxSizer(wxHORIZONTAL);
select_position_button = newd wxToggleButton(this, PALETTE_HOUSE_SELECT_EXIT_BUTTON, "Select Exit");
tmpsizer->Add(select_position_button);
sidesizer->Add(tmpsizer, wxSizerFlags(1).Center());
topsizer->Add(sidesizer, 0, wxEXPAND);
SetSizerAndFit(topsizer);
}
HousePalettePanel::~HousePalettePanel() {
////
}
void HousePalettePanel::SetMap(Map* m) {
g_gui.houseBrush->setHouse(nullptr);
map = m;
OnUpdate();
}
void HousePalettePanel::OnSwitchIn() {
PalettePanel::OnSwitchIn();
// Extremely ugly hack to fix layout issue
if (do_resize_on_display) {
fix_size_timer.Start(100, true);
do_resize_on_display = false;
}
}
void HousePalettePanel::OnLayoutFixTimer(wxTimerEvent &WXUNUSED(event)) {
wxWindow* w = this;
while ((w = w->GetParent()) && dynamic_cast<PaletteWindow*>(w) == nullptr)
;
if (w) {
w->SetSize(w->GetSize().GetWidth(), w->GetSize().GetHeight() + 1);
w->SetSize(w->GetSize().GetWidth(), w->GetSize().GetHeight() - 1);
}
}
void HousePalettePanel::SelectFirstBrush() {
SelectHouseBrush();
}
Brush* HousePalettePanel::GetSelectedBrush() const {
if (select_position_button->GetValue()) {
House* house = GetCurrentlySelectedHouse();
if (house) {
g_gui.houseExitBrush->setHouse(house);
}
return (g_gui.houseExitBrush->getHouseID() != 0 ? g_gui.houseExitBrush : nullptr);
} else if (house_brush_button->GetValue()) {
g_gui.houseBrush->setHouse(GetCurrentlySelectedHouse());
return (g_gui.houseBrush->getHouseID() != 0 ? g_gui.houseBrush : nullptr);
}
return nullptr;
}
bool HousePalettePanel::SelectBrush(const Brush* whatbrush) {
if (!whatbrush) {
return false;
}
if (whatbrush->isHouse() && map) {
const HouseBrush* house_brush = static_cast<const HouseBrush*>(whatbrush);
for (HouseMap::iterator house_iter = map->houses.begin(); house_iter != map->houses.end(); ++house_iter) {
if (house_iter->second->id == house_brush->getHouseID()) {
for (uint32_t i = 0; i < town_choice->GetCount(); ++i) {
Town* town = reinterpret_cast<Town*>(town_choice->GetClientData(i));
// If it's "No Town" (nullptr) select it, or if it has the same town ID as the house
if (town == nullptr || town->getID() == house_iter->second->townid) {
SelectTown(i);
for (uint32_t j = 0; j < house_list->GetCount(); ++j) {
if (house_iter->second->id == reinterpret_cast<House*>(house_list->GetClientData(j))->id) {
SelectHouse(j);
return true;
}
}
return true;
}
}
}
}
} else if (whatbrush->isSpawnMonster()) {
SelectExitBrush();
} else if (whatbrush->isSpawnNpc()) {
SelectExitBrush();
}
return false;
}
int HousePalettePanel::GetSelectedBrushSize() const {
return 0;
}
PaletteType HousePalettePanel::GetType() const {
return TILESET_HOUSE;
}
void HousePalettePanel::SelectTown(size_t index) {
ASSERT(town_choice->GetCount() >= index);
if (map == nullptr || town_choice->GetCount() == 0) {
// No towns :(
add_house_button->Enable(false);
} else {
Town* what_town = reinterpret_cast<Town*>(town_choice->GetClientData(index));
// Clear the old houselist
house_list->Clear();
for (HouseMap::iterator house_iter = map->houses.begin(); house_iter != map->houses.end(); ++house_iter) {
if (what_town) {
if (house_iter->second->townid == what_town->getID()) {
house_list->Append(wxstr(house_iter->second->getDescription()), house_iter->second);
}
} else {
// "No Town" selected!
if (map->towns.getTown(house_iter->second->townid) == nullptr) {
// The town doesn't exist
house_list->Append(wxstr(house_iter->second->getDescription()), house_iter->second);
}
}
}
house_list->Sort();
// Select first house
SelectHouse(0);
town_choice->SetSelection(index);
add_house_button->Enable(what_town != nullptr);
ASSERT(what_town == nullptr || add_house_button->IsEnabled() || !IsEnabled());
}
}
void HousePalettePanel::SelectHouse(size_t index) {
ASSERT(house_list->GetCount() >= index);
if (house_list->GetCount() > 0) {
edit_house_button->Enable(true);
remove_house_button->Enable(true);
select_position_button->Enable(true);
house_brush_button->Enable(true);
// Select the house
house_list->SetSelection(index);
SelectHouseBrush();
} else {
// No houses :(
edit_house_button->Enable(false);
remove_house_button->Enable(false);
select_position_button->Enable(false);
house_brush_button->Enable(false);
}
SelectHouseBrush();
g_gui.RefreshView();
}
House* HousePalettePanel::GetCurrentlySelectedHouse() const {
int selection = house_list->GetSelection();
if (house_list->GetCount() > 0 && selection != wxNOT_FOUND) {
return reinterpret_cast<House*>(house_list->GetClientData(selection));
}
return nullptr;
}
void HousePalettePanel::SelectHouseBrush() {
if (house_list->GetCount() > 0) {
house_brush_button->SetValue(true);
select_position_button->SetValue(false);
} else {
house_brush_button->SetValue(false);
select_position_button->SetValue(false);
}
}
void HousePalettePanel::SelectExitBrush() {
if (house_list->GetCount() > 0) {
house_brush_button->SetValue(false);
select_position_button->SetValue(true);
}
}
void HousePalettePanel::OnUpdate() {
int old_town_selection = town_choice->GetSelection();
town_choice->Clear();
house_list->Clear();
if (map == nullptr) {
return;
}
if (map->towns.count() != 0) {
// Create choice control
for (TownMap::iterator town_iter = map->towns.begin(); town_iter != map->towns.end(); ++town_iter) {
town_choice->Append(wxstr(town_iter->second->getName()), town_iter->second);
}
town_choice->Append("No Town", (void*)(nullptr));
if (old_town_selection <= 0) {
SelectTown(0);
} else if ((size_t)old_town_selection <= town_choice->GetCount()) {
SelectTown(old_town_selection);
} else {
SelectTown(old_town_selection - 1);
}
house_list->Enable(true);
} else {
town_choice->Append("No Town", (void*)(nullptr));
select_position_button->Enable(false);
select_position_button->SetValue(false);
house_brush_button->Enable(false);
house_brush_button->SetValue(false);
add_house_button->Enable(false);
edit_house_button->Enable(false);
remove_house_button->Enable(false);
SelectTown(0);
}
}
void HousePalettePanel::OnTownChange(wxCommandEvent &event) {
SelectTown(event.GetSelection());
g_gui.SelectBrush();
}
void HousePalettePanel::OnListBoxChange(wxCommandEvent &event) {
SelectHouse(event.GetSelection());
g_gui.SelectBrush();
}
void HousePalettePanel::OnListBoxDoubleClick(wxCommandEvent &event) {
House* house = reinterpret_cast<House*>(event.GetClientData());
// I find it extremly unlikely that one actually wants the exit at 0,0,0, so just treat it as the null value
if (house && house->getExit() != Position(0, 0, 0)) {
g_gui.SetScreenCenterPosition(house->getExit());
}
}
void HousePalettePanel::OnClickHouseBrushButton(wxCommandEvent &event) {
SelectHouseBrush();
g_gui.SelectBrush();
}
void HousePalettePanel::OnClickSelectExitButton(wxCommandEvent &event) {
SelectExitBrush();
g_gui.SelectBrush();
}
void HousePalettePanel::OnClickAddHouse(wxCommandEvent &event) {
if (map == nullptr) {
return;
}
House* new_house = newd House(*map);
new_house->id = map->houses.getEmptyID();
std::ostringstream os;
os << "Unnamed House #" << new_house->id;
new_house->name = os.str();
Town* town = reinterpret_cast<Town*>(town_choice->GetClientData(town_choice->GetSelection()));
ASSERT(town);
new_house->townid = town->getID();
map->houses.addHouse(new_house);
house_list->Append(wxstr(new_house->getDescription()), new_house);
SelectHouse(house_list->FindString(wxstr(new_house->getDescription())));
g_gui.SelectBrush();
refresh_timer.Start(300, true);
}
void HousePalettePanel::OnClickEditHouse(wxCommandEvent &event) {
if (house_list->GetCount() == 0) {
return;
}
if (map == nullptr) {
return;
}
int selection = house_list->GetSelection();
House* house = reinterpret_cast<House*>(house_list->GetClientData(selection));
if (house) {
wxDialog* d = newd EditHouseDialog(g_gui.root, map, house);
int ret = d->ShowModal();
if (ret == 1) {
// Something changed, change name of house
house_list->SetString(selection, wxstr(house->getDescription()));
house_list->Sort();
refresh_timer.Start(300, true);
}
}
}
void HousePalettePanel::OnClickRemoveHouse(wxCommandEvent &event) {
int selection = house_list->GetSelection();
if (selection != wxNOT_FOUND) {
House* house = reinterpret_cast<House*>(house_list->GetClientData(selection));
map->houses.removeHouse(house);
house_list->Delete(selection);
refresh_timer.Start(300, true);
if (int(house_list->GetCount()) <= selection) {
selection -= 1;
}
if (selection >= 0 && house_list->GetCount()) {
house_list->SetSelection(selection);
} else {
select_position_button->Enable(false);
select_position_button->SetValue(false);
house_brush_button->Enable(false);
house_brush_button->SetValue(false);
edit_house_button->Enable(false);
remove_house_button->Enable(false);
}
g_gui.SelectBrush();
}
g_gui.RefreshView();
}
#ifdef __APPLE__
// On wxMac it is possible to deselect a wxListBox. (Unlike on the other platforms)
// EVT_LISTBOX is not triggered when the deselection is happening. http://trac.wxwidgets.org/ticket/15603
// Here we find out if the listbox was deselected using a normal mouse up event so we know when to disable the buttons and brushes.
void HousePalettePanel::OnListBoxClick(wxMouseEvent &event) {
if (house_list->GetSelection() == wxNOT_FOUND) {
select_position_button->Enable(false);
select_position_button->SetValue(false);
house_brush_button->Enable(false);
house_brush_button->SetValue(false);
edit_house_button->Enable(false);
remove_house_button->Enable(false);
g_gui.SelectBrush();
}
}
#endif
// ============================================================================
// House Edit Dialog
BEGIN_EVENT_TABLE(EditHouseDialog, wxDialog)
EVT_BUTTON(wxID_OK, EditHouseDialog::OnClickOK)
EVT_BUTTON(wxID_CANCEL, EditHouseDialog::OnClickCancel)
END_EVENT_TABLE()
EditHouseDialog::EditHouseDialog(wxWindow* parent, Map* map, House* house) :
wxDialog(parent, wxID_ANY, "House", wxDefaultPosition, wxSize(250, 160)),
map(map),
what_house(house) {
ASSERT(map);
ASSERT(house);
// Create topsizer
wxSizer* sizer = newd wxBoxSizer(wxVERTICAL);
wxSizer* tmpsizer;
house_name = wxstr(house->name);
house_id = i2ws(house->id);
house_rent = i2ws(house->rent);
house_clientid = i2ws(house->clientid);
house_beds = i2ws(house->beds);
// House options
tmpsizer = newd wxStaticBoxSizer(wxHORIZONTAL, this, "Name");
name_field = newd wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(230, 20), 0, wxTextValidator(wxFILTER_ASCII, &house_name));
tmpsizer->Add(name_field);
sizer->Add(tmpsizer, wxSizerFlags().Border(wxALL, 20));
tmpsizer = newd wxStaticBoxSizer(wxHORIZONTAL, this, "Rent / ID");
rent_field = newd wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(160, 20), 0, wxTextValidator(wxFILTER_NUMERIC, &house_rent));
tmpsizer->Add(rent_field);
id_field = newd wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(70, 20), 0, wxTextValidator(wxFILTER_NUMERIC, &house_id));
id_field->Enable(false);
tmpsizer->Add(id_field);
sizer->Add(tmpsizer, wxSizerFlags().Border(wxALL, 20));
tmpsizer = newd wxStaticBoxSizer(wxHORIZONTAL, this, "Client ID");
clientid_field = newd wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(160, 20), 0, wxTextValidator(wxFILTER_NUMERIC, &house_clientid));
tmpsizer->Add(clientid_field);
sizer->Add(tmpsizer, wxSizerFlags().Border(wxALL, 20));
tmpsizer = newd wxStaticBoxSizer(wxHORIZONTAL, this, "Max beds");
beds_field = newd wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(160, 20), 0, wxTextValidator(wxFILTER_NUMERIC, &house_beds));
tmpsizer->Add(beds_field);
sizer->Add(tmpsizer, wxSizerFlags().Border(wxALL, 20));
// House options
guildhall_field = newd wxCheckBox(this, wxID_ANY, "Guildhall", wxDefaultPosition);
sizer->Add(guildhall_field, wxSizerFlags().Border(wxRIGHT | wxLEFT | wxBOTTOM, 20));
guildhall_field->SetValue(house->guildhall);
// OK/Cancel buttons
tmpsizer = newd wxBoxSizer(wxHORIZONTAL);
tmpsizer->Add(newd wxButton(this, wxID_OK, "OK"), wxSizerFlags(1).Center());
tmpsizer->Add(newd wxButton(this, wxID_CANCEL, "Cancel"), wxSizerFlags(1).Center());
sizer->Add(tmpsizer, wxSizerFlags(1).Center().Border(wxRIGHT | wxLEFT | wxBOTTOM, 20));
SetSizerAndFit(sizer);
}
EditHouseDialog::~EditHouseDialog() {
////
}
void EditHouseDialog::OnClickOK(wxCommandEvent &WXUNUSED(event)) {
if (Validate() && TransferDataFromWindow()) {
// Verify the newd information
long new_house_rent;
house_rent.ToLong(&new_house_rent);
long new_house_beds;
house_beds.ToLong(&new_house_beds);
long new_house_clientid;
house_clientid.ToLong(&new_house_clientid);
if (new_house_rent < 0) {
g_gui.PopupDialog(this, "Error", "House rent cannot be less than 0.", wxOK);
return;
}
if (new_house_beds < 0) {
g_gui.PopupDialog(this, "Error", "House beds cannot be less than 0.", wxOK);
return;
}
if (house_name.length() == 0) {
g_gui.PopupDialog(this, "Error", "House name cannot be nil.", wxOK);
return;
}
if (g_settings.getInteger(Config::WARN_FOR_DUPLICATE_ID)) {
Houses &houses = map->houses;
for (HouseMap::const_iterator house_iter = houses.begin(); house_iter != houses.end(); ++house_iter) {
House* house = house_iter->second;
ASSERT(house);
if (wxstr(house->name) == house_name && house->id != what_house->id) {
int ret = g_gui.PopupDialog(this, "Warning", "This house name is already in use, are you sure you want to continue?", wxYES | wxNO);
if (ret == wxID_NO) {
return;
}
}
}
}
// Transfer to house
what_house->name = nstr(house_name);
what_house->rent = new_house_rent;
what_house->clientid = new_house_clientid;
what_house->beds = new_house_beds;
what_house->guildhall = guildhall_field->GetValue();
EndModal(1);
}
}
void EditHouseDialog::OnClickCancel(wxCommandEvent &WXUNUSED(event)) {
// Just close this window
EndModal(0);
}