1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-10-26 19:37:40 +00:00

ru:: ghosts and bones

This commit is contained in:
Zeno Rogue
2025-05-10 02:05:31 +02:00
parent 447c8e469b
commit 7183c849a7
5 changed files with 83 additions and 9 deletions

View File

@@ -199,7 +199,7 @@ struct entity {
virtual double grav() { return 0.1; } virtual double grav() { return 0.1; }
bool on_floor, fallthru, on_ice, wallhug, on_bounce; bool on_floor, fallthru, on_ice, wallhug, on_bounce, is_stable;
bool destroyed; bool destroyed;
void kino(); void kino();
@@ -208,7 +208,7 @@ struct entity {
void apply_walls_reflect(); void apply_walls_reflect();
void apply_grav(); void apply_grav();
void apply_portal_grav(); void apply_portal_grav();
void stay_on_screen(); bool stay_on_screen(); /* returns true if flipped */
virtual void act() { kino(); } virtual void act() { kino(); }
double get_scale() { return get_scale_at(where.y); } double get_scale() { return get_scale_at(where.y); }
@@ -235,12 +235,14 @@ struct entity {
existing = false; existing = false;
} }
virtual int invinc_time() { return 150; }
virtual bool reduce_hp(int x) { virtual bool reduce_hp(int x) {
if(hp < 0) return false; if(hp < 0) return false;
if(gframeid < invinc_end) return false; if(gframeid < invinc_end) return false;
hp -= x; hp -= x;
if(hp <= 0) on_kill(); if(hp <= 0) on_kill();
invinc_end = gframeid + 150; invinc_end = gframeid + invinc_time();
return true; return true;
} }
@@ -350,6 +352,21 @@ struct boar : public enemy {
int max_hp() { return 60; } int max_hp() { return 60; }
}; };
struct ghost : public enemy {
int xp, hp;
bool flipped;
xy siz() override { return {12, 12}; }
string glyph() override { return "g"; }
color_t color() override { return 0x4040A0FF; }
void act() override;
void attacked(int s) override;
string get_name() override { return "ghost"; }
string get_help() override { return "This apparition looks strangely like you..."; }
int base_xp() { return hp; }
int max_hp() { return xp; }
void regenerate() override {}
};
struct snake : public enemy { struct snake : public enemy {
int dir, respawn_dir; int dir, respawn_dir;
xy siz() override { return {18, 8}; } xy siz() override { return {18, 8}; }

View File

@@ -115,6 +115,9 @@ void entity::apply_walls() {
else if(b != wFrozen) hit_wall(); else if(b != wFrozen) hit_wall();
} }
if(pixel_to_block(get_pixel_bbox_at(where + vel)).maxy <= y) where.y += vel.y; if(pixel_to_block(get_pixel_bbox_at(where + vel)).maxy <= y) where.y += vel.y;
if(walls[b].flags & W_STABLE) is_stable = true;
goto again; goto again;
} }
if((walls[b].flags & W_PAIN) && pain_effect()) goto again; if((walls[b].flags & W_PAIN) && pain_effect()) goto again;
@@ -206,11 +209,13 @@ void entity::apply_walls() {
} }
} }
void entity::stay_on_screen() { bool entity::stay_on_screen() {
if(where.x < l_margin_at && vel.x < 0) vel.x = -vel.x; bool res = false;
if(where.x > r_margin_at && vel.x > 0) vel.x = -vel.x; if(where.x < l_margin_at && vel.x < 0) vel.x = -vel.x, res = true;
if(where.y < t_margin_at && vel.y < 0) vel.y = -vel.y; if(where.x > r_margin_at && vel.x > 0) vel.x = -vel.x, res = true;
if(where.y > b_margin_at && vel.y > 0) vel.y = -vel.y; if(where.y < t_margin_at && vel.y < 0) vel.y = -vel.y, res = true;
if(where.y > b_margin_at && vel.y > 0) vel.y = -vel.y, res = true;
return res;
} }
void entity::kino() { void entity::kino() {
@@ -218,6 +223,7 @@ void entity::kino() {
on_ice = false; on_ice = false;
wallhug = false; wallhug = false;
on_bounce = false; on_bounce = false;
is_stable = false;
zero_vel = xy(0, 0); zero_vel = xy(0, 0);
// ld modv = 60. / game_fps; // ld modv = 60. / game_fps;
@@ -288,6 +294,29 @@ void boar::attacked(int dmg) {
if(on_floor) vel.x = dat.d * dat.modv * s * 2, vel.y = -dat.d * dat.modv * 2.5; if(on_floor) vel.x = dat.d * dat.modv * s * 2, vel.y = -dat.d * dat.modv * 2.5;
} }
void ghost::act() {
hyperpoint g = to_hyper(where);
hyperpoint h = to_hyper(m.where);
ld d = hdist(g, h);
ld angle = gframeid < invinc_end ? M_PI : d > 0.5 ? 90._deg : d > 0.05 ? 80._deg : 5._deg;
ld gv = d > 0.04 && gframeid > invinc_end ? 0.2 : 0.1;
if(flipped) angle = -angle;
hyperpoint g1 = rgpushxto0(g) * rspintox(gpushxto0(g) * h) * spin(angle) * xpush0(gv / game_fps);
vel = from_hyper(g1) - where;
if(stay_on_screen()) flipped = !flipped;
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 ghost passes through you!");
}
}
void ghost::attacked(int dmg) {
current_target = this;
reduce_hp(dmg);
if(!existing) addMessage("You kill the ghost."); else addMessage("You hit the ghost.");
}
void snake::act() { void snake::act() {
stay_on_screen(); stay_on_screen();
kino(); kino();

View File

@@ -65,12 +65,13 @@ flagtype W_PAIN = 16;
flagtype W_BOUNCY = 32; flagtype W_BOUNCY = 32;
flagtype W_FROZEN = 64; flagtype W_FROZEN = 64;
flagtype W_BLOCKBIRD = 128; flagtype W_BLOCKBIRD = 128;
flagtype W_STABLE = 256;
constexpr int qwall = int(wGUARD); constexpr int qwall = int(wGUARD);
ruwall walls[qwall] = { ruwall walls[qwall] = {
{"air", ".", 0x40404080, W_TRANS, "Looks like an empty space, but actually necessary for survival."}, {"air", ".", 0x40404080, W_TRANS, "Looks like an empty space, but actually necessary for survival."},
{"wall", "#", 0xFFFFFFFF, W_BLOCK, "These kinds of tough walls can never be destroyed."}, {"wall", "#", 0xFFFFFFFF, W_BLOCK | W_STABLE, "These kinds of tough walls can never be destroyed."},
{"bouncy wall", "#", 0x80FF80FF, W_BLOCK | W_BOUNCY, "Like walls, but things bounce off them."}, {"bouncy wall", "#", 0x80FF80FF, W_BLOCK | W_BOUNCY, "Like walls, but things bounce off them."},
{"spike", "^", 0xC08080FF, W_TRANS | W_PAIN | W_BLOCKBIRD, "Dangerous!"}, {"spike", "^", 0xC08080FF, W_TRANS | W_PAIN | W_BLOCKBIRD, "Dangerous!"},
{"water", "~", 0x0000FFFF, W_BLOCK | W_TRANS | W_BLOCKBIRD, "Not used yet."}, {"water", "~", 0x0000FFFF, W_BLOCK | W_TRANS | W_BLOCKBIRD, "Not used yet."},

View File

@@ -6,6 +6,9 @@ bool on_fountain;
room *fountain_room; room *fountain_room;
xy fountain_where; xy fountain_where;
room *stable_room;
xy stable_where;
void regenerate_all() { void regenerate_all() {
m.hp = m.max_hp(); m.hp = m.max_hp();
for(auto& p: powers) p.refill(); for(auto& p: powers) p.refill();
@@ -37,6 +40,11 @@ void check_fountains() {
void man::act() { void man::act() {
kino(); kino();
if(is_stable) {
stable_room = current_room;
stable_where = where;
}
current_stats = next_stats; current_stats = next_stats;
next_stats = base_stats; next_stats = base_stats;
auto dat = get_dat(); auto dat = get_dat();

View File

@@ -144,6 +144,7 @@ power& gen_power(int key, string name, string desc, string glyph, color_t color,
} }
power *extra_life; power *extra_life;
int gold_id;
void gen_powers() { void gen_powers() {
powers.reserve(100); powers.reserve(100);
@@ -158,6 +159,9 @@ void gen_powers() {
d.p->flags |= IDENTIFIED; d.p->flags |= IDENTIFIED;
if(d.keystate == 1) { if(d.keystate == 1) {
if(!m.existing) { if(!m.existing) {
auto w = m.where;
auto cr = current_room;
int hp = m.max_hp();
revert_all(death_revert); revert_all(death_revert);
regenerate_all(); regenerate_all();
if(!(extra_life->flags & ACTIVE)) extra_life->qty_filled = 0; if(!(extra_life->flags & ACTIVE)) extra_life->qty_filled = 0;
@@ -168,6 +172,19 @@ void gen_powers() {
addMessage("You wake up at the Magic Fountain."); addMessage("You wake up at the Magic Fountain.");
else else
addMessage("You wake up from a very bad nightmare. Wow, you are really stressed."); addMessage("You wake up from a very bad nightmare. Wow, you are really stressed.");
if(m.experience >= 50) {
auto g = std::make_unique<ghost>();
g->where = w; g->hp = hp; g->xp = m.experience/2; m.experience -= g->xp; g->postfix();
cr->entities.emplace_back(std::move(g));
}
auto bones = std::make_unique<item>();
bones->qty = 10;
bones->where = stable_where;
bones->id = gold_id;
bones->pickup_message = "You got it back.";
stable_room->entities.emplace_back(std::move(bones));
} }
else if(!d.p->qty_filled) else if(!d.p->qty_filled)
addMessage("You need to find a Magic Fountain to prepare this potion."); addMessage("You need to find a Magic Fountain to prepare this potion.");
@@ -386,6 +403,8 @@ void gen_powers() {
[] (data& d) { if(d.keystate == 1) d.p->flags |= (PARTIAL | IDENTIFIED); } [] (data& d) { if(d.keystate == 1) d.p->flags |= (PARTIAL | IDENTIFIED); }
).be_potion(), ).be_potion(),
gold_id = isize(powers);
gen_power('g', "gold", gen_power('g', "gold",
"For some weird reason, people love gold, and they will give you anything if you give them enough gold.\n\n" "For some weird reason, people love gold, and they will give you anything if you give them enough gold.\n\n"
"This can be used to buy things in shops. " "This can be used to buy things in shops. "