Files
oboe/osx/pcedit/pc.editors.cpp
2014-12-14 02:51:01 -05:00

642 lines
21 KiB
C++

#include "pc.graphics.h"
#include "pc.global.h"
#include "classes.h"
#include "pc.editors.h"
#include "graphtool.h"
#include "dialog.h"
#include "control.h"
#include "button.h"
#include "dlogutil.h"
#include "winutil.h"
#include <boost/lexical_cast.hpp>
/*
* These three are not declared in any included header.
* Instead they are declared in pc.actions.h, which is not
* included here because it contains additional functions that
* should not be available to the game (which also includes
* this file).
*
* For the game's purposes, these are declared in
* boe.infodlg.h and boe.party.h.
*/
void display_pc(short pc_num,short mode,cDialog* parent);
void display_alchemy(bool allowEdit);
bool spend_xp(short pc_num, short mode, cDialog* parent);
// TODO: There's probably a more logical way of arranging this
/* Adventure globals */
//extern party_record_type party;
//extern outdoor_record_type outdoors[2][2];
//extern current_town_type c_town;
//extern big_tr_type t_d;
//extern town_item_list t_i;
//extern unsigned char out[96][96],out_e[96][96];
//extern setup_save_type setup_save;
//extern stored_items_list_type stored_items[3];
extern cUniverse univ;
extern short store_flags[3];
extern short current_active_pc;
extern sf::RenderWindow mainPtr;
extern RECT d_rects[80];
extern short d_rect_index[80];
extern bool diff_depth_ok,current_file_has_maps;
bool choice_active[6];
extern short which_pc_displayed;
cPlayer *store_pc;
sf::Texture button_num_gworld;
std::map<eSkill,short> skill_cost = {
{eSkill::STRENGTH,3}, {eSkill::DEXTERITY,3}, {eSkill::INTELLIGENCE,3},
{eSkill::EDGED_WEAPONS,2}, {eSkill::BASHING_WEAPONS,2}, {eSkill::POLE_WEAPONS,2},
{eSkill::THROWN_MISSILES,1}, {eSkill::ARCHERY,2}, {eSkill::DEFENSE,2},
{eSkill::MAGE_SPELLS,6}, {eSkill::PRIEST_SPELLS,5}, {eSkill::MAGE_LORE,1},
{eSkill::ALCHEMY,2}, {eSkill::ITEM_LORE,4}, {eSkill::DISARM_TRAPS,2},
{eSkill::LOCKPICKING,1}, {eSkill::ASSASSINATION,4}, {eSkill::POISON,2},
{eSkill::LUCK,5},
};
std::map<eSkill,short> skill_max = {
{eSkill::STRENGTH,20}, {eSkill::DEXTERITY,20}, {eSkill::INTELLIGENCE,20},
{eSkill::EDGED_WEAPONS,20}, {eSkill::BASHING_WEAPONS,20}, {eSkill::POLE_WEAPONS,20},
{eSkill::THROWN_MISSILES,20}, {eSkill::ARCHERY,20}, {eSkill::DEFENSE,20},
{eSkill::MAGE_SPELLS,7}, {eSkill::PRIEST_SPELLS,7}, {eSkill::MAGE_LORE,20},
{eSkill::ALCHEMY,20}, {eSkill::ITEM_LORE,10}, {eSkill::DISARM_TRAPS,20},
{eSkill::LOCKPICKING,20}, {eSkill::ASSASSINATION,20}, {eSkill::POISON,20},
{eSkill::LUCK,20},
};
std::map<eSkill,short> skill_g_cost = {
{eSkill::STRENGTH,50}, {eSkill::DEXTERITY,503}, {eSkill::INTELLIGENCE,50},
{eSkill::EDGED_WEAPONS,40}, {eSkill::BASHING_WEAPONS,40}, {eSkill::POLE_WEAPONS,40},
{eSkill::THROWN_MISSILES,30}, {eSkill::ARCHERY,50}, {eSkill::DEFENSE,40},
{eSkill::MAGE_SPELLS,250}, {eSkill::PRIEST_SPELLS,250}, {eSkill::MAGE_LORE,25},
{eSkill::ALCHEMY,100}, {eSkill::ITEM_LORE,200}, {eSkill::DISARM_TRAPS,30},
{eSkill::LOCKPICKING,20}, {eSkill::ASSASSINATION,100}, {eSkill::POISON,80},
{eSkill::LUCK,0},
};
// The index here is the skill's level, not the skill itself; thus 20 is the max index since no spell can go above 20.
short skill_bonus[21] = {
-3,-3,-2,-1,0,0,1,1,1,2,
2,2,3,3,3,3,4,4,4,5,5};
// These are the IDs used to refer to the skills in the dialog
const char* skill_ids[19] = {
"str","dex","int",
"edge","bash","pole","thrown","bow","def",
"mage","priest","lore","alch","item",
"trap","lock","assassin","poison","luck"
};
// Variables for spending xp
bool talk_done = false;
long val_for_text;
bool keep_change = false;
short store_h,store_sp,i,store_skp;
long store_g;
short store_train_mode,store_train_pc;
static bool select_pc_event_filter (cDialog& me, std::string item_hit, eKeyMod) {
me.toast(true);
if(item_hit != "cancel") {
short which_pc = item_hit[item_hit.length() - 1] - '1';
me.setResult<short>(which_pc);
} else me.setResult<short>(6);
return true;
}
short char_select_pc(short active_only,short free_inv_only,const char *title)
//active_only; // 0 - no 1 - yes 2 - disarm trap
{
short item_hit,i;
make_cursor_sword();
cDialog selectPc("select-pc.xml");
selectPc.attachClickHandlers(select_pc_event_filter, {"cancel", "pick1", "pick2", "pick3", "pick4", "pick5", "pick6"});
selectPc["title"].setText(title);
for (i = 0; i < 6; i++) {
std::string n = boost::lexical_cast<std::string>(i + 1);
if(univ.party[i].main_status == eMainStatus::ABSENT ||
(active_only && univ.party[i].main_status > eMainStatus::ALIVE) ||
(free_inv_only == 1 && pc_has_space(i) == 24) || univ.party[i].main_status == eMainStatus::FLED) {
selectPc["pick" + n].hide();
}
// TODO: Wouldn't this lead to blank name fields for non-active characters if those characters are allowed?
if(univ.party[i].main_status != eMainStatus::ABSENT) {
selectPc["pc" + n].setText(univ.party[i].name);
}
else selectPc["pc" + n].hide();
}
selectPc.run();
item_hit = selectPc.getResult<short>();
return item_hit;
}
short select_pc(short active_only,short free_inv_only)
//active_only; // 0 - no 1 - yes 2 - disarm trap
{
if (active_only == 2)
return char_select_pc(active_only,free_inv_only,"Trap! Who will disarm?");
else return char_select_pc(active_only,free_inv_only,"Select a character:");
}
static short party_total_level()
{
short i,j = 0;
for (i = 0; i < 6; i++)
if(univ.party[i].main_status == eMainStatus::ALIVE)
j += univ.party[i].level;
return j;
}
static void put_pc_spells(cDialog& me, const short store_trait_mode) {
short i;
for (i = 0; i < 62; i++) {
std::string id = "spell" + boost::lexical_cast<std::string>(i + 1);
cLed& cur = dynamic_cast<cLed&>(me[id]);
if (((store_trait_mode == 0) && univ.party[which_pc_displayed].mage_spells[i]) ||
((store_trait_mode == 1) && univ.party[which_pc_displayed].priest_spells[i]))
cur.setState(led_red);
else cur.setState(led_off);
}
me["who"].setText(univ.party[which_pc_displayed].name.c_str());
}
static bool display_pc_event_filter(cDialog& me, std::string item_hit, const short trait_mode) {
short pc_num;
pc_num = which_pc_displayed;
if(item_hit == "done") {
me.toast(true);
} else if(item_hit == "left") {
do {
pc_num = (pc_num == 0) ? 5 : pc_num - 1;
} while(univ.party[pc_num].main_status == eMainStatus::ABSENT);
which_pc_displayed = pc_num;
put_pc_spells(me, trait_mode);
} else if(item_hit == "right") {
do {
pc_num = (pc_num == 5) ? 0 : pc_num + 1;
} while(univ.party[pc_num].main_status == eMainStatus::ABSENT);
which_pc_displayed = pc_num;
put_pc_spells(me, trait_mode);
}
return true;
}
void display_pc(short pc_num,short mode, cDialog* parent) {
using namespace std::placeholders;
short i;
std::string label_str;
if(univ.party[pc_num].main_status == eMainStatus::ABSENT) {
for (pc_num = 0; pc_num < 6; pc_num++)
if(univ.party[pc_num].main_status == eMainStatus::ALIVE)
break;
}
which_pc_displayed = pc_num;
make_cursor_sword();
cDialog pcInfo("pc-spell-info.xml", parent);
pcInfo.attachClickHandlers(std::bind(display_pc_event_filter, _1, _2, mode),{"done","left","right"});
for (i = 0; i < 62; i++) {
std::string id = "spell" + boost::lexical_cast<std::string>(i + 1);
label_str = get_str((mode == 0) ? "mage-spells" : "priest-spells",i * 2 + 1);
pcInfo[id].setText(label_str);
}
put_pc_spells(pcInfo, mode);
dynamic_cast<cPict&>(pcInfo["pic"]).setPict(14 + mode,PIC_DLOG);
pcInfo.run();
}
static void display_traits_graphics(cDialog& me)
{
short i,store;
std::string race = "race" + boost::lexical_cast<std::string>(int(store_pc->race) + 1);
dynamic_cast<cLedGroup&>(me["race"]).setSelected(race);
for (i = 0; i < 10; i++) {
std::string id = "good" + boost::lexical_cast<std::string>(i + 1);
eTrait trait = eTrait(i);
dynamic_cast<cLed&>(me[id]).setState((store_pc->traits[trait] > 0) ? led_red : led_off);
}
for (i = 0; i < 5; i++) {
// TODO: Pacifist
std::string id = "bad" + boost::lexical_cast<std::string>(i + 1);
eTrait trait = eTrait(i + 10);
dynamic_cast<cLed&>(me[id]).setState((store_pc->traits[trait] > 0) ? led_red : led_off);
}
store = store_pc->get_tnl();
me["xp"].setTextToNum(store);
}
static bool pick_race_select_led(cDialog& me, std::string item_hit, bool losing, const short store_trait_mode) {
if(losing) return true;
std::string abil_str;
cPlayer *pc;
pc = store_pc;
if(item_hit == "race") {
eRace race;
switch(item_hit[4] - '1') {
case 0: race = eRace::HUMAN; break;
case 1: race = eRace::NEPHIL; break;
case 2: race = eRace::SLITH; break;
case 3: race = eRace::VAHNATAI; break;
}
if(store_trait_mode == 0)
pc->race = race;
display_traits_graphics(me);
abil_str = get_str("traits",16 + int(race));
me["info"].setText(abil_str);
} else if(item_hit.substr(0,3) == "bad") {
int hit = item_hit[3] - '1';
eTrait trait = eTrait(hit + 10);
if(store_trait_mode != 1)
pc->traits[trait] = !pc->traits[trait];
display_traits_graphics(me);
abil_str = get_str("traits",hit + 11);
me["info"].setText(abil_str);
} else if(item_hit.substr(0,4) == "good") {
int hit = item_hit[4] - '1';
eTrait trait = eTrait(hit);
if(store_trait_mode != 1)
pc->traits[trait] = !pc->traits[trait];
display_traits_graphics(me);
abil_str = get_str("traits",hit + 1);
me["info"].setText(abil_str);
}
return store_trait_mode == 0;
}
void pick_race_abil(cPlayer *pc,short mode)
//mode; // 0 - edit 1 - just display 2 - can't change race
{
using namespace std::placeholders;
static const char*const start_str1 = "Click on button by name for description.";
static const char*const start_str2 = "Click on advantage button to add/remove.";
store_pc = pc;
make_cursor_sword();
cDialog pickAbil("pick-race-abil.xml");
pickAbil["done"].attachClickHandler(std::bind(&cDialog::toast, &pickAbil, true));
auto led_selector = std::bind(pick_race_select_led, _1, _2, _3, mode);
pickAbil.attachFocusHandlers(led_selector, {"race", "bad1", "bad2", "bad3", "bad4", "bad5"});
pickAbil.attachFocusHandlers(led_selector, {"good1", "good2", "good3", "good4", "good5"});
pickAbil.attachFocusHandlers(led_selector, {"good6", "good7", "good8", "good9", "good10"});
display_traits_graphics(pickAbil);
if (mode == 1)
pickAbil["info"].setText(start_str1);
else pickAbil["info"].setText(start_str2);
pickAbil.run();
}
const char* alch_names[20] = {
"Weak Curing Potion (1)","Weak Healing Potion (1)","Weak Poison (1)",
"Weak Speed Potion (3)","Medium Poison (3)",
"Medium Heal Potion (4)","Strong Curing (5)","Medium Speed Potion (5)",
"Graymold Salve (7)","Weak Energy Potion (9)",
"Potion of Clarity (9)","Strong Poison (10)","Strong Heal Potion (12)","Killer Poison (12)",
"Resurrection Balm (9)","Medium Energy Ptn. (14)","Knowledge Brew (19)" ,
"Strong Strength (10)","Bliss (16)","Strong Power (20)"
};
void display_alchemy(bool allowEdit)
{
short i;
make_cursor_sword();
cChoiceDlog showAlch("pc-alchemy-info.xml", {"done"});
for(i = 0; i < 20; i++) {
std::string id = "potion" + boost::lexical_cast<std::string>(i + 1);
showAlch->addLabelFor(id, alch_names[i], LABEL_LEFT, 83, true);
if(!allowEdit)
showAlch->getControl(id).attachClickHandler(&cLed::noAction);
cLed& led = dynamic_cast<cLed&>(showAlch->getControl(id));
if (univ.party.alchemy[i] > 0)
led.setState(led_red);
else led.setState(led_off);
}
showAlch.show();
if(!allowEdit) return;
for(i = 0; i < 20; i++) {
std::string id = "potion" + boost::lexical_cast<std::string>(i + 1);
cLed& led = dynamic_cast<cLed&>(showAlch->getControl(id));
if(led.getState() == led_red) univ.party.alchemy[i] = true;
else univ.party.alchemy[i] = false;
}
}
// TODO: This dialog needs some kind of context system really badly to avoid the rampant globals
// MARK: Start spend XP dialog
static void do_xp_keep(short pc_num,short mode,std::map<eSkill,short>& store_skills)
{
for(i = 0; i < 19; i++) {
eSkill skill = eSkill(i);
univ.party[pc_num].skills[skill] = store_skills[skill];
}
univ.party[pc_num].cur_health += store_h - univ.party[pc_num].max_health;
univ.party[pc_num].max_health = store_h;
univ.party[pc_num].cur_sp += store_sp - univ.party[pc_num].max_sp;
univ.party[pc_num].max_sp = store_sp;
if (mode == 1)
univ.party.gold = store_g;
univ.party[pc_num].skill_pts = store_skp;
}
static void draw_xp_skills(cDialog& me,std::map<eSkill,short>& store_skills)
{
short i;
// TODO: Wouldn't it make more sense for it to be red when you can't buy the skill rather than red when you can?
for (i = 0; i < 19; i++) {
cControl& cur = me[skill_ids[i]];
eSkill skill = eSkill(i);
if ((store_skp >= skill_cost[skill]) && (store_g >= skill_g_cost[skill]))
cur.setColour(sf::Color::Red);
else cur.setColour(me.getDefTextClr());
cur.setTextToNum(store_skills[skill]);
}
cControl& sp = me["sp"];
cControl& hp = me["hp"];
if ((store_skp >= 1) && (store_g >= 10))
hp.setColour(sf::Color::Red);
else hp.setColour(me.getDefTextClr());
hp.setTextToNum(store_h);
if ((store_skp >= 1) && (store_g >= 15))
sp.setColour(sf::Color::Red);
else sp.setColour(me.getDefTextClr());
sp.setTextToNum(store_sp);
}
static void update_gold_skills(cDialog& me) {
me["gold"].setTextToNum(((store_train_mode == 0) ? 0 : store_g));
me["skp"].setTextToNum(store_skp);
}
static void do_xp_draw(cDialog& me,std::map<eSkill,short>& store_skills) {
char get_text[256];
short mode,pc_num;
mode = store_train_mode;
pc_num = store_train_pc;
if (mode == 0) {
if(univ.party[pc_num].main_status == eMainStatus::ALIVE)
sprintf((char *) get_text, "%s",(char *) univ.party[pc_num].name.c_str());
else sprintf((char *) get_text, "New PC");
}
else sprintf((char *) get_text, "%s",(char *) univ.party[pc_num].name.c_str());
me["recipient"].setText(get_text);
for(i = 0; i < 19; i++) {
eSkill skill = eSkill(i);
store_skills[skill] = univ.party[pc_num].skills[skill];
}
store_h = univ.party[pc_num].max_health;
store_sp = univ.party[pc_num].max_sp;
store_g = (mode == 0) ? 20000 : univ.party.gold;
store_skp = univ.party[pc_num].skill_pts;
draw_xp_skills(me,store_skills);
update_gold_skills(me);
}
static bool spend_xp_navigate_filter(cDialog& me, std::string item_hit,std::map<eSkill,short>& store_skills) {
short mode,pc_num;
bool talk_done = false;
mode = store_train_mode;
pc_num = store_train_pc;
if(item_hit == "cancel") {
// TODO: Um, I'm pretty sure this can never happen.
if(mode == 0 && univ.party[pc_num].main_status < eMainStatus::ABSENT)
univ.party[pc_num].main_status = eMainStatus::ABSENT;
me.setResult(false);
talk_done = true;
} else if(item_hit == "help") {
univ.party.help_received[10] = 0;
// give_help(210,11,me);
} else if(item_hit == "keep") {
do_xp_keep(pc_num,mode,store_skills);
me.setResult(true);
talk_done = true;
} else if(item_hit == "left") {
// TODO: Try not forcing a commit when using the arrows?
if (mode != 0) {
do_xp_keep(pc_num,mode,store_skills);
do {
pc_num = (pc_num == 0) ? 5 : pc_num - 1;
} while (univ.party[pc_num].main_status != eMainStatus::ALIVE);
store_train_pc = pc_num;
do_xp_draw(me,store_skills);
} else
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
} else if(item_hit == "right") {
// TODO: If they don't work in mode 0, why are they visible?
if (mode != 0) {
do_xp_keep(pc_num,mode,store_skills);
do {
pc_num = (pc_num == 5) ? 0 : pc_num + 1;
} while (univ.party[pc_num].main_status != eMainStatus::ALIVE);
store_train_pc = pc_num;
do_xp_draw(me,store_skills);
} else
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
}
store_train_pc = pc_num;
if (talk_done == true) {
me.toast(item_hit == "keep");
}
return true;
}
static bool spend_xp_event_filter(cDialog& me, std::string item_hit, eKeyMod mods,std::map<eSkill,short>& store_skills) {
short mode = store_train_mode, pc_num = store_train_pc;
if(item_hit.substr(0,2) == "hp") {
if(mod_contains(mods, mod_alt)) {
cStrDlog aboutHP(get_str("help",63),"","About Health",24,PIC_DLOG,&me);
aboutHP.setSound(57);
aboutHP.show();
} else if (((store_h >= 250) && (item_hit[3] == 'p')) ||
((store_h == univ.party[pc_num].max_health) && (item_hit[3] == 'm') && (mode == 1)) ||
((store_h == 6) && (item_hit[3] == 'm') && (mode == 0)))
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
else if(item_hit == "hp-m") {
store_g += 10;
store_h -= 2;
store_skp += 1;
}
else {
if ((store_g < 10) || (store_skp < 1)) {
// if (store_g < 10)
// give_help(24,0,me);
// else give_help(25,0,me);
}
else {
store_g -= 10;
store_h += 2;
store_skp -= 1;
}
}
update_gold_skills(me);
me["hp"].setTextToNum(store_h);
draw_xp_skills(me,store_skills);
} else if(item_hit.substr(0,2) == "sp") {
if(mod_contains(mods, mod_alt)) {
cStrDlog aboutSP(get_str("help",64),"","About Spell Points",24,PIC_DLOG,&me);
aboutSP.setSound(57);
aboutSP.show();
} else if (((store_sp >= 150) && (item_hit[3] == 'p')) ||
((store_sp == univ.party[pc_num].max_sp) && (item_hit[3] == 'm') && (mode == 1)) ||
((store_sp == 0) && (item_hit[3] == 'm') && (mode == 0)))
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
else if(item_hit == "sp-m") {
store_g += 15;
store_sp -= 1;
store_skp += 1;
}
else {
if ((store_g < 15) || (store_skp < 1)) {
// if (store_g < 15)
// give_help(24,0,me);
// else give_help(25,0,me);
}
else {
store_sp += 1;
store_g -= 15;
store_skp -= 1;
}
}
update_gold_skills(me);
me["sp"].setTextToNum(store_sp);
draw_xp_skills(me,store_skills);
} else {
eSkill which_skill = eSkill::INVALID;
for(int i = 0; i < 19; i++) {
size_t n = strlen(skill_ids[i]);
if(item_hit.length() < n + 2) continue;
if(item_hit.substr(0, item_hit.length() - 2) == skill_ids[i]) {
which_skill = eSkill(i);
break;
}
}
if(which_skill == eSkill::INVALID) return true;
/* if(mod_contains(mods, mod_alt)) display_skills(which_skill,&me);
else */{
char dir = item_hit[item_hit.length() - 1];
// TODO: This is a game event, so it should have a game sound, not a system alert.
if(store_skills[which_skill] >= skill_max[which_skill] && dir == 'p')
beep();
else if(store_skills[which_skill] == univ.party[pc_num].skills[which_skill] && dir == 'm' && mode == 1)
beep();
else if(store_skills[which_skill] == 0 && dir == 'm' && mode == 0 &&
which_skill != eSkill::STRENGTH && which_skill != eSkill::DEXTERITY && which_skill != eSkill::INTELLIGENCE)
beep();
else if(store_skills[which_skill] == 1 && dir == 'm' && mode == 0 &&
(which_skill == eSkill::STRENGTH || which_skill == eSkill::DEXTERITY || which_skill == eSkill::INTELLIGENCE))
beep();
else {
if(dir == 'm') {
store_g += skill_g_cost[which_skill];
store_skills[which_skill] -= 1;
store_skp += skill_cost[which_skill];
}
else {
if ((store_g < skill_g_cost[which_skill]) || (store_skp < skill_cost[which_skill])) {
// if (store_g < skill_g_cost[which_skill])
// give_help(24,0,me);
// else give_help(25,0,me);
}
else {
store_skills[which_skill] += 1;
store_g -= skill_g_cost[which_skill];
store_skp -= skill_cost[which_skill];
}
}
update_gold_skills(me);
me[skill_ids[int(which_skill)]].setTextToNum(store_skills[which_skill]);
draw_xp_skills(me,store_skills);
}
}
}
return true;
}
bool spend_xp(short pc_num, short mode, cDialog* parent)
//short mode; // 0 - create 1 - train
// returns 1 if cancelled
{
using namespace std::placeholders;
store_train_pc = pc_num;
store_train_mode = mode;
make_cursor_sword();
std::map<eSkill,short> skills = univ.party[pc_num].skills;
cDialog xpDlog("spend-xp.xml",parent);
xpDlog.addLabelFor("hp","Health (1/10)",LABEL_LEFT,75,true);
xpDlog.addLabelFor("sp","Spell Pts. (1/15)",LABEL_LEFT,75,true);
auto spend_xp_filter = std::bind(spend_xp_event_filter,_1,_2,_3,std::ref(skills));
std::string minus = "-m", plus = "-p";
for (i = 54; i < 73; i++) {
std::ostringstream sout;
eSkill skill = eSkill(i - 54);
sout << get_str("skills",1 + 2 * (i - 54)) << ' ' << '(';
sout << skill_cost[skill] << '/' << skill_g_cost[skill] << ')';
xpDlog.addLabelFor(skill_ids[i - 54],sout.str(),LABEL_LEFT,(i < 63) ? 75 : 69,true);
xpDlog[skill_ids[i - 54] + minus].attachClickHandler(spend_xp_filter);
xpDlog[skill_ids[i - 54] + plus].attachClickHandler(spend_xp_filter);
}
do_xp_draw(xpDlog,skills);
xpDlog.attachClickHandlers(std::bind(spend_xp_navigate_filter,_1,_2,std::ref(skills)),{"keep","cancel","left","right","help"});
xpDlog.attachClickHandlers(spend_xp_filter,{"sp-m","sp-p","hp-m","hp-p"});
if (univ.party.help_received[10] == 0) {
// TODO: Is an initial draw even needed?
// cd_initial_draw(1010);
// give_help(10,11,xpDlog);
}
xpDlog.run();
return xpDlog.getResult<bool>();
}