Skip to content

Commit

Permalink
Merge pull request #563 from Hypexion/fix-push-fall-interaction
Browse files Browse the repository at this point in the history
Fix interaction when pushing character off of falling tile
  • Loading branch information
mrimer committed Dec 28, 2023
2 parents 6ffdcc6 + 19948c2 commit aaf9606
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 4 deletions.
4 changes: 4 additions & 0 deletions DRODLib/Character.cpp
Expand Up @@ -7233,6 +7233,10 @@ void CCharacter::MoveCharacter(
room.ActivateToken(CueEvents, this->wX, this->wY, this);
}

//If another monster was pushed, the destination tile may have fallen
if (this->bPushedOtherMonster)
room.CheckForFallingAt(this->wX, this->wY, CueEvents);

SetWeaponSheathed();
RefreshBriars();

Expand Down
3 changes: 3 additions & 0 deletions DRODLib/Construct.cpp
Expand Up @@ -232,6 +232,9 @@ void CConstruct::Process(
UINT tTile = room.GetTSquare(this->wX, this->wY);
if (bIsTLayerCoveringItem(tTile))
room.PushTLayerObject(this->wX, this->wY, this->wX + dx, this->wY + dy, CueEvents);
//If another monster was pushed, the destination tile may have fallen
if (this->bPushedOtherMonster)
room.CheckForFallingAt(this->wX, this->wY, CueEvents);
}
}

Expand Down
8 changes: 5 additions & 3 deletions DRODLib/CurrentGame.cpp
Expand Up @@ -5996,6 +5996,7 @@ void CCurrentGame::ProcessPlayer(
const UINT wTTileNo = this->pRoom->GetTSquare(this->swordsman.wX, this->swordsman.wY);
bool bEnteredTunnel = false;
bool bMovingPlatform = false;
bool bPushedCharacter = false;

//Look for obstacles and set dx/dy accordingly.
const UINT wMoveO = nGetO(dx, dy);
Expand Down Expand Up @@ -6252,9 +6253,6 @@ void CCurrentGame::ProcessPlayer(
//Player bumps into an NPC, see if we can push him first
CCharacter *pCharacter = DYN_CAST(CCharacter*, CMonster*, pMonster);


bool bPushedCharacter = false;

if (pCharacter->IsPushableByBody()){
const UINT wDestX = pCharacter->wX + dx;
const UINT wDestY = pCharacter->wY + dy;
Expand Down Expand Up @@ -6360,6 +6358,10 @@ void CCurrentGame::ProcessPlayer(
this->pRoom->DestroyTrapdoor(this->swordsman.wX - dx,
this->swordsman.wY - dy, CueEvents);

//If a character was pushed, the destination tile may have fallen
if (bPushedCharacter)
this->pRoom->CheckForFallingAt(this->swordsman.wX, this->swordsman.wY, CueEvents);

//Check for stepping on monster
CMonster* pMonster = this->pRoom->GetMonsterAtSquare(this->swordsman.wX, this->swordsman.wY);
if (pMonster)
Expand Down
4 changes: 4 additions & 0 deletions DRODLib/Mimic.cpp
Expand Up @@ -347,6 +347,10 @@ void CMimic::ApplyMimicMove(int dx, int dy, int nCommand, const UINT wMovementO,
if (bIsPit(wOTile) || bIsDeepWater(wOTile))
room.MovePlatform(this->wX - dx, this->wY - dy, nGetO(dx,dy));
}

//If another monster was pushed, the destination tile may have fallen
if (this->bPushedOtherMonster)
room.CheckForFallingAt(this->wX, this->wY, CueEvents);
}

//Check for movement onto a checkpoint.
Expand Down
5 changes: 4 additions & 1 deletion DRODLib/Monster.cpp
Expand Up @@ -105,7 +105,7 @@ CMonster::CMonster(
, bAlive(true)
, bForceWeaponAttack(false)
, stunned(0), bNewStun(false)
, bPushedThisTurn(false)
, bPushedThisTurn(false), bPushedOtherMonster(false)
, bWaitedOnHotFloorLastTurn(false)
, pNext(NULL), pPrevious(NULL)
, pCurrentGame(NULL)
Expand Down Expand Up @@ -134,6 +134,7 @@ void CMonster::Clear()
this->stunned = 0;
this->bNewStun = false;
this->bPushedThisTurn = false;
this->bPushedOtherMonster = false;
this->ExtraVars.Clear();
while (this->Pieces.size())
{
Expand Down Expand Up @@ -2160,12 +2161,14 @@ void CMonster::Move(
CDbRoom& room = *(this->pCurrentGame->pRoom);
CMonster *pMonster = room.GetMonsterAtSquare(wDestX,wDestY);
bool bFluffPoison = false;
bPushedOtherMonster = false;
if (pMonster)
{
ASSERT(pCueEvents);

if (pMonster->IsPushableByBody() && this->CanPushMonsters()){
pMonster->PushInDirection(sgn(wDestX - this->wX), sgn(wDestY - this->wY), false, *pCueEvents);
bPushedOtherMonster = true;
}
else
{
Expand Down
1 change: 1 addition & 0 deletions DRODLib/Monster.h
Expand Up @@ -365,6 +365,7 @@ class CMonster : public CEntity
UINT stunned; //whether monster is stunned and skips turn
bool bNewStun; //whether monster stun was inflicted this turn
bool bPushedThisTurn; //whether monster was pushed this turn
bool bPushedOtherMonster; //if another monster was pushed during most recent movement
bool bWaitedOnHotFloorLastTurn; //for hasteable playerdoubles
bool bSafeToDelete;

Expand Down
172 changes: 172 additions & 0 deletions DRODLibTests/src/tests/Scripting/ImperativePushable/PushableByBody.cpp
Expand Up @@ -8,6 +8,19 @@ static void AddPushableCharacter(UINT x, UINT y){
RoomBuilder::AddCommand(character, CCharacterCommand::CC_Imperative, ScriptFlag::PushableByBody);
}

static void AddPushableDropperCharacter(UINT x, UINT y) {
CCharacter* character = RoomBuilder::AddVisibleCharacter(x, y);
RoomBuilder::AddCommand(character, CCharacterCommand::CC_Imperative, ScriptFlag::PushableByBody);
RoomBuilder::AddCommand(character, CCharacterCommand::CC_Behavior, ScriptFlag::DropTrapdoors, 1);
}

static void AddPusherCharacter(UINT x, UINT y, MovementType movement) {
CCharacter* character = RoomBuilder::AddVisibleCharacter(x, y);
RoomBuilder::AddCommand(character, CCharacterCommand::CC_SetMovementType, movement);
RoomBuilder::AddCommand(character, CCharacterCommand::CC_Behavior, ScriptFlag::PushMonsters, 1);
RoomBuilder::AddCommand(character, CCharacterCommand::CC_MoveRel, 1, 0);
}

static void TestCharacterCanBePushedByCharacterWithIdentity(const UINT identity) {
char name[100];
sprintf(name, "Character ID #%d should be able to push the character", identity);
Expand All @@ -24,6 +37,89 @@ static void TestCharacterCanBePushedByCharacterWithIdentity(const UINT identity)
}
}

static void TestDeadlyFallingTilePlayerPush(UINT tile) {
char name[100];
sprintf(name, "Player dies pushing trapdoor dropper from tile ID #%d", tile);

SECTION(name) {
RoomBuilder::Plot(tile, 10, 10);
AddPushableDropperCharacter(10, 10);

CCurrentGame* game = Runner::StartGame(9, 10, S);
Runner::ExecuteCommand(CMD_E);

AssertMonster(11, 10);
AssertPlayerIsDead();
}
}

static void TestDeadlyFallingTileMimicPush(UINT tile) {
char name[100];
sprintf(name, "Mimic dies pushing trapdoor dropper from tile ID #%d", tile);

SECTION(name) {
RoomBuilder::Plot(tile, 10, 10);
AddPushableDropperCharacter(10, 10);
RoomBuilder::AddMonster(M_MIMIC, 9, 10, S);

CCurrentGame* game = Runner::StartGame(15, 10, S);
Runner::ExecuteCommand(CMD_E);

AssertMonster(11, 10);
AssertNoMonster(10, 10);
}
}

static void TestDeadlyFallingTileConstructPush(UINT tile) {
char name[100];
sprintf(name, "Construct dies pushing trapdoor dropper from tile ID #%d", tile);

SECTION(name) {
RoomBuilder::Plot(tile, 10, 10);
AddPushableDropperCharacter(10, 10);
RoomBuilder::AddMonster(M_CONSTRUCT, 9, 10, S);

CCurrentGame* game = Runner::StartGame(15, 10, S);
Runner::ExecuteCommand(CMD_WAIT);

AssertMonster(11, 10);
AssertNoMonster(10, 10);
}
}

static void TestDeadlyFallingTileCharacterPush(MovementType movement, UINT tile) {
char name[100];
sprintf(name, "Character with movement %d dies pushing trapdoor dropper from tile ID #%d", movement, tile);

SECTION(name) {
RoomBuilder::Plot(tile, 10, 10);
AddPushableDropperCharacter(10, 10);
AddPusherCharacter(9, 10, movement);

CCurrentGame* game = Runner::StartGame(15, 10, S);
Runner::ExecuteCommand(CMD_E);

AssertMonster(11, 10);
AssertNoMonster(10, 10);
}
}

static void TestSafeFallingTileCharacterPush(MovementType movement, UINT tile) {
char name[100];
sprintf(name, "Character with movement %d survives pushing trapdoor dropper from tile ID #%d", movement, tile);

SECTION(name) {
RoomBuilder::Plot(tile, 10, 10);
AddPushableDropperCharacter(10, 10);
AddPusherCharacter(9, 10, movement);

CCurrentGame* game = Runner::StartGame(15, 10, S);
Runner::ExecuteCommand(CMD_E);

AssertMonster(11, 10);
AssertMonster(10, 10);
}
}

TEST_CASE("Invulnerable character with 'Imperative: Pushable by body'", "[game][player moves][beethro][scripting][imperative][push]") {
RoomBuilder::ClearRoom();
Expand Down Expand Up @@ -147,3 +243,79 @@ TEST_CASE("Invulnerable character with 'Imperative: Pushable by body'", "[game][
TestCharacterCanBePushedByCharacterWithIdentity(M_TARTECHNICIAN);
TestCharacterCanBePushedByCharacterWithIdentity(M_CONSTRUCT);
}

TEST_CASE("Push character off of falling tile", "[game][player moves][beethro][scripting][imperative][push]") {
RoomBuilder::ClearRoom();

TestDeadlyFallingTilePlayerPush(T_TRAPDOOR);
TestDeadlyFallingTilePlayerPush(T_TRAPDOOR2);
TestDeadlyFallingTilePlayerPush(T_THINICE);

SECTION("Player survives pushing trapdoor dropper from shallow thin ice") {
RoomBuilder::Plot(T_THINICE_SH, 10, 10);
AddPushableDropperCharacter(10, 10);

CCurrentGame* game = Runner::StartGame(9, 10, S);
Runner::ExecuteCommand(CMD_E);

AssertMonster(11, 10);
AssertPlayerIsAlive();
}

TestDeadlyFallingTileMimicPush(T_TRAPDOOR);
TestDeadlyFallingTileMimicPush(T_TRAPDOOR2);
TestDeadlyFallingTileMimicPush(T_THINICE);

SECTION("Mimic survives pushing trapdoor dropper from shallow thin ice") {
RoomBuilder::Plot(T_THINICE_SH, 10, 10);
AddPushableDropperCharacter(10, 10);
RoomBuilder::AddMonster(M_MIMIC, 9, 10, S);

CCurrentGame* game = Runner::StartGame(15, 10, S);
Runner::ExecuteCommand(CMD_E);

AssertMonsterType(10, 10, M_MIMIC);
AssertMonster(11, 10);
}

TestDeadlyFallingTileConstructPush(T_TRAPDOOR);
TestDeadlyFallingTileConstructPush(T_TRAPDOOR2);
TestDeadlyFallingTileConstructPush(T_THINICE);

SECTION("Construct survives pushing trapdoor dropper from shallow thin ice") {
RoomBuilder::Plot(T_THINICE_SH, 10, 10);
AddPushableDropperCharacter(10, 10);
RoomBuilder::AddMonster(M_CONSTRUCT, 9, 10, S);

CCurrentGame* game = Runner::StartGame(15, 10, S);
Runner::ExecuteCommand(CMD_WAIT);

AssertMonsterType(10, 10, M_CONSTRUCT);
AssertMonster(11, 10);
}

TestDeadlyFallingTileCharacterPush(GROUND, T_TRAPDOOR);
TestDeadlyFallingTileCharacterPush(GROUND, T_TRAPDOOR2);
TestDeadlyFallingTileCharacterPush(GROUND, T_THINICE);
TestDeadlyFallingTileCharacterPush(GROUND, T_THINICE_SH);

TestSafeFallingTileCharacterPush(AIR, T_TRAPDOOR);
TestSafeFallingTileCharacterPush(AIR, T_TRAPDOOR2);
TestSafeFallingTileCharacterPush(AIR, T_THINICE);
TestSafeFallingTileCharacterPush(AIR, T_THINICE_SH);

TestDeadlyFallingTileCharacterPush(WALL, T_TRAPDOOR);
TestDeadlyFallingTileCharacterPush(WALL, T_TRAPDOOR2);
TestDeadlyFallingTileCharacterPush(WALL, T_THINICE);
TestDeadlyFallingTileCharacterPush(WALL, T_THINICE_SH);

TestDeadlyFallingTileCharacterPush(WATER, T_TRAPDOOR);
TestSafeFallingTileCharacterPush(WATER, T_TRAPDOOR2);
TestSafeFallingTileCharacterPush(WATER, T_THINICE);
TestSafeFallingTileCharacterPush(WATER, T_THINICE_SH);

TestDeadlyFallingTileCharacterPush(GROUND_AND_SHALLOW_WATER, T_TRAPDOOR);
TestDeadlyFallingTileCharacterPush(GROUND_AND_SHALLOW_WATER, T_TRAPDOOR2);
TestDeadlyFallingTileCharacterPush(GROUND_AND_SHALLOW_WATER, T_THINICE);
TestSafeFallingTileCharacterPush(GROUND_AND_SHALLOW_WATER, T_THINICE_SH);
}

0 comments on commit aaf9606

Please sign in to comment.