Add a location picker for selecting a location in a town or outdoor sector.
It's currently used in special node editing and in advanced town details.
This commit is contained in:
@@ -8,6 +8,7 @@ scened_sources = Split("""
|
||||
scen.fileio.cpp
|
||||
scen.graphics.cpp
|
||||
scen.keydlgs.cpp
|
||||
scen.locpicker.cpp
|
||||
scen.main.cpp
|
||||
scen.townout.cpp
|
||||
../view_dialogs.cpp
|
||||
@@ -49,4 +50,4 @@ else:
|
||||
|
||||
env.Package(scened, install_dir, scened_info)
|
||||
if debug_symbols is not None:
|
||||
env.Install(install_dir, debug_symbols)
|
||||
env.Install(install_dir, debug_symbols)
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "utility.hpp"
|
||||
#include "scen.graphics.hpp"
|
||||
#include "scen.keydlgs.hpp"
|
||||
#include "scen.locpicker.hpp"
|
||||
#include "scen.core.hpp"
|
||||
#include "dialogxml/dialogs/dialog.hpp"
|
||||
#include "dialogxml/widgets/control.hpp"
|
||||
@@ -1035,6 +1036,88 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t&
|
||||
store = choose_graphic(val, type, &me);
|
||||
if(store < 0) store = val;
|
||||
} break;
|
||||
case eSpecPicker::LOCATION: {
|
||||
auto otherField = get_control_for_field(fcn.continuation);
|
||||
location loc;
|
||||
loc.x = val;
|
||||
loc.y = me[otherField].getTextAsNum();
|
||||
static const std::string townStr = "Select a location in the current town:";
|
||||
static const std::string outStr = "Select a location in the current outdoors:";
|
||||
static const std::string specStr = "Select a location in ";
|
||||
switch(fcn.loc_type) {
|
||||
case eLocType::ACTIVE_OUT: loc = cLocationPicker(loc, *current_terrain, outStr, &me).run(); break;
|
||||
case eLocType::ACTIVE_TOWN: loc = cLocationPicker(loc, *town, townStr, &me).run(); break;
|
||||
case eLocType::ACTIVE_AUTO: {
|
||||
switch(edit_stack.top().mode) {
|
||||
case 1: loc = cLocationPicker(loc, *current_terrain, outStr, &me).run(); break;
|
||||
case 2: loc = cLocationPicker(loc, *town, townStr, &me).run(); break;
|
||||
case 0: {
|
||||
bool is_town = true;
|
||||
cLocationPicker picker(loc, *town, townStr, &me);
|
||||
auto& switchBtn = picker->getControl("switch");
|
||||
switchBtn.show();
|
||||
switchBtn.attachClickHandler([&picker, &is_town](cDialog&, std::string, eKeyMod) {
|
||||
if(is_town) {
|
||||
is_town = false;
|
||||
picker.setArea(*current_terrain);
|
||||
picker.setTitle(outStr);
|
||||
} else {
|
||||
is_town = true;
|
||||
picker.setArea(*town);
|
||||
picker.setTitle(townStr);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
loc = picker.run();
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
case eLocType::SPECIFIED_TOWN: {
|
||||
node_function_t y_fcn = get_field_function(spec, otherField);
|
||||
auto townField = get_control_for_field(y_fcn.continuation);
|
||||
int townNum = me[townField].getTextAsNum();
|
||||
townNum = minmax(0, scenario.towns.size() - 1, townNum);
|
||||
cLocationPicker picker(loc, *scenario.towns[townNum], specStr + scenario.towns[townNum]->name + ":", &me);
|
||||
auto& switchBtn = picker->getControl("switch");
|
||||
switchBtn.show();
|
||||
switchBtn.attachClickHandler([&picker, &townNum](cDialog& me, std::string, eKeyMod) {
|
||||
townNum = choose_text(STRT_TOWN, townNum, &me, "Which town?");
|
||||
picker.setArea(*scenario.towns[townNum]);
|
||||
picker.setTitle(specStr + scenario.towns[townNum]->name + ":");
|
||||
return true;
|
||||
});
|
||||
loc = picker.run();
|
||||
me[townField].setTextToNum(townNum);
|
||||
} break;
|
||||
case eLocType::SPECIFIED_OUT: {
|
||||
node_function_t y_fcn = get_field_function(spec, otherField);
|
||||
auto sectorXField = get_control_for_field(y_fcn.continuation);
|
||||
int sectorX = me[sectorXField].getTextAsNum();
|
||||
sectorX = minmax(0, scenario.outdoors.width() - 1, sectorX);
|
||||
node_function_t sec_fcn = get_field_function(spec, sectorXField);
|
||||
auto sectorYField = get_control_for_field(sec_fcn.continuation);
|
||||
int sectorY = me[sectorYField].getTextAsNum();
|
||||
sectorY = minmax(0, scenario.outdoors.height() - 1, sectorY);
|
||||
cLocationPicker picker(loc, *scenario.outdoors[sectorX][sectorY], specStr + scenario.outdoors[sectorX][sectorY]->name + ":", &me);
|
||||
auto& switchBtn = picker->getControl("switch");
|
||||
switchBtn.show();
|
||||
switchBtn.attachClickHandler([&picker, §orX, §orY](cDialog& me, std::string, eKeyMod) {
|
||||
int val = sectorX * scenario.outdoors.height() + sectorY;
|
||||
val = choose_text(STRT_SECTOR, val, &me, "Which sector?");
|
||||
sectorY = val % scenario.outdoors.height();
|
||||
sectorX = val / scenario.outdoors.height();
|
||||
picker.setArea(*scenario.outdoors[sectorX][sectorY]);
|
||||
picker.setTitle(specStr + scenario.outdoors[sectorX][sectorY]->name + ":");
|
||||
return true;
|
||||
});
|
||||
loc = picker.run();
|
||||
me[sectorXField].setTextToNum(sectorX);
|
||||
me[sectorYField].setTextToNum(sectorY);
|
||||
} break;
|
||||
}
|
||||
store = loc.x;
|
||||
me[otherField].setTextToNum(loc.y);
|
||||
} break;
|
||||
case eSpecPicker::FIELD: store = choose_field_type(val, &me, fcn.augmented); break;
|
||||
case eSpecPicker::DAMAGE_TYPE: store = choose_damage_type(val, &me, true); break;
|
||||
case eSpecPicker::EXPLOSION: store = choose_boom_type(val, &me); break;
|
||||
|
130
src/scenedit/scen.locpicker.cpp
Normal file
130
src/scenedit/scen.locpicker.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// scen.locpicker.cpp
|
||||
// BoE
|
||||
//
|
||||
// Created by Celtic Minstrel on 2025-02-28.
|
||||
//
|
||||
|
||||
#include "scen.locpicker.hpp"
|
||||
|
||||
#include "dialogxml/widgets/pict.hpp"
|
||||
#include "dialogxml/widgets/tilemap.hpp"
|
||||
#include "scenario/area.hpp"
|
||||
#include "scenario/scenario.hpp"
|
||||
#include "fileio/resmgr/res_dialog.hpp"
|
||||
|
||||
extern cScenario scenario;
|
||||
|
||||
cLocationPicker::cLocationPicker(location loc, cArea& area, const std::string& title, cDialog* parent)
|
||||
: initial_loc(loc)
|
||||
, chosen_loc(loc)
|
||||
, area(&area)
|
||||
, dlog(*ResMgr::dialogs.get("choose-location"), parent)
|
||||
{
|
||||
clamp_loc();
|
||||
map = dynamic_cast<cTilemap*>(&dlog["map"]);
|
||||
fill_terrain();
|
||||
handle_scroll(""); // Hide unusable scroll buttons
|
||||
dlog["switch"].hide();
|
||||
setTitle(title);
|
||||
using namespace std::placeholders;
|
||||
dlog.attachClickHandlers(std::bind(&cLocationPicker::handle_close, this, _2), {"done", "cancel"});
|
||||
dlog.attachClickHandlers(std::bind(&cLocationPicker::handle_scroll, this, _2), {"up", "down", "left", "right"});
|
||||
map->attachClickHandlers(std::bind(&cLocationPicker::handle_select, this, _2), "");
|
||||
}
|
||||
|
||||
void cLocationPicker::clamp_loc() {
|
||||
chosen_loc.x = minmax(0, area->max_dim - 1, chosen_loc.x);
|
||||
chosen_loc.y = minmax(0, area->max_dim - 1, chosen_loc.y);
|
||||
viewport.x = 18 * floor(chosen_loc.x / 18.0);
|
||||
viewport.y = 18 * floor(chosen_loc.y / 18.0);
|
||||
}
|
||||
|
||||
void cLocationPicker::setArea(cArea& newArea) {
|
||||
area = &newArea;
|
||||
clamp_loc();
|
||||
fill_terrain();
|
||||
}
|
||||
|
||||
void cLocationPicker::setTitle(const std::string& title) {
|
||||
dlog["prompt"].setText(title);
|
||||
}
|
||||
|
||||
void cLocationPicker::fill_terrain() {
|
||||
for(int x = 0; x < 18; x++) {
|
||||
for(int y = 0; y < 18; y++) {
|
||||
auto& pict = dynamic_cast<cPict&>(map->getChild(x, y));
|
||||
location ter_loc = viewport;
|
||||
ter_loc.x += x;
|
||||
ter_loc.y += y;
|
||||
if(ter_loc.x >= area->max_dim || ter_loc.y >= area->max_dim) pict.setPict(74);
|
||||
else {
|
||||
ter_num_t ter = area->terrain[ter_loc.x][ter_loc.y];
|
||||
pict.setPict(scenario.ter_types[ter].map_pic);
|
||||
}
|
||||
}
|
||||
}
|
||||
place_pointer();
|
||||
}
|
||||
|
||||
void cLocationPicker::place_pointer() {
|
||||
if(chosen_loc.x >= viewport.x && chosen_loc.x < viewport.x + 18 && chosen_loc.y >= viewport.y && chosen_loc.y < viewport.y + 18) {
|
||||
location relative_loc = chosen_loc;
|
||||
relative_loc.x -= viewport.x;
|
||||
relative_loc.y -= viewport.y;
|
||||
location ctrlPos = map->getCellPos(relative_loc.x, relative_loc.y);
|
||||
ctrlPos.x += 3;
|
||||
ctrlPos.y += 3;
|
||||
dlog["zpointer"].relocate(ctrlPos);
|
||||
dlog["zpointer"].show();
|
||||
} else {
|
||||
dlog["zpointer"].hide();
|
||||
}
|
||||
}
|
||||
|
||||
bool cLocationPicker::handle_close(std::string item_hit) {
|
||||
dlog.toast(item_hit == "done");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cLocationPicker::handle_scroll(std::string item_hit) {
|
||||
if(item_hit == "up") {
|
||||
if(viewport.y > 0) viewport.y -= 9;
|
||||
} else if(item_hit == "down") {
|
||||
if(viewport.y < area->max_dim - 18) viewport.y += 9;
|
||||
} else if(item_hit == "left") {
|
||||
if(viewport.x > 0) viewport.x -= 9;
|
||||
} else if(item_hit == "right") {
|
||||
if(viewport.x < area->max_dim - 18) viewport.x += 9;
|
||||
}
|
||||
if(viewport.x == 0) dlog["left"].hide();
|
||||
else dlog["left"].show();
|
||||
if(viewport.y == 0) dlog["up"].hide();
|
||||
else dlog["up"].show();
|
||||
if(viewport.x >= area->max_dim - 18) dlog["right"].hide();
|
||||
else dlog["right"].show();
|
||||
if(viewport.y >= area->max_dim - 18) dlog["down"].hide();
|
||||
else dlog["down"].show();
|
||||
fill_terrain();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cLocationPicker::handle_select(std::string item_hit) {
|
||||
location clickedLoc = map->getCellPos(dlog[item_hit]);
|
||||
location check{viewport.x + clickedLoc.x / 24, viewport.y + clickedLoc.y / 24};
|
||||
if(check.x < area->max_dim && check.y < area->max_dim) {
|
||||
chosen_loc = check;
|
||||
place_pointer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
location cLocationPicker::run() {
|
||||
dlog.run();
|
||||
if(dlog.accepted()) return chosen_loc;
|
||||
return initial_loc;
|
||||
}
|
||||
|
||||
cDialog* cLocationPicker::operator->() {
|
||||
return &dlog;
|
||||
}
|
37
src/scenedit/scen.locpicker.hpp
Normal file
37
src/scenedit/scen.locpicker.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// scen.locpicker.hpp
|
||||
// BoE
|
||||
//
|
||||
// Created by Celtic Minstrel on 2025-02-28.
|
||||
//
|
||||
|
||||
#ifndef BoE_scen_locpicker_h
|
||||
#define BoE_scen_locpicker_h
|
||||
|
||||
#include <string>
|
||||
#include "location.hpp"
|
||||
#include "dialogxml/dialogs/dialog.hpp"
|
||||
|
||||
class cArea;
|
||||
class cTilemap;
|
||||
|
||||
class cLocationPicker {
|
||||
location initial_loc, chosen_loc, viewport;
|
||||
cArea* area;
|
||||
cDialog dlog;
|
||||
cTilemap* map;
|
||||
void clamp_loc();
|
||||
void fill_terrain();
|
||||
void place_pointer();
|
||||
bool handle_close(std::string item_hit);
|
||||
bool handle_scroll(std::string item_hit);
|
||||
bool handle_select(std::string item_hit);
|
||||
public:
|
||||
cLocationPicker(location loc, cArea& area, const std::string& title, cDialog* parent);
|
||||
void setArea(cArea& newArea) ;
|
||||
void setTitle(const std::string& title);
|
||||
location run();
|
||||
cDialog* operator->();
|
||||
};
|
||||
|
||||
#endif
|
@@ -13,11 +13,13 @@
|
||||
#include "scen.graphics.hpp"
|
||||
#include "scen.townout.hpp"
|
||||
#include "scen.keydlgs.hpp"
|
||||
#include "scen.locpicker.hpp"
|
||||
#include "scen.fileio.hpp"
|
||||
#include "scen.core.hpp"
|
||||
#include "mathutil.hpp"
|
||||
#include "dialogxml/widgets/button.hpp"
|
||||
#include "dialogxml/widgets/field.hpp"
|
||||
#include "dialogxml/dialogs/strchoice.hpp"
|
||||
#include "dialogxml/dialogs/strdlog.hpp"
|
||||
#include "dialogxml/dialogs/choicedlog.hpp"
|
||||
#include "tools/winutil.hpp"
|
||||
@@ -815,6 +817,49 @@ void edit_advanced_town() {
|
||||
town_dlg.attachFocusHandlers(loc_check, {"exit1-x", "exit2-x", "exit3-x", "exit4-x"});
|
||||
town_dlg.attachFocusHandlers(loc_check, {"exit1-y", "exit2-y", "exit3-y", "exit4-y"});
|
||||
town_dlg.attachClickHandlers(edit_advanced_town_special, {"edit-onexit1", "edit-onexit2", "edit-onexit3", "edit-onexit4", "edit-onenter", "edit-onenterdead", "edit-onhostile"});
|
||||
town_dlg.attachClickHandlers([](cDialog& me, std::string which, eKeyMod) {
|
||||
auto entrances = scenario.find_town_entrances(cur_town);
|
||||
std::vector<location> sectors;
|
||||
for(const auto& entrance : entrances) {
|
||||
if(std::find(sectors.begin(), sectors.end(), entrance.out_sec) == sectors.end()) {
|
||||
sectors.push_back(entrance.out_sec);
|
||||
}
|
||||
}
|
||||
int which_sector = 0;
|
||||
if(sectors.size() == 0) {
|
||||
int sec = -1;
|
||||
sec = choose_text(STRT_SECTOR, 0, &me, "Exit to which sector?");
|
||||
if(sec == -1) return true; // cancelled
|
||||
sectors.emplace_back(sec / scenario.outdoors.height(), sec % scenario.outdoors.height());
|
||||
}
|
||||
location loc(me[which + "-x"].getTextAsNum(), me[which + "-y"].getTextAsNum());
|
||||
if((loc.x < 0 || loc.y < 0 || loc.x >= 48 || loc.y >= 48) && !entrances.empty()) {
|
||||
loc = entrances[0].loc;
|
||||
}
|
||||
static const std::string directions[4]{"top", "left", "bottom", "right"};
|
||||
std::string title = "Select outdoor location when exiting from the " + directions[which.back() - '1'] + ":";
|
||||
cLocationPicker picker(loc, *scenario.outdoors[sectors[which_sector].x][sectors[which_sector].y], title, &me);
|
||||
if(sectors.size() > 1) {
|
||||
std::vector<std::string> out_names;
|
||||
for(const auto& sec : sectors) {
|
||||
out_names.push_back(scenario.outdoors[sec.x][sec.y]->name);
|
||||
}
|
||||
cControl& switchBtn = picker->getControl("switch");
|
||||
switchBtn.show();
|
||||
switchBtn.attachClickHandler([out_names, §ors, &which_sector, &picker](cDialog& me, std::string, eKeyMod) {
|
||||
cStringChoice pickstr(out_names, "Exit to which sector?", &me);
|
||||
which_sector = pickstr.show(which_sector);
|
||||
picker.setArea(*scenario.outdoors[sectors[which_sector].x][sectors[which_sector].y]);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
loc = picker.run();
|
||||
if(picker->accepted()) {
|
||||
me[which + "-x"].setTextToNum(loc.x);
|
||||
me[which + "-y"].setTextToNum(loc.y);
|
||||
}
|
||||
return true;
|
||||
}, {"exit1", "exit2", "exit3", "exit4"});
|
||||
town_dlg.attachClickHandlers([](cDialog& me, std::string which, eKeyMod) -> bool {
|
||||
std::string fld = which.replace(0, 4, "bg");
|
||||
int bg_i = me[fld].getTextAsNum();
|
||||
|
Reference in New Issue
Block a user