fix(mummy): evita soft-lock per drift sub-pixel a HURT vora del mur
This commit is contained in:
1 parent
0d58da536c
commit
e9f99a0542
2 files changed
+128
-5
No files matched your search
@@ -496,6 +496,99 @@ namespace {
|
||||
REQUIRE(mummy.getState() == Mummy::State::ROAMING);
|
||||
}
|
||||
|
||||
void testMummyHurtSnapsAtPlayfieldWall() {
|
||||
std::cout << "[T18] mummy HURT amarra a la vora del playfield en topar\n";
|
||||
// Regressió: amb dt no-exacte (joc real → SDL_GetTicks ms) el drift
|
||||
// sub-pixel de la cursa HURT deixava la BOX a 0.x px dins de l'última
|
||||
// cel·la quan topava amb el mur. Com que la BOX 16×16 cabe exacta al
|
||||
// corredor 16×16, qualsevol drift fa que la cantonada perpendicular
|
||||
// sobresurti cap als sarcs adjacents — i el `chooseHurtDirection` del
|
||||
// proper fueteig classifica les perpendiculars com a bloquejades per
|
||||
// micro-overlap, retornant NONE i deixant la mòmia eternament quieta
|
||||
// amb l'animació de córrer activa. El fix és snap a la vora exacta
|
||||
// dins de `hurtStep` abans de transitar a ROAMING.
|
||||
Map::loadFrom("data/maps/pyramid.yaml");
|
||||
|
||||
fkyaml::node tmpl = EntityLoader::loadTemplate("mummy");
|
||||
Mummy mummy(tmpl);
|
||||
// Caixa real del joc (YAML): collision 16×16 als peus del sprite 16×32.
|
||||
mummy.setCollisionBox({.offset_x = 0, .offset_y = 16, .width = 16, .height = 16});
|
||||
// Comencem prou a prop del mur perquè la cursa hi arribi abans que
|
||||
// expiri el timer (60 px/s · 0.6 s = 36 px de runway).
|
||||
mummy.setPixelRect({.x = 40.0F, .y = 160.0F, .w = 16.0F, .h = 32.0F});
|
||||
REQUIRE(mummy.getCollisionRect().x == 40.0F);
|
||||
|
||||
mummy.enterHurt(Mummy::Direction::WEST);
|
||||
REQUIRE(mummy.getState() == Mummy::State::HURT);
|
||||
REQUIRE(mummy.getDirX() == -1.0F);
|
||||
|
||||
// Avancem amb dt no-divisor de 1 (joc real) fins que topa el mur.
|
||||
for (int i = 0; i < 200 && mummy.getState() == Mummy::State::HURT; ++i) {
|
||||
mummy.update(0.0173F);
|
||||
}
|
||||
REQUIRE(mummy.getState() == Mummy::State::ROAMING);
|
||||
// El fix garanteix snap exacte a playfield_x = 24.
|
||||
REQUIRE(mummy.getCollisionRect().x == static_cast<float>(Map::playfield_x));
|
||||
}
|
||||
|
||||
void testMummyRoamingRecoversFromZeroDir() {
|
||||
std::cout << "[T19] mummy ROAMING amb dir=(0,0) re-decideix la propera step()\n";
|
||||
// Xarxa de seguretat: si per qualsevol via la mòmia entra a ROAMING
|
||||
// amb dir_x_/dir_y_ a zero, el `step()` anterior feia early return i
|
||||
// es quedava quieta per sempre. Ara re-decideix com a una intersecció
|
||||
// si troba alguna direcció vàlida.
|
||||
Map::loadFrom("data/maps/pyramid.yaml");
|
||||
|
||||
fkyaml::node tmpl = EntityLoader::loadTemplate("mummy");
|
||||
Mummy mummy(tmpl);
|
||||
mummy.setCollisionBox({.offset_x = 0, .offset_y = 16, .width = 16, .height = 16});
|
||||
// Posicionem-la a un creuament aliniat (interior, lluny de murs i
|
||||
// sarcs problemàtics): vertical x=88 ∩ horitzontal y=80.
|
||||
mummy.setPixelRect({.x = 88.0F, .y = 64.0F, .w = 16.0F, .h = 32.0F});
|
||||
// Forcem dir=(0,0) sense passar per HURT — simula l'estat patològic.
|
||||
mummy.setDirection(0.0F, 0.0F);
|
||||
REQUIRE(mummy.getDirX() == 0.0F);
|
||||
REQUIRE(mummy.getDirY() == 0.0F);
|
||||
|
||||
// Un sol step amb dt típic ha d'assignar alguna direcció no-zero.
|
||||
mummy.step(0.0167F);
|
||||
const bool RECOVERED = mummy.getDirX() != 0.0F || mummy.getDirY() != 0.0F;
|
||||
REQUIRE(RECOVERED);
|
||||
}
|
||||
|
||||
void testMummyHurtCornerNoSoftLock() {
|
||||
std::cout << "[T20] mummy HURT contra cantonada + re-whip no fa soft-lock\n";
|
||||
// Integració: cursa HURT cap a una cantonada del playfield, després
|
||||
// fueteig immediat al mateix lloc. Amb el bug, la mòmia quedava amb
|
||||
// dir=(0,0) i no es movia per molts fueteigs que rebés. Amb el fix
|
||||
// (snap a hurtStep + recovery a step), o bé `chooseHurtDirection`
|
||||
// troba una direcció lliure des de la posició aliniada, o bé el
|
||||
// proper step la rescata. En cap cas pot quedar permanentment quieta.
|
||||
Map::loadFrom("data/maps/pyramid.yaml");
|
||||
|
||||
fkyaml::node tmpl = EntityLoader::loadTemplate("mummy");
|
||||
Mummy mummy(tmpl);
|
||||
mummy.setCollisionBox({.offset_x = 0, .offset_y = 16, .width = 16, .height = 16});
|
||||
// Mateix runway que T18.
|
||||
mummy.setPixelRect({.x = 40.0F, .y = 160.0F, .w = 16.0F, .h = 32.0F});
|
||||
|
||||
mummy.enterHurt(Mummy::Direction::WEST);
|
||||
for (int i = 0; i < 200 && mummy.getState() == Mummy::State::HURT; ++i) {
|
||||
mummy.update(0.0173F);
|
||||
}
|
||||
// Fueteig immediat: enterHurt al moment exacte de quedar al mur.
|
||||
mummy.enterHurt(Mummy::Direction::WEST);
|
||||
|
||||
// Després d'una desena de frames, la mòmia s'ha de moure ja sigui via
|
||||
// la fugida HURT (perpendicular trobada) o via recovery a ROAMING.
|
||||
const auto BEFORE = mummy.getPixelRect();
|
||||
for (int i = 0; i < 60; ++i) { mummy.update(0.0173F); }
|
||||
const auto AFTER = mummy.getPixelRect();
|
||||
const float DX_TOTAL = AFTER.x - BEFORE.x;
|
||||
const float DY_TOTAL = AFTER.y - BEFORE.y;
|
||||
REQUIRE((DX_TOTAL != 0.0F) || (DY_TOTAL != 0.0F));
|
||||
}
|
||||
|
||||
void testMummyWhippableFlag() {
|
||||
std::cout << "[T17] mummy YAML llig `whippable` correctament\n";
|
||||
fkyaml::node tmpl = EntityLoader::loadTemplate("mummy");
|
||||
@@ -584,6 +677,9 @@ auto main() -> int {
|
||||
testMummyStep();
|
||||
testMummyHurtState();
|
||||
testMummyHurtAgainstWall();
|
||||
testMummyHurtSnapsAtPlayfieldWall();
|
||||
testMummyRoamingRecoversFromZeroDir();
|
||||
testMummyHurtCornerNoSoftLock();
|
||||
testMummyWhippableFlag();
|
||||
testTorchAnimation();
|
||||
testExitedAggregator();
|
||||
|
||||
Reference in new issue
Block a user