Get the edit monster abilities dialog all working and updated (except for editing individual abilities)

Game changes:
- Remove support for playing a sound and displaying strings when a monster is first seen, since these behaviours can easily be obtained by using the special node called in the same context.
Dialog engine changes:
- Support for changing the font size of LEDs
- When setting the page, a stack now applies a default value if the map is missing the required data
- Add utility "addPage" method to stacks
- If reducing the page count means the current page is deleted, a stack now switches to the last page
This commit is contained in:
2015-01-18 17:25:08 -05:00
parent 1441f6bfe9
commit 7084991e55
14 changed files with 157 additions and 80 deletions

View File

@@ -5,31 +5,18 @@
<!--
TODO: Assign numeric types (type = 'int' or 'uint') to fields as appropriate
-->
<field name='poison' top='53' left='206' width='52' height='16'/>
<field name='breath-str' top='77' left='206' width='52' height='16'/>
<field name='abil-xtra' top='204' left='382' width='53' height='17'/>
<field name='loot-item' top='246' left='194' width='64' height='16'/>
<field name='loot-chance' top='247' left='464' width='64' height='16'/>
<button name='okay' type='regular' top='325' left='572'>OK</button>
<button name='cancel' type='regular' top='325' left='506' def-key='esc'>Cancel</button>
<!--
TODO: Originally these LED labels would've been bold, I think
Note: These LEDs were shifted left 100 pixels to account for the labels swapping sides
-->
<group name='breath-type'>
<led name='fire' state='off' top='82' left='394' width='100'>Fire</led>
<led name='cold' state='off' top='97' left='394' width='100'>Cold</led>
<led name='shock' state='off' top='112' left='394' width='100'>Electricity</led>
<led name='dark' state='off' top='127' left='394' width='100'>Darkness</led>
</group>
<led name='magic-res' state='off' top='283' left='167' width='100'>Resist Magic</led>
<led name='fire-res' state='off' top='298' left='167' width='100'>Resist Fire</led>
<led name='cold-res' state='off' top='313' left='167' width='100'>Resist Cold</led>
<led name='poison-res' state='off' top='328' left='167' width='100'>Resist Poison</led>
<led name='magic-imm' state='off' top='283' left='320' width='100'>Immune To Magic</led>
<led name='fire-imm' state='off' top='298' left='320' width='100'>Immune To Fire</led>
<led name='cold-imm' state='off' top='313' left='320' width='100'>Immune To Cold</led>
<led name='poison-imm' state='off' top='328' left='320' width='100'>Immune To Poison</led>
<field name='loot-item' top='178' left='194' width='64' height='16'/>
<field name='loot-chance' top='178' left='464' width='64' height='16'/>
<text top='216' left='120' width='40' height='16'>Magic:</text>
<field name='magic-res' top='215' left='167' width='80' height='16'/>
<text top='246' left='120' width='40' height='16'>Fire:</text>
<field name='fire-res' top='245' left='167' width='80' height='16'/>
<text top='216' left='273' width='40' height='16'>Cold:</text>
<field name='cold-res' top='215' left='320' width='80' height='16'/>
<text top='246' left='273' width='40' height='16'>Poison:</text>
<field name='poison-res' top='245' left='320' width='80' height='16'/>
<led name='mindless' top='215' left='415' width='70'>Mindless</led>
<led name='invuln' top='245' left='415' width='70'>Invulnerable</led>
<pict type='dlog' num='16' top='8' left='8'/>
<text size='large' top='6' left='50' width='158' height='16'>Edit Monster Abilities</text>
<text framed='true' top='6' left='222' width='111' height='14'>Monster number:</text>
@@ -38,29 +25,46 @@
Enter properties for this monster type. For a detailed
description of the fields, see the documentation.
</text>
<text top='54' left='7' width='179' height='14'>Monster Poison: (0 - 8)</text>
<text top='77' left='7' width='190' height='68'>Monster Breath Weapon strength (0 - 40):</text>
<text top='78' left='268' width='131' height='14'>Breath weapon type:</text>
<text size='large' top='152' left='7' width='120' height='14'>Special ability:</text>
<text name='abil-name' framed='true' top='152' left='184' width='183' height='14'/>
<button name='abils' type='regular' top='149' left='374'>Choose</button>
<text size='large' top='178' left='7' width='166' height='15'>Create monsters/fields:</text>
<text name='radiate-name' framed='true' top='178' left='184' width='183' height='14'/>
<button name='radiate' type='regular' top='175' left='374'>Choose</button>
<text top='205' left='54' width='118' height='16'/>
<text name='radiate-param' framed='true' top='205' left='180' width='195' height='14'/>
<text size='large' top='228' left='7' width='158' height='16'>Special treasure:</text>
<text top='247' left='23' width='162' height='14'>Item to drop when killed:</text>
<text top='247' left='270' width='186' height='14'>Chance of dropping: (0-100)</text>
<text top='279' left='9' width='141' height='14'>Monster resistances:</text>
<!--
Note: These LEDs were shifted left 80 pixels to account for the labels swapping sides
-->
<text size='large' top='54' left='7' width='110' height='14'>Special abilities:</text>
<stack name='abils'>
<text name='abil-name1' framed='true' top='54' left='124' width='183' height='14'/>
<text name='abil-name2' framed='true' top='80' left='124' width='183' height='14'/>
<text name='abil-name3' framed='true' top='106' left='124' width='183' height='14'/>
<text name='abil-name4' framed='true' top='132' left='124' width='183' height='14'/>
<button name='abil-edit1' type='regular' top='51' left='314'>Add</button>
<button name='abil-edit2' type='regular' top='77' left='314'>Add</button>
<button name='abil-edit3' type='regular' top='103' left='314'>Add</button>
<button name='abil-edit4' type='regular' top='129' left='314'>Add</button>
</stack>
<button name='abil-up' type='up' top='82' left='30'/>
<button name='abil-down' type='down' top='107' left='30'/>
<text size='large' top='160' left='7' width='158' height='16'>Special treasure:</text>
<text top='179' left='23' width='162' height='14'>Item to drop when killed:</text>
<text top='179' left='270' width='186' height='14'>Chance of dropping: (0-100)</text>
<text top='211' left='9' width='108' height='112'>Monster resistances:<br/><br/>
This is the % of full damage the monster takes when of this type.<br/>
e.g. 0 - no damage, 100 - normal, 200 - double
</text>
<text top='81' left='420' width='94' height='14'>Summon type:</text>
<group name='summon'>
<led name='s0' state='off' top='153' left='525' width='100'>No Summon</led>
<led name='s1' state='off' top='168' left='525' width='100'>Type 1</led>
<led name='s2' state='off' top='183' left='525' width='100'>Type 2</led>
<led name='s3' state='off' top='198' left='525' width='100'>Type 3</led>
<led name='s0' state='off' top='85' left='505' width='100'>Weak (no summon)</led>
<led name='s1' state='off' top='100' left='505' width='100'>Type 1</led>
<led name='s2' state='off' top='115' left='505' width='100'>Type 2</led>
<led name='s3' state='off' top='130' left='505' width='100'>Type 3</led>
<led name='s4' state='off' top='145' left='505' width='100'>Unique (no summon)</led>
</group>
<text top='149' left='440' width='94' height='14'>Summon type:</text>
<led name='invis' size='small' top='280' left='120' width='450'>
This monster is naturally and permanently invisible.
</led>
<led name='guard' size='small' top='296' left='120' width='450'>
This monster is a guard - when the town goes hostile, it will hunt the party down.
</led>
<text top='331' left='10' width='250' height='16'>Special node to call when monster first seen</text>
<field name='onsee' top='330' left='270' width='50' height='16'/>
<button name='edit-see' type='large' top='327' left='330'>Create/Edit</button>
<text top='361' left='10' width='250' height='16'>Monster vocalization sound</text>
<field name='snd' top='360' left='270' width='50' height='16'/>
<button name='pick-snd' type='regular' top='357' left='330'>Choose</button>
<button name='okay' type='regular' top='385' left='572'>OK</button>
<button name='cancel' type='regular' top='385' left='506' def-key='esc'>Cancel</button>
</dialog>

View File

@@ -229,24 +229,11 @@ void draw_monsters() {
}
void play_see_monster_str(unsigned short m, location monst_loc) {
short str1, str2, pic, snd, spec;
short pic, spec;
ePicType type;
str1 = univ.scenario.scen_monsters[m].see_str1;
str2 = univ.scenario.scen_monsters[m].see_str2;
pic = univ.scenario.scen_monsters[m].picture_num;
type = get_monst_pictype(m);
snd = univ.scenario.scen_monsters[m].see_sound;
spec = univ.scenario.scen_monsters[m].see_spec;
// First display strings, if any
if((str1 >= 0 && str1 < 100) || (str2 >= 0 && str2 < 100)) {
short where1 = is_out() ? univ.party.outdoor_corner.x + univ.party.i_w_c.x : univ.town.num;
short where2 = is_out() ? univ.party.outdoor_corner.y + univ.party.i_w_c.y : univ.town.num;
std::string placename = is_out() ? univ.out->out_name : univ.town->town_name;
cStrDlog display_strings(str1 < 100 ? univ.scenario.spec_strs[str1] : "", str2 < 100 ? univ.scenario.spec_strs[str2] : "", "", pic, type, NULL);
display_strings.setSound(snd);
display_strings.setRecordHandler(cStringRecorder(NOTE_SCEN).string1(str1).string2(str2).from(where1,where2).at(placename));
display_strings.show();
}
// Then run the special, if any
if(spec > -1){
queue_special(eSpecCtx::SEE_MONST, 0, spec, monst_loc);

View File

@@ -132,8 +132,6 @@ void cMonster::append(legacy::monster_record_type& old){
picture_num = old.picture_num;
if(picture_num == 122) picture_num = 119;
see_spec = -1;
see_str1 = see_str2 = -1;
see_sound = 0;
ambient_sound = 0;
}
@@ -371,8 +369,6 @@ void cMonster::addAbil(eMonstAbilTemplate what, int param) {
cMonster::cMonster(){
magic_res = poison_res = fire_res = cold_res = 100;
// TODO: Fill in
see_str1 = -1;
see_str2 = -1;
see_spec = -1;
}
@@ -792,17 +788,14 @@ void cMonster::writeTo(std::ostream& file) const {
}
void cMonster::readFrom(std::istream& file) {
// On-see event is not exported, so make sure the fields are not filled with garbage data
see_sound = 0;
see_str1 = -1;
see_str2 = -1;
// On-see event is not exported, so make sure the field ise not filled with garbage data
see_spec = -1;
std::istringstream bin;
std::string cur;
getline(file, cur, '\f');
bin.str(cur);
while(bin) {
int temp1, temp2, temp3;
int temp1, temp2;
getline(bin, cur);
std::istringstream line(cur);
line >> cur;

View File

@@ -140,8 +140,7 @@ public:
unsigned char summon_type;
pic_num_t default_facial_pic;
pic_num_t picture_num;
str_num_t see_str1, see_str2;
snd_num_t see_sound, ambient_sound; // ambient_sound has a chance of being played every move
snd_num_t ambient_sound; // has a chance of being played every move
spec_num_t see_spec;
public:

View File

@@ -225,11 +225,13 @@ bool cLed::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
void cLed::setFormat(eFormat prop, short val) throw(xUnsupportedProp){
if(prop == TXT_FONT) textFont = (eFont) val;
else if(prop == TXT_SIZE) textSize = val;
else throw xUnsupportedProp(prop);
}
short cLed::getFormat(eFormat prop) throw(xUnsupportedProp){
if(prop == TXT_FONT) return textFont;
else if(prop == TXT_SIZE) return textSize;
else throw xUnsupportedProp(prop);
}
@@ -240,8 +242,8 @@ void cLed::draw(){
if(visible){
TextStyle style;
style.pointSize = 9;
style.lineHeight = 8;
style.pointSize = textSize;
style.lineHeight = textSize - 1;
from_rect = ledRects[state][depressed];
to_rect = frame;
to_rect.right = to_rect.left + 14;
@@ -265,6 +267,7 @@ void cLed::restore(storage_t to) {
cButton::restore(to);
if(to.find("led-state") != to.end())
setState(boost::any_cast<eLedState>(to["led-state"]));
else setState(led_off);
}
cLedGroup::cLedGroup(cDialog* parent) :
@@ -501,4 +504,5 @@ void cLedGroup::restore(storage_t to) {
cControl::restore(to);
if(to.find("led-select") != to.end())
setSelected(boost::any_cast<std::string>(to["led-select"]));
else setSelected("");
}

View File

@@ -326,4 +326,5 @@ cControl::storage_t cControl::store() {
void cControl::restore(storage_t to) {
if(to.find("text") != to.end())
lbl = boost::any_cast<std::string>(to["text"]);
else lbl = "";
}

View File

@@ -583,7 +583,7 @@ template<> pair<string,cLed*> cDialog::parse(Element& who /*LED*/){
p.second->setFormat(TXT_SIZE, 12);
else if(val == "small")
p.second->setFormat(TXT_SIZE, 10);
if(val == "title")
else if(val == "title")
p.second->setFormat(TXT_SIZE, 18);
else throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname);
}else if(name == "color" || name == "colour"){

View File

@@ -493,6 +493,8 @@ void cTextField::restore(storage_t to) {
cControl::restore(to);
if(to.find("fld-ip") != to.end())
insertionPoint = boost::any_cast<int>(to["fld-ip"]);
else insertionPoint = getText().length();
if(to.find("fld-sp") != to.end())
selectionPoint = boost::any_cast<int>(to["fld-sp"]);
else selectionPoint = 0;
}

View File

@@ -1128,6 +1128,8 @@ void cPict::restore(storage_t to) {
cControl::restore(to);
if(to.find("pic-num") != to.end())
picNum = boost::any_cast<pic_num_t>(to["pic-num"]);
else picNum = -1;
if(to.find("pic-type") != to.end())
picType = boost::any_cast<ePicType>(to["pic-type"]);
else picNum = PIC_DLOG;
}

View File

@@ -199,4 +199,5 @@ void cScrollbar::restore(storage_t to) {
cControl::restore(to);
if(to.find("scroll-pos") != to.end())
pos = boost::any_cast<int>(to["scroll-pos"]);
else pos = 0;
}

View File

@@ -96,10 +96,16 @@ size_t cStack::getPage() {
}
void cStack::setPageCount(size_t n) {
if(curPage >= n && n > 0)
setPage(nPages - 1);
nPages = n;
storage.resize(nPages);
}
void cStack::addPage() {
setPageCount(getPageCount() + 1);
}
size_t cStack::getPageCount() {
return nPages;
}
@@ -142,5 +148,5 @@ void cStack::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reve
}
}
cStack::cStack(cDialog& parent) : cControl(CTRL_STACK, parent) {}
cStack::cStack(cDialog& parent) : cControl(CTRL_STACK, parent), curPage(0), nPages(0) {}

View File

@@ -63,7 +63,11 @@ public:
/// Set the number of pages in the stack.
/// @param n The new number of pages
/// @note If you reduce the number of pages, some data will be destroyed.
/// @note If the current page is deleted by this function, the last page will be shown, if there are any left.
void setPageCount(size_t n);
/// Add a new page to the end of the stack.
/// This is equivalent to calling setPageCount(getPageCount() + 1).
void addPage();
// Get the number of pages in the stack.
/// @return The number of pages
size_t getPageCount();

View File

@@ -127,7 +127,7 @@ The `<led>` tag accepts the following attributes:
Attributes** above.
* `state` - Specifies the starting state of the LED. Can be one of
`red`, `green`, or `off`; defaults to `off`.
* `font`, `size`, `color`, `colour` - See **Common Attributes** above.
* `font`, `size`, `color`, `colour` - See **Common Attributes** above. Note that, for an LED, omitting the size attribute gives a different result than any of the possible values.
The `<group>` tag
-----------------

View File

@@ -19,6 +19,7 @@
#include "fileio.hpp"
#include "field.hpp"
#include "restypes.hpp"
#include "stack.hpp"
extern short cen_x, cen_y,cur_town;
extern bool mouse_button_held;
@@ -600,8 +601,46 @@ static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst, short wh
me["loot-item"].setTextToNum(store_monst.corpse_item);
me["loot-chance"].setTextToNum(store_monst.corpse_item_chance);
me["magic-res"].setTextToNum(store_monst.magic_res);
me["fire-res"].setTextToNum(store_monst.fire_res);
me["cold-res"].setTextToNum(store_monst.cold_res);
me["poison-res"].setTextToNum(store_monst.poison_res);
me["onsee"].setTextToNum(store_monst.see_spec);
me["snd"].setTextToNum(store_monst.ambient_sound);
if(store_monst.mindless)
dynamic_cast<cLed&>(me["mindless"]).setState(led_red);
if(store_monst.invuln)
dynamic_cast<cLed&>(me["invuln"]).setState(led_red);
if(store_monst.invisible)
dynamic_cast<cLed&>(me["invis"]).setState(led_red);
if(store_monst.guard)
dynamic_cast<cLed&>(me["guard"]).setState(led_red);
dynamic_cast<cLedGroup&>(me["summon"]).setSelected("s" + boost::lexical_cast<std::string,short>(store_monst.summon_type));
cStack& abils = dynamic_cast<cStack&>(me["abils"]);
abils.setPageCount(0); // This clears out any data still in the stack
abils.setPageCount(1);
int i = 0;
for(auto& abil : store_monst.abil) {
if(!abil.second.active) continue;
std::string id = std::to_string((i % 4) + 1);
if(i % 4 == 0) abils.setPage(i / 4);
else if(i % 4 == 3) abils.addPage();
me["abil-name" + id].setText(abil.second.to_string(abil.first));
me["abil-edit" + id].setText("Edit");
i++;
}
abils.setPage(0);
if(abils.getPageCount() <= 1) {
me["abil-up"].hide();
me["abil-down"].hide();
} else {
me["abil-up"].show();
me["abil-down"].show();
}
}
static bool save_monst_abils(cDialog& me, cMonster& store_monst) {
@@ -609,10 +648,21 @@ static bool save_monst_abils(cDialog& me, cMonster& store_monst) {
store_monst.corpse_item = me["loot-item"].getTextAsNum();
store_monst.corpse_item_chance = me["loot-chance"].getTextAsNum();
store_monst.magic_res = me["magic-res"].getTextAsNum();
store_monst.fire_res = me["fire-res"].getTextAsNum();
store_monst.cold_res = me["cold-res"].getTextAsNum();
store_monst.poison_res = me["poison-res"].getTextAsNum();
store_monst.see_spec = me["onsee"].getTextAsNum();
store_monst.ambient_sound = me["snd"].getTextAsNum();
store_monst.mindless = dynamic_cast<cLed&>(me["mindless"]).getState() != led_off;
store_monst.invuln = dynamic_cast<cLed&>(me["invuln"]).getState() != led_off;
store_monst.invisible = dynamic_cast<cLed&>(me["invis"]).getState() != led_off;
store_monst.guard = dynamic_cast<cLed&>(me["guard"]).getState() != led_off;
return true;
}
static bool edit_monst_abil_event_filter(cDialog& me,std::string item_hit,cMonster& store_monst,short which_monst) {
static bool edit_monst_abil_event_filter(cDialog& me,std::string item_hit,cMonster& store_monst) {
using namespace std::placeholders;
if(item_hit == "cancel") {
@@ -621,6 +671,30 @@ static bool edit_monst_abil_event_filter(cDialog& me,std::string item_hit,cMonst
} else if(item_hit == "okay") {
if(save_monst_abils(me, store_monst))
me.toast(true);
} else if(item_hit == "abil-up") {
cStack& abils = dynamic_cast<cStack&>(me["abils"]);
if(abils.getPage() == 0) abils.setPage(abils.getPageCount() - 1);
else abils.setPage(abils.getPage() - 1);
} else if(item_hit == "abil-down") {
cStack& abils = dynamic_cast<cStack&>(me["abils"]);
if(abils.getPage() >= abils.getPageCount() - 1) abils.setPage(0);
else abils.setPage(abils.getPage() + 1);
} else if(item_hit == "edit-see") {
short spec = me["onsee"].getTextAsNum();
if(spec < 0 || spec > 255) {
spec = get_fresh_spec(0);
if(spec < 0) {
giveError("You can't create a new scenario 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(edit_spec_enc(spec,0,&me))
me["onsee"].setTextToNum(spec);
} else if(item_hit == "pick-snd") {
int i = me["snd"].getTextAsNum();
i = choose_text(STRT_SND, i, &me, "Select monster vocalization sound:");
me["snd"].setTextToNum(i);
}
return true;
}
@@ -632,7 +706,7 @@ cMonster edit_monst_abil(cMonster starting_record,short which_monst) {
cDialog monst_dlg("edit-monster-abils");
monst_dlg["loot-item"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 399, "Item To Drop", "-1 for no item"));
monst_dlg["loot-chance"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 100, "Dropping Chance", "-1 for no item"));
monst_dlg.attachClickHandlers(std::bind(edit_monst_abil_event_filter, _1, _2, std::ref(store_monst),which_monst), {"okay", "cancel", "abils", "radiate"});
monst_dlg.attachClickHandlers(std::bind(edit_monst_abil_event_filter, _1, _2, std::ref(store_monst)), {"okay", "cancel", "abil-up", "abil-down", "edit-see", "pick-snd"});
put_monst_abils_in_dlog(monst_dlg, store_monst, which_monst);