1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2026-07-04 17:32:43 +00:00

ru:: loading saved games is now available

This commit is contained in:
Zeno Rogue
2025-12-07 22:54:18 +01:00
parent 01c675146c
commit fe53b19f5a
7 changed files with 186 additions and 25 deletions
+7 -1
View File
@@ -94,7 +94,7 @@ struct room {
string id, roomname;
renderbuffer *rbuf;
cell *where;
short block_at[room_y][room_x];
array<array<short, room_x>, room_y> block_at, orig_block_at;
bool fov[room_y][room_x];
bool which_map_rendered;
@@ -338,6 +338,8 @@ struct entity {
virtual bool hit_by_missile(missile *m) { return false; }
virtual void on_fountain();
virtual void on_reset_all() {}
};
struct statdata {
@@ -605,6 +607,7 @@ struct ghost : public enemy {
s.act("extra_invinc", extra_invinc, 2 * game_fps).
only_full().act("flipped", flipped, false).act("xp", ghost_xp, 50).act("hp", ghost_hp, 100).act("where", where, {0, 0});
}
void on_reset_all() override { destroyed = true; }
};
struct snake : public enemy {
@@ -633,6 +636,7 @@ struct disnake : public snake {
void unact() override { destroyed = true; }
int bite() override { return 5; }
void on_fountain() override { destroyed = true; }
void on_reset_all() override { destroyed = true; }
virtual void hs(stater& s) override { snake::hs(s); s.act("respawn", respawn, {0, 0}); }
};
@@ -788,6 +792,7 @@ struct loot : public item {
struct ghost_item : public item {
virtual void hs(stater& s) override { item::hs(s); s.act("respawn", respawn, {0, 0}).act("qty", qty, 0); }
void on_reset_all() override { destroyed = true; }
};
struct missile : public entity {
@@ -801,6 +806,7 @@ struct missile : public entity {
struct missile* as_missile() override { return this; }
virtual void hs(stater& s) override { entity::hs(s); s.act("power", power, 0).act("where", where, {0, 0}); }
void on_fountain() override { destroyed = true; }
void on_reset_all() override { destroyed = true; }
};
struct ice_missile : public missile {
+1
View File
@@ -331,6 +331,7 @@ void load_map(string fname) {
else err("load_map", s);
}
for(auto& [c,r]: rooms) {
r.orig_block_at = r.block_at;
for(auto& e: r.entities) {
e->hs(resetter);
if(e->id != "") {
+18 -16
View File
@@ -10,7 +10,7 @@ room *stable_room;
xy stable_where;
void regenerate_all() {
m.hs(fountain_resetter);
m.hp = m.max_hp();
for(auto& p: powers) p.refill();
for(auto& r: rooms) for(auto& e: r.second.entities) e->on_fountain();
revert_all(fountain_revert);
@@ -57,33 +57,35 @@ man::man() {
}
void man::hs(stater& s) {
entity::hs(s);
s.act("facing", facing, 1)
auto& s1 = s.only_full();
s1.act("facing", facing, 1)
.act("attack_facing", attack_facing, 1)
.act("attack_when", attack_when, 0)
.act("on_floor_when", on_floor_when, 0)
.act("xp", experience, 0)
.act("last_action", last_action, 0);
sact(s, "hair", hair);
sact(s, "eyes", eye);
sact(s1, "hair", hair);
sact(s1, "eyes", eye);
string z = unspace(backstory);
s.act("backstory", z, "");
s1.act("backstory", z, "");
backstory = respace(z);
int prof = (int) profession; s.act("profession", prof, -1); profession = (stat) prof;
for(auto st: allstats) s.act(statinfos[st].name, base_stats[st], 10);
int prof = (int) profession; s1.act("profession", prof, -1); profession = (stat) prof;
for(auto st: allstats) s1.act(statinfos[st].name, base_stats[st], 10);
auto sdata = [&s] (statdata& sd, string prefix) {
for(auto st: allstats) s.act(prefix + statinfos[st].name, sd.stats[st], 10);
s.act(prefix + "jump_control", sd.jump_control, 0);
s.act(prefix + "coyote_time", sd.coyote_time, 0);
s.act(prefix + "hallucinating", sd.hallucinating, 0);
s.act(prefix + "detect_area", sd.detect_area, 0);
s.act(prefix + "detect_cross", sd.detect_cross, 0);
s.act(prefix + "rough_detect", sd.rough_detect, 0);
auto sdata = [&s1] (statdata& sd, string prefix) {
for(auto st: allstats) s1.act(prefix + statinfos[st].name, sd.stats[st], 10);
s1.act(prefix + "jump_control", sd.jump_control, 0);
s1.act(prefix + "coyote_time", sd.coyote_time, 0);
s1.act(prefix + "hallucinating", sd.hallucinating, 0);
s1.act(prefix + "detect_area", sd.detect_area, 0);
s1.act(prefix + "detect_cross", sd.detect_cross, 0);
s1.act(prefix + "rough_detect", sd.rough_detect, 0);
};
sdata(current, "curr.");
sdata(next, "next.");
entity::hs(s);
}
void man::act() {
+2
View File
@@ -142,6 +142,7 @@ randeff morph_cat("Cat", "Turns you into a cat.", "You turn into a cat!", [] (da
}
else {
auto mcat = new cat;
mcat->id = "cat";
mcat->col = morph_cat_color;
m.morphed = mcat;
addMessage("You morph into a " + m.morphed->get_name() + "!");
@@ -156,6 +157,7 @@ randeff morph_capy("Capybara", "Turns you into a capybara.", "You turn into a ca
}
else {
m.morphed = new capybara;
m.morphed->id = "capy";
addMessage("You morph into a lovely capybara!");
}
}
+2
View File
@@ -393,6 +393,7 @@ void enable() {
geometry = gBinary4;
showstartmenu = false;
init_stats();
set_sval();
init_scales();
gen_powers();
@@ -496,6 +497,7 @@ void start_new_game() {
auto chk = arg::add3("-ru", enable)
+ arg::add3("-ru-start", start_new_game)
+ arg::add3("-ru-load", [] { arg::shift(); enable(); load_from(arg::args()); })
+ arg::add3("-ru-cheat", [] { arg::shift(); load_cheat(arg::args()); })
+ addHook(mapstream::hooks_loadmap, 100, [] (hstream& f, int id) {
if(id == 67) {
+152 -6
View File
@@ -33,8 +33,10 @@ template<class T> void save_via_stater(fhstream& f, T& t, string cat, bool alway
}
}
void save() {
fhstream f(save_name, "wt");
void save_as(string fname) {
fhstream f(fname, "wt");
println(f, "TIME ", gframeid);
save_via_stater(f, m, "MAN", false);
@@ -55,11 +57,17 @@ void save() {
if(!seen_count) continue;
println(f, "ROOM ", r.roomname);
shstream ss;
println(f, "ROOM ", r.id);
string code;
for(int y=0; y<room_y; y++)
for(int x=0; x<room_y; x++) println(ss, r.fov[y][x] ? '1' : '0');
println(f, as_hexstring(compress_string(ss.s)));
for(int x=0; x<room_y; x++) code += (r.fov[y][x] ? '1' : '0');
println(f, as_hexstring(compress_string(code)));
if(&r == current_room) println(f, "HERE");
for(int y=0; y<room_y; y++)
for(int x=0; x<room_y; x++) if(r.block_at[y][x] != r.orig_block_at[y][x])
println(f, "AT ", y, " ", x, " ", r.block_at[y][x]);
for(auto& e: r.entities) save_via_stater(f, *e, "ENTITY");
println(f);
@@ -78,4 +86,142 @@ void save() {
}
}
void save() {
save_as(save_name);
}
void reset_all() {
m.hs(resetter);
for(auto& p: powers) p.hs(resetter);
for(auto r: *all_effects) r.second->hs(resetter);
fountain_revert.clear();
death_revert.clear();
for(auto& [c,r]: rooms) {
for(auto& e: r.entities) { e->on_reset_all(); e->hs(resetter); }
for(int y=0; y<room_y; y++)
for(int x=0; x<room_y; x++) {
r.fov[y][x] = false;
}
r.block_at = r.orig_block_at;
}
}
void load_reverts(fhstream& f, revert_stack& stack) {
string s;
while(true) {
s = scanline_noblank(f);
if(s == "") return;
s += " ";
revert_type rev;
string cur = "";
for(char c: s) if(c == ' ') rev.push_back(cur), cur = ""; else cur += c;
stack.push_back(rev);
}
}
template<class T> void load_hs(fhstream &f, T& t) {
string s;
while(true) {
s = scanline_noblank(f);
if(s == "") return;
auto pos = s.find("=");
if(pos == string::npos) { println(hlog, "warning: incorrect HS line: ", s); return; }
loader l;
l.name = s.substr(0, pos);
l.value = s.substr(pos + 1);
t.hs(l);
if(!l.loaded) println(hlog, "warning: could not read hs: ", s, " for entity with ID: ", t.id);
}
}
void load_from(string fname) {
reset_all();
fhstream f(fname, "rt");
room *current_room = nullptr;
power *current_power = nullptr;
entity* current_entity = nullptr;
string s;
while(!feof(f.f)) {
s = scanline_noblank(f);
if(s == "") continue;
auto pos = s.find(" ");
if(pos == string::npos) {
if(s == "HERE") { if(!current_room) { println(hlog, "warning: no current room"); continue; } rogue_unlike::current_room = current_room; }
else if(s == "FOUNTAINS")
load_reverts(f, fountain_revert);
else if(s == "DEATH")
load_reverts(f, death_revert);
else
println(hlog, "warning: ill-formed string in save: ", s); continue;
}
string cap = s.substr(0, pos);
string param = s.substr(pos+1);
if(cap == "TIME") {
sscanf(param.c_str(), "%d", &gframeid);
}
else if(cap == "ROOM") {
try {
current_room = find_room_by_id(param);
string code = decompress_string(from_hexstring(scanline_noblank(f)));
int pos = 0;
for(int y=0; y<room_y; y++) for(int x=0; x<room_y; x++) current_room->fov[y][x] = code[pos++] == '1';
}
catch(hr_name_error& e) {
println(hlog, "warning: could not find room: ", param);
}
}
else if(cap == "AT") {
if(!current_room) { println(hlog, "warning: no current room"); continue; }
int x, y, c;
sscanf(param.c_str(), "%d%d%d", &x, &y, &c);
current_room->block_at[y][x] = c;
}
else if(cap == "ENTITY") {
if(!current_room) { println(hlog, "warning: no current room"); continue; }
auto add = [&] (std::unique_ptr<entity>&& e) { current_room->entities.emplace_back(std::move(e)); current_entity = &*(current_room->entities.back()); };
if(param == "GHOST") add(std::make_unique<ghost>());
else if(param == "BONES") add(std::make_unique<ghost_item>());
else if(param == "FIREMISSILE") add(std::make_unique<fire_missile>());
else if(param == "DISNAKE") add(std::make_unique<disnake>());
else try { current_entity = find_entity_by_id(param); } catch(hr_name_error& e) { current_entity = nullptr; continue; }
load_hs(f, *current_entity);
}
else if(cap == "MORPH") {
if(param == "cat") { m.morphed = new cat; load_hs(f, *m.morphed); }
else if(param == "capy") { m.morphed = new capybara; load_hs(f, *m.morphed); }
}
else if(cap == "MAN") {
load_hs(f, m);
}
else if(cap == "POWER") {
current_power = nullptr;
try {
current_power = &find_power_by_id(param);
load_hs(f, *current_power);
current_power->randeffs = {};
}
catch(hr_name_error& e) {
println(hlog, "warning: unknown power: ", param);
}
}
else if(cap == "EFFECT") {
if(!all_effects->count(param)) {
println(hlog, "warning: unknown effect: ", param);
}
else {
randeff* current_effect = (*all_effects)[param];
if(current_power) current_power->randeffs.push_back(current_effect);
load_hs(f, *current_effect);
}
}
else {
println(hlog, "warning: could not understand save line: ", s);
}
}
}
}
+4 -2
View File
@@ -91,8 +91,7 @@ statarray<statinfo> profdata;
vector<string> professions = { "Warrior", "Morpher", "Sorcerer", "Rogue" };
void stat_screen(bool editable) {
void init_stats() {
statinfos[stat::str] = {'s', "Strength", "Affects the strength of your physical attacks."};
statinfos[stat::con] = {'t', "Toughness", "Affects the amount of hitpoints you have."};
statinfos[stat::wis] = {'w', "Wisdom", "Affects the power of your alchemy."};
@@ -102,6 +101,9 @@ void stat_screen(bool editable) {
profdata[stat::con] = {'m', "Morpher", "Morpher start with an ability to transform into small animals."};
profdata[stat::wis] = {'s', "Sorcerer", "Sorcerers start with an ability to cast fire spells."};
profdata[stat::dex] = {'r', "Rogue", "Rogues start with an ability to detect hidden passages and traps."};
}
void stat_screen(bool editable) {
render_the_map();
draw_inventory_frame();