From 03a3833e94e4542970c8de249881d260c7501b92 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Mon, 24 Aug 2026 14:41:45 +0200 Subject: [PATCH] ru:: armor actually works now --- rogueviz/ru/classes.cpp | 15 +++++++++++--- rogueviz/ru/entity.cpp | 45 +++++++++++++++++++++-------------------- rogueviz/ru/man.cpp | 31 ++++++++++++++++++++++++---- rogueviz/ru/powers.cpp | 29 ++++++++++++++++++++------ rogueviz/ru/ru.cpp | 4 ++++ 5 files changed, 89 insertions(+), 35 deletions(-) diff --git a/rogueviz/ru/classes.cpp b/rogueviz/ru/classes.cpp index 29f8fba6..83ebe3d0 100644 --- a/rogueviz/ru/classes.cpp +++ b/rogueviz/ru/classes.cpp @@ -249,6 +249,10 @@ struct stater { virtual stater& only_full() { return self; } }; +/* reduce_hp flags */ +constexpr flagtype NON_DODGEABLE = 1; +constexpr flagtype SPIKES = 2; + struct entity { string id; virtual ~entity() {} @@ -330,7 +334,7 @@ struct entity { virtual void attacked(int s, power *p) {} virtual void spiked() { - reduce_hp(10); + reduce_hp(10, nullptr, NON_DODGEABLE); spike_message(); } @@ -354,7 +358,7 @@ struct entity { virtual int invinc_time() { return 150; } - virtual bool reduce_hp(int x) { + virtual bool reduce_hp(int x, entity *attacker = nullptr, flagtype flags = 0) { if(hp < 0) return false; if(gframeid < invinc_end) return false; hp -= x; @@ -390,6 +394,7 @@ struct statdata { statarray stats; int jump_control, coyote_time, hallucinating; ld detect_area, detect_cross, rough_detect; + int heavy_armor, spikes, max_mageshield, stealth_bonus, dodge_value; void reset(); vector mods; vector> on_hit; @@ -428,6 +433,8 @@ struct man : public entity { virtual int max_hp() { return 10 * current.stats[stat::con]; } + int current_mageshield, mageshield_recharge_time; + void handle_morph(entity *m); bool can_see(entity& e); man(); @@ -457,7 +464,9 @@ struct man : public entity { addMessage("OUCH! These spikes hurt!"); } - bool reduce_hp(int x) override; + double stealth_ratio() { return 3 / (3. + current.stealth_bonus); } + + bool reduce_hp(int x, entity *attacker = nullptr, flagtype flags = 0) override; void launch_attack(power *p, int fac, boxfun f); diff --git a/rogueviz/ru/entity.cpp b/rogueviz/ru/entity.cpp index 02f1f1a0..f445240f 100644 --- a/rogueviz/ru/entity.cpp +++ b/rogueviz/ru/entity.cpp @@ -228,7 +228,7 @@ void entity::apply_walls() { for(int x = jbb.minx; x < jbb.maxx; x++) for(int y = obb.maxy; y < jbb.maxy; y++) { eWall b = current_room->at(x, y); if((walls[b].flags & W_PAIN_DOWN) && hurt_by_spikes()) { - reduce_hp(40); spike_message(); + reduce_hp(40, nullptr, NON_DODGEABLE); spike_message(); } } @@ -451,7 +451,7 @@ void boar::act() { kino(); if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { int s = where.x < m.where.x ? -1 : 1; - if(m.reduce_hp(15)) addMessage("The " + hal()->get_name() + " gores you!"); + if(m.reduce_hp(15, this)) addMessage("The " + hal()->get_name() + " gores you!"); auto dat = get_dat(); auto mdat = m.get_dat(); if(m.on_floor) m.vel.x = mdat.d * mdat.modv * -s * 1.5, m.vel.y = -mdat.d * mdat.modv * 2; @@ -462,15 +462,15 @@ void boar::act() { if(vel.x > 0) vel.x = max(vel.x - dat.d * dat.moda * 0.05, 0); if(vel.x < 0) vel.x = min(vel.x + dat.d * dat.moda * 0.05, 0); if(gframeid > invinc_end) { - if(intersect(extend(get_pixel_bbox(), 60 * dat.d, 0, 0, 0), m.get_pixel_bbox())) vel.x -= dat.d * dat.moda * 0.2; - if(intersect(extend(get_pixel_bbox(), 0, 60 * dat.d, 0, 0), m.get_pixel_bbox())) vel.x += dat.d * dat.moda * 0.2; + if(intersect(extend(get_pixel_bbox(), 60 * dat.d * m.stealth_ratio(), 0, 0, 0), m.get_pixel_bbox())) vel.x -= dat.d * dat.moda * 0.2; + if(intersect(extend(get_pixel_bbox(), 0, 60 * dat.d * m.stealth_ratio(), 0, 0), m.get_pixel_bbox())) vel.x += dat.d * dat.moda * 0.2; } } } void enemy::attacked(int dmg, power *p) { current_target = this; - if(reduce_hp(dmg)) { + if(reduce_hp(dmg, &m)) { if(!existing) addMessage("You kill the " + hal()->get_name() + "."); else addMessage("You hit the " + hal()->get_name() + "."); } } @@ -487,7 +487,7 @@ void frog::act() { kino(); if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { int s = where.x < m.where.x ? -1 : 1; - if(m.reduce_hp(20)) addMessage("The giant frog crushes you!"); + if(m.reduce_hp(20, this)) addMessage("The giant frog crushes you!"); auto dat = get_dat(); auto mdat = m.get_dat(); if(m.on_floor) m.vel.x = mdat.d * mdat.modv * -s * 1.5, m.vel.y = -mdat.d * mdat.modv * 2; @@ -497,7 +497,8 @@ void frog::act() { vel.x = 0; if(gframeid > invinc_end) { auto dat = get_dat(); - if(jphase == 0 && gframeid >= jump_at && intersect(extend(get_pixel_bbox(), 100 * dat.d, 100 * dat.d, 20 * dat.d, 20 * dat.d), m.get_pixel_bbox())) { + ld f = m.stealth_ratio(); + if(jphase == 0 && gframeid >= jump_at && intersect(extend(get_pixel_bbox(), 100 * dat.d * f, 100 * dat.d * f, 20 * dat.d * f, 20 * dat.d * f), m.get_pixel_bbox())) { vel.y = -2 * dat.modv; jphase = 1; } @@ -550,7 +551,7 @@ void ghost::act() { apply_vel(); if(intersect(get_pixel_bbox(), m.get_pixel_bbox()) && gframeid > invinc_end) { invinc_end = gframeid + 200; - if(m.reduce_hp(20)) addMessage("The " + hal()->get_name() + " passes through you!"); + if(m.reduce_hp(20, this)) addMessage("The " + hal()->get_name() + " passes through you!"); } } @@ -565,7 +566,7 @@ void snake::act() { vel.x = zero_vel.x + dat.d * dat.modv * dir; } if(intersect(get_pixel_bbox(), m.get_pixel_bbox()) && gframeid > invinc_end) { - if(m.reduce_hp(bite())) addMessage("The " + hal()->get_name() + " bites you!"); + if(m.reduce_hp(bite(), this)) addMessage("The " + hal()->get_name() + " bites you!"); } } @@ -589,11 +590,11 @@ void naga_warrior::act() { if(gmod(gframeid, 100) == 0) { bbox b = get_pixel_bbox_at(xy{where.x + dir * dsiz().x, where.y}); if(intersect(b, m.get_pixel_bbox()) && gframeid > invinc_end) { - if(m.reduce_hp(chop())) addMessage("The " + hal()->get_name() + " chops you!"); + if(m.reduce_hp(chop(), this)) addMessage("The " + hal()->get_name() + " chops you!"); } } if(intersect(get_pixel_bbox(), m.get_pixel_bbox()) && gframeid > invinc_end) { - if(m.reduce_hp(bite())) addMessage("The " + hal()->get_name() + " bites you!"); + if(m.reduce_hp(bite(), this)) addMessage("The " + hal()->get_name() + " bites you!"); } } @@ -732,7 +733,7 @@ void saw::act() { where = base->location_at(gframeid); auto bb = get_pixel_bbox(); if(intersect(bb, m.get_pixel_bbox())) { - if(m.reduce_hp(40)) addMessage("The " + get_name() + " shreds you!"); + if(m.reduce_hp(40, this)) addMessage("The " + get_name() + " shreds you!"); } } @@ -818,7 +819,7 @@ void kestrel::act() { apply_vel(); if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { - if(m.reduce_hp(chop())) addMessage("The " + hal()->get_name() + " claws you!"); + if(m.reduce_hp(chop(), this)) addMessage("The " + hal()->get_name() + " claws you!"); } } @@ -838,7 +839,7 @@ void healthbubble::act() { void gridbug::act() { if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { - if(m.reduce_hp(15)) addMessage("The " + hal()->get_name() + " zaps you!"); + if(m.reduce_hp(15, this)) addMessage("The " + hal()->get_name() + " zaps you!"); } if(gframeid < next_move || !visible(current_room) || gframeid < invinc_end) return; @@ -914,7 +915,7 @@ void bat::act() { apply_vel(); if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { - if(m.reduce_hp(15)) addMessage("The " + hal()->get_name() + " bites you!"); + if(m.reduce_hp(15, this)) addMessage("The " + hal()->get_name() + " bites you!"); } } @@ -970,7 +971,7 @@ void guineapig::act() { if(nonblocked(ca+j-3, s).second) { where = nonblocked(ca = gmod(ca+j-3, 8), s).first; if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { - if(m.reduce_hp(15)) { + if(m.reduce_hp(15, this)) { addMessage("The " + hal()->get_name() + " bites you!"); spindir *= -1; } @@ -1001,7 +1002,7 @@ void vtrap::act() { apply_vel(); if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { - if(m.reduce_hp(200)) addMessage("The " + hal()->get_name() + " zaps you!"); + if(m.reduce_hp(200, this)) addMessage("The " + hal()->get_name() + " zaps you!"); } } @@ -1062,7 +1063,7 @@ void icicle::act() { } if(state != 0) { if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { - if(m.reduce_hp(50)) addMessage("The " + hal()->get_name() + " falls on you!"); + if(m.reduce_hp(50, this)) addMessage("The " + hal()->get_name() + " falls on you!"); } } } @@ -1101,7 +1102,7 @@ void rollingsaw::act() { vel.x = zero_vel.x + dat.d * dat.modv * dir; } if(intersect(get_pixel_bbox(), m.get_pixel_bbox()) && gframeid > invinc_end) { - if(m.reduce_hp(60)) addMessage("The " + hal()->get_name() + " shreds you!"); + if(m.reduce_hp(60, this)) addMessage("The " + hal()->get_name() + " shreds you!"); } } @@ -1146,7 +1147,7 @@ void fight_trader::attacked(int dmg, power *p) { if(p->flags & WEAPON_AXE) { angered = true; current_target = this; - if(reduce_hp(dmg)) { + if(reduce_hp(dmg, this)) { if(!existing) addMessage("You kill the " + hal()->get_name() + "."); else addMessage("You hit the " + hal()->get_name() + "."); if(!existing) { walls[wShopDoor].glyph = '\''; @@ -1173,11 +1174,11 @@ void fight_trader::act() { if(gmod(gframeid, 100) == 0) { bbox b = get_pixel_bbox_at(xy{where.x + dir * dsiz().x, where.y}); if(intersect(b, m.get_pixel_bbox()) && gframeid > invinc_end) { - if(m.reduce_hp(30)) addMessage("The " + hal()->get_name() + " chops you!"); + if(m.reduce_hp(30, this)) addMessage("The " + hal()->get_name() + " chops you!"); } } if(intersect(get_pixel_bbox(), m.get_pixel_bbox()) && gframeid > invinc_end) { - if(m.reduce_hp(20)) addMessage("The " + hal()->get_name() + " slaps you!"); + if(m.reduce_hp(20, this)) addMessage("The " + hal()->get_name() + " slaps you!"); } } diff --git a/rogueviz/ru/man.cpp b/rogueviz/ru/man.cpp index 6199e1bf..8f885833 100644 --- a/rogueviz/ru/man.cpp +++ b/rogueviz/ru/man.cpp @@ -44,6 +44,12 @@ void statdata::reset() { detect_area = 0; detect_cross = 0; rough_detect = 0; + + heavy_armor = 0; + spikes = 0; + max_mageshield = 0; + stealth_bonus = 0; + dodge_value = 0; hallucinating = false; mods.clear(); on_hit.clear(); @@ -66,7 +72,9 @@ void man::hs(stater& s) { .act("xp", experience, 0) .act("last_action", last_action, 0) .act("gameseed", gameseed, 0) - .act("dresstime", dresstime, 0); + .act("dresstime", dresstime, 0) + .act("current_mageshield", current_mageshield, 0) + .act("mageshield_recharge_time", mageshield_recharge_time, 0); sact(s1, "hair", hair); sact(s1, "eyes", eye); string z = unspace(backstory); @@ -83,6 +91,12 @@ void man::hs(stater& s) { 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); + + s1.act(prefix + "heavy_armor", sd.heavy_armor, 0); + s1.act(prefix + "spikes", sd.spikes, 0); + s1.act(prefix + "max_mageshield", sd.max_mageshield, 0); + s1.act(prefix + "stealth_bonus", sd.stealth_bonus, 0); + s1.act(prefix + "dodge_value", sd.dodge_value, 0); }; sdata(current, "curr."); @@ -110,9 +124,14 @@ void man::act() { if(dresstime) { dresstime--; - if(dresstime == 0) addMessage("You finish redressing."); + if(dresstime == 0) { addMessage("You finish redressing."); mageshield_recharge_time = game_fps * 5; } } + if(current_mageshield > current.max_mageshield) current.max_mageshield = 0; + + if(mageshield_recharge_time) mageshield_recharge_time--; + else if(current_mageshield < current.max_mageshield) current_mageshield++; + if(on_floor) on_floor_when = gframeid; fallthru = false; @@ -139,11 +158,15 @@ void man::act() { check_fountains(); } -bool man::reduce_hp(int x) { +bool man::reduce_hp(int x, entity *attacker, flagtype flags) { + if(m.current.dodge_value > 0 && rand() % 100 < m.current.dodge_value && !(flags & NON_DODGEABLE)) { x = 1; } + if(m.current_mageshield > 0) { int q = min(m.current_mageshield, x); x -= q; m.current_mageshield -= q; m.mageshield_recharge_time = game_fps * 5; } + if(m.current.heavy_armor > 0) { x = max((x+4)/5, x - m.current.heavy_armor); } + if(attacker && m.current.spikes > 0 && !(flags & SPIKES)) attacker->reduce_hp(m.current.spikes, this, SPIKES); if(gframeid >= invinc_end) for(auto& f: m.current.on_hit) f(x); - return entity::reduce_hp(x); + return entity::reduce_hp(x, attacker, flags); } bool man::can_see(entity& e) { diff --git a/rogueviz/ru/powers.cpp b/rogueviz/ru/powers.cpp index c6017903..a12463f4 100644 --- a/rogueviz/ru/powers.cpp +++ b/rogueviz/ru/powers.cpp @@ -424,24 +424,41 @@ void gen_powers() { gen_power('a', 1, "heavy armor", "This kind of armor reduces the amount of damage you take from hits.", "]", 0xC0C0C0FF, - [] (data& d) {}).be_armor({{"a chain shirt", "plate armor"}, {"an iron cap", "an iron helmet"}, {"an iron gorget"}, {"bracers"}, {"gauntlets"}, {"iron-shod boots"}, {"greaves"}}), + [] (data& d) { + if(d.p->flags & ACTIVE) if(!m.dresstime) { + m.next.heavy_armor += 2 * d.p->qty_filled; + } + }).be_armor({{"a chain shirt", "plate armor"}, {"an iron cap", "an iron helmet"}, {"an iron gorget"}, {"bracers"}, {"gauntlets"}, {"iron-shod boots"}, {"greaves"}}), thief_power = &gen_power('t', 1, "thief garments", "This outfit makes it harder for enemies to notice or hit you.", "]", 0xC08000FF, - [] (data& d) {}).be_armor({{"a leather vest"}, {"a hood"}, {"comfortable boots", "muffled boots", "boots of dodging"}, {"a cloak", "chameleon cloak"}, {"leather gloves"}, {"leather pants"}}), + [] (data& d) { + if(d.p->flags & ACTIVE) if(!m.dresstime) { + m.next.dodge_value += m.current.stats[stat::dex] + 5 * d.p->qty_filled; + m.next.stealth_bonus += d.p->qty_filled; + } + }).be_armor({{"a leather vest"}, {"a hood"}, {"comfortable boots", "muffled boots", "boots of dodging"}, {"a cloak", "chameleon cloak"}, {"leather gloves"}, {"leather pants"}}), gen_power('w', 1, "wizard attire", "This outfit provides a magical aura that prevents you from taking damage. However, eventually, " "with lots of attacks, the aura will lose its power. You need to spend some time without being attacked to " "regenerate it.", "]", 0x0080C0FF, - [] (data& d) {}).be_armor({{"a simple magic robe", "embroidered robe"}, {"a wizard hat"}, {"runed boots", "silk runed boots"}, {"runed gloves", "silk runed gloves"}, {"a silver circlet", "a golden circlet"}}), + [] (data& d) { + if(d.p->flags & ACTIVE) if(!m.dresstime) { + m.next.max_mageshield += 5 * d.p->qty_filled; + } + }).be_armor({{"a simple magic robe", "embroidered robe"}, {"a wizard hat"}, {"runed boots", "silk runed boots"}, {"runed gloves", "silk runed gloves"}, {"a silver circlet", "a golden circlet"}}), - gen_power('d', 1, "druid outfit", - "This outfit reduces the damage you take from hits, makes it harder for enemies to hit you, and also provides some magical aura protection.", + gen_power('r', 1, "druid outfit", + "This outfit has nasty spikes that damages those who attack you.", "]", 0x40C040FF, - [] (data& d) {}).be_armor({{"a light fur", "a heavy fur"}, {"a horned cap", "a horned helmet"}, {"enchanted cloak"}, {"furry gloves", "beast gloves"}, {"furry boots"}, {"bracers"}}), + [] (data& d) { + if(d.p->flags & ACTIVE) if(!m.dresstime) { + m.next.spikes += 5 + 3 * d.p->qty_filled; + } + }).be_armor({{"a light fur", "a heavy fur"}, {"a horned cap", "a horned helmet"}, {"enchanted cloak"}, {"furry gloves", "beast gloves"}, {"furry boots"}, {"bracers"}}), gen_power(' ', 0, "dagger", "This sharp dagger is very useful during the preparation of alchemical ingredients, but it works as a basic weapon too.", diff --git a/rogueviz/ru/ru.cpp b/rogueviz/ru/ru.cpp index f97b94cb..e02cc2d2 100644 --- a/rogueviz/ru/ru.cpp +++ b/rogueviz/ru/ru.cpp @@ -308,6 +308,10 @@ void run() { shstream ss; print(ss, "HP ", m.hp, "/", m.max_hp()); for(auto& sts: m.current.status_strings) sts(ss); + if(m.current.max_mageshield) print(ss, " SH ", m.current_mageshield, "/", m.current.max_mageshield); + if(m.current.dodge_value) print(ss, " DV ", m.current.dodge_value, "%"); + if(m.current.heavy_armor) print(ss, " PV ", m.current.heavy_armor, "%"); + if(m.current.spikes) print(ss, " SP ", m.current.spikes); 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);