From 296a7c6ba715223682a6de5e49e1891bcbd9fb4d Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Mon, 14 Sep 2026 11:23:48 +0200 Subject: [PATCH] rogueviz:: ru:: extra reach powers implemented --- rogueviz/ru/classes.cpp | 39 ++++++++++++++++++++++ rogueviz/ru/entity.cpp | 60 +++++++++++++++++++++++++++++++++- rogueviz/ru/man.cpp | 13 ++++++++ rogueviz/ru/powers.cpp | 13 +++++++- rogueviz/ru/randeff.cpp | 71 ++++++++++++++++++++++++++++++++++++++--- rogueviz/ru/ru.cpp | 1 + 6 files changed, 190 insertions(+), 7 deletions(-) diff --git a/rogueviz/ru/classes.cpp b/rogueviz/ru/classes.cpp index 468eb1c0..5d6d471d 100644 --- a/rogueviz/ru/classes.cpp +++ b/rogueviz/ru/classes.cpp @@ -317,6 +317,7 @@ struct entity { bool on_floor, fallthru, wallhug, on_bounce, is_stable; int on_ice; + moving_platform *on_floor_platform; bool destroyed; void kino(); @@ -402,6 +403,8 @@ struct statdata { int jump_control, coyote_time, hallucinating; ld detect_area, detect_cross, rough_detect; int heavy_armor, spikes, max_mageshield, stealth_bonus, dodge_value; + ld jump_power, gravity_value; + int extra_jumps, boost_max; void reset(); vector mods; vector> on_hit; @@ -423,6 +426,7 @@ struct man : public entity { int facing; int on_floor_when; int dresstime; + int jumps_used, boost_left; entity *morphed = nullptr; vector effects; power *use_next_turn; @@ -478,6 +482,7 @@ struct man : public entity { void launch_attack(power *p, int fac, boxfun f); virtual void hs(stater& s); + virtual double grav() { return entity::grav() / current.gravity_value; } }; extern man m; @@ -500,6 +505,40 @@ struct moving_platform : public entity { string get_name() override { return get_shape_name() + " platform"; } string get_help() override { return "Moving platforms move."; } bool nonstatic() override { return false; } + virtual void jumped_on() {} + }; + +struct freemoving_platform : public moving_platform { + xy last_position; + int position_time; + xy location_at(ld t) override; + virtual void hs(stater& s) { + moving_platform::hs(s); + s.act("last_position", last_position, xy(0,0)) + .act("position_time", position_time, 0); + } + }; + +struct floating_bubble_platform : public freemoving_platform { + virtual int width() { return 1; } + xy siz() override { return {6.*width(), 6}; } + void act() override; + string glyph() override { return "O"; } + color_t color() override { return 0xC0FFFFFF; } + string get_name() override { return "floating bubble"; } + string get_help() override { return "This upward-floating bubble acts as a temporary platform."; } + void jumped_on() override; + }; + +struct levitating_bubble_platform : public freemoving_platform { + virtual int width() { return 1; } + xy siz() override { return {6.*width(), 6}; } + void act() override; + string glyph() override { return "O"; } + color_t color() override { return 0xC0FFFFFF; } + string get_name() override { return "levitating bubble"; } + string get_help() override { return "This levitating bubble acts as a temporary platform."; } + void jumped_on() override; }; struct ferris_platform : public moving_platform { diff --git a/rogueviz/ru/entity.cpp b/rogueviz/ru/entity.cpp index 402ec524..b598b69b 100644 --- a/rogueviz/ru/entity.cpp +++ b/rogueviz/ru/entity.cpp @@ -274,13 +274,17 @@ void entity::apply_walls() { bool reset = false; if(p->platform_type() == wStaircase) { - if(!fallthru) { on_floor = true; zero_vel = zvel; if(rvel.y > 0) rvel.y = 0; } + if(!fallthru) { + on_floor = true; zero_vel = zvel; if(rvel.y > 0) rvel.y = 0; + on_floor_platform = p; + } } else if(intersect(pb, get_pixel_bbox_at(rmow))) { /* should not happen */ } else if(!intersect(pb, get_pixel_bbox_at(rmow + xy(rvel.x, 0))) && rvel.y > 0) { on_floor = true; + on_floor_platform = p; rvel.y /= 2; if(abs(rvel.y) < 1e-6) rvel.y = 0; reset = true; @@ -361,6 +365,7 @@ void entity::kill_off_screen() { void entity::kino() { on_floor = false; + on_floor_platform = nullptr; on_ice = 0; wallhug = false; on_bounce = false; @@ -846,6 +851,59 @@ void kestrel::act() { } } +xy freemoving_platform::location_at(ld t) { + act(); + if(t == gframeid-1) return last_position; + if(t == gframeid) return where; + auto h1 = to_hyper(last_position); + auto h2 = to_hyper(where); + auto d = hdist(h1, h2); + auto x = d * (t - (gframeid - 1)); + return from_hyper(rgpushxto0(h1) * rspintox(gpushxto0(h1) * h2) * xpush0(x)); + } + +void floating_bubble_platform::act() { + if(position_time == gframeid) return; + last_position = where; + position_time = gframeid; + + stay_on_screen(); + apply_walls_reflect(); + apply_vel(); + + auto dat = get_dat(); + bool man_on = intersect(extend(get_pixel_bbox(), 0, 0, dat.d, 0), m.get_pixel_bbox()); + + if(man_on) vel.y += dat.d * dat.moda * 0.01; + else vel.y -= dat.d * dat.moda * 0.01; + + vel.x = lerp(vel.x, 0, 1. / game_fps); + vel.y = lerp(vel.y, 0, 1. / game_fps); + } + +void floating_bubble_platform::jumped_on() { + auto d = get_dat(); + vel.y += 1.5 * d.d * d.modv * m.current.jump_power; + } + +void levitating_bubble_platform::act() { + if(position_time == gframeid) return; + last_position = where; + position_time = gframeid; + + stay_on_screen(); + apply_walls_reflect(); + apply_vel(); + + vel.x = lerp(vel.x, 0, 10. / game_fps); + vel.y = lerp(vel.y, 0, 10. / game_fps); + } + +void levitating_bubble_platform::jumped_on() { + auto d = get_dat(); + vel.y += 1.5 * d.d * d.modv * m.current.jump_power; + } + void healthbubble::act() { stay_on_screen(); apply_walls_reflect(); diff --git a/rogueviz/ru/man.cpp b/rogueviz/ru/man.cpp index 54518696..96ef174f 100644 --- a/rogueviz/ru/man.cpp +++ b/rogueviz/ru/man.cpp @@ -54,6 +54,11 @@ void statdata::reset() { mods.clear(); on_hit.clear(); status_strings.clear(); + + jump_power = 1; + gravity_value = 1; + extra_jumps = 0; + boost_max = 0; } man::man() { @@ -83,6 +88,9 @@ void man::hs(stater& s) { 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); + s1.act("jumps_used", jumps_used, 0); + s1.act("boost_left", boost_left, 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); @@ -97,6 +105,11 @@ void man::hs(stater& s) { 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); + + s1.act(prefix + "jump_power", sd.jump_power, 1); + s1.act(prefix + "gravity_value", sd.gravity_value, 1); + s1.act(prefix + "extra_jumps", sd.extra_jumps, 0); + s1.act(prefix + "boost_max", sd.boost_max, 0); }; sdata(current, "curr."); diff --git a/rogueviz/ru/powers.cpp b/rogueviz/ru/powers.cpp index ee996d26..aeae318d 100644 --- a/rogueviz/ru/powers.cpp +++ b/rogueviz/ru/powers.cpp @@ -396,11 +396,22 @@ void gen_powers() { "^", 0xFF0000FF, [] (data& d) { if(d.keystate & 1) { + if(m.on_floor) m.jumps_used = 0; bool can_jump = m.on_floor; if(gframeid <= m.on_floor_when + m.current.coyote_time) can_jump = true; if(m.dresstime) can_jump = false; - if(can_jump) m.vel.y = m.zero_vel.y-(non_hyperbolic ? 3 : 5) * d.d * d.modv, m.on_floor_when = -1000; + if(!can_jump && m.current.extra_jumps > m.jumps_used && !(d.keystate & 2) && !m.dresstime) { + m.jumps_used++; + can_jump = true; + } + if(can_jump) { + if(m.on_floor_platform) m.on_floor_platform->jumped_on(); + m.vel.y = m.zero_vel.y-(non_hyperbolic ? 3 : 5) * d.d * d.modv * m.current.jump_power, m.on_floor_when = -1000; + } + if(m.boost_left > 0) m.next.gravity_value += 1, m.boost_left --; } + if(m.boost_left < m.current.boost_max && m.on_floor) m.boost_left++; + if(m.boost_left > m.current.boost_max) m.boost_left--; } ).is_starting(), diff --git a/rogueviz/ru/randeff.cpp b/rogueviz/ru/randeff.cpp index bf156f33..1dcf0b54 100644 --- a/rogueviz/ru/randeff.cpp +++ b/rogueviz/ru/randeff.cpp @@ -35,10 +35,71 @@ randeff confux("Confusion", "Makes you unable to tell left and right.", "You fee }); // jump powers -randeff jump_double("Double Jump", "Lets you jump while already in air.", "You feel a strange force in your legs!", [] (data &d) { }); -randeff jump_high("High Jump", "Lets you jump higher.", "Your legs feel stronger!", [] (data &d) { }); -randeff jump_bubble("Bubble", "Lets you create bubbles to reach higher places.", "You feel strange bubbles growing in your mouth!", [] (data &d) { }); -randeff jump_light("Lightness", "Causes you to be less affected by gravity.", "You feel lighter!", [] (data &d) { }); +randeff jump_double("Double Jump", "Lets you jump while already in air.", "You feel a strange force in your legs!", [] (data &d) { + m.next.extra_jumps++; + }); + +randeff jump_high("High Jump", "Lets you jump higher.", "Your legs feel stronger!", [] (data &d) { + m.next.jump_power += 0.5; + }); + +randeff jump_boost("Boost", "Lets you move upwards during jump.", "Your feet feel magical!", [] (data &d) { + m.next.boost_max += game_fps; + }); + +randeff jump_floating_bubble("Floating Bubble", "Lets you create floating bubbles to reach higher places.", "You feel strange bubbles growing in your mouth!", [] (data &d) { + if(d.mode == rev::start || (d.mode == rev::active && d.keystate == 1)) { + int bubbles_found = 0; + for(auto& e: current_room->entities) if(e->id == "FBUBBLE") { + if(e->existing) bubbles_found++; + e->existing = false; + e->destroyed = true; + } + if(bubbles_found) return; + // auto d = m.get_dat(); + auto mi = std::make_unique(); + mi->id = "FBUBBLE"; + // mi->ctr = m.where + xy(m.facing * m.get_scale() * 1.5, 0); + mi->hs(fountain_resetter); + mi->where = m.where + xy(m.facing * m.get_scale() * m.siz().y * 0.45, 0); + mi->vel = m.vel + xy(m.facing * d.modv, 0); + current_room->entities.emplace_back(std::move(mi)); + } + }); + +randeff jump_levitating_bubble("Levitating Bubble", "Lets you create levitating bubbles to reach higher places.", "You feel strange bubbles growing in your stomach!", [] (data &d) { + + auto& re = *d.re; + auto& bubble_available = re.a; + + if(d.mode == rev::start) bubble_available = false; + + if(m.on_floor) bubble_available = true; + if(m.on_floor_platform && m.on_floor_platform->id == "LBUBBLE") bubble_available = false; + + if(d.mode == rev::start || (d.mode == rev::active && d.keystate == 1)) { + + int bubbles_found = 0; + for(auto& e: current_room->entities) if(e->id == "LBUBBLE") { + if(e->existing) bubbles_found++; + e->existing = false; + e->destroyed = true; + } + if(bubbles_found || !bubble_available) return; + + bubble_available = false; + auto mi = std::make_unique(); + mi->id = "LBUBBLE"; + mi->hs(fountain_resetter); + mi->where = m.where + xy(0, m.facing * m.get_scale() * m.siz().y * 0.25); + mi->vel = m.vel + xy(0, d.modv * 2); + current_room->entities.emplace_back(std::move(mi)); + } + }); + +randeff jump_light("Lightness", "Causes you to be less affected by gravity.", "You feel lighter!", [] (data &d) { + m.next.gravity_value += 1; + }); // trap powers randeff trap_detect("Detect traps", "Lets you see traps and secret passages in a circle around you.", "You see things you could not see before!", [] (data &d) { @@ -308,7 +369,7 @@ void assign_potion_powers() { 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] }; + find_power("reach").randeffs = relist{ pick(&jump_double, &jump_high, &jump_light, &jump_boost, &jump_floating_bubble, &jump_levitating_bubble), random_powers[3] }; find_power("fire").randeffs = relist{ pick(&fire_spit, &fire_weapon), random_powers[4] }; find_power("mystery").randeffs = relist{ random_powers[5], random_powers[6], random_powers[7] }; find_power("surprise").randeffs = relist{ random_powers[8] }; // need more random powers diff --git a/rogueviz/ru/ru.cpp b/rogueviz/ru/ru.cpp index 128cb35b..fdab96a6 100644 --- a/rogueviz/ru/ru.cpp +++ b/rogueviz/ru/ru.cpp @@ -314,6 +314,7 @@ void run() { 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); + if(m.current.boost_max) print(ss, " B ", m.boost_left, "/", m.current.boost_max); 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);