1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-07-29 22:02:50 +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; }
bool on_floor, fallthru, on_ice, wallhug, on_bounce;
bool on_floor, fallthru, on_ice, wallhug, on_bounce, is_stable;
bool destroyed;
void kino();
@ -208,7 +208,7 @@ struct entity {
void apply_walls_reflect();
void apply_grav();
void apply_portal_grav();
void stay_on_screen();
bool stay_on_screen(); /* returns true if flipped */
virtual void act() { kino(); }
double get_scale() { return get_scale_at(where.y); }
@ -235,12 +235,14 @@ struct entity {
existing = false;
}
virtual int invinc_time() { return 150; }
virtual bool reduce_hp(int x) {
if(hp < 0) return false;
if(gframeid < invinc_end) return false;
hp -= x;
if(hp <= 0) on_kill();
invinc_end = gframeid + 150;
invinc_end = gframeid + invinc_time();
return true;
}
@ -350,6 +352,21 @@ struct boar : public enemy {
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 {
int dir, respawn_dir;
xy siz() override { return {18, 8}; }

View File

@ -115,6 +115,9 @@ void entity::apply_walls() {
else if(b != wFrozen) hit_wall();
}
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;
}
if((walls[b].flags & W_PAIN) && pain_effect()) goto again;
@ -206,11 +209,13 @@ void entity::apply_walls() {
}
}
void entity::stay_on_screen() {
if(where.x < l_margin_at && vel.x < 0) vel.x = -vel.x;
if(where.x > r_margin_at && vel.x > 0) vel.x = -vel.x;
if(where.y < t_margin_at && vel.y < 0) vel.y = -vel.y;
if(where.y > b_margin_at && vel.y > 0) vel.y = -vel.y;
bool entity::stay_on_screen() {
bool res = false;
if(where.x < l_margin_at && vel.x < 0) vel.x = -vel.x, res = true;
if(where.x > r_margin_at && vel.x > 0) vel.x = -vel.x, res = true;
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() {
@ -218,6 +223,7 @@ void entity::kino() {
on_ice = false;
wallhug = false;
on_bounce = false;
is_stable = false;
zero_vel = xy(0, 0);
// 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;
}
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() {
stay_on_screen();
kino();

View File

@ -65,12 +65,13 @@ flagtype W_PAIN = 16;
flagtype W_BOUNCY = 32;
flagtype W_FROZEN = 64;
flagtype W_BLOCKBIRD = 128;
flagtype W_STABLE = 256;
constexpr int qwall = int(wGUARD);
ruwall walls[qwall] = {
{"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."},
{"spike", "^", 0xC08080FF, W_TRANS | W_PAIN | W_BLOCKBIRD, "Dangerous!"},
{"water", "~", 0x0000FFFF, W_BLOCK | W_TRANS | W_BLOCKBIRD, "Not used yet."},

View File

@ -6,6 +6,9 @@ bool on_fountain;
room *fountain_room;
xy fountain_where;
room *stable_room;
xy stable_where;
void regenerate_all() {
m.hp = m.max_hp();
for(auto& p: powers) p.refill();
@ -37,6 +40,11 @@ void check_fountains() {
void man::act() {
kino();
if(is_stable) {
stable_room = current_room;
stable_where = where;
}
current_stats = next_stats;
next_stats = base_stats;
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;
int gold_id;
void gen_powers() {
powers.reserve(100);
@ -158,6 +159,9 @@ void gen_powers() {
d.p->flags |= IDENTIFIED;
if(d.keystate == 1) {
if(!m.existing) {
auto w = m.where;
auto cr = current_room;
int hp = m.max_hp();
revert_all(death_revert);
regenerate_all();
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.");
else
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)
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); }
).be_potion(),
gold_id = isize(powers);
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"
"This can be used to buy things in shops. "