1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-06-26 15:12:48 +00:00

implemented the magic fountains (except ghosts and bones mechanics)

This commit is contained in:
Zeno Rogue 2025-05-05 18:56:13 +02:00
parent 1e23eed4e3
commit 2ae913ccfa
6 changed files with 96 additions and 18 deletions

View File

@ -25,7 +25,7 @@ struct power {
flagtype flags; flagtype flags;
int random_flavor; int random_flavor;
void init(); void init();
hr::function<void(data&)> act, paused_act; hr::function<void(data&)> act, paused_act, dead_act;
hr::function<string()> get_name; hr::function<string()> get_name;
hr::function<string()> get_desc; hr::function<string()> get_desc;
hr::function<string()> get_glyph; hr::function<string()> get_glyph;
@ -37,6 +37,7 @@ struct power {
power& be_weapon(); power& be_weapon();
power& be_resource(string plural); power& be_resource(string plural);
power& while_paused(); power& while_paused();
power& while_dead();
power& identified_name(string, string); power& identified_name(string, string);
power& be_wearable(string wear_effect, string remove_effect); power& be_wearable(string wear_effect, string remove_effect);
power& be_jewelry(string jtype, string desc); power& be_jewelry(string jtype, string desc);
@ -44,6 +45,11 @@ struct power {
}; };
extern vector<power> powers; extern vector<power> powers;
extern power *extra_life;
flagtype IDENTIFIED = Flag(1);
flagtype ACTIVE = Flag(2);
flagtype PARTIAL = Flag(4);
struct bbox { struct bbox {
int minx, miny, maxx, maxy; int minx, miny, maxx, maxy;
@ -230,9 +236,10 @@ struct entity {
} }
virtual bool reduce_hp(int x) { virtual bool reduce_hp(int x) {
if(hp < 0) return false;
if(gframeid < invinc_end) return false; if(gframeid < invinc_end) return false;
hp -= x; hp -= x;
if(hp < 0) on_kill(); if(hp <= 0) on_kill();
invinc_end = gframeid + 150; invinc_end = gframeid + 150;
return true; return true;
} }
@ -270,6 +277,8 @@ struct man : public entity {
virtual bool hurt_by_spikes() { return true; } virtual bool hurt_by_spikes() { return true; }
string get_name() override { return "alchemist"; } string get_name() override { return "alchemist"; }
string get_help() override { return "This is you."; } string get_help() override { return "This is you."; }
void on_kill() override;
}; };
extern man m; extern man m;
@ -403,6 +412,9 @@ struct item : public entity {
kino(); kino();
if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) {
addMessage(pickup_message); addMessage(pickup_message);
int q0 = powers[id].qty_filled;
int q1 = powers[id].qty_owned;
add_revert(death_revert, [this, q0, q1] { existing = true; powers[id].qty_filled = q0; powers[id].qty_owned = q1; });
powers[id].picked_up(qty); powers[id].picked_up(qty);
existing = false; existing = false;
} }

View File

@ -148,4 +148,12 @@ void asciiletter(ld minx, ld miny, ld maxx, ld maxy, const string& ch, color_t c
void render_the_map(); void render_the_map();
void shuffle_all(); void shuffle_all();
using revert_stack = vector<reaction_t>;
revert_stack death_revert, fountain_revert;
void add_revert(revert_stack& s, const reaction_t& what);
void revert_all(revert_stack& s);
} }

View File

@ -4,6 +4,16 @@ void handle_powers(data& d);
bool on_fountain; bool on_fountain;
room *fountain_room; room *fountain_room;
xy fountain_where;
void regenerate_all() {
m.hp = m.max_hp();
for(auto& p: powers) p.refill();
for(auto& r: rooms) for(auto& e: r.second.entities) e->regenerate();
revert_all(fountain_revert);
current_target = nullptr;
shuffle_all();
}
void check_fountains() { void check_fountains() {
bool next_on_fountain = false; bool next_on_fountain = false;
@ -14,13 +24,12 @@ void check_fountains() {
if(b == wFountain) next_on_fountain = true; if(b == wFountain) next_on_fountain = true;
} }
if(next_on_fountain && !on_fountain) { if(next_on_fountain && !on_fountain) {
if(extra_life->flags & ACTIVE) {
fountain_room = current_room; fountain_room = current_room;
fountain_where = m.where;
}
addMessage("A magic fountain! You feel safe and refill your potions."); addMessage("A magic fountain! You feel safe and refill your potions.");
m.hp = m.max_hp(); regenerate_all();
for(auto& p: powers) p.refill();
for(auto& r: rooms) for(auto& e: r.second.entities) e->regenerate();
current_target = nullptr;
shuffle_all();
} }
swap(on_fountain, next_on_fountain); swap(on_fountain, next_on_fountain);
} }
@ -58,4 +67,20 @@ void man::act() {
check_fountains(); check_fountains();
} }
void man::on_kill() {
entity::on_kill();
if(extra_life->flags & ACTIVE)
addMessage("You die... Press [key:Extra Life] to revive.");
else
addMessage("You die... permanently. You will have to create a new character. Or just press [key:Extra Life] for a narrative cheat.");
}
void add_revert(revert_stack& s, const reaction_t& what) {
s.push_back(what);
}
void revert_all(revert_stack& s) {
while(!s.empty()) { s.back()(); s.pop_back(); }
}
} }

View File

@ -1,14 +1,12 @@
namespace rogue_unlike { namespace rogue_unlike {
flagtype IDENTIFIED = Flag(1);
flagtype ACTIVE = Flag(2);
flagtype PARTIAL = Flag(4);
data fakedata; data fakedata;
power& power::is_starting() { qty_filled = qty_owned = 1; return self; } power& power::is_starting() { qty_filled = qty_owned = 1; return self; }
power& power::while_paused() { paused_act = act; return self; } power& power::while_paused() { paused_act = act; dead_act = act; return self; }
power& power::while_dead() { dead_act = act; return self; }
power& power::identified_name(string s, string desc) { power& power::identified_name(string s, string desc) {
auto gn = get_name; auto gn = get_name;
@ -122,6 +120,7 @@ void power::init() {
flags = 0; flags = 0;
act = [this] (data& d) { pf(d); }; act = [this] (data& d) { pf(d); };
paused_act = [] (data&) {}; paused_act = [] (data&) {};
dead_act = [] (data&) {};
get_name = [this] { return name; }; get_name = [this] { return name; };
get_desc = [this] { return desc; }; get_desc = [this] { return desc; };
get_color = [this] { return color; }; get_color = [this] { return color; };
@ -144,17 +143,47 @@ power& gen_power(int key, string name, string desc, string glyph, color_t color,
return p; return p;
} }
power *extra_life;
void gen_powers() { void gen_powers() {
powers.reserve(100); powers.reserve(100);
gen_power('1', "Extra Life", extra_life = &gen_power('1', "Extra Life",
"You are really proud of this potion, which, after you die, will let you return to the moment of time when you drank it. " "You are really proud of this potion, which, after you die, will let you return to the moment of time when you drank it. "
"Unfortunately it still requires an ingredient found only in the magical fountains of the Dungeons of Alchemy.\n\n" "Unfortunately it still requires an ingredient found only in the magical fountains of the Dungeons of Alchemy.\n\n"
"You can only drink this potion when at a magical fountain. To protect yourself from dying permanently, when you drink it, " "You can only drink this potion when at a magical fountain. To protect yourself from dying permanently, when you drink it, "
"you drink it automatically whenever you are at a magical fountain.", "you drink it automatically whenever you are at a magical fountain.",
"!", 0xFFFF00FF, "!", 0xFFFF00FF,
[] (data& d) { d.p->flags |= IDENTIFIED; } [] (data& d) {
).is_starting().be_potion(), d.p->flags |= IDENTIFIED;
if(d.keystate == 1) {
if(!m.existing) {
revert_all(death_revert);
regenerate_all();
if(!(extra_life->flags & ACTIVE)) extra_life->qty_filled = 0;
m.existing = true;
m.where = fountain_where;
current_room = fountain_room;
if(d.p->flags & ACTIVE)
addMessage("You wake up at the Magic Fountain.");
else
addMessage("You wake up from a very bad nightmare. Wow, you are really stressed.");
}
else if(!d.p->qty_filled)
addMessage("You need to find a Magic Fountain to prepare this potion.");
else if(d.p->flags & ACTIVE)
addMessage("This potion is drank automatically whenever you visit a Magic Fountain.");
else if(!on_fountain)
addMessage("For safety, you can only drink " + d.p->get_name() + " at the Magic Fountain.");
else {
d.p->flags = ACTIVE;
fountain_room = current_room; fountain_where = m.where;
addMessage("You drink the " + d.p->get_name() + " and you feel that nothing will stop you now!");
}
}
}
).is_starting().be_potion().while_dead();
extra_life->qty_filled = 0;
gen_power('d', "move right", gen_power('d', "move right",
"A special power of human beings, and most other animals, that they earn early in their life.", "A special power of human beings, and most other animals, that they earn early in their life.",
@ -212,6 +241,8 @@ void gen_powers() {
if(b == wDoor) { if(b == wDoor) {
current_room->replace_block(x, y, wSmashedDoor); current_room->replace_block(x, y, wSmashedDoor);
addMessage("You smash the door!"); addMessage("You smash the door!");
auto cr = current_room;
add_revert(fountain_revert, [cr, x, y] { cr->replace_block(x, y, wDoor); });
} }
} }
}).be_weapon(), }).be_weapon(),
@ -374,6 +405,7 @@ void handle_powers(data& d) {
if(keywasheld(p.key)) d.keystate |= 2; if(keywasheld(p.key)) d.keystate |= 2;
d.p = &p; d.p = &p;
if(cmode == mode::paused) p.paused_act(d); if(cmode == mode::paused) p.paused_act(d);
else if(!m.existing) p.dead_act(d);
else p.act(d); else p.act(d);
} }
} }

View File

@ -251,7 +251,7 @@ void man::draw() {
void render_room_objects(room *r) { void render_room_objects(room *r) {
initquickqueue(); initquickqueue();
if(r == current_room && m.visible_inv()) m.draw(); if(r == current_room && m.visible_inv() && m.existing) m.draw();
for(auto& e: r->entities) for(auto& e: r->entities)
if(e->existing && (cmode == mode::editmap || (e->visible(r) && e->visible_inv()))) if(e->existing && (cmode == mode::editmap || (e->visible(r) && e->visible_inv())))
e->draw(); e->draw();

View File

@ -105,8 +105,9 @@ void load_room(fhstream& f, cell *c) {
string cap = s.substr(0, pos); string cap = s.substr(0, pos);
string param = s.substr(pos+1); string param = s.substr(pos+1);
if(cap == "START") { if(cap == "START") {
current_room = &r; fountain_room = current_room = &r;
sscanf(param.c_str(), "%lf%lf", &m.where.x, &m.where.y); sscanf(param.c_str(), "%lf%lf", &m.where.x, &m.where.y);
fountain_where = m.where;
} }
else if(cap == "ITEM") { else if(cap == "ITEM") {
auto b = std::make_unique<item>(); auto b = std::make_unique<item>();