From da166fdd3e3e2dda876a1d61c1829335ac65dbfc Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Tue, 23 Dec 2025 18:09:34 +0100 Subject: [PATCH] implemented three more health powers --- rogueviz/ru/classes.cpp | 17 +++++++++++++++- rogueviz/ru/entity.cpp | 13 ++++++++++++ rogueviz/ru/globals.cpp | 2 ++ rogueviz/ru/man.cpp | 44 ++++++++++++++++++++++++++++++++++++----- rogueviz/ru/powers.cpp | 2 ++ rogueviz/ru/randeff.cpp | 12 +++++++---- rogueviz/ru/ru.cpp | 12 ++++++++++- 7 files changed, 91 insertions(+), 11 deletions(-) diff --git a/rogueviz/ru/classes.cpp b/rogueviz/ru/classes.cpp index cd2c5aa6..fd8d6019 100644 --- a/rogueviz/ru/classes.cpp +++ b/rogueviz/ru/classes.cpp @@ -28,7 +28,7 @@ struct randeff { void hs(struct stater& s); }; -enum class mod { burning, freezing, disarming }; +enum class mod { burning, freezing, disarming, vampire }; struct power { int key; @@ -379,6 +379,8 @@ struct man : public entity { entity *morphed = nullptr; vector effects; + int vampire, protection, healbubble; + int last_action; int experience; @@ -420,6 +422,8 @@ struct man : public entity { addMessage("OUCH! These spikes hurt!"); } + bool reduce_hp(int x) override; + void launch_attack(power *p, int fac, boxfun f); virtual void hs(stater& s); @@ -688,6 +692,17 @@ struct kestrel : public enemy { xy default_vel() override { return respawn_vel; } }; +struct healthbubble : public entity { + ld power; + xy siz() override { return {10, 10}; } + string glyph() override { return "o"; } + color_t color() override { return 0xA04040FF; } + void act() override; + string get_name() override { return "health bubble"; } + string get_help() override { return "Can be catched to replenish your health."; } + virtual void hs(stater& s) override { entity::hs(s); s.act("power", power, 0); } + }; + struct gridbug : public enemy { int next_move; xy siz() override { return {10, 10}; } diff --git a/rogueviz/ru/entity.cpp b/rogueviz/ru/entity.cpp index 0a69de7f..36947804 100644 --- a/rogueviz/ru/entity.cpp +++ b/rogueviz/ru/entity.cpp @@ -691,6 +691,19 @@ void kestrel::act() { } } +void healthbubble::act() { + stay_on_screen(); + apply_walls_reflect(); + apply_vel(); + + if(intersect(get_pixel_bbox(), m.get_pixel_bbox()) && gframeid >= invinc_end) { + addMessage("The " + hal()->get_name() + " heals you!"); + m.hp += power; + existing = false; + if(m.hp > m.max_hp()) m.hp = m.max_hp(); + } + } + void gridbug::act() { if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { diff --git a/rogueviz/ru/globals.cpp b/rogueviz/ru/globals.cpp index 2a35fa0d..167195a5 100644 --- a/rogueviz/ru/globals.cpp +++ b/rogueviz/ru/globals.cpp @@ -189,6 +189,8 @@ extern shiftmatrix scrm; struct hr_name_error : hr_exception { hr_name_error(const char *s) : hr_exception(s) {} }; +extern vector> new_entities; + string unspace(const string& s); string respace(const string& s); diff --git a/rogueviz/ru/man.cpp b/rogueviz/ru/man.cpp index 62525834..a45976f0 100644 --- a/rogueviz/ru/man.cpp +++ b/rogueviz/ru/man.cpp @@ -86,6 +86,9 @@ void man::hs(stater& s) { sdata(next, "next."); entity::hs(s); + s.act("protection", protection, 0); + s.act("vampire", vampire, 0); + s.act("healbubble", healbubble, 0); } void man::act() { @@ -131,6 +134,32 @@ void man::act() { check_fountains(); } +bool man::reduce_hp(int x) { + if(protection && gframeid >= invinc_end) { + ld fraction = 1 - 100 / (protection + 100); + int take = ceil(x * fraction); + if(take > protection) take = protection; + protection -= take; + x -= protection; + } + if(healbubble && gframeid >= invinc_end) { + int take = x; + if(take > healbubble) take = healbubble; + healbubble -= take; + auto d = m.get_dat(); + auto mi = std::make_unique(); + mi->id = "HEALBUBBLE"; + ld r = (rand() % 360) * degree; + mi->hs(fountain_resetter); + mi->power = take * 2; + mi->where = m.where; + mi->vel = { cos(r) * d.modv * 3, sin(r) * d.modv * 3 }; + mi->invinc_end = gframeid + 300; + new_entities.emplace_back(std::move(mi)); + } + return entity::reduce_hp(x); + } + bool man::can_see(entity& e) { if(m.current.detect_area) { ld d = hdist(to_hyper(m.where), to_hyper(e.where)); @@ -179,11 +208,16 @@ void man::launch_attack(power *p, int fac, boxfun f) { for(auto& e: current_room->entities) if(e->existing && intersect(e->get_pixel_bbox(), pb)) { int sav = e->invinc_end; - e->attacked((m.current.stats[stat::str] + 1) * 3 / 2); - for(auto& [m, qty]: p->mods) { - if(m == mod::burning) { e->invinc_end = sav; e->attacked(qty); } - if(m == mod::freezing) { e->invinc_end = sav; e->attacked(qty); } - if(m == mod::disarming && e->hidden()) { + int dam = (m.current.stats[stat::str] + 1) * 3 / 2; + e->attacked(dam); + for(auto& [md, qty]: p->mods) { + if(md == mod::burning) { e->invinc_end = sav; e->attacked(qty); } + if(md == mod::freezing) { e->invinc_end = sav; e->attacked(qty); } + if(md == mod::vampire) { + int dam1 = min(dam, m.vampire); + hp += dam1; m.vampire -= dam1; + } + if(md == mod::disarming && e->hidden()) { e->existing = false; addMessage("You have disarmed a "+e->hal()->get_name()+"."); } diff --git a/rogueviz/ru/powers.cpp b/rogueviz/ru/powers.cpp index efdde4ab..ada5de8b 100644 --- a/rogueviz/ru/powers.cpp +++ b/rogueviz/ru/powers.cpp @@ -32,12 +32,14 @@ power& power::be_weapon() { if(m == mod::burning) s = "flaming " + s; if(m == mod::freezing) s = "freezing " + s; if(m == mod::disarming) s = "disarming " + s; + if(m == mod::vampire) s = "vampiric " + s; } return s; }; auto gc = get_color; get_color = [gc, this] { auto col = gc(); for(auto& [m, qty]: mods) { + if(m == mod::vampire) col = 0x802020FF; if(m == mod::disarming) col = 0x4040C0FF; if(m == mod::burning) col = gradient(0xFFFF00FF, 0xFF0000FF, -1, sin(ticks/100), 1); if(m == mod::freezing) col = 0x8080FFFF; diff --git a/rogueviz/ru/randeff.cpp b/rogueviz/ru/randeff.cpp index d45addd0..e7152132 100644 --- a/rogueviz/ru/randeff.cpp +++ b/rogueviz/ru/randeff.cpp @@ -103,13 +103,17 @@ randeff health_regen("Regeneration", "Heals you over a period of time.", "You fe } }); randeff health_protect("Protection", "Makes you more resistant to damage.", "You feel protected!", [] (data &d) { - // m.protection += 3 * m.current.stats[stat::wis] + m.hp * m.current.stats[stat::wis] * 2 / 100; + m.protection += 3 * m.current.stats[stat::wis] + m.hp * m.current.stats[stat::wis] * 2 / 100; }); randeff health_vampire("Vampirism", "Attacks with your [weapon] restore your health.", "Your [weapon] wants blood!", [] (data &d) { - // m.vampire += 4 * m.current.stats[stat::wis] + m.max_hp() * m.current.stats[stat::wis] * 3 / 100; + if(d.mode == rev::start) + m.vampire += 4 * m.current.stats[stat::wis] + m.max_hp() * m.current.stats[stat::wis] * 3 / 100; + if(d.mode == rev::active && m.vampire) + m.next.mods.emplace_back(d.re->which_weapon, mod::vampire, 2 * m.current.stats[stat::wis] + 1e-6); }); randeff health_bubbles("Bubbles", "When you are attacked, you produce red bubbles that you can collect to heal yourself back.", "You feel something bouncy growing inside you!", [] (data &d) { - // m.healbubbles += 4 * m.current.stats[stat::wis] + m.max_hp() * m.current.stats[stat::wis] * 3 / 100; + if(d.mode == rev::start) + m.healbubble += 4 * m.current.stats[stat::wis] + m.max_hp() * m.current.stats[stat::wis] * 3 / 100; }); // fire powers @@ -181,7 +185,7 @@ void assign_potion_powers() { fire_weapon.which_weapon = wpn[0]; trap_disarm.which_weapon = wpn[1]; using relist = vector; - find_power("health").randeffs = relist{ pick(&health_heal, &health_regen, &health_protect), random_powers[0] }; + find_power("health").randeffs = relist{ pick(&health_heal, &health_regen, &health_protect, &health_vampire, &health_bubbles, &health_protect), random_powers[0] }; find_power("the thief").randeffs = relist{ pick(&trap_detect, &trap_snake, &trap_disarm, &trap_detect_cross), random_powers[1] }; find_power("polymorph").randeffs = relist{ pick(&morph_cat, &morph_capy), random_powers[2] }; find_power("reach").randeffs = relist{ pick(&jump_double, &jump_high, &jump_bubble, &jump_light), random_powers[3] }; diff --git a/rogueviz/ru/ru.cpp b/rogueviz/ru/ru.cpp index fe41a892..6d8269bc 100644 --- a/rogueviz/ru/ru.cpp +++ b/rogueviz/ru/ru.cpp @@ -145,6 +145,8 @@ tuple get_next_room(xy w, room *r, int which) { return {w, 1, -1}; } +vector> new_entities; + void playing_frame() { m.act(); @@ -156,6 +158,9 @@ void playing_frame() { for(auto& e: ents) if(!e->destroyed) *(mb++) = std::move(e); ents.resize(mb - ents.begin()); + for(auto &e: new_entities) ents.push_back(std::move(e)); + new_entities.clear(); + if(one_room) return; auto [w1, vmul, cr] = get_next_room(m.where, current_room); @@ -283,7 +288,12 @@ void run() { if(cmode == mode::playing) { titlecolor = 0xFFFFFF; mouseovers = current_room->roomname; - displayfr(vid.fsize, vid.fsize, 2, vid.fsize, "HP " + its(m.hp) + "/" + its(m.max_hp()), titlecolor, 0); + shstream ss; + print(ss, "HP ", m.hp, "/", m.max_hp()); + if(m.vampire) print(ss, " V"); + if(m.healbubble) print(ss, " B"); + if(m.protection) print(ss, " P"); + displayfr(vid.fsize, vid.fsize, 2, vid.fsize, ss.s, titlecolor, 0); if(current_target && current_target->existing) displayfr(vid.xres - vid.fsize, vid.fsize, 2, vid.fsize, "HP " + its(current_target->hp) + "/" + its(current_target->max_hp()) + " " + current_target->get_name(), titlecolor, 16); }