1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-06-26 07:02:49 +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;
int random_flavor;
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_desc;
hr::function<string()> get_glyph;
@ -37,6 +37,7 @@ struct power {
power& be_weapon();
power& be_resource(string plural);
power& while_paused();
power& while_dead();
power& identified_name(string, string);
power& be_wearable(string wear_effect, string remove_effect);
power& be_jewelry(string jtype, string desc);
@ -44,6 +45,11 @@ struct power {
};
extern vector<power> powers;
extern power *extra_life;
flagtype IDENTIFIED = Flag(1);
flagtype ACTIVE = Flag(2);
flagtype PARTIAL = Flag(4);
struct bbox {
int minx, miny, maxx, maxy;
@ -230,9 +236,10 @@ struct entity {
}
virtual bool reduce_hp(int x) {
if(hp < 0) return false;
if(gframeid < invinc_end) return false;
hp -= x;
if(hp < 0) on_kill();
if(hp <= 0) on_kill();
invinc_end = gframeid + 150;
return true;
}
@ -270,6 +277,8 @@ struct man : public entity {
virtual bool hurt_by_spikes() { return true; }
string get_name() override { return "alchemist"; }
string get_help() override { return "This is you."; }
void on_kill() override;
};
extern man m;
@ -403,6 +412,9 @@ struct item : public entity {
kino();
if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) {
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);
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 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;
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() {
bool next_on_fountain = false;
@ -14,13 +24,12 @@ void check_fountains() {
if(b == wFountain) next_on_fountain = true;
}
if(next_on_fountain && !on_fountain) {
fountain_room = current_room;
if(extra_life->flags & ACTIVE) {
fountain_room = current_room;
fountain_where = m.where;
}
addMessage("A magic fountain! You feel safe and refill your potions.");
m.hp = m.max_hp();
for(auto& p: powers) p.refill();
for(auto& r: rooms) for(auto& e: r.second.entities) e->regenerate();
current_target = nullptr;
shuffle_all();
regenerate_all();
}
swap(on_fountain, next_on_fountain);
}
@ -58,4 +67,20 @@ void man::act() {
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 {
flagtype IDENTIFIED = Flag(1);
flagtype ACTIVE = Flag(2);
flagtype PARTIAL = Flag(4);
data fakedata;
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) {
auto gn = get_name;
@ -122,6 +120,7 @@ void power::init() {
flags = 0;
act = [this] (data& d) { pf(d); };
paused_act = [] (data&) {};
dead_act = [] (data&) {};
get_name = [this] { return name; };
get_desc = [this] { return desc; };
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;
}
power *extra_life;
void gen_powers() {
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. "
"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 drink it automatically whenever you are at a magical fountain.",
"!", 0xFFFF00FF,
[] (data& d) { d.p->flags |= IDENTIFIED; }
).is_starting().be_potion(),
[] (data& d) {
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",
"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) {
current_room->replace_block(x, y, wSmashedDoor);
addMessage("You smash the door!");
auto cr = current_room;
add_revert(fountain_revert, [cr, x, y] { cr->replace_block(x, y, wDoor); });
}
}
}).be_weapon(),
@ -374,6 +405,7 @@ void handle_powers(data& d) {
if(keywasheld(p.key)) d.keystate |= 2;
d.p = &p;
if(cmode == mode::paused) p.paused_act(d);
else if(!m.existing) p.dead_act(d);
else p.act(d);
}
}

View File

@ -251,7 +251,7 @@ void man::draw() {
void render_room_objects(room *r) {
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)
if(e->existing && (cmode == mode::editmap || (e->visible(r) && e->visible_inv())))
e->draw();

View File

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