diff --git a/src/scenario/special.cpp b/src/scenario/special.cpp index 4a6018dd2..a9bb3ea01 100644 --- a/src/scenario/special.cpp +++ b/src/scenario/special.cpp @@ -1121,22 +1121,46 @@ short cSpecial::get(eSpecField fld) { } } -// TODO oh god the edit stack won't be reflected when graphing on a stack -std::vector global_node_graph(std::vector& globals) { +static cSpecial* get_spec_ref(cScenario& scenario, node_stack_t& stack, size_t which, int town_num_or_out_x = -1, int out_y = -1) { + int mode = (out_y >= 0 ? 1 : (town_num_or_out_x >= 0 ? 2 : 0)); + for(auto it = stack.rbegin(); it != stack.rend(); ++it){ + if(it->mode == mode && it->which == which) + return &it->node; + } + switch(mode) { + case 0: + if(scenario.scen_specials.size() <= which) return nullptr; + return &scenario.scen_specials[which]; + case 1: + if(scenario.outdoors[town_num_or_out_x][out_y]->specials.size() <= which) return nullptr; + return &scenario.outdoors[town_num_or_out_x][out_y]->specials[which]; + case 2: + if(scenario.towns[town_num_or_out_x]->specials.size() <= which) return nullptr; + return &scenario.towns[town_num_or_out_x]->specials[which]; + default: + throw std::string {"Bad mode"}; + } +} +std::vector global_node_graph(cScenario& scenario, node_stack_t& stack) { std::vector graph_nodes; - for(int i = 0; i < globals.size(); ++ i){ - graph_node_t node = { std::make_pair(true, i) }; + // The edit stack might contain new nodes (maybe, I think? Ugh) so I don't think we can use + // the size of the list to determine how far to loop + int i = 0; + cSpecial* next; + while((next = get_spec_ref(scenario, stack, i)) != nullptr){ + node_id_t id; id.which = i++; + graph_node_t node = { id }; - cSpecial& special = globals[i]; - node_properties_t props = *(special.type); + node_properties_t props = *(next->type); // Forward connections for(int j = 1; j <= static_cast(eSpecField::JUMP); ++j){ eSpecField fld = static_cast(j); - if(props.get(special, fld).button == eSpecPicker::NODE){ - if(special.get(fld) >= 0){ - node.to_nodes.insert(std::make_pair(true, special.get(fld))); + if(props.get(*next, fld).button == eSpecPicker::NODE){ + if(next->get(fld) >= 0){ + node_id_t id; id.which = next->get(fld); + node.to_nodes.insert(id); } // TODO negative numbers can be pointers, which the graph can't follow, // but that ambiguity or the pointer's identity could be noted on the graph @@ -1146,27 +1170,84 @@ std::vector global_node_graph(std::vector& globals) { } // Backward connections - for(int i = 0; i < globals.size(); ++ i){ + for(i = 0; i < graph_nodes.size(); ++i){ graph_node_t& node = graph_nodes[i]; - for(node_id id : node.to_nodes){ - graph_nodes[id.second].from_nodes.insert(node.id); + for(node_id_t id : node.to_nodes){ + graph_nodes[id.which].from_nodes.insert(node.id); + } + } + + // Backward connections to Call Global nodes in towns/outdoors + for(int town = 0; town < scenario.towns.size(); ++town){ + i = 0; + while((next = get_spec_ref(scenario, stack, i, town)) != nullptr){ + if(next->type == eSpecType::CALL_GLOBAL){ + node_id_t from; from.which = i; from.town_num_or_out_x = town; + graph_nodes[next->jumpto].from_nodes.insert(from); + } + ++i; + } + } + for(int x = 0; x < scenario.outdoors.width(); ++x){ + for(int y = 0; y < scenario.outdoors.height(); ++y){ + i = 0; + while((next = get_spec_ref(scenario, stack, i, x, y)) != nullptr){ + if(next->type == eSpecType::CALL_GLOBAL){ + node_id_t from; from.which = i; from.town_num_or_out_x = x; from.out_y = y; + graph_nodes[next->jumpto].from_nodes.insert(from); + } + ++i; + } } } return graph_nodes; } -bool node_compare::operator()(node_id a, node_id b) const { +bool node_compare::operator()(node_id_t a, node_id_t b) const { // This is just a lexicographical ordering. - if(a.first != b.first) return a.first < b.first; - if(a.second != b.second) return a.second < b.second; + if(a.which != b.which) return a.which < b.which; + if(a.town_num_or_out_x != b.town_num_or_out_x) return a.town_num_or_out_x < b.town_num_or_out_x; + if(a.out_y != b.out_y) return a.out_y < b.out_y; return false; } -std::vector local_node_graph(std::vector& locals, std::vector& globals) { - std::vector global_nodes = global_node_graph(globals); +std::vector local_node_graph(cScenario& scenario, node_stack_t& stack, int town_num_or_out_x, int out_y) { + std::vector graph_nodes; + int i = 0; + cSpecial* next; + while((next = get_spec_ref(scenario, stack, i, town_num_or_out_x, out_y)) != nullptr){ + node_id_t id; id.which = i++; id.town_num_or_out_x = town_num_or_out_x; id.out_y = out_y; + graph_node_t node = { id }; - std::vector local_nodes; + node_properties_t props = *(next->type); - return local_nodes; + // Forward connections + for(int j = 1; j <= static_cast(eSpecField::JUMP); ++j){ + eSpecField fld = static_cast(j); + if(props.get(*next, fld).button == eSpecPicker::NODE){ + if(next->get(fld) >= 0){ + node_id_t id; id.which = next->get(fld); + if(next->type != eSpecType::CALL_GLOBAL){ + id.town_num_or_out_x = town_num_or_out_x; + id.out_y = out_y; + } + node.to_nodes.insert(id); + } + // TODO negative numbers can be pointers, which the graph can't follow, + // but that ambiguity or the pointer's identity could be noted on the graph + } + } + graph_nodes.push_back(node); + } + + // Backward connections + for(i = 0; i < graph_nodes.size(); ++i){ + graph_node_t& node = graph_nodes[i]; + for(node_id_t id : node.to_nodes){ + graph_nodes[id.which].from_nodes.insert(node.id); + } + } + + return graph_nodes; } \ No newline at end of file diff --git a/src/scenario/special.hpp b/src/scenario/special.hpp index afec6c5e4..5091f75b6 100644 --- a/src/scenario/special.hpp +++ b/src/scenario/special.hpp @@ -18,6 +18,7 @@ namespace legacy { struct special_node_type; }; class cUniverse; +class cScenario; enum class eSpecField; static const short SDF_COMPLETE = 250; @@ -115,19 +116,32 @@ public: std::string editor_hint(cUniverse& univ) const; }; -typedef std::pair node_id; +struct node_id_t { + int which; + int town_num_or_out_x = -1; + int out_y = -1; +}; + struct node_compare { - bool operator()(node_id a, node_id b) const; + bool operator()(node_id_t a, node_id_t b) const; }; struct graph_node_t { - node_id id; - std::set from_nodes; - std::set to_nodes; + node_id_t id; + std::set from_nodes; + std::set to_nodes; }; -std::vector global_node_graph(std::vector& globals); -std::vector local_node_graph(std::vector& locals, std::vector globals); +struct editing_node_t { + short which, mode; + cSpecial node; + bool is_new; +}; + +typedef std::vector node_stack_t; + +std::vector global_node_graph(cScenario& scenario, node_stack_t& edit_stack); +std::vector local_node_graph(cScenario& scenario, node_stack_t& edit_stack, int town_num_or_out_x, int out_y = -1); enum class eSpecCtxType { SCEN, OUTDOOR, TOWN, diff --git a/src/scenedit/scen.keydlgs.cpp b/src/scenedit/scen.keydlgs.cpp index 78b4467b1..45d965ba9 100644 --- a/src/scenedit/scen.keydlgs.cpp +++ b/src/scenedit/scen.keydlgs.cpp @@ -754,14 +754,6 @@ bool edit_area_rect_str(info_rect_t& r) { // MARK: Special node dialog -struct editing_node_t { - short which, mode; - cSpecial node; - bool is_new; -}; - -typedef std::vector node_stack_t; - static void setup_node_field(cDialog& me, std::string field, short value, const node_function_t& fcn) { me[field + "-lbl"].setText(fcn.label()); me[field].setTextToNum(value); @@ -816,14 +808,23 @@ static void put_spec_enc_in_dlog(cDialog& me, node_stack_t& edit_stack) { cSpecial& spec = edit_stack.back().node; // Graph fun! - std::vector globals = global_node_graph(scenario.scen_specials); - graph_node_t which = globals[edit_stack.back().which]; - - for(node_id from_id : which.from_nodes){ - LOG(fmt::format("{} -> {}", from_id.second, which.id.second)); + graph_node_t which; + int mode = edit_stack.back().mode; + if(mode == 0){ + std::vector globals = global_node_graph(scenario, edit_stack); + which = globals[edit_stack.back().which]; + }else if(mode == 1){ + std::vector locals = local_node_graph(scenario, edit_stack, cur_out.x, cur_out.y); + which = locals[edit_stack.back().which]; + }else if(mode == 2){ + std::vector locals = local_node_graph(scenario, edit_stack, cur_town); + which = locals[edit_stack.back().which]; } - for(node_id to_id : which.to_nodes){ - LOG(fmt::format("{} -> {}", which.id.second, to_id.second)); + for(node_id_t from_id : which.from_nodes){ + LOG(fmt::format("({}, {}, {}) -> ({}, {}, {})", from_id.which, from_id.town_num_or_out_x, from_id.out_y, which.id.which, which.id.town_num_or_out_x, which.id.out_y)); + } + for(node_id_t to_id : which.to_nodes){ + LOG(fmt::format("({}, {}, {}) -> ({}, {}, {})", which.id.which, which.id.town_num_or_out_x, which.id.out_y, to_id.which, to_id.town_num_or_out_x, to_id.out_y)); } // Show which node is being edited and what type of node it is