continue to check correct bounds when accessing scen_monsters, idem for party.summons

Scenario Editor: save default talking picture even if its value is NO_PICT
Character Editor: save correctly the number of days
This commit is contained in:
ALONSO Laurent
2021-11-14 12:57:32 +01:00
committed by Celtic Minstrel
parent c5044967aa
commit 5522c268bb
15 changed files with 95 additions and 76 deletions

View File

@@ -600,7 +600,7 @@ void check_if_monst_seen(unsigned short m_num, location at) {
// Make the monster vocalize if applicable
snd_num_t sound = -1;
if(m_num >= 10000)
sound = univ.party.summons[m_num - 10000].ambient_sound;
sound = univ.party.get_summon(m_num - 10000).ambient_sound;
else sound = univ.scenario.get_monster(m_num).ambient_sound;
if(sound > 0 && get_ran(1,1,100) < 10)
play_sound(-sound);

View File

@@ -681,7 +681,7 @@ void place_glands(location where,mon_num_t m_type) {
cMonster monst;
if(m_type >= 10000)
monst = univ.party.summons[m_type - 10000];
monst = univ.party.get_summon(m_type - 10000);
else monst = univ.scenario.get_monster(m_type);
if(monst.corpse_item >= 0 && monst.corpse_item < univ.scenario.scen_items.size() && get_ran(1,1,100) < monst.corpse_item_chance)

View File

@@ -125,7 +125,7 @@ location get_monst_head(short m_num) {
}
short get_monst_picnum(mon_num_t monst) {
if(monst >= 10000) return univ.party.summons[monst - 10000].picture_num;
if(monst >= 10000) return univ.party.get_summon(monst - 10000).picture_num;
return univ.scenario.get_monster(monst).picture_num;
}
@@ -133,7 +133,7 @@ ePicType get_monst_pictype(mon_num_t monst) {
ePicType type = PIC_MONST;
short n;
if(monst >= 10000)
n = univ.party.summons[monst - 10000].picture_num;
n = univ.party.get_summon(monst - 10000).picture_num;
else n = univ.scenario.get_monster(monst).picture_num;
if(n >= 1000){
type += PIC_CUSTOM;
@@ -157,7 +157,7 @@ ePicType get_monst_pictype(mon_num_t monst) {
}
std::pair<short,short> get_monst_dims(mon_num_t monst) {
cMonster& the_monst = monst >= 10000 ? univ.party.summons[monst - 10000] : univ.scenario.get_monster(monst);
cMonster& the_monst = monst >= 10000 ? univ.party.get_summon(monst - 10000) : univ.scenario.get_monster(monst);
return std::make_pair(the_monst.x_width, the_monst.y_width);
}
@@ -165,7 +165,7 @@ std::pair<short,short> get_monst_dims(mon_num_t monst) {
void set_up_monst(eAttitude mode,mon_num_t m_num) {
short which = univ.town.monst.size();
cMonster& monst = m_num >= 10000 ? univ.party.summons[m_num - 10000] : univ.scenario.get_monster(m_num);
cMonster& monst = m_num >= 10000 ? univ.party.get_summon(m_num - 10000) : univ.scenario.get_monster(m_num);
univ.town.monst.assign(which, cCreature(m_num), monst, univ.party.easy_mode, univ.difficulty_adjust());
univ.town.monst[which].active = 2;
univ.town.monst[which].summon_time = 0;
@@ -1101,7 +1101,7 @@ short place_monster(mon_num_t which,location where,bool forced) {
}
// 10000 or more means an exported summon saved with the party
cMonster& monst = which >= 10000 ? univ.party.summons[which - 10000] : univ.scenario.get_monster(which);
cMonster& monst = which >= 10000 ? univ.party.get_summon(which - 10000) : univ.scenario.get_monster(which);
univ.town.monst.assign(i, cCreature(which), monst, univ.party.easy_mode, univ.difficulty_adjust());
// TODO: Should this static_cast assignment be happening?
// One effect is resetting max health to ignore difficulty_adjust()

View File

@@ -2234,7 +2234,7 @@ mon_num_t pick_trapped_monst() {
else {
sp = get_m_name(which);
soulCrystal->getControl("slot" + n).setText(sp);
get_monst = which >= 10000 ? univ.party.summons[which - 10000] : univ.scenario.get_monster(which);
get_monst = which >= 10000 ? univ.party.get_summon(which - 10000) : univ.scenario.get_monster(which);
soulCrystal->getControl("lvl" + n).setTextToNum(get_monst.level);
}
}

View File

@@ -873,7 +873,7 @@ void notify_out_combat_began(cOutdoors::cWandering encounter,short *nums) {
}
std::string get_m_name(mon_num_t num) {
if(num >= 10000) return univ.party.summons[num - 10000].m_name;
if(num >= 10000) return univ.party.get_summon(num - 10000).m_name;
return univ.scenario.get_monster(num).m_name;
}
std::string get_ter_name(ter_num_t num) {

View File

@@ -132,7 +132,7 @@ void edit_day() {
dlog.run();
long long dialog_answer = minmax(0,500,dlog.getResult<long long>());
long long dialog_answer = minmax(0,500,dlog.getResult<long long>()-1);
univ.party.age = (long long) (3700) * (long long) (dialog_answer);
}

View File

@@ -54,6 +54,8 @@ public:
cItem &get_item(item_num_t item);
std::string &get_journal_string(int id);
std::string const &get_journal_string(int id) const;
cMonster &get_monster(mon_num_t monst);
cMonster const &get_monster(mon_num_t monst) const;
cQuest const &get_quest(int quest) const;
cQuest &get_quest(int quest);
cShop const &get_shop(int shop) const;
@@ -122,8 +124,6 @@ public:
ter_num_t get_trim_terrain(unsigned short ground, unsigned short trim_g, eTrimType trim);
cOutdoors& get_sector(int x, int y);
bool is_town_entrance_valid(spec_loc_t loc) const;
cMonster &get_monster(mon_num_t monst);
cMonster const &get_monster(mon_num_t monst) const;
bool is_ter_used(ter_num_t ter);
bool is_monst_used(mon_num_t monst);

View File

@@ -1146,7 +1146,7 @@ static bool handle_terpal_action(location cur_point, bool option_hit) {
set_string("Place the item:",scenario.get_item(mode_count).full_name);
break;
case DRAW_MONST:
if(k + 1 >= scenario.scen_monsters.size())
if(k + 1 < 0 || k + 1 >= scenario.scen_monsters.size())
break;
overall_mode = MODE_PLACE_CREATURE;
mode_count = k + 1;

View File

@@ -756,7 +756,7 @@ static bool edit_monst_type_event_filter(cDialog& me,std::string hit,cMonster& m
if(hit == "okay") {
if(save_monst_info(me,monst)) {
scenario.scen_monsters[which] = monst;
scenario.get_monster(which) = monst;
me.toast(true);
}
} else if(hit == "abils") {
@@ -767,17 +767,17 @@ static bool edit_monst_type_event_filter(cDialog& me,std::string hit,cMonster& m
put_monst_info_in_dlog(me,monst,which);
} else if(hit == "left") {
if(!save_monst_info(me,monst)) return false;
scenario.scen_monsters[which] = monst;
scenario.get_monster(which) = monst;
which--;
if(which < 1) which = scenario.scen_monsters.size() - 1;
monst = scenario.scen_monsters[which];
monst = scenario.get_monster(which);
put_monst_info_in_dlog(me,monst,which);
} else if(hit == "right") {
if(!save_monst_info(me,monst)) return false;
scenario.scen_monsters[which] = monst;
scenario.get_monster(which) = monst;
which++;
if(which >= scenario.scen_monsters.size()) which = 1;
monst = scenario.scen_monsters[which];
monst = scenario.get_monster(which);
put_monst_info_in_dlog(me,monst,which);
} else if(hit == "picktype") {
if(!save_monst_info(me,monst)) return false;
@@ -835,7 +835,7 @@ static bool pick_monst_picture(cDialog& me) {
bool edit_monst_type(short which) {
using namespace std::placeholders;
mon_num_t first = which;
cMonster monst = scenario.scen_monsters[which];
cMonster monst = scenario.get_monster(which);
cDialog monst_dlg("edit-monster");
monst_dlg["pickicon"].attachClickHandler(std::bind(pick_monst_picture,_1));
@@ -907,7 +907,7 @@ static void put_monst_abils_in_dlog(cDialog& me, cMonster& monst) {
} else if(i % 4 == 3) abils.addPage();
std::string name = abil.second.to_string(abil.first);
if(abil.first == eMonstAbil::SUMMON && abil.second.summon.type == eMonstSummon::TYPE)
name.replace(name.find("%s"), 2, scenario.scen_monsters[abil.second.summon.what].m_name);
name.replace(name.find("%s"), 2, scenario.get_monster(abil.second.summon.what).m_name);
me["abil-name" + id].setText(name);
me["abil-edit" + id].setText("Edit");
i++;
@@ -1000,7 +1000,7 @@ static void fill_monst_abil_detail(cDialog& me, cMonster& monst, eMonstAbil abil
me["monst"].setText(monst.m_name);
std::string name = detail.to_string(abil);
if(abil == eMonstAbil::SUMMON && detail.summon.type == eMonstSummon::TYPE)
name.replace(name.find("%s"), 2, scenario.scen_monsters[detail.summon.what].m_name);
name.replace(name.find("%s"), 2, scenario.get_monster(detail.summon.what).m_name);
me["name"].setText(detail.to_string(abil));
// These names start at line 80 in the strings file, but the first valid ability is ID 1, so add 79.
me["type"].setText(get_str("monster-abilities", 79 + int(abil)));
@@ -1098,7 +1098,7 @@ static void fill_monst_abil_detail(cDialog& me, cMonster& monst, eMonstAbil abil
else title.hide(), field.hide(), pick.hide();
} else if(cat == eMonstAbilCat::SUMMON) {
switch(detail.summon.type) {
case eMonstSummon::TYPE: me["summon"].setText(scenario.scen_monsters[detail.summon.what].m_name); break;
case eMonstSummon::TYPE: me["summon"].setText(scenario.get_monster(detail.summon.what).m_name); break;
case eMonstSummon::LEVEL: me["summon"].setTextToNum(detail.summon.what); break;
case eMonstSummon::SPECIES: me["summon"].setText(get_str("traits", detail.summon.what * 2 + 35)); break;
}
@@ -1526,7 +1526,7 @@ static void put_item_info_in_dlog(cDialog& me, cItem const &item, short which) {
std::string abil = item.getAbilName();
if(item.ability == eItemAbil::SUMMONING || item.ability == eItemAbil::MASS_SUMMONING)
abil.replace(abil.find("%s"), 2, scenario.scen_monsters[item.abil_data[1]].m_name);
abil.replace(abil.find("%s"), 2, scenario.get_monster(item.abil_data[1]).m_name);
me["abilname"].setText(abil);
}

View File

@@ -486,7 +486,7 @@ void writeMonstersToXml(ticpp::Printer&& data, cScenario& scenario) {
data.PushAttribute("id", i);
cMonster& monst = scenario.scen_monsters[i];
data.PushElement("name", monst.m_name);
if(monst.default_facial_pic > 0)
if(monst.default_facial_pic >= -1) // -1: means use monster pict
data.PushElement("default-face", monst.default_facial_pic);
data.OpenElement("pic");

View File

@@ -411,7 +411,7 @@ void set_up_terrain_buttons(bool reset) {
break;
}
case DRAW_MONST:
pic = scenario.scen_monsters[i].picture_num;
pic = scenario.get_monster(i).picture_num;
tiny_to = draw_rect;
frame_rect(mainPtr, tiny_to, sf::Color::Black);
if(pic >= 4000) {
@@ -716,7 +716,7 @@ void draw_terrain(){
need_hilite = true;
else if(overall_mode == MODE_PLACE_CREATURE || overall_mode == MODE_PLACE_SAME_CREATURE) {
extern short mode_count;
cMonster& monst = scenario.scen_monsters[mode_count];
cMonster& monst = scenario.get_monster(mode_count);
for(int x = 0; x < monst.x_width; x++) {
for(int y = 0; y < monst.y_width; y++) {
location this_spot = {where_draw.x - x, where_draw.y - y};
@@ -842,48 +842,49 @@ void draw_monsts() {
rectangle source_rect;
location where_draw,store_loc;
for(short i = 0; i < town->creatures.size(); i++)
if(town->creatures[i].number != 0) {
where_draw.x = town->creatures[i].start_loc.x - cen_x + 4;
where_draw.y = town->creatures[i].start_loc.y - cen_y + 4;
width = scenario.scen_monsters[town->creatures[i].number].x_width;
height = scenario.scen_monsters[town->creatures[i].number].y_width;
for(auto const &creature : town->creatures) {
if(creature.number == 0) continue;
where_draw.x = creature.start_loc.x - cen_x + 4;
where_draw.y = creature.start_loc.y - cen_y + 4;
auto const &monster=scenario.get_monster(creature.number);
width = monster.x_width;
height = monster.y_width;
for(short k = 0; k < width * height; k++) {
store_loc = where_draw;
if((where_draw.x == minmax(0,8,where_draw.x)) &&
(where_draw.y == minmax(0,8,where_draw.y)) &&
(scenario.scen_monsters[town->creatures[i].number].picture_num >= 1000)) {
std::tie(from_gworld,source_rect) = spec_scen_g.find_graphic((scenario.scen_monsters[town->creatures[i].number].picture_num + k) % 1000);
store_loc.x += k % width;
store_loc.y += k / width;
}
else if(scenario.scen_monsters[town->creatures[i].number].picture_num < 1000) {
m_start_pic = m_pic_index[scenario.scen_monsters[town->creatures[i].number].picture_num].i + k;
int which_sheet = m_start_pic / 20;
from_gworld = *ResMgr::textures.get("monst" + std::to_string(1 + which_sheet));
m_start_pic = m_start_pic % 20;
source_rect = calc_rect(2 * (m_start_pic / 10), m_start_pic % 10);
store_loc.x += k % width;
store_loc.y += k / width;
}
if(store_loc.x < 0 || store_loc.x > 8 || store_loc.y < 0 || store_loc.y > 8)
continue;
rectangle destrec;
destrec.left = 8 + BITMAP_WIDTH * store_loc.x;
destrec.right = destrec.left + BITMAP_WIDTH;
destrec.top = 8 + BITMAP_HEIGHT * store_loc.y;
destrec.bottom = destrec.top + BITMAP_HEIGHT;
destrec.left = destrec.right - (source_rect.right - source_rect.left);
destrec.top = destrec.bottom - (source_rect.bottom - source_rect.top);
destrec.offset(TER_RECT_UL_X,TER_RECT_UL_Y);
rect_draw_some_item(from_gworld, source_rect, mainPtr, destrec, sf::BlendAlpha);
for(short k = 0; k < width * height; k++) {
store_loc = where_draw;
if(where_draw.x == minmax(0,8,where_draw.x) && where_draw.y == minmax(0,8,where_draw.y) && monster.picture_num >= 1000) {
std::tie(from_gworld,source_rect) = spec_scen_g.find_graphic((monster.picture_num + k) % 1000);
store_loc.x += k % width;
store_loc.y += k / width;
}
else if(monster.picture_num>=0 && monster.picture_num < 1000 && monster.picture_num<m_pic_index.size()) {
m_start_pic = m_pic_index[monster.picture_num].i + k;
int which_sheet = m_start_pic / 20;
from_gworld = *ResMgr::textures.get("monst" + std::to_string(1 + which_sheet));
m_start_pic = m_start_pic % 20;
source_rect = calc_rect(2 * (m_start_pic / 10), m_start_pic % 10);
store_loc.x += k % width;
store_loc.y += k / width;
}
else
continue;
if(store_loc.x < 0 || store_loc.x > 8 || store_loc.y < 0 || store_loc.y > 8)
continue;
rectangle destrec;
destrec.left = 8 + BITMAP_WIDTH * store_loc.x;
destrec.right = destrec.left + BITMAP_WIDTH;
destrec.top = 8 + BITMAP_HEIGHT * store_loc.y;
destrec.bottom = destrec.top + BITMAP_HEIGHT;
destrec.left = destrec.right - (source_rect.right - source_rect.left);
destrec.top = destrec.bottom - (source_rect.bottom - source_rect.top);
destrec.offset(TER_RECT_UL_X,TER_RECT_UL_Y);
rect_draw_some_item(from_gworld, source_rect, mainPtr, destrec, sf::BlendAlpha);
}
}
}
static void update_item_rectangle(cPictNum const &pict, rectangle &rect)
@@ -1135,7 +1136,7 @@ void place_location() {
bool draw_field = false;
if(overall_mode == MODE_PLACE_CREATURE || overall_mode == MODE_PLACE_SAME_CREATURE) {
rectangle to_rect = draw_rect;
picture_wanted = scenario.scen_monsters[mode_count].picture_num;
picture_wanted = scenario.get_monster(mode_count).picture_num;
if(picture_wanted >= 4000) {
picture_wanted %= 1000;
to_rect.width() = to_rect.width() / 2;

View File

@@ -42,7 +42,7 @@ const char *day_str_2[] = {"Unused","Event code (0 - no event)","Event code (0 -
static void put_placed_monst_in_dlog(cDialog& me, cTownperson& monst, const short which) {
me["num"].setTextToNum(which);
me["type"].setText(scenario.scen_monsters[monst.number].m_name);
me["type"].setText(scenario.get_monster(monst.number).m_name);
// TODO: Make attitude an enum
dynamic_cast<cLedGroup&>(me["attitude"]).setSelected(boost::lexical_cast<std::string>(monst.start_attitude));
dynamic_cast<cLedGroup&>(me["mobility"]).setSelected("mob" + std::to_string(monst.mobility + 1));
@@ -50,7 +50,7 @@ static void put_placed_monst_in_dlog(cDialog& me, cTownperson& monst, const shor
me["picnum"].setTextToNum(monst.facial_pic);
// TODO: Use -1 instead of 0 for "no pic", since 0 is a valid talking picture
if(short(monst.facial_pic) < 0)
dynamic_cast<cPict&>(me["pic"]).setPict(scenario.scen_monsters[monst.number].picture_num, PIC_MONST);
dynamic_cast<cPict&>(me["pic"]).setPict(scenario.get_monster(monst.number).picture_num, PIC_MONST);
else if((monst.facial_pic >= 1000))
dynamic_cast<cPict&>(me["pic"]).setPict(monst.facial_pic - 1000,PIC_CUSTOM_TALK);
else dynamic_cast<cPict&>(me["pic"]).setPict(monst.facial_pic,PIC_TALK);
@@ -116,7 +116,7 @@ void edit_placed_monst(short which_m) {
static void put_placed_monst_adv_in_dlog(cDialog& me, cTownperson& monst, const short which) {
me["num"].setTextToNum(which);
me["type"].setText(scenario.scen_monsters[monst.number].m_name);
me["type"].setText(scenario.get_monster(monst.number).m_name);
int iTime = 0;
switch(monst.time_flag) {
case eMonstTime::ALWAYS: iTime = 0; break;
@@ -520,14 +520,14 @@ static void put_out_wand_in_dlog(cDialog& me, short which, const cOutdoors::cWan
if(wand.monst[i] == 0)
me[id].setText("Empty");
// TODO: Wait a second, if 0 is no monster, does that mean it's impossible to use monster 0? Should 1 be subtracted here?
else me[id].setText(scenario.scen_monsters[wand.monst[i]].m_name);
else me[id].setText(scenario.get_monster(wand.monst[i]).m_name);
}
for(short i = 0; i < 3; i++) {
std::string id = "ally" + std::to_string(i + 1);
if(wand.friendly[i] == 0)
me[id].setText("Empty");
// TODO: Wait a second, if 0 is no monster, does that mean it's impossible to use monster 0? Should 1 be subtracted here?
else me[id].setText(scenario.scen_monsters[wand.friendly[i]].m_name);
else me[id].setText(scenario.get_monster(wand.friendly[i]).m_name);
}
dynamic_cast<cLed&>(me["no-flee"]).setState(wand.cant_flee ? led_red : led_off);
dynamic_cast<cLed&>(me["forced"]).setState(wand.forced ? led_red : led_off);
@@ -604,7 +604,7 @@ static bool edit_out_wand_monst(cDialog& me, std::string hit, short which, cOutd
i = choose_text(STRT_MONST,wand.friendly[fld[4] - '1']-1,&me,"Choose Which Monster:") + 1;
if(i >= 0) wand.friendly[fld[4] - '1'] = i;
}
me[fld].setText(scenario.scen_monsters[i].m_name);
me[fld].setText(scenario.get_monster(i).m_name);
return true;
}

View File

@@ -222,6 +222,22 @@ cVehicle const &cParty::get_horse(int id) const {
return bad_vehicle;
}
cMonster const &cParty::get_summon(mon_num_t id) const
{
if (id<summons.size())
return summons[id];
static cMonster bad_monster=cMonster::bad();
return bad_monster;
}
cMonster &cParty::get_summon(mon_num_t id) {
if (id<summons.size())
return summons[id];
static cMonster bad_monster;
bad_monster=cMonster::bad();
return bad_monster;
}
void cParty::import_legacy(legacy::party_record_type const & old, cUniverse& univ){
scen_name = old.scen_name;
age = old.age;
@@ -1103,9 +1119,9 @@ void cParty::readFrom(std::istream& file){
cMonster monst;
bin >> std::ws;
monst.readFrom(bin);
if(i >= summons.size())
if(i >= summons.size() && i<1000)
summons.resize(i + 1);
summons[i] = monst;
get_summon(i) = monst;
} else if(cur == "JOURNAL") {
cJournal entry;
bin >> entry.day;

View File

@@ -221,6 +221,8 @@ public:
cVehicle const &get_boat(int id) const;
cVehicle &get_horse(int id);
cVehicle const &get_horse(int id) const;
cMonster &get_summon(mon_num_t id);
cMonster const &get_summon(mon_num_t id) const;
cParty(ePartyPreset party_preset = PARTY_DEFAULT);
~cParty();

View File

@@ -1154,7 +1154,7 @@ void cCustomUpdateState::check_item(cUniverse &universe, cItem& item) {
if(item.ability == eItemAbil::SUMMONING || item.ability == eItemAbil::MASS_SUMMONING) {
mon_num_t monst = item.abil_data[1];
if(monst >= 10000)
check_monst(universe, universe.party.summons[monst - 10000]);
check_monst(universe, universe.party.get_summon(monst - 10000));
else check_monst(universe, universe.scenario.get_monster(monst));
}
if(item.variety == eItemType::ARROW || item.variety == eItemType::BOLTS || item.variety == eItemType::MISSILE_NO_AMMO || item.variety == eItemType::THROWN_MISSILE)
@@ -1188,7 +1188,7 @@ void cUniverse::exportGraphics() {
if(monst > 0 && monst < scenario.scen_monsters.size())
state.check_monst(*this, scenario.scen_monsters[monst]);
else if(monst >= 10000 && monst - 10000 < party.summons.size())
state.check_monst(*this, party.summons[monst - 10000]);
state.check_monst(*this, party.get_summon(monst - 10000));
}
// And then, just add all the graphics, and update references to them
for(auto const &pic : state.pcs) {