Implement a dialog to edit shops in the scenario editor

This commit is contained in:
2015-02-04 13:55:22 -05:00
parent 24a128b5b8
commit 36491c818c
21 changed files with 631 additions and 20 deletions

View File

@@ -29,7 +29,8 @@ title {
border: dashed green thin;
}
.dark .text, .dark .led {
.dark .text, .dark .led,
.dark .button.tiny, .dark .button.push {
color: white;
}
@@ -69,7 +70,7 @@ title {
height: 32px;
}
.pict.missile {
.pict.missile, .pict.item.tiny {
width: 18px;
height: 18px;
}

View File

@@ -29,7 +29,8 @@
pict
<xsl:if test='@framed = "true" or not(@framed)'>framed </xsl:if>
<xsl:value-of select='./@type'/>
<xsl:if test='@size = "large" and @type != "monst"'>large</xsl:if>
<xsl:if test='@size = "large" and @type != "monst"'> large</xsl:if>
<xsl:if test='@size = "small" and @type = "item"'> tiny</xsl:if>
</xsl:attribute>
<xsl:if test='/dialog/@debug = "true" and @name'>
<xsl:attribute name='title'>

View File

@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
<dialog defbtn='okay' debug='true'>
<pict type='dlog' num='16' top='8' left='8'/>
<text size='large' top='8' left='50' width='150' height='17'>Editing Shop Entry</text>
<text name='item' framed='true' top='50' left='10' width='200' height='16'/>
<button name='choose' type='regular' top='47' left='220'>Choose</button>
<text top='75' left='10' width='100' height='16'>Quantity:</text>
<field name='amount' type='uint' top='73' left='110' width='80' height='16'/>
<text top='75' left='200' width='80' height='16'>0 = infinite</text>
<text name='chance-prompt' top='100' left='10' width='100' height='16'>Chance:</text>
<field name='chance' type='uint' top='98' left='110' width='80' height='16'/>
<button name='cancel' type='regular' def-key='esc' top='120' left='10'>Cancel</button>
<button name='okay' type='regular' top='120' left='220'>OK</button>
</dialog>

View File

@@ -0,0 +1,22 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
<dialog defbtn='okay' debug='true'>
<pict type='dlog' num='16' top='8' left='8'/>
<text size='large' top='8' left='50' width='150' height='17'>Editing Shop Entry</text>
<text top='50' left='10' width='100' height='16'>Name:</text>
<field name='name' top='48' left='110' width='200' height='16'/>
<text top='75' left='10' width='100' height='16'>Special Node:</text>
<field name='node' top='74' left='110' width='80' height='16'/>
<button name='edit' type='large' top='71' left='200'>Create/Edit</button>
<text top='100' left='10' width='100' height='16'>Quantity:</text>
<field name='amount' type='uint' top='98' left='110' width='80' height='16'/>
<text top='125' left='10' width='100' height='16'>Cost:</text>
<field name='cost' type='uint' top='123' left='110' width='80' height='16'/>
<text top='100' left='190' width='30' height='16'>Icon:</text>
<pict name='icon' type='item' size='small' num='0' top='100' left='225'/>
<button name='pickicon' top='96' left='247' type='regular'>Choose</button>
<text top='150' left='10' width='100' height='16'>Description:</text>
<field name='descr' top='148' left='110' width='200' height='100'/>
<button name='cancel' type='regular' def-key='esc' top='255' left='10'>Cancel</button>
<button name='okay' type='regular' top='255' left='247'>OK</button>
</dialog>

View File

@@ -0,0 +1,90 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
<dialog defbtn='okay' debug='true'>
<pict type='dlog' num='16' top='8' left='8'/>
<text size='large' top='8' left='50' width='200' height='17'>Editing Shop</text>
<text top='8' left='250' width='100' height='16'>Shop number:</text>
<text name='num' top='8' left='350' width='50' height='16'/>
<text top='50' left='10' width='100' height='16'>Shop name:</text>
<field name='name' top='48' left='110' width='200' height='16'/>
<text top='30' left='320' width='150' height='58'>
This is just a name to make it easier for you to identify the shop in the list of shops.
It's not used in-game.
</text>
<text top='70' left='10' width='100' height='16'>Shop type:</text>
<group name='type'>
<led name='t1' top='70' left='110' width='180'>Standard shop (living only)</led>
<led name='t2' top='90' left='110' width='180'>Healing/Alchemy (dead can shop)</led>
<led name='t3' top='110' left='110' width='180'>Randomly generated</led>
</group>
<text top='130' left='10' width='100' height='16'>Message to show:</text>
<group name='prompt'>
<led name='p1' top='130' left='110' width='80'>Shopping</led>
<led name='p2' top='150' left='110' width='80'>Healing</led>
<led name='p3' top='170' left='110' width='80'>Mage Spells</led>
<led name='p4' top='130' left='210' width='80'>Priest Spells</led>
<led name='p5' top='150' left='210' width='80'>Mixed Spells</led>
<led name='p6' top='170' left='210' width='80'>Alchemy</led>
<led name='p7' top='130' left='310' width='80'>Training</led>
</group>
<button name='rand' type='tiny' top='190' left='10' width='440'>
Click here to generate a standard random items shop.
(This will replace the entire shop definition.)
</button>
<text top='210' left='28' width='300' height='16'>Or edit the shop item list below:</text>
<stack name='items'>
<text name='n1' top='230' left='10' width='15' height='16'/>
<pict name='pict1' type='item' num='0' size='small' top='230' left='30'/>
<text name='item1' framed='true' top='230' left='55' width='200' height='16'/>
<text name='n2' top='255' left='10' width='15' height='16'/>
<pict name='pict2' type='item' num='0' size='small' top='255' left='30'/>
<text name='item2' framed='true' top='255' left='55' width='200' height='16'/>
<text name='n3' top='280' left='10' width='15' height='16'/>
<pict name='pict3' type='item' num='0' size='small' top='280' left='30'/>
<text name='item3' framed='true' top='280' left='55' width='200' height='16'/>
<text name='n4' top='305' left='10' width='15' height='16'/>
<pict name='pict4' type='item' num='0' size='small' top='305' left='30'/>
<text name='item4' framed='true' top='305' left='55' width='200' height='16'/>
<text name='n5' top='330' left='10' width='15' height='16'/>
<pict name='pict5' type='item' num='0' size='small' top='330' left='30'/>
<text name='item5' framed='true' top='330' left='55' width='200' height='16'/>
</stack>
<button name='ed1' type='regular' top='227' left='265'>Edit</button>
<button name='del1' type='regular' top='227' left='330'>Delete</button>
<button name='ed2' type='regular' top='252' left='265'>Edit</button>
<button name='del2' type='regular' top='252' left='330'>Delete</button>
<button name='ed3' type='regular' top='277' left='265'>Edit</button>
<button name='del3' type='regular' top='277' left='330'>Delete</button>
<button name='ed4' type='regular' top='302' left='265'>Edit</button>
<button name='del4' type='regular' top='302' left='330'>Delete</button>
<button name='ed5' type='regular' top='327' left='265'>Edit</button>
<button name='del5' type='regular' top='327' left='330'>Delete</button>
<button name='up' type='up' top='227' left='410'/>
<button name='down' type='down' top='327' left='410'/>
<text top='350' left='10' width='200'>Add an item:</text>
<button name='item' type='regular' top='365' left='10'>Item</button>
<button name='mage' type='regular' top='365' left='75'>Mage</button>
<button name='priest' type='regular' top='365' left='140'>Priest</button>
<button name='alch' type='regular' top='365' left='205'>Alchemy</button>
<button name='skill' type='regular' top='365' left='270'>Skill</button>
<button name='heal' type='regular' top='390' left='10'>Healing</button>
<button name='treas' type='regular' top='390' left='75'>Treasure</button>
<button name='class' type='regular' top='390' left='140'>Class</button>
<button name='opt' type='regular' top='390' left='205'>Optional</button>
<button name='spec' type='regular' top='390' left='270'>Special</button>
<text top='360' left='370' width='100' height='16'>Shopping Face:</text>
<button name='pickface' type='regular' top='390' left='410'>Choose</button>
<pict name='face' type='talk' num='0' top='380' left='370'/>
<button name='left' type='left' top='430' left='10'/>
<button name='right' type='right' top='430' left='75'/>
<button name='cancel' type='regular' top='430' left='345'>Cancel</button>
<button name='okay' type='regular' top='430' left='410'>OK</button>
</dialog>

View File

@@ -0,0 +1,10 @@
Heal Wounds
Cure Poison
Cure Disease
Cure Acid
Cure Paralysis
Remove Curse
Destone
Raise Dead
Resurrect
Cure Dumbfounding

View File

@@ -199,6 +199,12 @@ cItem::cItem(long preset) : cItem() {
name = "Potion";
magic = true;
break;
case 'spec':
item_level = -1;
full_name = "Call Special Node";
case 'shop':
graphic_num = 105; // The blank graphic
break;
}
}

View File

@@ -55,6 +55,8 @@ cShop::cShop(eShopType type, eShopPrompt prompt, pic_num_t pic, int adj, std::st
face(pic)
{}
cShop::cShop(std::string name) : cShop(eShopType::NORMAL, eShopPrompt::SHOPPING, 0, 0, name) {}
cShop::cShop(long preset) {
const short loot_index[10] = {1,1,1,1,2,2,2,3,3,4};
@@ -96,7 +98,7 @@ size_t cShop::size() {
});
}
void cShop::addItem(cItem item, size_t quantity, int chance) {
void cShop::addItem(size_t n, cItem item, size_t quantity, int chance) {
size_t i = firstEmpty();
if(i >= items.size()) return;
if(item.variety == eItemType::NO_ITEM) return;
@@ -104,6 +106,7 @@ void cShop::addItem(cItem item, size_t quantity, int chance) {
items[i].item = item;
items[i].item.ident = true;
items[i].quantity = quantity;
items[i].index = n;
if(chance < 100)
items[i].quantity = min(999,quantity) + chance * 1000;
}
@@ -168,6 +171,12 @@ static cItem store_alchemy(short which_s) {
}
void cShop::addSpecial(eShopItemType type, int n) {
if(type == eShopItemType::EMPTY || type == eShopItemType::CALL_SPECIAL) return;
if(type == eShopItemType::OPTIONAL || type == eShopItemType::ITEM) return;
replaceSpecial(firstEmpty(), type, n);
}
void cShop::replaceSpecial(size_t i, eShopItemType type, int n) {
// TODO: Make this a std::map instead
static const short heal_costs[10] = {50,30,80,90,100,250,500,1000,3000,100};
static const char*const heal_types[] = {
@@ -178,9 +187,11 @@ void cShop::addSpecial(eShopItemType type, int n) {
if(type == eShopItemType::ITEM) return;
if(type == eShopItemType::OPTIONAL) return;
if(type == eShopItemType::CALL_SPECIAL) return;
size_t i = firstEmpty();
if(i >= items.size()) return;
items[i].type = type;
if(type >= eShopItemType::HEAL_WOUNDS)
items[i].index = int(type) - int(eShopItemType::HEAL_WOUNDS);
else items[i].index = n;
if(type == eShopItemType::MAGE_SPELL)
items[i].item = store_mage_spells(n);
else if(type == eShopItemType::PRIEST_SPELL)
@@ -191,19 +202,21 @@ void cShop::addSpecial(eShopItemType type, int n) {
items[i].item.graphic_num = 108;
items[i].item.full_name = get_str("skills", n * 2 + 1);
} else if(type == eShopItemType::TREASURE) {
items[i].item.graphic_num = 45;
items[i].item.item_level = n;
items[i].item.full_name = "Treasure (type " + std::to_string(n) + ")";
} else if(type == eShopItemType::CLASS) {
items[i].item.graphic_num = 65;
items[i].item.special_class = n;
items[i].item.full_name = "Item of special class " + std::to_string(n);
} else {
items[i].item.graphic_num = 109;
items[i].item.full_name = heal_types[int(type) - int(eShopItemType::HEAL_WOUNDS)];
items[i].item.full_name = heal_types[items[i].index];
}
if(type == eShopItemType::SKILL)
items[i].item.value = skill_g_cost[eSkill(n)] * 1.5;
else if(type >= eShopItemType::HEAL_WOUNDS)
items[i].item.value = heal_costs[int(type) - int(eShopItemType::HEAL_WOUNDS)];
items[i].item.value = heal_costs[items[i].index];
items[i].quantity = 0;
}
@@ -251,6 +264,18 @@ void cShop::setCostAdjust(int adj) {
cost_adj = adj;
}
void cShop::setFace(pic_num_t pic) {
face = pic;
}
void cShop::setType(eShopType t) {
type = t;
}
void cShop::setPrompt(eShopPrompt p) {
prompt = p;
}
void cShop::takeOne(size_t i) {
if(items[i].quantity == 1) {
items[i].type = eShopItemType::EMPTY;

View File

@@ -30,6 +30,7 @@ enum class eShopItemType {
CLASS,
OPTIONAL,
CALL_SPECIAL,
// All non-healing types must be above here and all healing types below, with HEAL_WOUNDS kept first
HEAL_WOUNDS,
CURE_POISON,
CURE_DISEASE,
@@ -44,8 +45,8 @@ enum class eShopItemType {
struct cShopItem {
eShopItemType type = eShopItemType::EMPTY;
size_t quantity;
cItem item;
size_t quantity, index;
cItem item = cItem('shop');
int getCost(int adj);
};
@@ -61,14 +62,16 @@ public:
static const size_t INFINITE = 0;
cShop();
cShop(eShopType type, eShopPrompt prompt, pic_num_t pic, int adj, std::string name);
explicit cShop(std::string name);
explicit cShop(long preset);
void addItem(cItem item, size_t quantity, int chance = 100);
void addItem(size_t i, cItem item, size_t quantity, int chance = 100);
void addSpecial(std::string name, std::string descr, pic_num_t pic, int node, int cost, int quantity);
void addSpecial(eShopItemType type, int n = 0);
template<typename Iter> void addItems(Iter begin, Iter end, size_t quantity) {
while(begin != end) addItem(*begin++, quantity);
template<typename Iter> void addItems(size_t start, Iter begin, Iter end, size_t quantity) {
while(begin != end) addItem(start++, *begin++, quantity);
}
void replaceItem(size_t i, cShopItem newItem);
void replaceSpecial(size_t i, eShopItemType type, int n = 0);
size_t size();
cShopItem getItem(size_t i) const;
eShopType getType() const;
@@ -78,6 +81,9 @@ public:
eShopPrompt getPrompt() const;
void setCostAdjust(int adj);
void setName(std::string name);
void setType(eShopType t);
void setFace(pic_num_t pic);
void setPrompt(eShopPrompt p);
void takeOne(size_t i);
void clearItem(size_t i);
void clear();

View File

@@ -51,7 +51,7 @@ template<> pair<string,cPict*> cDialog::parse(Element& who /*pict*/){
Iterator<Attribute> attr;
std::string name;
ePicType type;
bool wide = false, tall = false, custom = false;
bool wide = false, tall = false, custom = false, tiny = false;
bool foundTop = false, foundLeft = false, foundType = false, foundNum = false; // required attributes
rectangle frame;
int width = 0, height = 0;
@@ -111,6 +111,7 @@ template<> pair<string,cPict*> cDialog::parse(Element& who /*pict*/){
if(val == "wide") wide = true;
else if(val == "tall") tall = true;
else if(val == "large") wide = tall = true;
else if(val == "small") tiny = true;
else throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname);
}else if(name == "def-key"){
std::string val;
@@ -154,6 +155,9 @@ template<> pair<string,cPict*> cDialog::parse(Element& who /*pict*/){
else if(p.second->getPicType() == PIC_SCEN) p.second->setPict(wasPic, PIC_SCEN_LG);
else if(p.second->getPicType() == PIC_DLOG) p.second->setPict(wasPic, PIC_DLOG_LG);
}
} else if(tiny && type == PIC_ITEM) {
pic_num_t wasPic = p.second->getPicNum();
p.second->setPict(wasPic, PIC_TINY_ITEM);
}
frame.right = frame.left;
frame.bottom = frame.top;

View File

@@ -27,6 +27,7 @@ void cPict::init(){
drawPict()[PIC_TALK] = &cPict::drawPresetTalk;
drawPict()[PIC_SCEN] = &cPict::drawPresetScen;
drawPict()[PIC_ITEM] = &cPict::drawPresetItem;
drawPict()[PIC_TINY_ITEM] = &cPict::drawPresetTinyItem;
drawPict()[PIC_PC] = &cPict::drawPresetPc;
drawPict()[PIC_FIELD] = &cPict::drawPresetField;
drawPict()[PIC_BOOM] = &cPict::drawPresetBoom;
@@ -46,6 +47,7 @@ void cPict::init(){
drawPict()[PIC_CUSTOM_TALK] = &cPict::drawCustomTalk;
drawPict()[PIC_CUSTOM_SCEN] = &cPict::drawCustomTalk;
drawPict()[PIC_CUSTOM_ITEM] = &cPict::drawCustomItem;
drawPict()[PIC_CUSTOM_TINY_ITEM] = &cPict::drawCustomTinyItem;
drawPict()[PIC_CUSTOM_FULL] = &cPict::drawFullSheet;
drawPict()[PIC_CUSTOM_MISSILE] = &cPict::drawCustomMissile;
drawPict()[PIC_CUSTOM_DLOG_LG] = &cPict::drawCustomDlogLg;
@@ -176,6 +178,8 @@ ePicType operator+ (ePicType lhs, ePicTypeMod rhs){
case PIC_CUSTOM_ITEM:
case PIC_PARTY_ITEM:
return PIC_ITEM;
case PIC_CUSTOM_TINY_ITEM:
return PIC_TINY_ITEM;
case PIC_CUSTOM_FULL:
return PIC_FULL;
case PIC_CUSTOM_MISSILE:
@@ -233,6 +237,12 @@ ePicType operator+ (ePicType lhs, ePicTypeMod rhs){
return lhs;
}
case PIC_LARGE:
if(lhs == PIC_DLOG)
return PIC_DLOG_LG;
else if(lhs == PIC_CUSTOM_DLOG)
return PIC_CUSTOM_DLOG_LG;
else if(lhs == PIC_SCEN)
return PIC_SCEN_LG;
return (lhs + PIC_WIDE) + PIC_TALL;
case PIC_CUSTOM:
switch(lhs){
@@ -250,6 +260,8 @@ ePicType operator+ (ePicType lhs, ePicTypeMod rhs){
return PIC_CUSTOM_SCEN;
case PIC_ITEM:
return PIC_CUSTOM_ITEM;
case PIC_TINY_ITEM:
return PIC_CUSTOM_TINY_ITEM;
case PIC_FULL:
return PIC_CUSTOM_FULL;
case PIC_MISSILE:
@@ -330,6 +342,12 @@ ePicType operator- (ePicType lhs, ePicTypeMod rhs){
return lhs;
}
case PIC_LARGE:
if(lhs == PIC_DLOG_LG)
return PIC_DLOG;
else if(lhs == PIC_CUSTOM_DLOG_LG)
return PIC_CUSTOM_DLOG_LG;
else if(lhs == PIC_SCEN_LG)
return PIC_SCEN;
return (lhs - PIC_WIDE) - PIC_TALL;
case PIC_CUSTOM:
switch(lhs){
@@ -347,6 +365,8 @@ ePicType operator- (ePicType lhs, ePicTypeMod rhs){
return PIC_SCEN;
case PIC_CUSTOM_ITEM:
return PIC_ITEM;
case PIC_CUSTOM_TINY_ITEM:
return PIC_TINY_ITEM;
case PIC_CUSTOM_FULL:
return PIC_FULL;
case PIC_CUSTOM_MISSILE:
@@ -440,6 +460,7 @@ void cPict::recalcRect() {
bounds.width() = 32;
bounds.height() = 32;
break;
case PIC_TINY_ITEM: case PIC_CUSTOM_TINY_ITEM:
case PIC_MISSILE: case PIC_CUSTOM_MISSILE:
bounds.width() = 18;
bounds.height() = 18;
@@ -755,6 +776,17 @@ void cPict::drawPresetItem(short num, rectangle to_rect){
rect_draw_some_item(*from_gw, from_rect, *inWindow, to_rect, sf::BlendAlpha);
}
void cPict::drawPresetTinyItem(short num, rectangle to_rect){
to_rect.right = to_rect.left + 18;
to_rect.bottom = to_rect.top + 18;
fill_rect(*inWindow, to_rect, sf::Color::Black);
std::shared_ptr<sf::Texture> from_gw;
rectangle from_rect = {0,0,18,18};
from_gw = getSheet(SHEET_TINY_ITEM);
from_rect.offset(18 * (num % 10), 18 * (num / 10));
rect_draw_some_item(*from_gw, from_rect, *inWindow, to_rect, sf::BlendAlpha);
}
void cPict::drawPresetPc(short num, rectangle to_rect){
std::shared_ptr<sf::Texture> from_gw = getSheet(SHEET_PC);
rectangle from_rect = calc_rect(2 * (num / 8), num % 8);
@@ -975,6 +1007,16 @@ void cPict::drawCustomItem(short num, rectangle to_rect){
rect_draw_some_item(*from_gw, from_rect, *inWindow, to_rect, sf::BlendAlpha);
}
void cPict::drawCustomTinyItem(short num, rectangle to_rect){
to_rect.right = to_rect.left + 18;
to_rect.bottom = to_rect.top + 18;
rectangle from_rect;
sf::Texture* from_gw;
graf_pos_ref(from_gw, from_rect) = spec_scen_g.find_graphic(num);
fill_rect(*inWindow, to_rect, sf::Color::Black);
rect_draw_some_item(*from_gw, from_rect, *inWindow, to_rect, sf::BlendAlpha);
}
void cPict::drawCustomMissile(short num, rectangle to_rect){
num += animFrame % 8;
rectangle from_rect;

View File

@@ -100,6 +100,7 @@ private:
void drawPresetScen(short num, rectangle to_rect);
void drawPresetScenLg(short num, rectangle to_rect);
void drawPresetItem(short num, rectangle to_rect);
void drawPresetTinyItem(short num, rectangle to_rect);
void drawPresetPc(short num, rectangle to_rect);
void drawPresetField(short num, rectangle to_rect);
void drawPresetBoom(short num, rectangle to_rect);
@@ -117,6 +118,7 @@ private:
void drawCustomDlogLg(short num, rectangle to_rect);
void drawCustomTalk(short num, rectangle to_rect);
void drawCustomItem(short num, rectangle to_rect);
void drawCustomTinyItem(short num, rectangle to_rect);
void drawCustomMissile(short num, rectangle to_rect);
void drawCustomTerMap(short num, rectangle to_rect);
void drawPartyMonstSm(short num, rectangle to_rect);

View File

@@ -31,8 +31,9 @@ enum ePicType {
PIC_MISSILE = 12, ///< 18x18 missile graphic from the missiles sheet
PIC_DLOG_LG = 13, ///< 72x72 dialog graphic from the dialog sheet
PIC_SCEN_LG = 14, ///< 64x64 scenario graphic (currently each is on its own sheet)
PIC_TER_MAP = 15, ///< 12x12 map graphic... or should it be 6x6?
PIC_TER_MAP = 15, ///< 12x12 map graphic from the terrain map sheet, expanded to 24x24
PIC_STATUS = 16, ///< 12x12 status icon
PIC_TINY_ITEM = 17, ///< 18x18 item graphic from the small item sheet
PIC_MONST_WIDE = 23, ///< 56x36 monster graphic from the preset sheets, resized to fit and centred in a 28x36 space
PIC_MONST_TALL = 43, ///< 28x72 monster graphic from the preset sheets, resized to fit and centred in a 28x36 space
PIC_MONST_LG = 63, ///< 56x72 monster graphic from the preset sheets, resized to fit in a 28x36 space
@@ -46,7 +47,8 @@ enum ePicType {
PIC_CUSTOM_FULL = 111, ///< entire sheet graphic, drawn from scenname.exr/sheetxxx.png where xxx is the number
PIC_CUSTOM_MISSILE = 112, ///< 18x18 missile graphic drawn from the the custom sheets
PIC_CUSTOM_DLOG_LG = 113, ///< 72x72 dialog graphic from the custom sheet, taken from 8 successive slots
PIC_CUSTOM_TER_MAP = 115, ///< 12x12 map graphic (should it be 6x6?) taken from the custom sheet
PIC_CUSTOM_TER_MAP = 115, ///< 12x12 map graphic taken from the custom sheet and expanded to 24x24
PIC_CUSTOM_TINY_ITEM = 117, ///< 28x36 custom item graphic shrunk down into an 18x18 space
PIC_CUSTOM_MONST_WIDE = 123,///< 56x36 monster graphic from the custom sheets, resized to fit and centred in a 28x36 space
PIC_CUSTOM_MONST_TALL = 143,///< 28x72 monster graphic from the custom sheets, resized to fit and centred in a 28x36 space
PIC_CUSTOM_MONST_LG = 163, ///< 56x72 monster graphic from the custom sheets, resized to fit in a 28x36 space

View File

@@ -236,6 +236,9 @@ bool handle_action(location the_point,sf::Event /*event*/) {
case LB_EDIT_QUEST:
start_quest_editing();
break;
case LB_EDIT_SHOPS:
start_shops_editing();
break;
case LB_LOAD_OUT:
if(change_made) {
if(!save_check("save-section-confirm"))
@@ -447,6 +450,14 @@ bool handle_action(location the_point,sf::Event /*event*/) {
} else edit_quest(j);
start_quest_editing();
break;
case RB_SHOP:
if(option_hit) {
if(j == scenario.shops.size() - 1)
scenario.shops.pop_back();
else scenario.shops[j] = cShop("Unused Shop");
} else edit_shop(j);
start_shops_editing();
break;
}
mouse_button_held = false;
}
@@ -2074,6 +2085,7 @@ void set_up_main_screen() {
set_lb(-1,LB_TEXT,LB_EDIT_TEXT,"Edit Scenario Text");
set_lb(-1,LB_TEXT,LB_EDIT_SPECITEM,"Edit Special Items");
set_lb(-1,LB_TEXT,LB_EDIT_QUEST,"Edit Quests");
set_lb(-1,LB_TEXT,LB_EDIT_SHOPS,"Edit Shops");
set_lb(-1,LB_TEXT,LB_NO_ACTION,"");
set_lb(-1,LB_TEXT,LB_NO_ACTION,"Outdoors Options");
strb << " Section x = " << cur_out.x << ", y = " << cur_out.y;
@@ -2259,6 +2271,27 @@ void start_quest_editing() {
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Command-click or right-click to delete",true);
}
void start_shops_editing() {
int num_options = scenario.shops.size() + 1;
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
right_sbar->setPosition(0);
reset_rb();
right_sbar->setMaximum(num_options - NRSONPAGE);
for(int i = 0; i < num_options; i++) {
std::string title;
if(i == scenario.shops.size())
title = "Create New Shop";
else title = scenario.shops[i].getName();
title = std::to_string(i) + " - " + title;
set_rb(i, RB_SHOP, i, title);
}
redraw_screen();
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Command-click or right-click to delete",true);
}
extern size_t num_strs(short mode); // defined in scen.keydlgs.cpp
// mode 0 - scen 1 - out 2 - town 3 - journal

View File

@@ -30,6 +30,7 @@ void start_monster_editing(short just_redo_text);
void start_item_editing(short just_redo_text);
void start_special_item_editing();
void start_quest_editing();
void start_shops_editing();
void start_string_editing(short mode,short just_redo_text);
void start_special_editing(short mode,short just_redo_text);
void town_entry(location spot_hit);

View File

@@ -10,6 +10,7 @@ enum eLBAction {
LB_EDIT_TEXT,
LB_EDIT_SPECITEM,
LB_EDIT_QUEST,
LB_EDIT_SHOPS,
LB_LOAD_OUT,
LB_EDIT_OUT,
LB_LOAD_TOWN,
@@ -36,6 +37,7 @@ enum eRBAction {
RB_OUT_SIGN = 14,
RB_TOWN_SIGN = 15,
RB_QUEST = 16,
RB_SHOP = 17,
};
enum eLBMode {

View File

@@ -23,6 +23,7 @@
#include "restypes.hpp"
#include "stack.hpp"
#include "spell.hpp"
#include "mathutil.hpp"
extern short cen_x, cen_y,cur_town;
extern bool mouse_button_held;
@@ -184,6 +185,8 @@ static bool pick_picture(ePicType type, cDialog& parent, std::string result_fld,
cPict& pic_ctrl = dynamic_cast<cPict&>(parent[pic_fld]);
if(type == PIC_TER_ANIM && pic < 1000)
pic += 960;
if(type == PIC_ITEM && pic_ctrl.getPicType() == PIC_TINY_ITEM)
type = PIC_TINY_ITEM;
pic_ctrl.setPict(pic,type);
}
}
@@ -944,11 +947,11 @@ static bool edit_monst_abil_event_filter(cDialog& me,std::string hit,cMonster& m
return true;
}
static short get_monst_abil_num(std::string prompt, int min, int max, cDialog& parent) {
static short get_monst_abil_num(std::string prompt, int min, int max, cDialog& parent, int cur = -1) {
cDialog numPanel("get-mabil-num", &parent);
numPanel["okay"].attachClickHandler(std::bind(&cDialog::toast, &numPanel, false));
numPanel["prompt"].setText(prompt + " (" + std::to_string(min) + "-" + std::to_string(max) + ") ");
numPanel["number"].setTextToNum(min);
numPanel["number"].setTextToNum(minmax(min,max,cur));
numPanel.run();
int result = numPanel["number"].getTextAsNum();
@@ -2052,6 +2055,344 @@ void edit_quest(size_t which_quest) {
quest_dlg.run();
}
static bool put_shop_item_in_dlog(cPict& pic, cControl& num, cControl& title, const cShop& shop, int which) {
cShopItem entry = shop.getItem(which);
num.setTextToNum(which);
pic.setPict(entry.item.graphic_num);
if(entry.type == eShopItemType::EMPTY) {
title.setText("");
return false;
}
std::string name = entry.item.full_name;
int amount = entry.quantity;
if(entry.type == eShopItemType::OPTIONAL) {
name += " [" + std::to_string(amount / 1000) + "% chance]";
amount %= 1000;
}
if(amount > 0) {
name = std::to_string(amount) + "x " + name;
}
title.setText(name);
return true;
}
static void put_shop_in_dlog(cDialog& me, const cShop& shop, size_t which_shop) {
me["num"].setText(std::to_string(which_shop) + " of " + std::to_string(scenario.shops.size()));
me["name"].setText(shop.getName());
dynamic_cast<cPict&>(me["face"]).setPict(shop.getFace());
dynamic_cast<cLedGroup&>(me["type"]).setSelected("t" + std::to_string(int(shop.getType()) + 1));
dynamic_cast<cLedGroup&>(me["prompt"]).setSelected("p" + std::to_string(int(shop.getPrompt()) + 1));
cStack& items = dynamic_cast<cStack&>(me["items"]);
for(int i = 0; i < 6; i++) {
items.setPage(i);
for(int j = 0; j < 5; j++) {
std::string id = std::to_string(j + 1);
if(put_shop_item_in_dlog(dynamic_cast<cPict&>(me["pict" + id]), me["n" + id], me["item" + id], shop, i * 5 + j)) {
if(i == 0) {
me["ed" + id].show();
me["del" + id].show();
}
} else if(i == 0) {
me["ed" + id].hide();
me["del" + id].hide();
}
}
}
items.setPage(0);
}
static bool save_shop_from_dlog(cDialog& me, cShop& shop, size_t which_shop, bool close) {
if(!me.toast(true)) return false;
shop.setName(me["name"].getText());
shop.setType(eShopType(dynamic_cast<cLedGroup&>(me["type"]).getSelected()[1] - '1'));
shop.setPrompt(eShopPrompt(dynamic_cast<cLedGroup&>(me["prompt"]).getSelected()[1] - '1'));
shop.setFace(dynamic_cast<cPict&>(me["face"]).getPicNum());
// Items are filled in as they're added by the dialog, so that's all we need to do here
scenario.shops[which_shop] = shop;
if(!close) me.untoast();
return true;
}
static bool change_shop_dlog_page(cDialog& me, std::string dir, cShop& shop, size_t& which_shop) {
if(!save_shop_from_dlog(me, shop, which_shop, false))
return true;
if(dir == "left") {
if(which_shop == 0)
which_shop = scenario.shops.size();
which_shop--;
} else if(dir == "right") {
which_shop++;
if(which_shop == scenario.shops.size())
which_shop = 0;
}
shop = scenario.shops[which_shop];
put_shop_in_dlog(me, shop, which_shop);
return true;
}
static bool change_shop_dlog_items_page(cDialog& me, std::string dir, const cShop& shop) {
cStack& items = dynamic_cast<cStack&>(me["items"]);
int curPage = items.getPage(), maxPage = items.getPageCount();
if(dir == "up") {
if(curPage == 0)
curPage = maxPage - 1;
else curPage--;
} else if(dir == "down") {
if(curPage == maxPage - 1)
curPage = 0;
else curPage++;
}
for(int i = 0; i < 5; i++) {
std::string id = std::to_string(i + 1);
if(shop.getItem(curPage * 5 + i).type == eShopItemType::EMPTY) {
me["ed" + id].hide();
me["del" + id].hide();
} else {
me["ed" + id].show();
me["del" + id].show();
}
}
items.setPage(curPage);
return true;
}
static void edit_shop_item(cDialog& parent, size_t& item, size_t& quantity, bool optional) {
using namespace std::placeholders;
int was_item = item;
cDialog item_dlg("edit-shop-item", &parent);
item_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false));
item_dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, _1, true));
if(optional) {
item_dlg["amount"].setTextToNum(quantity % 1000);
item_dlg["chance"].setTextToNum(quantity / 1000);
item_dlg["amount"].attachFocusHandler(std::bind(check_range, _1, _2, _3, 0, 999, "amount"));
item_dlg["chance"].attachFocusHandler(std::bind(check_range, _1, _2, _3, 1, 100, "chance"));
} else {
item_dlg["amount"].setTextToNum(quantity);
item_dlg["chance"].hide();
item_dlg["chance-prompt"].hide();
}
item_dlg["item"].setText(scenario.scen_items[item].full_name);
item_dlg["choose"].attachClickHandler([&item](cDialog& me, std::string, eKeyMod) -> bool {
item = choose_text(STRT_ITEM, item, &me, "Which item?");
me["item"].setText(scenario.scen_items[item].full_name);
return true;
});
item_dlg.run();
if(item_dlg.accepted()) {
quantity = item_dlg["amount"].getTextAsNum();
if(optional)
quantity += 1000 * item_dlg["chance"].getTextAsNum();
} else item = was_item;
}
static void edit_shop_special(cDialog& parent, cItem& item, size_t& quantity) {
using namespace std::placeholders;
cDialog spec_dlg("edit-shop-special", &parent);
spec_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false));
spec_dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, _1, true));
spec_dlg["cost"].attachFocusHandler(std::bind(check_range, _1, _2, _3, 0, 10000, "cost"));
spec_dlg["pickicon"].attachClickHandler(std::bind(pick_picture, PIC_ITEM, _1, "", "icon"));
spec_dlg["edit"].attachClickHandler([](cDialog& me, std::string, eKeyMod) -> bool {
int spec = me["node"].getTextAsNum();
if(spec < 0) spec = get_fresh_spec(0);
if(edit_spec_enc(spec, 0, &me))
me["node"].setTextToNum(spec);
return true;
});
spec_dlg["name"].setText(item.full_name);
spec_dlg["descr"].setText(item.desc);
spec_dlg["node"].setTextToNum(item.item_level);
spec_dlg["cost"].setTextToNum(item.value);
spec_dlg["amount"].setTextToNum(quantity);
dynamic_cast<cPict&>(spec_dlg["icon"]).setPict(item.graphic_num);
spec_dlg.run();
if(spec_dlg.accepted()) {
item.full_name = spec_dlg["name"].getText();
item.desc = spec_dlg["descr"].getText();
item.item_level = spec_dlg["node"].getTextAsNum();
item.value = spec_dlg["cost"].getTextAsNum();
quantity = spec_dlg["amount"].getTextAsNum();
item.graphic_num = dynamic_cast<cPict&>(spec_dlg["icon"]).getPicNum();
}
}
static bool edit_shop_entry(cDialog& me, std::string which, cShop& shop) {
int btn_i = which[2] - '0';
int i = dynamic_cast<cStack&>(me["items"]).getPage() * 5 + btn_i - 1;
cShopItem entry = shop.getItem(i);
eStrType list;
std::string prompt;
bool need_string = true;
switch(entry.type) {
case eShopItemType::EMPTY: return true;
case eShopItemType::ITEM:
case eShopItemType::OPTIONAL:
edit_shop_item(me, entry.index, entry.quantity, entry.type == eShopItemType::OPTIONAL);
entry.item = scenario.scen_items[entry.index];
shop.replaceItem(i, entry);
need_string = false;
break;
case eShopItemType::CLASS:
entry.index = get_monst_abil_num("Which special class?", 1, 100, me, entry.index);
shop.replaceSpecial(i, eShopItemType::CLASS, entry.index);
need_string = false;
break;
case eShopItemType::CALL_SPECIAL:
edit_shop_special(me, entry.item, entry.quantity);
shop.replaceItem(i, entry);
need_string = false;
break;
case eShopItemType::MAGE_SPELL: list = STRT_MAGE; prompt = "Which mage spell?"; break;
case eShopItemType::PRIEST_SPELL: list = STRT_PRIEST; prompt = "Which priest spell?"; break;
case eShopItemType::ALCHEMY: list = STRT_ALCHEMY; prompt = "Which recipe?"; break;
case eShopItemType::SKILL: list = STRT_SKILL; prompt = "Which skill?"; break;
case eShopItemType::TREASURE: list = STRT_TREASURE; prompt = "What class of treasure?"; break;
case eShopItemType::CURE_ACID: case eShopItemType::CURE_DISEASE: case eShopItemType::CURE_POISON:
case eShopItemType::CURE_DUMBFOUNDING: case eShopItemType::CURE_PARALYSIS: case eShopItemType::DESTONE:
case eShopItemType::HEAL_WOUNDS: case eShopItemType::RAISE_DEAD: case eShopItemType::REMOVE_CURSE:
case eShopItemType::RESURRECT:
list = STRT_HEALING;
break;
}
if(need_string) {
entry.index = choose_text(list, entry.index, &me, prompt);
if(list == STRT_HEALING)
shop.replaceSpecial(i, eShopItemType(int(eShopItemType::HEAL_WOUNDS) + entry.index));
else shop.replaceSpecial(i, entry.type, entry.index);
}
std::string pic_id = which, num_id = which, title_id = which;
pic_id.replace(0,2,"pict"); num_id.replace(0,2,"n"); title_id.replace(0,2,"item");
put_shop_item_in_dlog(dynamic_cast<cPict&>(me[pic_id]), me[num_id], me[title_id], shop, i);
return true;
}
static bool delete_shop_entry(cDialog& me, std::string which, cShop& shop, size_t which_shop) {
cStack& items = dynamic_cast<cStack&>(me["items"]);
int page = items.getPage();
shop.clearItem((which[3] - '1') + 5 * page);
put_shop_in_dlog(me, shop, which_shop);
items.setPage(page);
change_shop_dlog_items_page(me, "stay", shop);
return true;
}
static bool add_shop_entry(cDialog& me, std::string type, cShop& shop, size_t which_shop) {
if(shop.size() == 30) {
giveError("There is no more room in this shop to add another item. Each shop can only have up to 30 items.", &me);
return true;
}
if(type == "item" || type == "opt") {
size_t which_item = 0, amount = 0;
edit_shop_item(me, which_item, amount, type == "opt");
if(scenario.scen_items[which_item].variety == eItemType::NO_ITEM)
return true;
if(scenario.scen_items[which_item].variety == eItemType::GOLD)
return true;
if(type == "item")
shop.addItem(which_item, scenario.scen_items[which_item], amount);
else shop.addItem(which_item, scenario.scen_items[which_item], amount % 1000, amount / 1000);
} else if(type == "spec") {
cItem item('spec');
size_t amount = 0;
edit_shop_special(me, item, amount);
if(item.item_level < 0)
return true;
shop.addSpecial(item.full_name, item.desc, item.graphic_num, item.item_level, item.value, amount);
} else if(type == "class") {
int n = get_monst_abil_num("Which special class?", 0, 100, me);
if(n == 0) return true;
shop.addSpecial(eShopItemType::CLASS, n);
} else {
eStrType list;
eShopItemType item;
std::string prompt;
if(type == "mage") {
list = STRT_MAGE;
item = eShopItemType::MAGE_SPELL;
prompt = "Which mage spell?";
} else if(type == "priest") {
list = STRT_PRIEST;
item = eShopItemType::PRIEST_SPELL;
prompt = "Which priest spell?";
} else if(type == "alch") {
list = STRT_ALCHEMY;
item = eShopItemType::ALCHEMY;
prompt = "Which recipe?";
} else if(type == "skill") {
list = STRT_SKILL;
item = eShopItemType::SKILL;
prompt = "Which skill?";
} else if(type == "treas") {
list = STRT_TREASURE;
item = eShopItemType::TREASURE;
prompt = "What class of treasure?";
} else if(type == "heal") {
list = STRT_HEALING;
prompt = "What kind of healing?";
}
int i = choose_text(list, -1, &me, prompt);
if(i == -1) return true;
if(list == STRT_HEALING) {
item = eShopItemType(int(eShopItemType::HEAL_WOUNDS) + i);
i = 0;
}
shop.addSpecial(item, i);
}
put_shop_in_dlog(me, shop, which_shop);
int atItem = shop.size() - 1;
int onPage = atItem / 5;
dynamic_cast<cStack&>(me["items"]).setPage(onPage);
change_shop_dlog_items_page(me, "stay", shop);
return true;
}
void edit_shop(size_t which_shop, cDialog* parent) {
using namespace std::placeholders;
if(which_shop == scenario.shops.size())
scenario.shops.emplace_back("New Shop");
cShop shop = scenario.shops[which_shop];
cDialog shop_dlg("edit-shop", parent);
shop_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false));
shop_dlg["okay"].attachClickHandler(std::bind(save_shop_from_dlog, _1, std::ref(shop), std::ref(which_shop), true));
shop_dlg["pickface"].attachClickHandler(std::bind(pick_picture, PIC_TALK, _1, "", "face"));
shop_dlg.attachClickHandlers(std::bind(change_shop_dlog_items_page, _1, _2, std::ref(shop)), {"up", "down"});
shop_dlg.attachClickHandlers(std::bind(delete_shop_entry, _1, _2, std::ref(shop), std::ref(which_shop)), {"del1", "del2", "del3", "del4", "del5"});
shop_dlg.attachClickHandlers(std::bind(edit_shop_entry, _1, _2, std::ref(shop)), {"ed1", "ed2", "ed3", "ed4", "ed5"});
shop_dlg.attachClickHandlers(std::bind(add_shop_entry, _1, _2, std::ref(shop), std::ref(which_shop)), {"item", "opt", "spec", "mage", "priest", "alch", "skill", "treas", "heal", "class"});
shop_dlg["rand"].attachClickHandler([&shop,which_shop](cDialog& me, std::string, eKeyMod) -> bool {
shop = cShop('junk');
put_shop_in_dlog(me, shop, which_shop);
return true;
});
if(scenario.shops.size() == 1 || parent != nullptr) {
shop_dlg["left"].hide();
shop_dlg["right"].hide();
} else {
shop_dlg.attachClickHandlers(std::bind(change_shop_dlog_page, _1, _2, std::ref(shop), std::ref(which_shop)), {"left", "right"});
}
dynamic_cast<cStack&>(shop_dlg["items"]).setPageCount(6);
put_shop_in_dlog(shop_dlg, shop, which_shop);
shop_dlg.run();
}
static void put_save_rects_in_dlog(cDialog& me) {
short i;

View File

@@ -9,6 +9,7 @@ short edit_item_type(short which_item);
cItem edit_item_abil(cItem starting_record,short which_item,cDialog& parent);
void edit_spec_item(short which_item);
void edit_quest(size_t which_quest);
void edit_shop(size_t which_shop, cDialog* parent = nullptr);
void edit_save_rects();
void edit_horses();
void edit_add_town();

View File

@@ -177,7 +177,7 @@ short choose_text_res(std::string res_list,short first_t,short last_t,unsigned s
return dlog.show(cur_choice);
}
short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, const char* title) {
short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, std::string title) {
location view_loc;
std::vector<std::string> strings;
@@ -263,6 +263,9 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, con
case STRT_TRAP:
strings = *ResMgr::get<StringRsrc>("trap-types");
break;
case STRT_HEALING:
strings = *ResMgr::get<StringRsrc>("shop-specials");
break;
case STRT_BUTTON:
for(int btn : available_btns) {
strings.push_back(basic_buttons[btn].label);
@@ -327,6 +330,9 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, con
case STRT_QUEST_STATUS:
strings = {"Available", "Started", "Completed", "Failed"};
break;
case STRT_TREASURE:
strings = {"0 - Junk", "1 - Lousy", "2 - So-so", "3 - Good", "4 - Great"};
break;
}
if(cur_choice < 0 || cur_choice >= strings.size())
cur_choice = -1;

View File

@@ -12,6 +12,7 @@ enum eStrType {
STRT_SHOP, STRT_COST_ADJ, STRT_STAIR_MODE, STRT_TALK_NODE,
STRT_STATUS, STRT_SPELL_PAT, STRT_SUMMON, STRT_TALK,
STRT_ENCHANT, STRT_DIR, STRT_QUEST, STRT_QUEST_STATUS,
STRT_HEALING, STRT_TREASURE,
};
bool cre(short val,short min,short max,std::string text1,std::string text2,cDialog* parent) ;
@@ -21,7 +22,7 @@ void put_choice_pics(short g_type);
pic_num_t choose_graphic(short cur_choice,ePicType g_type,cDialog* parent);
short choose_background(short cur_choice, cDialog* parent);
short choose_text_res(std::string res_list,short first_t,short last_t,unsigned short cur_choice,cDialog* parent,const char *title);
short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent,const char* title);
short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent,std::string title);
void edit_text_str(short which_str,short mode);
bool edit_spec_enc(short which_node,short mode,cDialog* parent);
short get_fresh_spec(short which_mode);

View File

@@ -218,7 +218,7 @@ bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
if(info.type == eShopItemType::ITEM) {
int end = info.first + info.count;
end = min(end, scenario.scen_items.size());
shop.addItems(scenario.scen_items.begin() + info.first, scenario.scen_items.begin() + end, cShop::INFINITE);
shop.addItems(info.first, scenario.scen_items.begin() + info.first, scenario.scen_items.begin() + end, cShop::INFINITE);
} else {
int max = 62;
if(info.type == eShopItemType::ALCHEMY)