Skip to content
Permalink
Browse files
Allow taking items from a scenario even if they summon monsters or ha…
…ve custom graphics. Breaks older saved games.

(Older saved games might still work if not in a scenario.)
- Remove items that call a special node when entering a new scenario
- Remove stone block if a monster is placed on it
- Monsters captured by Capture Soul now persist across scenarios
- Fix barrels/crates not being restored when re-entering the town
- Fix issue when saving monster status effects
- Fix version number stored in saved game file; also, it's now stored in hexadecimal
- Fix issue with saving which would have caused all but the first timer to be ignored when loading the saved game
- Fix timers being written to out-of-bounds memory when loading a saved game
- Fix use of std::skipws where std::ws was intended
- Fix issue with the fields array being shifted right by one tile on loading; also, fields array is now saved as hexadecimal
- Fields and terrain array use the town's dimension instead of dimension hard-coded (in the case of fields) or stored in the file
- Fix PC editor remove from scenario option not working properly
- Reconstruct the universe when loading a saved game to ensure there isn't leakage from the previous universe
- Fix excess padding in output tarballs when the filesize is a multiple of 512
- Add hasFile function to tarball class
  • Loading branch information
CelticMinstrel committed Jan 8, 2015
1 parent 095ab3b commit 8350a22ecb5801e65c582b619fe945fe69180444
@@ -894,7 +894,11 @@ static void handle_get_items(bool& did_something, bool& need_redraw, bool& need_
need_reprint = true;
}

static void handle_victory() {reload_startup();
static void handle_victory() {
// This is the point at which we need to transfer any exported graphics over to the party sheet.
univ.exportGraphics();
univ.exportSummons();
reload_startup();
overall_mode = MODE_STARTUP;
in_scen_debug = false;
ghost_mode = false;
@@ -2630,6 +2634,10 @@ void start_new_game() {
// And now, reconstruct the universe.
new(&univ) cUniverse(party_type);

// Destroy party graphics
extern cCustomGraphics spec_scen_g;
spec_scen_g.party_sheet.reset();

// Default is save maps
PSD[SDF_NO_MAPS] = 0;
save_maps = true;
@@ -197,67 +197,20 @@ void draw_monsters() {
// TODO: Windows special-cases the bear and drake, whose graphics are split between two columns/sheets. Is this necessary?
// It really doesn't look necessary to me, since each quadrant of the graphic is fetched separately.
// Technically what they do is always pass 0 as the final argument to get_monster_template_rect, instead of passing k; they also hardcode the sheet to look on (4 for drake, 5 for bear).
m_num_t m_num = univ.party.out_c[i].what_monst.monst[j];
pic_num_t this_monst = univ.scenario.scen_monsters[m_num].picture_num;
source_rect = get_monster_template_rect(this_monst,(univ.party.out_c[i].direction < 4) ? 0 : 1,k);
source_rect = get_monster_template_rect(picture_wanted,(univ.party.out_c[i].direction < 4) ? 0 : 1,k);
to_rect = monst_rects[(width - 1) * 2 + height - 1][k];
to_rect.offset(13 + 28 * where_draw.x,13 + 36 * where_draw.y);
rect_draw_some_item(monst_gworld[m_pic_index[this_monst].i/20], source_rect, terrain_screen_gworld,to_rect, sf::BlendAlpha);
rect_draw_some_item(monst_gworld[m_pic_index[picture_wanted].i/20], source_rect, terrain_screen_gworld,to_rect, sf::BlendAlpha);
}
}
}
}
}
if(is_town())
for(i = 0; i < univ.town->max_monst(); i++)
if((univ.town.monst[i].active != 0) && (univ.town.monst[i].spec_skill != 11))
if(party_can_see_monst(i)) {
check_if_monst_seen(univ.town.monst[i].number, univ.town.monst[i].cur_loc);
where_draw.x = univ.town.monst[i].cur_loc.x - center.x + 4;
where_draw.y = univ.town.monst[i].cur_loc.y - center.y + 4;
get_monst_dims(univ.town.monst[i].number,&width,&height);

for(k = 0; k < width * height; k++) {
store_loc = where_draw;
store_loc.x += k % width;
store_loc.y += k / width;
// customize?
if(univ.town.monst[i].picture_num >= 1000) {
sf::Texture* src_gw;
graf_pos_ref(src_gw, source_rect) = spec_scen_g.find_graphic((univ.town.monst[i].picture_num % 1000) +
k + ((univ.town.monst[i].direction < 4) ? 0 : width * height)
+ ((combat_posing_monster == i + 100) ? (2 * width * height) : 0));
ter = univ.town->terrain(univ.town.monst[i].cur_loc.x,univ.town.monst[i].cur_loc.y);
// in bed?
if((store_loc.x >= 0) && (store_loc.x < 9) && (store_loc.y >= 0) && (store_loc.y < 9) &&
univ.scenario.ter_types[ter].special == eTerSpec::BED &&
isHumanoid(univ.town.monst[i].m_type)
&& ((univ.town.monst[i].active == 1) || (univ.town.monst[i].target == 6)) &&
(width == 1) && (height == 1)) ////
draw_one_terrain_spot((short) where_draw.x,(short) where_draw.y,10000 + univ.scenario.ter_types[ter].flag1.u);
else Draw_Some_Item(*src_gw, source_rect, terrain_screen_gworld, store_loc, 1, 0);
}
if(univ.town.monst[i].picture_num < 1000) {
pic_num_t this_monst = univ.town.monst[i].picture_num;
source_rect = get_monster_template_rect(this_monst,
((univ.town.monst[i].direction < 4) ? 0 : 1) + ((combat_posing_monster == i + 100) ? 10 : 0),k);
ter = univ.town->terrain(univ.town.monst[i].cur_loc.x,univ.town.monst[i].cur_loc.y);
// in bed?
if((store_loc.x >= 0) && (store_loc.x < 9) && (store_loc.y >= 0) && (store_loc.y < 9) &&
(univ.scenario.ter_types[ter].special == eTerSpec::BED) &&
isHumanoid(univ.town.monst[i].m_type)
&& ((univ.town.monst[i].active == 1) || (univ.town.monst[i].target == 6)) &&
(width == 1) && (height == 1)) ////
draw_one_terrain_spot((short) where_draw.x,(short) where_draw.y,10000 + univ.scenario.ter_types[ter].flag1.u);
else Draw_Some_Item(monst_gworld[m_pic_index[this_monst].i/20], source_rect, terrain_screen_gworld, store_loc, 1, 0);
}
}
}
if(is_combat()) {
if(is_town() || is_combat()) {
for(i = 0; i < univ.town->max_monst(); i++)
if((univ.town.monst[i].active != 0) && (univ.town.monst[i].spec_skill != 11))
if(point_onscreen(center,univ.town.monst[i].cur_loc) && party_can_see_monst(i)) {
check_if_monst_seen(univ.town.monst[i].number,univ.town.monst[i].cur_loc);
check_if_monst_seen(univ.town.monst[i].number, univ.town.monst[i].cur_loc);
where_draw.x = univ.town.monst[i].cur_loc.x - center.x + 4;
where_draw.y = univ.town.monst[i].cur_loc.y - center.y + 4;
get_monst_dims(univ.town.monst[i].number,&width,&height);
@@ -266,35 +219,27 @@ void draw_monsters() {
store_loc = where_draw;
store_loc.x += k % width;
store_loc.y += k / width;
// customize?
if(univ.town.monst[i].picture_num >= 1000) {
ter = univ.town->terrain(univ.town.monst[i].cur_loc.x,univ.town.monst[i].cur_loc.y);
// in bed?
if(store_loc.x >= 0 && store_loc.x < 9 && store_loc.y >= 0 && store_loc.y < 9 &&
(univ.scenario.ter_types[ter].special == eTerSpec::BED) && isHumanoid(univ.town.monst[i].m_type)
&& (univ.town.monst[i].active == 1 || univ.town.monst[i].target == 6) &&
width == 1 && height == 1)
draw_one_terrain_spot((short) where_draw.x,(short) where_draw.y,10000 + univ.scenario.ter_types[ter].flag1.u);
else if(univ.town.monst[i].picture_num >= 1000) {
bool isParty = univ.town.monst[i].picture_num >= 10000;
sf::Texture* src_gw;
pic_num_t need_pic = (univ.town.monst[i].picture_num % 1000) + k;
if(univ.town.monst[i].direction >= 4) need_pic += width * height;
if(combat_posing_monster == i + 100) need_pic += (2 * width * height);
graf_pos_ref(src_gw, source_rect) = spec_scen_g.find_graphic(need_pic);
ter = univ.town->terrain(univ.town.monst[i].cur_loc.x,univ.town.monst[i].cur_loc.y);
if((store_loc.x >= 0) && (store_loc.x < 9) && (store_loc.y >= 0) && (store_loc.y < 9) &&
univ.scenario.ter_types[ter].special == eTerSpec::BED &&
isHumanoid(univ.town.monst[i].m_type)
&& ((univ.town.monst[i].active == 1) || (univ.town.monst[i].target == 6)) &&
(width == 1) && (height == 1))
draw_one_terrain_spot((short) where_draw.x,(short) where_draw.y,10000 + univ.scenario.ter_types[ter].flag1.u);
else Draw_Some_Item(*src_gw, source_rect, terrain_screen_gworld, store_loc, 1, 0);
}
if(univ.town.monst[i].picture_num < 1000) {
Draw_Some_Item(*src_gw, source_rect, terrain_screen_gworld, store_loc, 1, 0);
} else {
pic_num_t this_monst = univ.town.monst[i].picture_num;
int pic_mode = (univ.town.monst[i].direction) < 4 ? 0 : 1;
pic_mode += (combat_posing_monster == i + 100) ? 10 : 0;
source_rect = get_monster_template_rect(this_monst, pic_mode, k);
ter = univ.town->terrain(univ.town.monst[i].cur_loc.x,univ.town.monst[i].cur_loc.y);
if((store_loc.x >= 0) && (store_loc.x < 9) && (store_loc.y >= 0) && (store_loc.y < 9) &&
univ.scenario.ter_types[ter].special == eTerSpec::BED &&
isHumanoid(univ.town.monst[i].m_type)
&& ((univ.town.monst[i].active == 1) || (univ.town.monst[i].target == 6)) &&
(width == 1) && (height == 1))
draw_one_terrain_spot((short) where_draw.x,(short) where_draw.y,10000 + univ.scenario.ter_types[ter].flag1.u);
else Draw_Some_Item(monst_gworld[m_pic_index[this_monst].i/20], source_rect, terrain_screen_gworld, store_loc, 1, 0);
Draw_Some_Item(monst_gworld[m_pic_index[this_monst].i/20], source_rect, terrain_screen_gworld, store_loc, 1, 0);
}
}
}
@@ -315,7 +260,7 @@ void play_see_monster_str(unsigned short m, location monst_loc) {
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 ? univ.scenario.monst_strs[str1] : "", str2 ? univ.scenario.monst_strs[str2] : "", "", pic, type, NULL);
cStrDlog display_strings(str1 < 100 ? univ.scenario.monst_strs[str1] : "", str2 < 100 ? univ.scenario.monst_strs[str2] : "", "", pic, type, NULL);
display_strings.setSound(snd);
display_strings.setRecordHandler(cStringRecorder(NOTE_MONST).string1(str1).string2(str2).from(where1,where2).at(placename));
display_strings.show();
@@ -389,10 +334,16 @@ void draw_items(location where){
if(univ.town.items[i].variety != eItemType::NO_ITEM && univ.town.items[i].item_loc == where) {
if(univ.town.items[i].contained) continue;
if(party_can_see(where) >= 6) continue;
if(univ.town.items[i].graphic_num >= 1000){
if(univ.town.items[i].graphic_num >= 10000){
sf::Texture* src_gw;
graf_pos_ref(src_gw, from_rect) = spec_scen_g.find_graphic(univ.town.items[i].graphic_num - 10000, true);
to_rect = coord_to_rect(where_draw.x,where_draw.y);
terrain_there[where_draw.x][where_draw.y] = -1;
rect_draw_some_item(*src_gw,from_rect,terrain_screen_gworld,to_rect,sf::BlendAlpha);
}else if(univ.town.items[i].graphic_num >= 1000){
sf::Texture* src_gw;
graf_pos_ref(src_gw, from_rect) = spec_scen_g.find_graphic(univ.town.items[i].graphic_num - 1000);
to_rect = coord_to_rect(where.x,where.y);
to_rect = coord_to_rect(where_draw.x,where_draw.y);
terrain_there[where_draw.x][where_draw.y] = -1;
rect_draw_some_item(*src_gw,from_rect,terrain_screen_gworld,to_rect,sf::BlendAlpha);
}else{
@@ -753,14 +704,17 @@ char get_fluid_trim(location where,ter_num_t ter_type) {
}

// Sees if party has seen a monster of this sort, gives special messages as necessary
void check_if_monst_seen(unsigned short m_num, location at) {
void check_if_monst_seen(unsigned short m_num, location at) {\
// Give special messages if necessary
if(!univ.party.m_seen[m_num]) {
if(m_num < 10000 && !univ.party.m_seen[m_num]) {
univ.party.m_seen[m_num] = true;
play_see_monster_str(m_num, at);
}
// Make the monster vocalize if applicable
snd_num_t sound = univ.scenario.scen_monsters[m_num].ambient_sound;
snd_num_t sound;
if(m_num >= 10000)
sound = univ.party.summons[m_num - 10000].ambient_sound;
else sound = univ.scenario.scen_monsters[m_num].ambient_sound;
if(get_ran(1,1,100) < 10) play_sound(-sound);
}

@@ -194,9 +194,7 @@ static void put_item_info(cDialog& me,const cItemRec& s_i) {
std::string desc_str;

cPict& pic = dynamic_cast<cPict&>(me["pic"]);
if(s_i.graphic_num >= 1000) // was 150
pic.setPict(s_i.graphic_num - 1000, PIC_CUSTOM_ITEM);
else pic.setPict(s_i.graphic_num, PIC_ITEM);
pic.setPict(s_i.graphic_num, PIC_ITEM);

// id? magic?
cLed& id = dynamic_cast<cLed&>(me["id"]);
@@ -1210,7 +1210,9 @@ void place_glands(location where,m_num_t m_type) {
cItemRec store_i;
cMonster monst;

monst = univ.scenario.scen_monsters[m_type];
if(m_type >= 10000)
monst = univ.party.summons[m_type - 10000];
else monst = univ.scenario.scen_monsters[m_type];

if((monst.corpse_item >= 0) && (monst.corpse_item < 400) && (get_ran(1,1,100) < monst.corpse_item_chance)) {
store_i = get_stored_item(monst.corpse_item);
@@ -176,12 +176,16 @@ location get_monst_head(short m_num) {
}

short get_monst_picnum(m_num_t monst) {
if(monst >= 10000) return univ.party.summons[monst - 10000].picture_num;
return univ.scenario.scen_monsters[monst].picture_num;
}

ePicType get_monst_pictype(m_num_t monst) {
ePicType type = PIC_MONST;
short n = univ.scenario.scen_monsters[monst].picture_num;
short n;
if(monst >= 10000)
n = univ.party.summons[monst - 10000].picture_num;
else n = univ.scenario.scen_monsters[monst].picture_num;
if(n >= 1000){
type += PIC_CUSTOM;
switch(n / 1000){
@@ -204,9 +208,9 @@ ePicType get_monst_pictype(m_num_t monst) {
}

void get_monst_dims(m_num_t monst,short *width, short *height) {

*width = univ.scenario.scen_monsters[monst].x_width;
*height = univ.scenario.scen_monsters[monst].y_width;
cMonster& the_monst = monst >= 10000 ? univ.party.summons[monst - 10000] : univ.scenario.scen_monsters[monst];
*width = the_monst.x_width;
*height = the_monst.y_width;
}

// Used to set up monsters for outdoor wandering encounters.
@@ -216,7 +220,8 @@ void set_up_monst(short mode,m_num_t m_num) {

for(which = 0; which < univ.town->max_monst(); which++)
if(univ.town.monst[which].active == 0) {
univ.town.monst.assign(which, cCreature(m_num), univ.scenario.scen_monsters[m_num], PSD[SDF_EASY_MODE], univ.difficulty_adjust());
cMonster& monst = m_num >= 10000 ? univ.party.summons[m_num - 10000] : univ.scenario.scen_monsters[m_num];
univ.town.monst.assign(which, cCreature(m_num), monst, PSD[SDF_EASY_MODE], univ.difficulty_adjust());
univ.town.monst[which].active = 2;
univ.town.monst[which].summoned = 0;
univ.town.monst[which].attitude = mode + 1;
@@ -1270,9 +1275,11 @@ short place_monster(m_num_t which,location where) {
}

if(i < univ.town->max_monst()) {
univ.town.monst.assign(i, cCreature(which), univ.scenario.scen_monsters[which], PSD[SDF_EASY_MODE], univ.difficulty_adjust());
static_cast<cMonster&>(univ.town.monst[i]) = univ.scenario.scen_monsters[which];
univ.town.monst[i].attitude = univ.scenario.scen_monsters[which].default_attitude;
// 10000 or more means an exported summon saved with the party
cMonster& monst = which >= 10000 ? univ.party.summons[which - 10000] : univ.scenario.scen_monsters[which];
univ.town.monst.assign(i, cCreature(which), monst, PSD[SDF_EASY_MODE], univ.difficulty_adjust());
static_cast<cMonster&>(univ.town.monst[i]) = monst;
univ.town.monst[i].attitude = monst.default_attitude;
if(univ.town.monst[i].attitude % 2 == 0)
univ.town.monst[i].attitude = 1;
univ.town.monst[i].mobility = 1;
@@ -1283,6 +1290,7 @@ short place_monster(m_num_t which,location where) {

univ.town.set_crate(where.x,where.y,false);
univ.town.set_barrel(where.x,where.y,false);
univ.town.set_block(where.x,where.y,false);

return i;
}
@@ -161,8 +161,6 @@ void init_party_scen_data() {
for(i = 0; i < 5; i++)
for(j = 0; j < 10; j++)
univ.party.magic_store_items[i][j].variety = eItemType::NO_ITEM;
for(i = 0; i < 4; i++)
univ.party.imprisoned_monst[i] = 0;
for(i = 0; i < 256; i++)
univ.party.m_seen[i] = univ.party.m_noted[i] = 0;
// for(i = 0; i < 50; i++)
@@ -251,17 +249,8 @@ void put_party_in_scen(std::string scen_name) {
for(j = 0; j < 6; j++)
for(i = 23; i >= 0; i--) {
univ.party[j].items[i].special_class = 0;
// TODO: Don't take items just because they have a special graphic
if(univ.party[j].items[i].graphic_num >= 150) {
take_item(j,i + 30); // strip away special items
item_took = true;
}
if(univ.party[j].items[i].ability == eItemAbil::SUMMONING) {
take_item(j,i + 30); // strip away summoning items
item_took = true;
}
if(univ.party[j].items[i].ability == eItemAbil::MASS_SUMMONING) {
take_item(j,i + 30); // strip away summoning items
if(univ.party[j].items[i].ability == eItemAbil::CALL_SPECIAL) {
take_item(j,i + 30);
item_took = true;
}
}
@@ -2571,9 +2560,10 @@ m_num_t pick_trapped_monst() {
soulCrystal->getControl("pick" + n).hide();
}
else {
sp = get_m_name(univ.party.imprisoned_monst[i]);
m_num_t which = univ.party.imprisoned_monst[i];
sp = get_m_name(which);
soulCrystal->getControl("slot" + n).setText(sp);
get_monst = univ.scenario.scen_monsters[univ.party.imprisoned_monst[i]];
get_monst = which >= 10000 ? univ.party.summons[which - 10000] : univ.scenario.scen_monsters[which];
soulCrystal->getControl("lvl" + n).setTextToNum(get_monst.level);
}
}
@@ -445,7 +445,11 @@ void place_item_button(short which_button_to_put,short which_slot,short which_bu
to_rect.inset(-1,-1);
to_rect.offset(20,1);
from_rect.inset(2,2);
if(extra_val >= 1000) {
if(extra_val >= 10000) {
sf::Texture* src_gw;
graf_pos_ref(src_gw, from_rect) = spec_scen_g.find_graphic(extra_val - 10000, true);
rect_draw_some_item(*src_gw, from_rect, item_stats_gworld, to_rect,sf::BlendAlpha);
} else if(extra_val >= 1000) {
sf::Texture* src_gw;
graf_pos_ref(src_gw, from_rect) = spec_scen_g.find_graphic(extra_val - 1000);
rect_draw_some_item(*src_gw, from_rect, item_stats_gworld, to_rect,sf::BlendAlpha);
@@ -947,9 +951,7 @@ void notify_out_combat_began(cOutdoors::cWandering encounter,short *nums) {
}

std::string get_m_name(m_num_t num) {

////
//strcpy((char *) str,(char *) scenario.scen_monsters[num].m_name);
if(num >= 10000) return univ.party.summons[num - 10000].m_name;
return univ.scenario.scen_monsters[num].m_name;
}
std::string get_ter_name(ter_num_t num) {
@@ -274,8 +274,7 @@ void start_town_mode(short which_town, short entry_dir) {
for(k = 0; k < univ.town->max_dim(); k++) { // now load in saved setup,
// except that pushable things restore to orig locs
temp = univ.party.setup[i][j][k] << 8;
temp &= OBJECT_CRATE | OBJECT_BARREL | OBJECT_BLOCK;
univ.town.fields[j][k] &= ~(OBJECT_CRATE | OBJECT_BARREL | OBJECT_BLOCK);
temp &= ~(OBJECT_CRATE | OBJECT_BARREL | OBJECT_BLOCK);
univ.town.fields[j][k] |= temp;
}
}

0 comments on commit 8350a22

Please sign in to comment.