1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-08-28 08:22:18 +00:00

ru:: hairsnakes implemented

This commit is contained in:
Zeno Rogue 2025-08-01 17:30:08 +02:00
parent b2c163ca6c
commit c72b369aa5
3 changed files with 58 additions and 4 deletions

View File

@ -214,7 +214,7 @@ struct entity {
virtual struct trader* as_trader() { return nullptr; } virtual struct trader* as_trader() { return nullptr; }
virtual struct missile* as_missile() { return nullptr; } virtual struct missile* as_missile() { return nullptr; }
virtual bool is_disarmer() override { return false; } virtual bool is_disarmer() { return false; }
int hp; int hp;
int invinc_end; int invinc_end;
@ -260,6 +260,7 @@ struct entity {
void apply_grav(); void apply_grav();
void apply_portal_grav(); void apply_portal_grav();
bool stay_on_screen(); /* returns true if flipped */ bool stay_on_screen(); /* returns true if flipped */
void kill_off_screen(); /* returns true if flipped */
virtual void act() { kino(); } virtual void act() { kino(); }
/* for things which can act while not existing */ /* for things which can act while not existing */
virtual void unact() { } virtual void unact() { }
@ -582,6 +583,17 @@ struct snake : public enemy {
int max_hp() { return 30; } int max_hp() { return 30; }
}; };
struct disnake : public snake {
color_t color() override { return 0x2020F0FF; }
void act() override;
bool is_disarmer() override { return true; }
string get_name() override { return "hairsnake"; }
string get_help() override { return "A magically animated hair."; }
int base_xp() { return 0; }
int max_hp() { return 1; }
virtual void unact() { destroyed = true; }
};
struct kestrel : public enemy { struct kestrel : public enemy {
xy respawn_vel; xy respawn_vel;
xy siz() override { return {10, 10}; } xy siz() override { return {10, 10}; }

View File

@ -105,9 +105,18 @@ void entity::apply_walls() {
return true; return true;
}; };
auto allside = [this] (int x, int y, eWall b) {
if(b == wRogueWallHidden && is_disarmer()) {
current_room->replace_block_frev(x, y, wRogueWall);
if(current_room->fov[y][x])
addMessage("Your hairsnake exposes a fake wall!");
}
};
for(int x = obb.minx; x < obb.maxx; x++) for(int y = obb.maxy; y < jbb.maxy; y++) { for(int x = obb.minx; x < obb.maxx; x++) for(int y = obb.maxy; y < jbb.maxy; y++) {
eWall b = current_room->at(x, y); eWall b = current_room->at(x, y);
if(walls[b].flags & W_DOWNWARD) continue; if(walls[b].flags & W_DOWNWARD) continue;
allside(x, y, b);
if(walls[b].flags & blocking) { if(walls[b].flags & blocking) {
if(walls[b].flags & W_BOUNCY) { vel.y = -vel.y; apply_grav(); apply_grav(); if(vel.y > 0) vel.y = 0; on_bounce = true; goto again; } if(walls[b].flags & W_BOUNCY) { vel.y = -vel.y; apply_grav(); apply_grav(); if(vel.y > 0) vel.y = 0; on_bounce = true; goto again; }
on_floor = true; on_floor = true;
@ -135,6 +144,7 @@ void entity::apply_walls() {
for(int x = obb.minx; x < obb.maxx; x++) for(int y = jbb.miny; y < obb.miny; y++) { for(int x = obb.minx; x < obb.maxx; x++) for(int y = jbb.miny; y < obb.miny; y++) {
eWall b = current_room->at(x, y); eWall b = current_room->at(x, y);
allside(x, y, b);
if(walls[b].flags & W_BLOCK) { if(walls[b].flags & W_BLOCK) {
if(walls[b].flags & W_BOUNCY) { vel.y = -vel.y; apply_grav(); apply_grav(); on_bounce = true; goto again; } if(walls[b].flags & W_BOUNCY) { vel.y = -vel.y; apply_grav(); apply_grav(); on_bounce = true; goto again; }
vel.y /= 2; vel.y /= 2;
@ -158,6 +168,7 @@ void entity::apply_walls() {
for(int x = obb.maxx; x < jbb.maxx; x++) for(int y = jbb.miny; y < jbb.maxy; y++) { for(int x = obb.maxx; x < jbb.maxx; x++) for(int y = jbb.miny; y < jbb.maxy; y++) {
eWall b = current_room->at(x, y); eWall b = current_room->at(x, y);
allside(x, y, b);
if((walls[b].flags & W_DOWNWARD) && b == current_room->at(x-1, y)) continue; if((walls[b].flags & W_DOWNWARD) && b == current_room->at(x-1, y)) continue;
if(walls[b].flags & W_BLOCK) { if(walls[b].flags & W_BLOCK) {
if(walls[b].flags & W_BOUNCY) { vel.x = -vel.x; on_bounce = true; goto again; } if(walls[b].flags & W_BOUNCY) { vel.x = -vel.x; on_bounce = true; goto again; }
@ -175,6 +186,7 @@ void entity::apply_walls() {
for(int x = jbb.minx; x < obb.minx; x++) for(int y = jbb.miny; y < jbb.maxy; y++) { for(int x = jbb.minx; x < obb.minx; x++) for(int y = jbb.miny; y < jbb.maxy; y++) {
eWall b = current_room->at(x, y); eWall b = current_room->at(x, y);
allside(x, y, b);
if((walls[b].flags & W_DOWNWARD) && b == current_room->at(x+1, y)) continue; if((walls[b].flags & W_DOWNWARD) && b == current_room->at(x+1, y)) continue;
if(walls[b].flags & W_BLOCK) { if(walls[b].flags & W_BLOCK) {
if(walls[b].flags & W_BOUNCY) { vel.x = -vel.x; on_bounce = true; goto again; } if(walls[b].flags & W_BOUNCY) { vel.x = -vel.x; on_bounce = true; goto again; }
@ -250,6 +262,13 @@ bool entity::stay_on_screen() {
return res; return res;
} }
void entity::kill_off_screen() {
if(where.x < l_margin_at && vel.x < 0) existing = false;
if(where.x > r_margin_at && vel.x > 0) existing = false;
if(where.y < t_margin_at && vel.y < 0) existing = false;
if(where.y > b_margin_at && vel.y > 0) existing = false;
}
void entity::kino() { void entity::kino() {
on_floor = false; on_floor = false;
on_ice = false; on_ice = false;
@ -451,7 +470,7 @@ void snake::act() {
vel.x = zero_vel.x + dat.d * dat.modv * dir; vel.x = zero_vel.x + dat.d * dat.modv * dir;
dir = -dir; dir = -dir;
} }
if(intersect(get_pixel_bbox(), m.get_pixel_bbox())) { if(intersect(get_pixel_bbox(), m.get_pixel_bbox()) && gframeid > invinc_end) {
if(m.reduce_hp(25)) addMessage("The snake bites you!"); if(m.reduce_hp(25)) addMessage("The snake bites you!");
} }
} }
@ -690,4 +709,15 @@ cat::cat() {
col = hrand_elt(cat_colors); col = hrand_elt(cat_colors);
} }
void disnake::act() {
snake::act();
kill_off_screen();
for(auto& e: current_room->entities)
if(e->hidden() && e->existing && intersect(e->get_pixel_bbox(), get_pixel_bbox())) {
addMessage("Your hear a sound of a " + e->get_name() + " colliding with a hairsnake.");
e->existing = false;
destroyed = true;
}
}
} }

View File

@ -49,7 +49,19 @@ randeff trap_detect_cross("Detect cross", "Lets you see traps and secret passage
m.next.detect_cross = m.current.detect_cross + 0.01 / game_fps * m.current.stats[stat::wis]; m.next.detect_cross = m.current.detect_cross + 0.01 / game_fps * m.current.stats[stat::wis];
}); });
randeff trap_snake("Snake Hair", "Lets you create snakes that can be used to disarm traps and secret passages.", "You grow snakes on your head!", [] (data &d) { }); randeff trap_snake("Snake Hair", "Lets you create snakes that can be used to disarm traps and secret passages.", "You grow snakes on your head!", [] (data &d) {
if(d.mode == rev::start || (d.mode == rev::active && d.keystate == 1)) {
auto d = m.get_dat();
auto mi = std::make_unique<disnake>();
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 * 2, -d.modv * 3.5);
mi->clearg();
mi->invinc_end = gframeid + 50;
mi->postfix();
mi->dir = m.facing * 2;
current_room->entities.emplace_back(std::move(mi));
}
});
randeff trap_disarm("Disarm traps", "Lets you see all traps on the level for a short time, and to attack them with your [weapon] to destroy them.", "You suddenly feel able to disarm traps with your [weapon]!", [] (data &d) { randeff trap_disarm("Disarm traps", "Lets you see all traps on the level for a short time, and to attack them with your [weapon] to destroy them.", "You suddenly feel able to disarm traps with your [weapon]!", [] (data &d) {
m.next.rough_detect = 0.1; m.next.rough_detect = 0.1;
@ -158,7 +170,7 @@ void assign_potion_powers() {
trap_disarm.which_weapon = wpn[1]; trap_disarm.which_weapon = wpn[1];
using relist = vector<randeff*>; using relist = vector<randeff*>;
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), random_powers[0] };
find_power("the thief").randeffs = relist{ pick(&trap_detect, &trap_snake, &trap_disarm), random_powers[1] }; 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("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_bubble, &jump_light), random_powers[3] };
find_power("fire").randeffs = relist{ pick(&fire_spit, &fire_weapon), random_powers[4] }; find_power("fire").randeffs = relist{ pick(&fire_spit, &fire_weapon), random_powers[4] };