Complete rewrite of special node edit dialog

- Now has space for the additional fields that have been added.
- The dialog text has been updated so that labels, names, etc reflect all (or at least most) changes that have been made to specials so far.
- A lot more fields provide a Choose button than previously. A few of these are still unimplemented though.
- Rect specials have their own button now, instead of being filed under Town specials.
- New help button (not yet implemented)
- You can now cancel when deep into a node chain. You're given a choice of discarding the entire chain or just the current node; choosing the latter is similar to clicking "Go Back", but doesn't save your changes.
- Nodes are no longer saved prior to clicking OK (which saves all the nodes you were working on) or "Go Back" (which saves just the current node). So, choosing the first option when clicking Cancel could lead to a lot lost.
- Incidental change: The arithmetic special nodes now use the message 1 and 2 fields in the standard way.

Supporting dialog engine changes:
- Picture choice dialog now has a way to get the index of the selection, rather than just the picture selected.
- Picture choice dialog no longer sorts the list of pictures. If sorting is desired, the list should be sorted prior to passing it in.
- Picture and string choice dialogs now support attaching a "select handler" to be called when the selected item changes, because the normal way to do this would override the all-important focus handler that the dialog uses to track the currently selected item.
This commit is contained in:
2014-12-26 02:02:56 -05:00
parent d6cc7bae53
commit bdcd2fc67c
31 changed files with 3678 additions and 2271 deletions

View File

@@ -2,6 +2,7 @@
#include <stack>
#include <set>
#include <map>
#include <boost/lexical_cast.hpp>
#include "scen.global.h"
#include "classes.h"
#include "graphtool.h"
@@ -10,6 +11,7 @@
#include "scen.core.h"
#include "dlogutil.hpp"
#include "restypes.hpp"
#include "spell.hpp"
extern short cen_x, cen_y/*, overall_mode*/;
extern bool mouse_button_held;
@@ -165,15 +167,94 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, con
strings.push_back(item.full_name);
}
break;
case STRT_SPEC_ITEM:
for(cSpecItem& item : scenario.special_items) {
strings.push_back(item.name);
}
break;
case STRT_TER:
for(cTerrain& ter : scenario.ter_types) {
strings.push_back(ter.name);
}
break;
case STRT_TOWN:
for(cTown* town : scenario.towns) {
strings.push_back(town->town_name);
}
break;
case STRT_SECTOR:
// TODO: Generate a list of all outdoor sectors
break;
case STRT_MAGE:
for(int i = 0; i < 62; i++) {
strings.push_back((*cSpell::fromNum(eSkill::MAGE_SPELLS, i)).name());
}
break;
case STRT_PRIEST:
for(int i = 0; i < 62; i++) {
strings.push_back((*cSpell::fromNum(eSkill::PRIEST_SPELLS, i)).name());
}
break;
case STRT_ALCHEMY:
for(int i = 0; i < 20; i++) {
strings.push_back(get_str("magic-names", i + 200));
}
break;
case STRT_SKILL:
for(int i = 0; i < 19; i++) {
strings.push_back(get_str("skills", i * 2 + 1));
}
strings.push_back("Maximum Health");
strings.push_back("Maximum Spell Points");
break;
case STRT_TRAIT:
// TODO: Generate a list of traits names
break;
case STRT_RACE:
// TODO: Generate a list of race names
break;
case STRT_PICT:
strings = *ResMgr::get<StringRsrc>("picture-types");
break;
case STRT_SND:
strings = *ResMgr::get<StringRsrc>("sound-names");
break;
case STRT_TRAP:
strings = *ResMgr::get<StringRsrc>("trap-types");
break;
case STRT_BUTTON:
for(int btn : available_btns) {
strings.push_back(basic_buttons[btn].label);
}
break;
case STRT_CMP:
strings = {"Less Than or Equal", "Less Than", "Equal", "Greater Than", "Grater Than or Equal"};
break;
case STRT_ACCUM:
strings = {"Current PC Only", "Cumulative", "Average", "Minimum", "Maximum"};
break;
case STRT_ATTITUDE:
strings = {"Docile", "Hostile Type A", "Friendly", "Hostile Type B"};
break;
case STRT_STAIR:
strings = {"Stairs Up", "Stairs Down", "Slope Up", "Slope Down", "Slimy Stairs Up", "Slimy Stairs Down", "Dark Slope Up", "Dark Slope Down"};
break;
case STRT_STAIR_MODE:
strings = {"Normal behaviour", "Allow triggering in combat", "Allow triggering when not moving (eg when looking at the space)", "Both of the above"};
break;
case STRT_LIGHT:
strings = {"Fully Lit", "Dark", "Very Dark", "Totally Dark"};
break;
case STRT_CONTEXT:
// TODO: Generate a list of special node contexts
break;
case STRT_SHOP:
// TODO: There are actually a few more shop types than this (like junk shops)
strings = {"Items", "Mage Spells", "Priest Spells", "Alchemy", "Healing"};
break;
case STRT_COST_ADJ:
strings = {"Extremely Cheap", "Very Reasonable", "Pretty Average", "Somewhat Pricey", "Expensive", "Exorbitant", "Utterly Ridiculous"};
break;
}
if(cur_choice < 0 || cur_choice >= strings.size())
cur_choice = 0;
@@ -277,459 +358,393 @@ bool edit_area_rect_str(short which_str,short mode) {
// MARK: Special node dialog
static bool save_spec_enc(cDialog& me, short which_mode, short which_node, cSpecial& store_spec_node) {
store_spec_node.sd1 = me["sdf1"].getTextAsNum();
store_spec_node.sd2 = me["sdf2"].getTextAsNum();
store_spec_node.m1 = me["msg1"].getTextAsNum();
store_spec_node.m2 = me["msg2"].getTextAsNum();
store_spec_node.pic = me["pict"].getTextAsNum();
if(store_spec_node.pic < 0)
store_spec_node.pic = 0;
store_spec_node.ex1a = me["x1a"].getTextAsNum();
store_spec_node.ex1b = me["x1b"].getTextAsNum();
store_spec_node.ex2a = me["x2a"].getTextAsNum();
store_spec_node.ex2b = me["x2b"].getTextAsNum();
store_spec_node.jumpto = me["jump"].getTextAsNum();
if((*store_spec_node.type).sdf_label == 1) {
if(cre(store_spec_node.sd1,-1,299,"The first part of a Stuff Done flag must be from 0 to 299 (or -1 if the Stuff Done flag is ignored.",
"",&me) > 0) return false;
if(cre(store_spec_node.sd2,-1,9,"The second part of a Stuff Done flag must be from 0 to 9 (or -1 if the Stuff Done flag is ignored.",
"",&me) > 0) return false;
struct editing_node_t {
short which, mode;
cSpecial node;
};
typedef std::stack<editing_node_t> node_stack_t;
static void setup_node_field(cDialog& me, std::string field, short value, std::string label, char buttonType) {
me[field + "-lbl"].setText(label);
me[field].setTextToNum(value);
std::string button = field + "-edit";
try { // Just make sure the button exists before fiddling with it.
me.getControl(button);
} catch(std::invalid_argument) {
return;
}
switch(buttonType) {
case ' ':
me[button].hide();
break;
case 'm': case 'M': case'$': case 'd': // messages
case 's': case 'S': // specials
me[button].show();
me[button].setText("Create/Edit");
break;
default:
me[button].show();
me[button].setText("Choose");
break;
}
}
static void put_spec_enc_in_dlog(cDialog& me, node_stack_t& edit_stack) {
cSpecial& spec = edit_stack.top().node;
if(which_mode == 0)
scenario.scen_specials[which_node] = store_spec_node;
if(which_mode == 1)
current_terrain->specials[which_node] = store_spec_node;
if(which_mode == 2)
town->specials[which_node] = store_spec_node;
// Show which node is being edited and what type of node it is
switch(edit_stack.top().mode) {
case 0: me["title"].setText("Edit Scenario Special Node"); break;
case 1: me["title"].setText("Edit Outdoors Special Node"); break;
case 2: me["title"].setText("Edit Town Special Node"); break;
}
me["num"].setTextToNum(edit_stack.top().which);
// Show the node opcode information
me["type"].setText((*spec.type).name());
node_properties_t info = *spec.type;
// Set up the labels, fields, and buttons
setup_node_field(me, "sdf1", spec.sd1, info.sdf1_lbl(), ' ');
setup_node_field(me, "sdf2", spec.sd2, info.sdf2_lbl(), ' ');
setup_node_field(me, "msg1", spec.m1, info.msg1_lbl(), info.m1_btn);
setup_node_field(me, "msg2", spec.m2, info.msg2_lbl(), info.m2_btn);
setup_node_field(me, "msg3", spec.m3, info.msg3_lbl(), info.m3_btn);
setup_node_field(me, "pict", spec.pic, info.pic_lbl(), info.p_btn);
setup_node_field(me, "pictype", spec.pictype, info.pt_lbl(), info.pt_btn);
setup_node_field(me, "x1a", spec.ex1a, info.ex1a_lbl(), info.x1a_btn);
setup_node_field(me, "x1b", spec.ex1b, info.ex1b_lbl(), info.x1b_btn);
setup_node_field(me, "x1c", spec.ex1c, info.ex1c_lbl(), info.x1c_btn);
setup_node_field(me, "x2a", spec.ex2a, info.ex2a_lbl(), info.x2a_btn);
setup_node_field(me, "x2b", spec.ex2b, info.ex2b_lbl(), info.x2b_btn);
setup_node_field(me, "x2c", spec.ex2c, info.ex2c_lbl(), info.x2c_btn);
setup_node_field(me, "jump", spec.jumpto, info.jmp_lbl(), 's');
}
static void save_spec_enc(cDialog& me, node_stack_t& edit_stack) {
cSpecial& the_node = edit_stack.top().node;
the_node.sd1 = me["sdf1"].getTextAsNum();
the_node.sd2 = me["sdf2"].getTextAsNum();
the_node.m1 = me["msg1"].getTextAsNum();
the_node.m2 = me["msg2"].getTextAsNum();
the_node.m3 = me["msg3"].getTextAsNum();
the_node.pic = me["pict"].getTextAsNum();
the_node.pictype = me["pictype"].getTextAsNum();
the_node.ex1a = me["x1a"].getTextAsNum();
the_node.ex1b = me["x1b"].getTextAsNum();
the_node.ex1c = me["x1c"].getTextAsNum();
the_node.ex2a = me["x2a"].getTextAsNum();
the_node.ex2b = me["x2b"].getTextAsNum();
the_node.ex2c = me["x2c"].getTextAsNum();
the_node.jumpto = me["jump"].getTextAsNum();
}
static bool commit_spec_enc(cDialog& me, std::string item_hit, node_stack_t& edit_stack) {
save_spec_enc(me, edit_stack);
int mode = edit_stack.top().mode, node = edit_stack.top().which;
switch(mode) {
case 0: scenario.scen_specials[node] = edit_stack.top().node; break;
case 1: town->specials[node] = edit_stack.top().node; break;
case 2: current_terrain->specials[node] = edit_stack.top().node; break;
}
edit_stack.pop();
if(item_hit == "okay") {
while(!edit_stack.empty())
commit_spec_enc(me, "unwind", edit_stack);
me.toast(true);
me.setResult(true);
} else if(item_hit == "back") {
put_spec_enc_in_dlog(me, edit_stack);
if(edit_stack.size() == 1)
me["back"].hide();
}
return true;
}
static void put_spec_enc_in_dlog(cDialog& me, short which_node, cSpecial& store_spec_node, std::stack<short>& last_node) {
std::string str;
me["num"].setTextToNum(which_node);
str = get_str("special-node-names",int(store_spec_node.type) + 1);
me["type"].setText(str);
if(last_node.empty())
me["back"].hide();
else me["back"].show();
me["sdf1"].setTextToNum(store_spec_node.sd1);
me["sdf2"].setTextToNum(store_spec_node.sd2);
switch((*store_spec_node.type).sdf_label) {
case 0:
me["sdf1-lbl"].setText("Unused.");
me["sdf2-lbl"].setText("Unused.");
break;
case 1:
me["sdf1-lbl"].setText("Stuff Done Flag Part A");
me["sdf2-lbl"].setText("Stuff Done Flag Part B");
break;
case 2:
me["sdf1-lbl"].setText("Chance of placing (0 - 100)");
me["sdf2-lbl"].setText("Unused");
break;
case 3:
me["sdf1-lbl"].setText("Stuff Done Flag Part A");
me["sdf2-lbl"].setText("Unused");
break;
case 4:
me["sdf1-lbl"].setText("X of space to move to");
me["sdf2-lbl"].setText("Y of space to move to");
break;
case 5:
me["sdf1-lbl"].setText("Terrain to change to");
me["sdf2-lbl"].setText("Chance of changing (0 - 100)");
break;
case 6:
me["sdf1-lbl"].setText("Switch this ter. type");
me["sdf2-lbl"].setText("with this ter. type");
break;
case 7:
me["sdf1-lbl"].setText("Chance of placing (0 - 100)");
me["sdf2-lbl"].setText("What to place (see docs.)");
break;
case 8:
me["sdf1-lbl"].setText("Chance of placing (0 - 100)");
me["sdf2-lbl"].setText("0 - web, 1 - barrel, 2 - crate");
break;
}
me["msg1"].setTextToNum(store_spec_node.m1);
me["msg2"].setTextToNum(store_spec_node.m2);
switch((*store_spec_node.type).msg_label) {
case 0:
me["msg1-lbl"].setText("Unused.");
me["msg2-lbl"].setText("Unused.");
me["msg1-edit"].hide();
me["msg2-edit"].hide();
break;
case 1:
me["msg1-lbl"].setText("First part of message");
me["msg2-lbl"].setText("Second part of message");
me["msg1-edit"].hide();
me["msg2-edit"].show();
break;
case 2:
me["msg1-lbl"].setText("Number of first message in dialog");
me["msg2-lbl"].setText("Unused");
me["msg1-edit"].hide();
me["msg2-edit"].show();
break;
case 3:
me["msg1-lbl"].setText("Name of Store");
me["msg2-lbl"].setText("Unused");
me["msg1-edit"].hide();
me["msg2-edit"].show();
break;
case 4:
me["msg1-lbl"].setText("Number of first message in dialog");
me["msg2-lbl"].setText("1 - add 'Leave'/'OK' button, else no");
me["msg1-edit"].hide();
me["msg2-edit"].show();
break;
case 5:
me["msg1-lbl"].setText("Number of first message in dialog");
me["msg2-lbl"].setText("Num. of spec. item to give (-1 none)");
me["msg1-edit"].hide();
me["msg2-edit"].show();
break;
}
me["pict"].setTextToNum(store_spec_node.pic);
switch((*store_spec_node.type).pic_label) {
case 0:
me["pict-lbl"].setText("Unused.");
me["pict-edit"].hide();
break;
case 1:
me["pict-lbl"].setText("Dialog Picture number");
me["pict-edit"].show();
break;
case 2:
me["pict-lbl"].setText("Terrain Picture number");
me["pict-edit"].show();
break;
case 3:
me["pict-lbl"].setText("Monster Picture number");
me["pict-edit"].show();
break;
case 4:
me["pict-lbl"].setText("Chance of changing (0 - 100)");
me["pict-edit"].hide();
break;
case 5:
me["pict-lbl"].setText("Number of letters to match");
me["pict-edit"].hide();
break;
case 6:
me["pict-lbl"].setText("Radius of explosion");
me["pict-edit"].hide();
break;
}
me["x1a"].setTextToNum(store_spec_node.ex1a);
str = get_str("special-x1a",int(store_spec_node.type) + 1);
me["x1a-lbl"].setText(str);
if((*store_spec_node.type).ex1a_choose == 1)
me["x1a-edit"].show();
else me["x1a-edit"].hide();
me["x1b"].setTextToNum(store_spec_node.ex1b);
str = get_str("special-x1b",int(store_spec_node.type) + 1);
me["x1b-lbl"].setText(str);
if((*store_spec_node.type).ex1b_choose == 1)
me["x1b-edit"].show();
else me["x1b-edit"].hide();
me["x2a"].setTextToNum(store_spec_node.ex2a);
str = get_str("special-x2a",int(store_spec_node.type) + 1);
me["x2a-lbl"].setText(str);
if((*store_spec_node.type).ex2a_choose == 1)
me["x2a-edit"].show();
else me["x2a-edit"].hide();
me["x2b"].setTextToNum(store_spec_node.ex2b);
str = get_str("special-x2b",int(store_spec_node.type) + 1);
me["x2b-lbl"].setText(str);
if((*store_spec_node.type).ex2b_choose == 1)
me["x2b-edit"].show();
else me["x2b-edit"].hide();
me["jump"].setTextToNum(store_spec_node.jumpto);
switch((*store_spec_node.type).jmp_label) {
case 0:
me["jump-lbl"].setText("Special to Jump To");
break;
case 1:
me["jump-lbl"].setText("Special node if not blocked");
break;
case 2:
me["jump-lbl"].setText("Special after trap finished");
break;
case 3:
me["jump-lbl"].setText("Otherwise call this special");
break;
case 4:
me["jump-lbl"].setText("Special if OK/Leave picked");
break;
}
static bool discard_spec_enc(cDialog& me, node_stack_t& edit_stack) {
if(edit_stack.size() > 1) {
std::string action = cChoiceDlog("discard-special-node.xml", {"save", "cancel", "revert"}).show();
if(action == "save") return true;
if(action == "cancel") return me.toast(false);
edit_stack.pop();
if(edit_stack.size() == 1)
me["back"].hide();
} else me.toast(false);
return true;
}
static bool edit_spec_enc_event_filter(cDialog& me, std::string item_hit, short& which_mode, short& which_node, cSpecial& store_spec_node, std::stack<short>& last_node) {
short i,node_to_change_to = -1,spec;
if(item_hit == "okay") {
if(save_spec_enc(me, which_mode, which_node, store_spec_node))
me.toast(true);
me.setResult(true);
} else if(item_hit == "back") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
if(!last_node.empty()) {
node_to_change_to = last_node.top();
last_node.pop();
static bool edit_spec_enc_type(cDialog& me, std::string item_hit, node_stack_t& edit_stack) {
save_spec_enc(me, edit_stack);
eSpecCat category = eSpecCat::INVALID;
if(item_hit == "general") category = eSpecCat::GENERAL;
else if(item_hit == "oneshot") category = eSpecCat::ONCE;
else if(item_hit == "affectpc") category = eSpecCat::AFFECT;
else if(item_hit == "ifthen") category = eSpecCat::IF_THEN;
else if(item_hit == "town") category = eSpecCat::TOWN;
else if(item_hit == "out") category = eSpecCat::OUTDOOR;
else if(item_hit == "rect") category = eSpecCat::RECT;
int start = -1, finish = -1, current = int(edit_stack.top().node.type);
for(int i = 0; i < std::numeric_limits<unsigned short>::max(); i++) {
eSpecType check = eSpecType(i);
eSpecCat checkCat = getNodeCategory(check);
if(start >= 0 && checkCat == eSpecCat::INVALID) {
finish = i - 1;
break;
} else if(checkCat == category && start < 0)
start = i;
}
if(start < 0 || finish < 0) return true;
std::vector<std::string> choices;
for(int i = start; i <= finish; i++) {
eSpecType node = eSpecType(i);
std::string name = (*node).name();
if(name.empty()) choices.push_back("Unused Node");
else choices.push_back(name);
}
size_t cancelled = -1, result;
cStringChoice choose(choices, "Select a special node type:");
result = choose.show((current < start || current > finish) ? cancelled : current - start);
if(result != cancelled) {
edit_stack.top().node.type = eSpecType(result + start);
put_spec_enc_in_dlog(me, edit_stack);
}
return true;
}
static short choose_field_type(short cur, cDialog* parent, bool includeSpec) {
static const char*const fieldNames[] = {
"Wall of Force", "Wall of Fire", "Antimagic Field", "Stinking Cloud", "Wall of Ice", "Wall of Blades", "Sleep Cloud",
"Stone Block", "Webs", "Crate", "Barrel", "Fire Barrier", "Force Barrier", "Quickfire",
"Small Blood", "Medium Blood", "Large Blood", "Small Slime", "Large Slime", "Ash", "Bones", "Rubble",
"Force Cage", "Cleanse Space", "Crumble Walls",
};
std::vector<std::pair<pic_num_t,ePicType>> pics = {
{8, PIC_FIELD}, {9, PIC_FIELD}, {10, PIC_FIELD}, {11, PIC_FIELD},
{12, PIC_FIELD}, {13, PIC_FIELD}, {14, PIC_FIELD},
{3, PIC_FIELD}, {5, PIC_FIELD}, {6, PIC_FIELD}, {7, PIC_FIELD},
{14, PIC_TER_ANIM}, {14, PIC_TER_ANIM}, {15, PIC_FIELD},
{24, PIC_FIELD}, {25, PIC_FIELD}, {26, PIC_FIELD}, {27, PIC_FIELD},
{28, PIC_FIELD}, {29, PIC_FIELD}, {30, PIC_FIELD}, {31, PIC_FIELD},
{0, PIC_FIELD},
};
if(includeSpec) {
pics.push_back({23, PIC_FIELD});
pics.push_back({95, PIC_TER});
}
short prev = cur;
cur--;
if(cur >= SPECIAL_SPOT) cur--;
if(cur >= 25) cur -= 7;
if(cur < 0 || cur >= pics.size()) cur = 0;
cPictChoice pic_dlg(pics, parent);
pic_dlg->getControl("prompt").setText("Select a field type:");
pic_dlg->getControl("help").setText(fieldNames[cur]);
pic_dlg.attachSelectHandler([](cPictChoice& me, int n) {
me->getControl("help").setText(fieldNames[n]);
});
bool made_choice = pic_dlg.show(cur);
size_t item_hit = pic_dlg.getSelected();
item_hit++;
if(item_hit >= SPECIAL_SPOT)
item_hit++;
if(item_hit >= 25)
item_hit += 7;
return made_choice ? item_hit : prev;
}
static pic_num_t choose_damage_type(short cur, cDialog* parent) {
static const char*const damageNames[] = {"Weapon", "Fire", "Poison", "Magic", "Unblockable", "Cold", "Undead", "Demon"};
static std::vector<pic_num_t> pics = {3,0,2,1,1,4,3,3};
short prev = cur;
if(cur < 0 || cur >= pics.size()) cur = 0;
cPictChoice pic_dlg(pics, PIC_BOOM, parent);
pic_dlg->getControl("prompt").setText("Select a damage type:");
pic_dlg->getControl("help").setText(damageNames[cur]);
pic_dlg.attachSelectHandler([](cPictChoice& me, int n) {
me->getControl("help").setText(damageNames[n]);
});
bool made_choice = pic_dlg.show(cur);
size_t item_hit = pic_dlg.getSelected();
return made_choice ? item_hit : prev;
}
static pic_num_t choose_boom_type(short cur, cDialog* parent) {
static const char*const boomNames[] = {"Fire", "Magic/Cold/Electricity", "Teleport", "Magic/Electricity"};
static std::vector<pic_num_t> pics = {12,28,20,36};
short prev = cur;
if(cur < 0 || cur >= pics.size()) cur = 0;
cPictChoice pic_dlg(pics, PIC_BOOM, parent);
pic_dlg->getControl("prompt").setText("Select a boom type:");
pic_dlg->getControl("help").setText(boomNames[cur]);
pic_dlg.attachSelectHandler([](cPictChoice& me, int n) {
me->getControl("help").setText(boomNames[n]);
});
bool made_choice = pic_dlg.show(cur);
size_t item_hit = pic_dlg.getSelected();
return made_choice ? item_hit : prev;
}
static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t& edit_stack) {
std::string field = item_hit.substr(0, item_hit.find_first_of('-'));
char btn;
eSpecType type = edit_stack.top().node.type;
if(field.substr(0,3) == "sdf") return true;
else if(field == "msg1") btn = (*type).m1_btn;
else if(field == "msg2") btn = (*type).m2_btn;
else if(field == "msg3") btn = (*type).m3_btn;
else if(field == "pict") btn = (*type).p_btn;
else if(field == "picttype") btn = (*type).pt_btn;
else if(field == "x1a") btn = (*type).x1a_btn;
else if(field == "x1b") btn = (*type).x1b_btn;
else if(field == "x1c") btn = (*type).x1c_btn;
else if(field == "x2a") btn = (*type).x2a_btn;
else if(field == "x2b") btn = (*type).x2b_btn;
else if(field == "x2c") btn = (*type).x2c_btn;
else if(field == "jump") {
if(type == eSpecType::CALL_GLOBAL) btn = 'S';
else btn = 's';
}
if(btn == '#') {
// TODO: Enumify the shop types
switch(me["x1b"].getTextAsNum()) {
case 0: btn = 'i'; break;
case 1: btn = 'A'; break;
case 2: btn = 'P'; break;
case 3: btn = 'a'; break;
}
} else if(item_hit == "cancel") {
if(!last_node.empty()) {
giveError("You can't cancel out of making a special until you're back at the beginning of the special chain.",
"Press the Go Back button until it disappears.",&me);
return true;
}
me.toast(false);
me.setResult(false);
} else if(me[item_hit].getText() == "Create/Edit") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
if(item_hit == "x1b-edit")
spec = me["x1b"].getTextAsNum();
else if(item_hit == "x2b-edit")
spec = me["x2b"].getTextAsNum();
else if(item_hit == "jump-edit")
spec = me["jump"].getTextAsNum();
if((spec < 0) || (spec >= num_specs[which_mode])) {
spec = -1;
//CDSN(822,8,-1)
// TODO: Generalize this situation of a node jumping to a scenario node
if(item_hit == "x1b-edit" && store_spec_node.type == eSpecType::SCEN_TIMER_START)
spec = get_fresh_spec(0);
else if(item_hit == "jump-edit" && store_spec_node.type == eSpecType::CALL_GLOBAL)
spec = get_fresh_spec(0);
else spec = get_fresh_spec(which_mode);
if(spec < 0) {
}
short val = me[field].getTextAsNum(), mode = (btn == 'S' || btn == '$') ? 0 : edit_stack.top().mode, store;
short pictype = me["pictype"].getTextAsNum();
bool choose_string = true;
eStrType strt;
short str_adj = 0;
const char* title;
cSpecial node_to_change_to;
switch(btn) {
case 'm':
choose_string = false;
store = me["msg2"].getTextAsNum();
edit_spec_text(mode, &val, &store, &me);
me["msg2"].setTextToNum(store);
store = val;
break;
case 'M': case '$':
choose_string = false;
// TODO: This might need a new dialog, or maybe a tweak of an existing one
break;
case 'd':
choose_string = false;
store = val;
edit_dialog_text(mode, &store, &me);
break;
case 's': case 'S':
choose_string = false;
store = val < 0 ? get_fresh_spec(mode) : val;
if(store < 0) {
giveError("You can't create a new special encounter because there are no more free special nodes.",
"To free a special node, set its type to No Special and set its Jump To special to -1.",&me);
return true;
}
if(item_hit == "x1b-edit")
me["x1b"].setTextToNum(spec);
else if(item_hit == "x2b-edit")
me["x2b"].setTextToNum(spec);
else if(item_hit == "jump-edit")
me["jump"].setTextToNum(spec);
/*
if(item_hit == 43)
store_spec_node.ex1b = spec;
if(item_hit == 44)
store_spec_node.ex2b = spec;
if(item_hit == 45)
store_spec_node.jumpto = spec;
*/
}
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
me[field].setTextToNum(store);
save_spec_enc(me, edit_stack);
if(mode == 0)
node_to_change_to = scenario.scen_specials[store];
else if(mode == 1)
node_to_change_to = current_terrain->specials[store];
else if(mode == 2)
node_to_change_to = town->specials[store];
if(node_to_change_to.pic < 0)
node_to_change_to.pic = 0;
edit_stack.push({store,mode,node_to_change_to});
put_spec_enc_in_dlog(me, edit_stack);
me["back"].show();
return true;
if(item_hit == "x1b-edit" && store_spec_node.type == eSpecType::SCEN_TIMER_START)
node_to_change_to = spec;
else if(item_hit == "jump-edit" && store_spec_node.type == eSpecType::CALL_GLOBAL)
node_to_change_to = spec;
else node_to_change_to = which_mode * 1000 + spec;
last_node.push(which_mode * 1000 + which_node);
} else if(item_hit == "x1a-edit") {
switch(store_spec_node.type) {
case eSpecType::FORCED_GIVE:
case eSpecType::ONCE_GIVE_ITEM:
case eSpecType::ONCE_GIVE_ITEM_DIALOG:
case eSpecType::ONCE_GIVE_ITEM_TERRAIN:
case eSpecType::ONCE_GIVE_ITEM_MONSTER:
i = choose_text(STRT_ITEM,store_spec_node.ex1a,&me,"Give which item?");
break;
case eSpecType::OUT_STORE:
i = choose_text(STRT_ITEM,store_spec_node.ex1a,&me,"First item in store?");
break;
case eSpecType::ONCE_DIALOG:
case eSpecType::ONCE_DIALOG_TERRAIN:
case eSpecType::ONCE_DIALOG_MONSTER:
i = choose_text(STRT_BUTTON,store_spec_node.ex1a,&me,"Which button label?");
break;
case eSpecType::TOWN_DESTROY_MONST:
i = choose_text(STRT_MONST,store_spec_node.ex1a,&me,"Choose Which Monster:");
break;
}
store_spec_node.ex1a = i;
me["x1a"].setTextToNum(store_spec_node.ex1a);
} else if(item_hit == "x2a-edit") {
switch(store_spec_node.type) {
case eSpecType::FORCED_GIVE:
case eSpecType::ONCE_GIVE_ITEM:
case eSpecType::TOWN_PLACE_ITEM:
case eSpecType::OUT_STORE: // TODO: This isn't right - it should be "number of items" or some such
i = choose_text(STRT_ITEM,store_spec_node.ex2a,&me,"Give which item?");
break;
case eSpecType::ONCE_DIALOG:
case eSpecType::ONCE_DIALOG_TERRAIN:
case eSpecType::ONCE_DIALOG_MONSTER:
case eSpecType::ONCE_GIVE_ITEM_DIALOG:
case eSpecType::ONCE_GIVE_ITEM_TERRAIN:
case eSpecType::ONCE_GIVE_ITEM_MONSTER:
i = choose_text(STRT_BUTTON,store_spec_node.ex2a,&me,"Which button label?");
break;
case eSpecType::TOWN_PLACE_MONST:
i = choose_text(STRT_MONST,store_spec_node.ex2a,&me,"Choose Which Monster:");
break;
case eSpecType::IF_TOWN_TER_TYPE:
case eSpecType::IF_OUT_TER_TYPE:
case eSpecType::TOWN_CHANGE_TER:
case eSpecType::TOWN_SWAP_TER:
case eSpecType::OUT_CHANGE_TER:
i = choose_text(STRT_TER,store_spec_node.ex2a,&me,"Which Terrain?");
break;
}
store_spec_node.ex2a = i;
me["x2a"].setTextToNum(store_spec_node.ex2a);
} else if(item_hit == "msg2-edit") { // TODO: What about msg1-edit?
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
if(((*store_spec_node.type).msg_label == 2) ||
((*store_spec_node.type).msg_label == 4) ||
((*store_spec_node.type).msg_label == 5)) {
edit_dialog_text(which_mode,&store_spec_node.m1,&me);
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
}
else if(((*store_spec_node.type).msg_label == 1) ||
((*store_spec_node.type).msg_label == 3)) {
edit_spec_text(which_mode,&store_spec_node.m1,
&store_spec_node.m2,&me);
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
}
} else if(item_hit == "pict-edit") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
i = -1;
switch((*store_spec_node.type).pic_label) {
case 1:
i = choose_graphic(store_spec_node.pic,PIC_DLOG,&me);
break;
case 2:
i = choose_graphic(store_spec_node.pic,PIC_TER,&me);
break;
case 3:
i = choose_graphic(store_spec_node.pic,PIC_MONST,&me);
break;
}
if(i != NO_PIC) {
store_spec_node.pic = i;
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
}
} else if(item_hit == "general") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
// TODO: I wonder if this can all be achieved without casts... if not, at least check getNodeCategory to ensure validity.
i = choose_text_res("special-node-names",1,28,int(store_spec_node.type) + 1,&me,"Choose General Use Special:");
if(i >= 0) {
store_spec_node.type = eSpecType(i - 1);
}
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
} else if(item_hit == "oneshot") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
i = choose_text_res("special-node-names",51,64,int(store_spec_node.type) + 1,&me,"Choose One-Shot Special:");
if(i >= 0) {
store_spec_node.type = eSpecType(i - 1);
store_spec_node.sd1 = -1;
store_spec_node.sd2 = -1;
if(store_spec_node.type == eSpecType::ONCE_DIALOG || store_spec_node.type == eSpecType::ONCE_DIALOG_TERRAIN || store_spec_node.type == eSpecType::ONCE_DIALOG_MONSTER)
store_spec_node.m2 = 1;
}
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
} else if(item_hit == "affectpc") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
i = choose_text_res("special-node-names",81,107,int(store_spec_node.type) + 1,&me,"Choose Affect Party Special:");
if(i >= 0) store_spec_node.type = eSpecType(i - 1);
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
} else if(item_hit == "ifthen") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
i = choose_text_res("special-node-names",131,156,int(store_spec_node.type) + 1,&me,"Choose If-Then Special:");
if(i >= 0) {
store_spec_node.type = eSpecType(i - 1);
}
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
} else if(item_hit == "town") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
i = choose_text_res("special-node-names",171,219,int(store_spec_node.type) + 1,&me,"Choose Town Special:");
if(i >= 0) store_spec_node.type = eSpecType(i - 1);
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
} else if(item_hit == "out") {
if(!save_spec_enc(me, which_mode, which_node, store_spec_node))
return true;
i = choose_text_res("special-node-names",226,230,int(store_spec_node.type) + 1,&me,"Choose Outdoor Special:");
if(i >= 0) store_spec_node.type = eSpecType(i - 1);
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
}
/*if((item_hit >= 37) && (item_hit <= 42)) {
if(cd_get_active(822,43) == 0)
CDSN(822,8,0);
if(cd_get_active(822,44) == 0)
CDSN(822,10,0);
}*/// Might be useful, but I forget what I was thinking when I added it.
if(node_to_change_to >= 0) {
which_mode = node_to_change_to / 1000;
which_node = node_to_change_to % 1000;
if(which_mode == 0)
store_spec_node = scenario.scen_specials[which_node];
if(which_mode == 1)
store_spec_node = current_terrain->specials[which_node];
if(which_mode == 2)
store_spec_node = town->specials[which_node];
if(store_spec_node.pic < 0)
store_spec_node.pic = 0;
put_spec_enc_in_dlog(me, which_node, store_spec_node, last_node);
case 'p':
choose_string = false;
if(pictype < 0) me["pictype"].setTextToNum(PIC_DLOG);
store = choose_graphic(val, pictype < 0 ? PIC_DLOG : ePicType(pictype), &me);
break;
case 'f': case 'F': choose_string = false; store = choose_field_type(val, &me, btn == 'F'); break;
case 'D': choose_string = false; store = choose_damage_type(val, &me); break;
case '!': choose_string = false; store = choose_boom_type(val, &me); break;
case 'i': strt = STRT_ITEM; title = "Which item?"; break;
case 'I': strt = STRT_SPEC_ITEM; title = "Which special item?"; break;
case 't': strt = STRT_TER; title = "Which terrain?"; break;
case 'c': strt = STRT_MONST; title = "Which monster?"; break;
case 'a': strt = STRT_ALCHEMY; title = "Which recipe?"; break;
case 'A': strt = STRT_MAGE; title = "Which spell?"; break;
case 'P': strt = STRT_PRIEST; title = "Which spell?"; break;
case 'k': case 'K': strt = STRT_SKILL; title = "Which statistic?"; break;
case 'q': strt = STRT_TRAIT; title = "Which trait?"; break;
case 'Q': strt = STRT_RACE; title = "Which species?"; break;
case 'T': strt = STRT_TOWN; title = "Which town?"; break;
case 'b': strt = STRT_BUTTON; title = "Which button?"; break;
case '?': strt = STRT_PICT; title = "Which picture type?"; break;
case 'x': strt = STRT_SND; title = "Which sound?"; break;
case 'X': strt = STRT_TRAP; title = "What trap type?"; break;
case '=': strt = STRT_CMP; title = "What comparison method?"; str_adj = 2; break;
case '+': strt = STRT_ACCUM; title = "What accumulation method?"; str_adj = 1; break;
case '@': strt = STRT_ATTITUDE; title = "What attitude?"; break;
case '/': strt = STRT_STAIR; title = "Which stairway text?"; break;
case 'L': strt = STRT_LIGHT; title = "What lighting type?"; break;
case '&': strt = STRT_SHOP; title = "What shop type?"; break;
case '%': strt = STRT_COST_ADJ; title = "What cost adjust?"; break;
case '*': strt = STRT_CONTEXT; title = "What context?"; break;
case ':': strt = STRT_STAIR_MODE; title = "Select trigger limitations:"; break;
case '#':
choose_string = false;
store = val;
giveError("Either you have not chosen a shop type yet, or the shop type you chose doesn't allow you to customize its items.");
break;
default:
choose_string = false;
store = val;
giveError("Whoops! Unknown edit type! (" + (btn + std::string(")")), "", &me);
break;
}
if(choose_string)
store = choose_text(strt, val, &me, title) - str_adj;
me[field].setTextToNum(store);
return true;
}
// mode - 0 scen 1 - out 2 - town
bool edit_spec_enc(short which_node,short mode,cDialog* parent) {
// ignore parent in Mac version
using namespace std::placeholders;
cSpecial store_spec_node;
std::stack<short> last_node;
cSpecial the_node;
node_stack_t edit_stack;
if(mode == 0)
store_spec_node = scenario.scen_specials[which_node];
if(mode == 1)
store_spec_node = current_terrain->specials[which_node];
if(mode == 2)
store_spec_node = town->specials[which_node];
if(store_spec_node.pic < 0)
store_spec_node.pic = 0;
the_node = scenario.scen_specials[which_node];
else if(mode == 1)
the_node = current_terrain->specials[which_node];
else if(mode == 2)
the_node = town->specials[which_node];
if(the_node.pic < 0)
the_node.pic = 0;
cDialog special("edit-special-node.xml",parent);
auto callback = std::bind(edit_spec_enc_event_filter, _1, _2, std::ref(mode), std::ref(which_node), std::ref(store_spec_node), std::ref(last_node));
special.attachClickHandlers(callback, {"okay", "cancel", "back"});
special.attachClickHandlers(callback, {"general", "oneshot", "affectpc", "ifthen", "town", "out"});
special.attachClickHandlers(callback, {"x1a-edit", "x1b-edit", "x2a-edit", "x2b-edit"});
special.attachClickHandlers(callback, {"msg1-edit", "msg2-edit", "pict-edit", "jump-edit"});
special.attachClickHandlers(std::bind(commit_spec_enc, _1, _2, std::ref(edit_stack)), {"okay", "back"});
special.attachClickHandlers(std::bind(edit_spec_enc_type, _1, _2, std::ref(edit_stack)), {
"general", "oneshot", "affectpc", "ifthen", "town", "out", "rect"
});
special.attachClickHandlers(std::bind(edit_spec_enc_value, _1, _2, std::ref(edit_stack)), {
"msg1-edit", "msg2-edit", "msg3-edit", "pict-edit", "pictype-edit", "jump-edit",
"x1a-edit", "x1b-edit", "x1c-edit", "x2a-edit", "x2b-edit", "x2c-edit",
});
special["cancel"].attachClickHandler(std::bind(discard_spec_enc, _1, std::ref(edit_stack)));
special["back"].hide();
put_spec_enc_in_dlog(special, which_node, store_spec_node, last_node);
edit_stack.push({which_node,mode,the_node});
put_spec_enc_in_dlog(special, edit_stack);
special.setResult(false);
special.run();
return special.getResult<bool>();
}
@@ -914,7 +929,7 @@ void edit_spec_text(short mode,short *str1,short *str2,cDialog* parent) {
short num_s_strs[3] = {100,90,100};
cDialog edit("edit-special-text.xml", parent);
edit.attachClickHandlers(std::bind(edit_spec_text_event_filter, _1, _2, mode, str1, str1), {"okay", "cancel"});
edit.attachClickHandlers(std::bind(edit_spec_text_event_filter, _1, _2, mode, str1, str2), {"okay", "cancel"});
if(*str1 >= num_s_strs[mode])
*str1 = -1;