The special node dictionary now supports specifying different labels or pickers based on the value of another field.

This commit is contained in:
2025-03-06 00:14:52 -05:00
committed by Celtic Minstrel
parent 246aec3253
commit b86ef8ae39
20 changed files with 416 additions and 75 deletions

View File

@@ -6,6 +6,7 @@
//
#include "special.hpp"
#include "special-conditions.hpp"
// Note: If adding a new node type below, be sure to adjust the end point here too.
node_category_info_t CAT_AFFECT{eSpecType::SELECT_TARGET, eSpecType::UNSTORE_PC};
@@ -15,7 +16,10 @@ namespace {
.msg()
.ex1a(STRT_TARG_TYPE)
.ex1b(eSpecPicker::NODE)
.ex2a(STRT_TARG_MODE);
.ex2a(STRT_TARG_MODE)
.when(eSpecField::EX2A == 2, 1)
.ex2b()
.end();
node_properties_t S_DAMAGE = node_builder_t(eSpecType::DAMAGE)
.msg()
.ex2b(eSpecPicker::DAMAGE_TYPE)

View File

@@ -6,6 +6,7 @@
//
#include "special.hpp"
#include "special-conditions.hpp"
// Note: If adding a new node type below, be sure to adjust the end point here too.
node_category_info_t CAT_COND{eSpecType::IF_SDF, eSpecType::IF_QUEST};
@@ -111,10 +112,14 @@ namespace {
.msg1(+eSpecPicker::MSG_SINGLE)
.pict(STRT_CMP_MODE)
.ptyp(eSpecPicker::NODE)
.ex1b(STRT_CMP)
.ex1c(eSpecPicker::NODE)
.ex2b(STRT_CMP)
.ex2c(eSpecPicker::NODE);
.ex2c(eSpecPicker::NODE)
.when(eSpecField::PICT == 2, 1)
.ex1a()
.ex1b(STRT_CMP)
.ex2a()
.ex2b(STRT_CMP)
.end();
node_properties_t S_BOAT = node_builder_t(eSpecType::IF_IN_BOAT)
.ex1b(STRT_BOAT)
.ex1c(eSpecPicker::NODE);

View File

@@ -0,0 +1,98 @@
//
// special-conditions.hpp
// BoE
//
// Created by Celtic Minstrel on 2025-03-06.
//
#ifndef BOE_DATA_SPECIAL_COND_H
#define BOE_DATA_SPECIAL_COND_H
struct node_literal_t {
using value_type = short;
short operator()(const cSpecial&) const { return value; }
node_literal_t(short v) : value(v) {}
private:
short value;
};
struct node_value_t {
using value_type = eSpecField;
short operator()(const cSpecial& spec) const { return spec->*field; }
node_value_t(eSpecField fld) : field(fld) {}
private:
eSpecField field;
};
template<template<class> class P, typename A, typename B>
struct node_comparison_t {
static const bool node_expr = true;
bool operator()(const cSpecial& spec) const {
short a = lhs(spec), b = rhs(spec);
return pred(a, b);
}
node_comparison_t(A a, B b, P<short> p) : lhs(a), rhs(b), pred(p) {}
private:
A lhs;
B rhs;
P<short> pred;
};
template<typename A, typename B>
struct node_logic_t {
static const bool node_expr = true;
bool operator()(const cSpecial& spec) const {
return conjunct ? (a(spec) && b(spec)) : (a(spec) || b(spec));
}
node_logic_t(A a, B b, bool conj) : a(a), b(b), conjunct(conj) {}
private:
A a;
B b;
bool conjunct;
};
template<typename T>
struct node_negate_t {
static const bool node_expr = true;
bool operator()(const cSpecial& spec) const {
return !pred(spec);
}
node_negate_t(T p) : pred(p) {}
private:
T pred;
};
#define DEFINE_CMP(LHS, RHS, OP, OP_NAME) \
inline node_comparison_t<std::OP_NAME, LHS, RHS> operator OP(LHS::value_type a, RHS::value_type b) { \
return node_comparison_t<std::OP_NAME, LHS, RHS>{ \
LHS{a}, \
RHS{b}, \
std::OP_NAME<short>() \
}; \
}
#define DEFINE_CMPS(OP, OP_NAME) \
DEFINE_CMP(node_value_t, node_literal_t, OP, OP_NAME); \
DEFINE_CMP(node_literal_t, node_value_t, OP, OP_NAME);
DEFINE_CMPS(==, equal_to);
DEFINE_CMPS(<=, less_equal);
DEFINE_CMPS(>=, greater_equal);
DEFINE_CMPS(!=, not_equal_to);
DEFINE_CMPS(<, less);
DEFINE_CMPS(>, greater);
#undef DEFINE_CMP
#undef DEFINE_CMPS
template<typename A, typename B>
inline node_logic_t<A, B> operator&&(typename std::enable_if<A::node_expr, A>::type a, typename std::enable_if<B::node_expr, B>::type b) {
return node_logic_t<A, B>{a, b, true};
}
template<typename A, typename B>
inline node_logic_t<A, B> operator||(typename std::enable_if<A::node_expr, A>::type a, typename std::enable_if<B::node_expr, B>::type b) {
return node_logic_t<A, B>{a, b, false};
}
template<typename T>
inline node_negate_t<T> operator!(typename std::enable_if<T::node_expr, T>::type v) {
return node_negate_t<T>{v};
};
#endif

View File

@@ -6,6 +6,7 @@
//
#include "special.hpp"
#include "special-conditions.hpp"
// Note: If adding a new node type below, be sure to adjust the end point here too.
node_category_info_t CAT_GENERAL{eSpecType::NONE, eSpecType::STR_BUF_TO_SIGN};
@@ -47,7 +48,9 @@ namespace{
node_properties_t S_PREVENT = node_builder_t(eSpecType::CANT_ENTER)
.msg()
.ex1a(eSpecPicker::TOGGLE)
.ex2a(eSpecPicker::TOGGLE);
.when(eSpecField::EX1A < 1, 1)
.ex2a(eSpecPicker::TOGGLE)
.end();
node_properties_t S_TIME = node_builder_t(eSpecType::CHANGE_TIME)
.msg();
node_properties_t S_TIMER = node_builder_t(eSpecType::SCEN_TIMER_START)
@@ -103,8 +106,18 @@ namespace{
.msg1(+eSpecPicker::MSG_SINGLE)
.ex2a(eSpecPicker::TOGGLE);
node_properties_t S_DEBUG = node_builder_t(eSpecType::PRINT_NUMS)
.sdf()
.pict(STRT_DEBUG_PRINT);
.pict(STRT_DEBUG_PRINT)
.when(eSpecField::PICT == 0, 1)
.sdf()
.end()
.when(eSpecField::PICT == 1, 1)
.ex1a()
.ex1b()
.ex1c()
.end()
.when(eSpecField::PICT == 2, 2)
.ex1a()
.end();
node_properties_t S_MULFLAG = node_builder_t(eSpecType::SDF_TIMES)
.sdf()
.msg();
@@ -151,7 +164,13 @@ namespace{
node_properties_t S_QUEST = node_builder_t(eSpecType::UPDATE_QUEST)
.msg()
.ex1a(STRT_QUEST)
.ex1b(STRT_QUEST_STATUS);
.ex1b(STRT_QUEST_STATUS)
.when(eSpecField::EX1B == 1, 1)
.ex2a(eSpecPicker::JOB_BOARD)
.end()
.when(eSpecField::EX1B == 3, 2)
.ex2a()
.end();
node_properties_t S_BUF_SWAP = node_builder_t(eSpecType::SWAP_STR_BUF)
.msg();
node_properties_t S_ALTER_SIGN = node_builder_t(eSpecType::STR_BUF_TO_SIGN)

View File

@@ -6,6 +6,7 @@
//
#include "special.hpp"
#include "special-conditions.hpp"
// Note: If adding a new node type below, be sure to adjust the end point here too.
node_category_info_t CAT_ONCE{eSpecType::ONCE_GIVE_ITEM, eSpecType::ONCE_TRAP};
@@ -55,5 +56,7 @@ namespace {
.msg()
.pic()
.ex1a(STRT_TRAP)
.ex2b(+eSpecPicker::NODE);
.when(eSpecField::EX1A == 13, 1)
.ex2b(+eSpecPicker::NODE)
.end();
}

View File

@@ -6,6 +6,7 @@
//
#include "special.hpp"
#include "special-conditions.hpp"
// Note: If adding a new node type below, be sure to adjust the end point here too.
node_category_info_t CAT_OUTD{eSpecType::OUT_MAKE_WANDER, eSpecType::OUT_MOVE_PARTY};

View File

@@ -6,6 +6,7 @@
//
#include "special.hpp"
#include "special-conditions.hpp"
// Note: If adding a new node type below, be sure to adjust the end point here too.
node_category_info_t CAT_RECT{eSpecType::RECT_PLACE_FIELD, eSpecType::RECT_UNLOCK};

View File

@@ -6,6 +6,7 @@
//
#include "special.hpp"
#include "special-conditions.hpp"
// Note: If adding a new node type below, be sure to adjust the end point here too.
node_category_info_t CAT_TOWN{eSpecType::MAKE_TOWN_HOSTILE, eSpecType::TOWN_PLACE_LABEL};

View File

@@ -584,37 +584,36 @@ std::string node_properties_t::opcode() const {
return get_str("specials-opcodes", int(self));
}
static std::string get_node_string(std::string base, eSpecType type, int which) {
static std::string get_node_string(std::string base, eSpecType type, int which, int sect) {
eSpecCat cat = getNodeCategory(type);
int offset = int((*cat).first), i = int(type);
int strnum = (i - offset) * 17 + which + 1;
switch(cat) {
case eSpecCat::GENERAL:
return get_str(base + "-general", strnum);
case eSpecCat::ONCE:
return get_str(base + "-once", strnum);
case eSpecCat::AFFECT:
return get_str(base + "-affect", strnum);
case eSpecCat::IF_THEN:
return get_str(base + "-ifthen", strnum);
case eSpecCat::TOWN:
return get_str(base + "-town", strnum);
case eSpecCat::RECT:
return get_str(base + "-rect", strnum);
case eSpecCat::OUTDOOR:
return get_str(base + "-outdoor", strnum);
case eSpecCat::INVALID:
return "error";
case eSpecCat::GENERAL: base += "-general"; break;
case eSpecCat::ONCE: base += "-once"; break;
case eSpecCat::AFFECT: base += "-affect"; break;
case eSpecCat::IF_THEN: base += "-ifthen"; break;
case eSpecCat::TOWN: base += "-town"; break;
case eSpecCat::RECT: base += "-rect"; break;
case eSpecCat::OUTDOOR: base += "-outdoor"; break;
case eSpecCat::INVALID: return "error";
}
return "";
std::string str = get_str(base, strnum);
if(sect < 0) return str;
size_t start = 0, end = str.find_first_of('|');
while(sect --> 0 && end != std::string::npos) {
start = end + 1;
end = str.find_first_of('|', end + 1);
}
return str.substr(start, end - start);
}
std::string node_properties_t::name() const {
return get_node_string("specials-text", self, 0);
return get_node_string("specials-text", self, 0, -1);
}
std::string node_properties_t::descr() const {
return get_node_string("specials-text", self, 15);
return get_node_string("specials-text", self, 15, -1);
}
node_function_t::node_function_t() {}
@@ -670,10 +669,16 @@ node_function_t operator+(eSpecPicker picker) {
}
std::string node_function_t::label() const {
return get_node_string("specials-text", self, lbl_idx);
return get_node_string("specials-text", self, lbl_idx, sub_idx);
}
node_function_t node_properties_t::get(eSpecField fld) const {
node_function_t node_properties_t::get(const cSpecial& spec, eSpecField fld) const {
for(const auto& cond : conditions) {
if(cond.first(spec)) {
auto iter = cond.second.fields.find(fld);
if(iter != cond.second.fields.end()) return iter->second;
}
}
auto iter = fields.find(fld);
if(iter != fields.end()) return iter->second;
static node_function_t nil_fcn;
@@ -688,60 +693,60 @@ void node_properties_t::set(eSpecField fld, node_function_t fcn) {
fields[fld] = fcn;
}
node_function_t node_properties_t::sdf1(const cSpecial&) const {
return get(eSpecField::SDF1);
node_function_t node_properties_t::sdf1(const cSpecial& spec) const {
return get(spec, eSpecField::SDF1);
}
node_function_t node_properties_t::sdf2(const cSpecial&) const {
return get(eSpecField::SDF2);
node_function_t node_properties_t::sdf2(const cSpecial& spec) const {
return get(spec, eSpecField::SDF2);
}
node_function_t node_properties_t::msg1(const cSpecial&) const {
return get(eSpecField::MSG1);
node_function_t node_properties_t::msg1(const cSpecial& spec) const {
return get(spec, eSpecField::MSG1);
}
node_function_t node_properties_t::msg2(const cSpecial&) const {
return get(eSpecField::MSG2);
node_function_t node_properties_t::msg2(const cSpecial& spec) const {
return get(spec, eSpecField::MSG2);
}
node_function_t node_properties_t::msg3(const cSpecial&) const {
return get(eSpecField::MSG3);
node_function_t node_properties_t::msg3(const cSpecial& spec) const {
return get(spec, eSpecField::MSG3);
}
node_function_t node_properties_t::pic(const cSpecial&) const {
return get(eSpecField::PICT);
node_function_t node_properties_t::pic(const cSpecial& spec) const {
return get(spec, eSpecField::PICT);
}
node_function_t node_properties_t::pictype(const cSpecial&) const {
return get(eSpecField::PTYP);
node_function_t node_properties_t::pictype(const cSpecial& spec) const {
return get(spec, eSpecField::PTYP);
}
node_function_t node_properties_t::ex1a(const cSpecial&) const {
return get(eSpecField::EX1A);
node_function_t node_properties_t::ex1a(const cSpecial& spec) const {
return get(spec, eSpecField::EX1A);
}
node_function_t node_properties_t::ex1b(const cSpecial&) const {
return get(eSpecField::EX1B);
node_function_t node_properties_t::ex1b(const cSpecial& spec) const {
return get(spec, eSpecField::EX1B);
}
node_function_t node_properties_t::ex1c(const cSpecial&) const {
return get(eSpecField::EX1C);
node_function_t node_properties_t::ex1c(const cSpecial& spec) const {
return get(spec, eSpecField::EX1C);
}
node_function_t node_properties_t::ex2a(const cSpecial&) const {
return get(eSpecField::EX2A);
node_function_t node_properties_t::ex2a(const cSpecial& spec) const {
return get(spec, eSpecField::EX2A);
}
node_function_t node_properties_t::ex2b(const cSpecial&) const {
return get(eSpecField::EX2B);
node_function_t node_properties_t::ex2b(const cSpecial& spec) const {
return get(spec, eSpecField::EX2B);
}
node_function_t node_properties_t::ex2c(const cSpecial&) const {
return get(eSpecField::EX2C);
node_function_t node_properties_t::ex2c(const cSpecial& spec) const {
return get(spec, eSpecField::EX2C);
}
node_function_t node_properties_t::jump(const cSpecial&) const {
return get(eSpecField::JUMP);
node_function_t node_properties_t::jump(const cSpecial& spec) const {
return get(spec, eSpecField::JUMP);
}
struct field_map {
@@ -763,9 +768,9 @@ struct field_map {
};
};
static field_map& fields() {
short operator->*(const cSpecial& spec, eSpecField fld) {
static field_map map;
return map;
return spec.*map.map[fld];
}
node_builder_t& node_builder_t::sdf() {
@@ -872,3 +877,139 @@ node_builder_t::operator node_properties_t() {
allNodeProps.emplace(node.self, node);
return node;
}
node_condition_builder_t node_builder_t::when(node_condition_t cond, int lbl_sub) {
return node_condition_builder_t(*this, cond, lbl_sub);
}
node_condition_builder_t::node_condition_builder_t(node_builder_t& parent, node_condition_t cond, int sub)
: cond(cond)
, self(parent.node.self)
, parent(parent)
, sub(sub)
{
self.node.fields.clear();
}
node_builder_t& node_condition_builder_t::end() {
for(auto& field : self.node.fields) {
field.second.sub_idx = sub;
}
parent.node.conditions.emplace_back(cond, self.node);
return parent;
}
node_condition_builder_t& node_condition_builder_t::field(eSpecField field, node_function_t picker) {
self.field(field, picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::field_pair(eSpecField main, eSpecField extra, node_function_t picker) {
self.field_pair(main, extra, picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::sdf1(node_function_t picker) {
self.sdf1(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::sdf2(node_function_t picker) {
self.sdf2(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::jump(node_function_t picker) {
self.jump(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::msg1(node_function_t picker) {
self.msg1(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::msg2(node_function_t picker) {
self.msg2(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::msg3(node_function_t picker) {
self.msg3(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::pict(node_function_t picker) {
self.pict(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::ptyp(node_function_t picker) {
self.ptyp(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::ex1a(node_function_t picker) {
self.ex1a(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::ex1b(node_function_t picker) {
self.ex1b(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::ex1c(node_function_t picker) {
self.ex1c(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::ex2a(node_function_t picker) {
self.ex2a(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::ex2b(node_function_t picker) {
self.ex2b(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::ex2c(node_function_t picker) {
self.ex2c(picker);
return *this;
}
node_condition_builder_t& node_condition_builder_t::sdf() {
self.sdf();
return *this;
}
node_condition_builder_t& node_condition_builder_t::msg() {
self.msg();
return *this;
}
node_condition_builder_t& node_condition_builder_t::pic() {
self.pic();
return *this;
}
node_condition_builder_t& node_condition_builder_t::rect(eLocType type) {
self.rect(type);
return *this;
}
node_condition_builder_t& node_condition_builder_t::sdf(eSpecField a, eSpecField b) {
self.sdf(a, b);
return *this;
}
node_condition_builder_t& node_condition_builder_t::loc(eSpecField a, eSpecField b, eLocType type) {
self.loc(a, b, type);
return *this;
}
node_condition_builder_t& node_condition_builder_t::loc(eSpecField a, eSpecField b, eLocType type, eSpecField where) {
self.loc(a, b, type, where);
return *this;
}

View File

@@ -167,6 +167,7 @@ enum class eLocType {
};
enum class eSpecField { NONE, SDF1, SDF2, MSG1, MSG2, MSG3, PICT, PTYP, EX1A, EX1B, EX1C, EX2A, EX2B, EX2C, JUMP };
short operator->*(const cSpecial& spec, eSpecField fld);
struct node_function_t {
eSpecPicker button = eSpecPicker::NONE;
@@ -194,10 +195,13 @@ private:
bool needs_split = false;
friend struct node_builder_t;
friend struct node_properties_t;
friend struct node_condition_builder_t;
};
node_function_t operator+(eSpecPicker);
using node_condition_t = std::function<bool(const cSpecial&)>;
struct node_properties_t {
eSpecType self;
eSpecCat cat;
@@ -211,16 +215,20 @@ struct node_properties_t {
node_properties_t() : node_properties_t(eSpecType::INVALID) {}
private:
node_properties_t(eSpecType type);
node_function_t get(eSpecField fld) const;
node_function_t get(const cSpecial& spec, eSpecField fld) const;
void set(eSpecField fld, node_function_t fcn);
std::map<eSpecField, node_function_t> fields;
std::vector<std::pair<node_condition_t, node_properties_t>> conditions;
friend struct node_builder_t;
friend struct node_condition_builder_t;
friend struct field_map;
};
const node_properties_t& operator* (eSpecType t);
const node_category_info_t& operator* (eSpecCat t);
struct node_condition_builder_t;
// Builds the information needed to display the correct buttons when editing a special node.
struct node_builder_t {
node_builder_t(eSpecType type) : node(type) {}
@@ -259,9 +267,45 @@ struct node_builder_t {
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);
node_condition_builder_t when(node_condition_t cond, int lbl_sub);
operator node_properties_t();
private:
node_properties_t node;
friend struct node_condition_builder_t;
};
struct node_condition_builder_t {
node_builder_t& end();
node_condition_builder_t& field(eSpecField field, node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& field_pair(eSpecField main, eSpecField extra, node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& sdf1(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& sdf2(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& jump(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& msg1(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& msg2(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& msg3(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& pict(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& ptyp(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& ex1a(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& ex1b(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& ex1c(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& ex2a(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& ex2b(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& ex2c(node_function_t picker = eSpecPicker::NONE);
node_condition_builder_t& sdf();
node_condition_builder_t& msg();
node_condition_builder_t& pic();
node_condition_builder_t& rect(eLocType type);
node_condition_builder_t& sdf(eSpecField a, eSpecField b);
node_condition_builder_t& loc(eSpecField a, eSpecField b, eLocType type);
node_condition_builder_t& loc(eSpecField a, eSpecField b, eLocType type, eSpecField where);
private:
node_condition_builder_t(node_builder_t& parent, node_condition_t cond, int sub);
node_condition_t cond;
node_builder_t self;
node_builder_t& parent;
int sub;
friend class node_builder_t;
};
// An overview of how the builder works.