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:
@@ -23,7 +23,7 @@ cStringChoice::cStringChoice(cDialog* parent, bool editable)
|
||||
, dlg(*ResMgr::dialogs.get(editable ? "choose-edit-string" : "choose-string"), parent)
|
||||
{}
|
||||
|
||||
cStringChoice::cStringChoice(std::vector<std::string>& strs, std::string title, cDialog* parent, bool editable)
|
||||
cStringChoice::cStringChoice(const std::vector<std::string>& strs, std::string title, cDialog* parent, bool editable)
|
||||
: cStringChoice(parent, editable)
|
||||
{
|
||||
setTitle(title);
|
||||
|
@@ -41,7 +41,7 @@ public:
|
||||
/// @param strs A list of all strings in the dialog.
|
||||
/// @param title The title to show in the dialog.
|
||||
/// @param parent Optionally, a parent dialog.
|
||||
explicit cStringChoice(std::vector<std::string>& strs, std::string title, cDialog* parent = nullptr, bool editable = false);
|
||||
explicit cStringChoice(const std::vector<std::string>& strs, std::string title, cDialog* parent = nullptr, bool editable = false);
|
||||
/// Initializes a dialog from an iterator pair.
|
||||
/// @param begin An iterator to the first string in the dialog.
|
||||
/// @param end An iterator to one past the last string in the dialog.
|
||||
|
@@ -16,8 +16,8 @@ typedef std::map<std::string,cControl*>::iterator ctrlIter;
|
||||
/// A superclass to represent a control that contains other controls.
|
||||
class cContainer : public cControl {
|
||||
void callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) override;
|
||||
std::string clicking;
|
||||
protected:
|
||||
std::string clicking;
|
||||
/// Parses a child control.
|
||||
/// @param elem The element defining the control.
|
||||
/// @param controls The map into which the control will be inserted.
|
||||
|
@@ -32,6 +32,18 @@ location cTilemap::getCell(const std::string& id) const {
|
||||
return location(std::stoi(x_str) * cellWidth, std::stoi(y_str) * cellHeight);
|
||||
}
|
||||
|
||||
location cTilemap::getCellPos(size_t x, size_t y) const {
|
||||
rectangle bounds = getBounds();
|
||||
bounds.offset(x * cellWidth, y * cellHeight);
|
||||
return bounds.topLeft();
|
||||
}
|
||||
|
||||
location cTilemap::getCellPos(cControl& child) const {
|
||||
// If it's not ours, return origin.
|
||||
if(!hasChild(child.getName())) return location();
|
||||
return getCell(child.getName());
|
||||
}
|
||||
|
||||
std::string cTilemap::buildId(const std::string& base, size_t x, size_t y) {
|
||||
std::ostringstream sout;
|
||||
if(!base.empty()) {
|
||||
@@ -92,14 +104,37 @@ bool cTilemap::isScrollable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cTilemap::handleClick(location where, cFramerateLimiter& fps_limiter) {
|
||||
bool success = false;
|
||||
isHandlingClick = true;
|
||||
forEach([&](std::string id, cControl& ctrl) {
|
||||
rectangle bounds = ctrl.getBounds();
|
||||
bounds.offset(getBounds().topLeft());
|
||||
bounds.offset(getCell(id));
|
||||
if(!success && ctrl.isClickable() && bounds.contains(where)) {
|
||||
rectangle localBounds = ctrl.getBounds();
|
||||
ctrl.setBounds(bounds);
|
||||
clicking = id;
|
||||
if(ctrl.handleClick(where, fps_limiter)){
|
||||
success = true;
|
||||
} else clicking.clear();
|
||||
ctrl.setBounds(localBounds);
|
||||
}
|
||||
});
|
||||
isHandlingClick = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
void cTilemap::draw() {
|
||||
if(!isVisible()) return;
|
||||
for(auto& ctrl : controls) {
|
||||
rectangle localBounds = ctrl.second->getBounds();
|
||||
rectangle globalBounds = localBounds;
|
||||
globalBounds.offset(getBounds().topLeft());
|
||||
globalBounds.offset(getCell(ctrl.first));
|
||||
ctrl.second->setBounds(globalBounds);
|
||||
if(isHandlingClick ? ctrl.first != clicking : true) {
|
||||
globalBounds.offset(getBounds().topLeft());
|
||||
globalBounds.offset(getCell(ctrl.first));
|
||||
ctrl.second->setBounds(globalBounds);
|
||||
}
|
||||
ctrl.second->draw();
|
||||
ctrl.second->setBounds(localBounds);
|
||||
}
|
||||
@@ -134,6 +169,25 @@ void cTilemap::recalcRect() {
|
||||
frame.bottom -= spacing;
|
||||
}
|
||||
|
||||
void cTilemap::attachClickHandlers(std::function<bool(cDialog&,std::string,eKeyMod)> handler, std::string prefix) {
|
||||
for(auto& ctrl : controls) {
|
||||
bool matches = false;
|
||||
if(prefix.empty()) {
|
||||
size_t first_x = ctrl.first.find_first_of("x"), last_x = ctrl.first.find_last_of("x"), first_y = ctrl.first.find_first_of("y"), last_y = ctrl.first.find_last_of("y");
|
||||
if(first_x == 0 && last_x == 0 && first_y != std::string::npos && first_y == last_y) {
|
||||
matches = true;
|
||||
}
|
||||
} else {
|
||||
if(ctrl.first.compare(0, prefix.size(), prefix)) {
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
if(matches) {
|
||||
ctrl.second->attachClickHandler(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cTilemap::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs) {
|
||||
for(auto p : controls) {
|
||||
cControl& ctrl = *p.second;
|
||||
|
@@ -19,6 +19,7 @@ class cTilemap : public cContainer {
|
||||
static std::string buildId(const std::string& base, size_t x, size_t y);
|
||||
std::string current_cell;
|
||||
mutable int id_tries = 0;
|
||||
bool isHandlingClick = false;
|
||||
public:
|
||||
std::string generateId(const std::string& baseId) const override;
|
||||
bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
|
||||
@@ -34,6 +35,17 @@ public:
|
||||
cControl& getChild(std::string id, size_t x, size_t y);
|
||||
bool hasChild(size_t x, size_t y) const;
|
||||
cControl& getChild(size_t x, size_t y);
|
||||
location getCellPos(size_t x, size_t y) const;
|
||||
location getCellPos(cControl& child) const;
|
||||
/// Attach the same click handler to every instance of a child control.
|
||||
/// @param handler The handler to attach.
|
||||
/// @param prefix The unique ID of the control template.
|
||||
/// @throw xHandlerNotSupported if any of the controls do not support click handlers.
|
||||
/// @throw std::invalid_argument if any of the controls do not exist.
|
||||
/// @see cControl::attachClickHandler()
|
||||
/// @deprecated in favour of @ref attachEventHandlers
|
||||
void attachClickHandlers(std::function<bool(cDialog&,std::string,eKeyMod)> handler, std::string prefix);
|
||||
bool handleClick(location where, cFramerateLimiter& fps_limiter) override;
|
||||
/// Recalculate the tilemap's bounding rect based on its contained controls.
|
||||
void recalcRect() override;
|
||||
/// Adds any fields in this tilemap to the tab order building arrays.
|
||||
|
@@ -28,7 +28,7 @@ namespace {
|
||||
.sdf(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.ex2b(eSpecPicker::NODE);
|
||||
node_properties_t S_TERRAIN = node_builder_t(eSpecType::IF_TER_TYPE)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_AUTO)
|
||||
.ex2a(STRT_TER)
|
||||
.ex2b(eSpecPicker::NODE);
|
||||
node_properties_t S_ALIVE = node_builder_t(eSpecType::IF_ALIVE)
|
||||
@@ -41,7 +41,7 @@ namespace {
|
||||
.ex1b(eSpecPicker::NODE)
|
||||
.ex2a(eSpecPicker::TOGGLE);
|
||||
node_properties_t S_ITEM_THERE = node_builder_t(eSpecType::IF_ITEM_CLASS_ON_SPACE)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(eSpecPicker::ITEM_CLASS)
|
||||
.ex2b(eSpecPicker::NODE)
|
||||
.ex2c(eSpecPicker::TOGGLE);
|
||||
@@ -72,7 +72,7 @@ namespace {
|
||||
node_properties_t S_DAY = node_builder_t(eSpecType::IF_DAY_REACHED)
|
||||
.ex1b(eSpecPicker::NODE);
|
||||
node_properties_t S_FIELDS = node_builder_t(eSpecType::IF_FIELDS)
|
||||
.rect()
|
||||
.rect(eLocType::ACTIVE_TOWN)
|
||||
.msg1(eSpecPicker::FIELD)
|
||||
.msg2(eSpecPicker::NODE);
|
||||
node_properties_t S_PARTY_SIZE = node_builder_t(eSpecType::IF_PARTY_SIZE)
|
||||
|
@@ -117,16 +117,16 @@ namespace{
|
||||
.msg();
|
||||
node_properties_t S_TERCHANGE = node_builder_t(eSpecType::CHANGE_TER)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_AUTO)
|
||||
.ex2a(STRT_TER);
|
||||
node_properties_t S_TERSWAP = node_builder_t(eSpecType::SWAP_TER)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_AUTO)
|
||||
.ex2a(STRT_TER)
|
||||
.ex2b(STRT_TER);
|
||||
node_properties_t S_TERTRANS = node_builder_t(eSpecType::TRANS_TER)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B);
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_AUTO);
|
||||
node_properties_t S_BUF_CLEAR = node_builder_t(eSpecType::CLEAR_BUF);
|
||||
node_properties_t S_BUF_ADDSTR = node_builder_t(eSpecType::APPEND_STRING)
|
||||
.ex1a(eSpecPicker::MSG_SINGLE)
|
||||
|
@@ -14,11 +14,12 @@ namespace {
|
||||
node_properties_t S_WANDER = node_builder_t(eSpecType::OUT_MAKE_WANDER);
|
||||
node_properties_t S_TOWN = node_builder_t(eSpecType::OUT_FORCE_TOWN)
|
||||
.msg()
|
||||
.loc(eSpecField::EX2A, eSpecField::EX2B, eLocType::SPECIFIED_TOWN, eSpecField::EX1A)
|
||||
.ex1a(STRT_TOWN)
|
||||
.ex1b(STRT_DIR);
|
||||
node_properties_t S_ENCOUNTER = node_builder_t(eSpecType::OUT_PLACE_ENCOUNTER)
|
||||
.msg();
|
||||
node_properties_t S_TELEPORT = node_builder_t(eSpecType::OUT_MOVE_PARTY)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B);
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_OUT);
|
||||
}
|
||||
|
@@ -13,36 +13,36 @@ node_category_info_t CAT_RECT{eSpecType::RECT_PLACE_FIELD, eSpecType::RECT_UNLOC
|
||||
namespace {
|
||||
node_properties_t S_FIELDS = node_builder_t(eSpecType::RECT_PLACE_FIELD)
|
||||
.msg()
|
||||
.rect()
|
||||
.rect(eLocType::ACTIVE_TOWN)
|
||||
.sdf2(+eSpecPicker::FIELD);
|
||||
node_properties_t S_EXPLORE = node_builder_t(eSpecType::RECT_SET_EXPLORED)
|
||||
.msg()
|
||||
.rect()
|
||||
.rect(eLocType::ACTIVE_AUTO)
|
||||
.sdf1(eSpecPicker::TOGGLE);
|
||||
node_properties_t S_MOVE = node_builder_t(eSpecType::RECT_MOVE_ITEMS)
|
||||
.msg()
|
||||
.rect()
|
||||
.rect(eLocType::ACTIVE_TOWN)
|
||||
.msg3(eSpecPicker::TOGGLE)
|
||||
.loc(eSpecField::SDF1, eSpecField::SDF2);
|
||||
.loc(eSpecField::SDF1, eSpecField::SDF2, eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_DESTROY = node_builder_t(eSpecType::RECT_DESTROY_ITEMS)
|
||||
.msg()
|
||||
.rect();
|
||||
.rect(eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_CHANGE = node_builder_t(eSpecType::RECT_CHANGE_TER)
|
||||
.msg()
|
||||
.rect()
|
||||
.rect(eLocType::ACTIVE_AUTO)
|
||||
.sdf1(STRT_TER);
|
||||
node_properties_t S_SWAP = node_builder_t(eSpecType::RECT_SWAP_TER)
|
||||
.msg()
|
||||
.rect()
|
||||
.rect(eLocType::ACTIVE_AUTO)
|
||||
.sdf1(STRT_TER)
|
||||
.sdf2(STRT_TER);
|
||||
node_properties_t S_TRANS = node_builder_t(eSpecType::RECT_TRANS_TER)
|
||||
.msg()
|
||||
.rect();
|
||||
.rect(eLocType::ACTIVE_AUTO);
|
||||
node_properties_t S_LOCK = node_builder_t(eSpecType::RECT_LOCK)
|
||||
.msg()
|
||||
.rect();
|
||||
.rect(eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_UNLOCK = node_builder_t(eSpecType::RECT_UNLOCK)
|
||||
.msg()
|
||||
.rect();
|
||||
.rect(eLocType::ACTIVE_TOWN);
|
||||
}
|
||||
|
@@ -17,38 +17,39 @@ namespace {
|
||||
node_properties_t S_MISSILE = node_builder_t(eSpecType::TOWN_RUN_MISSILE)
|
||||
.msg()
|
||||
.pict(PIC_MISSILE)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX2A, eSpecField::EX2B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.loc(eSpecField::EX2A, eSpecField::EX2B, eLocType::ACTIVE_TOWN)
|
||||
.ex1c(STRT_PATH)
|
||||
.ex2c(eSpecPicker::SOUND);
|
||||
node_properties_t S_ATTACK = node_builder_t(eSpecType::TOWN_MONST_ATTACK)
|
||||
.msg();
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_BOOM = node_builder_t(eSpecType::TOWN_BOOM_SPACE)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(PIC_BOOM)
|
||||
.ex2c(eSpecPicker::SOUND);
|
||||
node_properties_t S_TELEPORT = node_builder_t(eSpecType::TOWN_MOVE_PARTY)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(eSpecPicker::TOGGLE);
|
||||
node_properties_t S_HIT = node_builder_t(eSpecType::TOWN_HIT_SPACE)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2b(eSpecPicker::DAMAGE_TYPE);
|
||||
node_properties_t S_EXPLODE = node_builder_t(eSpecType::TOWN_EXPLODE_SPACE)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2b(eSpecPicker::DAMAGE_TYPE);
|
||||
node_properties_t S_LOCK = node_builder_t(eSpecType::TOWN_LOCK_SPACE)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B);
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_UNLOCK = node_builder_t(eSpecType::TOWN_UNLOCK_SPACE)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B);
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_BURST = node_builder_t(eSpecType::TOWN_SFX_BURST)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(eSpecPicker::EXPLOSION)
|
||||
.ex2b(eSpecPicker::TOGGLE)
|
||||
.ex2c(eSpecPicker::SOUND);
|
||||
@@ -56,23 +57,23 @@ namespace {
|
||||
.msg();
|
||||
node_properties_t S_SPAWN = node_builder_t(eSpecType::TOWN_PLACE_MONST)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(STRT_MONST)
|
||||
.ex2b(eSpecPicker::TOGGLE);
|
||||
node_properties_t S_KILL = node_builder_t(eSpecType::TOWN_DESTROY_MONST)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B);
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_NUKE = node_builder_t(eSpecType::TOWN_NUKE_MONSTS)
|
||||
.msg()
|
||||
.ex1a(STRT_MONST);
|
||||
node_properties_t S_LEVER_G = node_builder_t(eSpecType::TOWN_GENERIC_LEVER)
|
||||
.ex1b(eSpecPicker::NODE);
|
||||
node_properties_t S_PORTAL_G = node_builder_t(eSpecType::TOWN_GENERIC_PORTAL)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B);
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_BUTTON_G = node_builder_t(eSpecType::TOWN_GENERIC_BUTTON)
|
||||
.ex1b(eSpecPicker::NODE);
|
||||
node_properties_t S_STAIR_G = node_builder_t(eSpecType::TOWN_GENERIC_STAIR)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::SPECIFIED_TOWN, eSpecField::EX2A)
|
||||
.ex2a(STRT_TOWN)
|
||||
.ex2b(STRT_STAIR)
|
||||
.ex2c(STRT_STAIR_MODE)
|
||||
@@ -84,11 +85,11 @@ namespace {
|
||||
node_properties_t S_PORTAL = node_builder_t(eSpecType::TOWN_PORTAL)
|
||||
.msg1(eSpecPicker::MSG_SEQUENCE)
|
||||
.pic()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B);
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN);
|
||||
node_properties_t S_STAIR = node_builder_t(eSpecType::TOWN_STAIR)
|
||||
.msg1(eSpecPicker::MSG_SEQUENCE)
|
||||
.pic()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::SPECIFIED_TOWN, eSpecField::EX2A)
|
||||
.ex2a(STRT_TOWN)
|
||||
.ex2b(eSpecPicker::TOGGLE)
|
||||
.ex2c(STRT_STAIR_MODE)
|
||||
@@ -96,15 +97,15 @@ namespace {
|
||||
node_properties_t S_OUTDOOR = node_builder_t(eSpecType::TOWN_RELOCATE)
|
||||
.msg()
|
||||
.field_pair(eSpecField::EX1A, eSpecField::EX1B, STRT_SECTOR)
|
||||
.loc(eSpecField::EX2A, eSpecField::EX2B);
|
||||
.loc(eSpecField::EX2A, eSpecField::EX2B, eLocType::SPECIFIED_OUT, eSpecField::EX1A);
|
||||
node_properties_t S_ITEM = node_builder_t(eSpecType::TOWN_PLACE_ITEM)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(STRT_ITEM)
|
||||
.ex2b(eSpecPicker::TOGGLE);
|
||||
node_properties_t S_SPLIT = node_builder_t(eSpecType::TOWN_SPLIT_PARTY)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(eSpecPicker::SOUND);
|
||||
node_properties_t S_REUNITE = node_builder_t(eSpecType::TOWN_REUNITE_PARTY)
|
||||
.msg()
|
||||
@@ -133,21 +134,21 @@ namespace {
|
||||
.ex2c(eSpecPicker::TOGGLE);
|
||||
node_properties_t S_FIELDS = node_builder_t(eSpecType::TOWN_SPELL_PAT_FIELD)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex1c(+STRT_SPELL_PAT)
|
||||
.ex2a(+eSpecPicker::FIELD);
|
||||
node_properties_t S_PATTERN = node_builder_t(eSpecType::TOWN_SPELL_PAT_BOOM)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex1c(+STRT_SPELL_PAT)
|
||||
.ex2a(eSpecPicker::DAMAGE_TYPE)
|
||||
.ex2c(STRT_SPELL_PAT_MODE);
|
||||
node_properties_t S_WARP = node_builder_t(eSpecType::TOWN_RELOCATE_CREATURE)
|
||||
.msg()
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2b(STRT_POS_MODE);
|
||||
node_properties_t S_LABEL = node_builder_t(eSpecType::TOWN_PLACE_LABEL)
|
||||
.msg1(eSpecPicker::MSG_SINGLE)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B)
|
||||
.loc(eSpecField::EX1A, eSpecField::EX1B, eLocType::ACTIVE_TOWN)
|
||||
.ex2a(STRT_LABEL_ALIGN);
|
||||
}
|
||||
|
@@ -652,6 +652,11 @@ node_function_t::node_function_t(ePicType pic)
|
||||
, pic_type(pic)
|
||||
{}
|
||||
|
||||
node_function_t::node_function_t(eLocType loc)
|
||||
: button(eSpecPicker::LOCATION)
|
||||
, loc_type(loc)
|
||||
{}
|
||||
|
||||
node_function_t operator+(eSpecPicker picker) {
|
||||
node_function_t n(picker);
|
||||
if(picker == eSpecPicker::NODE || picker == eSpecPicker::MSG_PAIR || picker == eSpecPicker::MSG_SINGLE || picker == eSpecPicker::MSG_SEQUENCE) {
|
||||
@@ -765,10 +770,8 @@ node_builder_t& node_builder_t::msg() {
|
||||
return field_pair(eSpecField::MSG1, eSpecField::MSG2, eSpecPicker::MSG_PAIR);
|
||||
};
|
||||
|
||||
node_builder_t& node_builder_t::rect() {
|
||||
// The intent is to specify that ex1a,ex1b, and ex2a,ex2b are locations.
|
||||
// But specifying that two fields are a location isn't implemented yet.
|
||||
return loc(eSpecField::EX1A, eSpecField::EX1B).loc(eSpecField::EX2A, eSpecField::EX2B).pict(eSpecPicker::TOGGLE);
|
||||
node_builder_t& node_builder_t::rect(eLocType type) {
|
||||
return loc(eSpecField::EX1A, eSpecField::EX1B, type).loc(eSpecField::EX2A, eSpecField::EX2B, type).pict(eSpecPicker::TOGGLE);
|
||||
};
|
||||
|
||||
node_builder_t& node_builder_t::pic() {
|
||||
@@ -847,8 +850,14 @@ node_builder_t& node_builder_t::sdf(eSpecField a, eSpecField b) {
|
||||
return field_pair(a, b, eSpecPicker::SDF);
|
||||
}
|
||||
|
||||
node_builder_t& node_builder_t::loc(eSpecField a, eSpecField b) {
|
||||
return field_pair(a, b, eSpecPicker::LOCATION);
|
||||
node_builder_t& node_builder_t::loc(eSpecField a, eSpecField b, eLocType type) {
|
||||
return field_pair(a, b, type);
|
||||
}
|
||||
|
||||
node_builder_t& node_builder_t::loc(eSpecField a, eSpecField b, eLocType type, eSpecField where) {
|
||||
loc(a, b, type);
|
||||
(node.*fields().map[b]).continuation = where;
|
||||
return *this;
|
||||
}
|
||||
|
||||
node_builder_t::operator node_properties_t() {
|
||||
|
@@ -160,6 +160,11 @@ enum class eSpecPicker {
|
||||
POINTER,
|
||||
};
|
||||
|
||||
enum class eLocType {
|
||||
ACTIVE_AUTO, ACTIVE_TOWN, ACTIVE_OUT,
|
||||
SPECIFIED_TOWN, SPECIFIED_OUT
|
||||
};
|
||||
|
||||
enum class eSpecField { NONE, SDF1, SDF2, MSG1, MSG2, MSG3, PICT, PTYP, EX1A, EX1B, EX1C, EX2A, EX2B, EX2C, JUMP };
|
||||
|
||||
struct node_function_t {
|
||||
@@ -167,6 +172,7 @@ struct node_function_t {
|
||||
union {
|
||||
eStrType str_type; // for eSpecPicker::STRING only
|
||||
ePicType pic_type; // for eSpecPicker::PICTURE only; PIC_NONE = use pictype field from node
|
||||
eLocType loc_type; // for eSpecPicker::LOCATION only
|
||||
bool force_global; // for eSpecPicker::NODE and eSpecPicker::MSG_*
|
||||
// other pickers don't put anything in here
|
||||
};
|
||||
@@ -179,6 +185,7 @@ struct node_function_t {
|
||||
node_function_t(eSpecPicker button);
|
||||
node_function_t(eStrType str);
|
||||
node_function_t(ePicType pic);
|
||||
node_function_t(eLocType loc);
|
||||
private:
|
||||
eSpecType self = eSpecType::NONE;
|
||||
std::string lbl;
|
||||
@@ -246,11 +253,13 @@ struct node_builder_t {
|
||||
// Specifies that ex1a,ex1b and ex2a,ex2b define opposing corners of a rectangle.
|
||||
// DO NOT use for other cases where there are two points, for example defining a path!
|
||||
// Also DO NOT use for cases where an outdoor sector is identified by its coordinates.
|
||||
node_builder_t& rect();
|
||||
node_builder_t& rect(eLocType type);
|
||||
// Specifies that the indicated two fields combine to define an SDF.
|
||||
node_builder_t& sdf(eSpecField a, eSpecField b);
|
||||
// Specifies that the indicated two fields combine to define a location.
|
||||
node_builder_t& loc(eSpecField a, eSpecField b);
|
||||
node_builder_t& loc(eSpecField a, eSpecField b, eLocType type);
|
||||
// As above, but also notes that the area the location is in will be specified by the indicated field.
|
||||
node_builder_t& loc(eSpecField a, eSpecField b, eLocType type, eSpecField where);
|
||||
operator node_properties_t();
|
||||
private:
|
||||
node_properties_t node;
|
||||
|
@@ -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