From 4c3378020302663ecd466073d4c59aad27ee391f Mon Sep 17 00:00:00 2001 From: Lutz Date: Wed, 11 Oct 2017 21:16:00 -0500 Subject: [PATCH 1/7] + Added function 'EntityInFrontOfEntity()' to handle collision for pushing Boulders --- src/collision.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++ src/collision.hpp | 1 + 2 files changed, 82 insertions(+) diff --git a/src/collision.cpp b/src/collision.cpp index baed0a88f..f0b173725 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -256,6 +256,87 @@ bool entityInsideEntity(Entity* entity1, Entity* entity2) return false; } +/*------------------------------------------------------------------------------- + + EntityInFrontOfEntity + + Checks whether an entity is intersecting another entity from the front + Used for boulders to prevent harming the Player pushing the boulder + +-------------------------------------------------------------------------------*/ + +bool EntityInFrontOfEntity(Entity* entity1, Entity* entity2, Sint32 direction) +{ + switch ( direction ) + { + case 0: // East + if ( (entity1->x + entity1->sizex) > (entity2->x - entity2->sizex) ) // Check everything East (+X) Is the Entity in front of the boulder? + { + if ( (entity1->x + entity1->sizex) < (entity2->x + entity2->sizex) ) // Check everything West (-X) Is the Entity intersecting the boulder? + { + if ( (entity1->y - entity1->sizey) < (entity2->y + entity2->sizey) ) // Check everything North (-Y) Is the Entity not too far to the left of the boulder? + { + if ( (entity1->y + entity1->sizey) > (entity2->y - entity2->sizey) ) // Check everything South (+Y) Is the Entity not too far to the right of the boulder? + { + return true; + } + } + } + } + return false; + break; + case 1: // South + if ( (entity1->y + entity1->sizey) > (entity2->y - entity2->sizey) ) // Check everything South (+Y) Is the Entity in front of the boulder? + { + if ( (entity1->y + entity1->sizey) < (entity2->y + entity2->sizey) ) // Check everything North (-Y) Is the Entity intersecting the boulder? + { + if ( (entity1->x + entity1->sizex) > (entity2->x - entity2->sizex) ) // Check everything East (+X) Is the Entity not too far to the left of the boulder? + { + if ( (entity1->x - entity1->sizex) < (entity2->x + entity2->sizex) ) // Check everything West (-X) Is the Entity not too far to the right of the boulder? + { + return true; + } + } + } + } + return false; + break; + case 2: // West + if ( (entity1->x - entity1->sizex) < (entity2->x + entity2->sizex) ) // Check everything West (-X) Is the Entity in front of the boulder? + { + if ( (entity1->x - entity1->sizex) > (entity2->x - entity2->sizex) ) // Check everything East (+X) Is the Entity intersecting the boulder? + { + if ( (entity1->y + entity1->sizey) > (entity2->y - entity2->sizey) ) // Check everything South (+Y) Is the Entity too far to the left of the boulder? + { + if ( (entity1->y - entity1->sizey) < (entity2->y + entity2->sizey) ) // Check everything North (-Y) Is the Entity too far to the right of the boulder? + { + return true; + } + } + } + } + return false; + break; + case 3: // North + if ( (entity1->y - entity1->sizey) < (entity2->y + entity2->sizey) ) // Check everything North (-Y) Is the Entity in front of the boulder? + { + if ( (entity1->y - entity1->sizey) > (entity2->y - entity2->sizey) ) // Check everything South (+Y) Is the Entity intersecting the boulder? + { + if ( (entity1->x - entity1->sizex) < (entity2->x + entity2->sizex) ) // Check everything West (-X) Is the Entity not too far to the left of the boulder? + { + if ( (entity1->x + entity1->sizex) > (entity2->x - entity2->sizex) ) // Check everything East (+X) Is the Entity not too far to the right of the boulder? + { + return true; + } + } + } + } + return false; + break; + default: return false; // Should never happen + } +} + /*------------------------------------------------------------------------------- entityInsideSomething diff --git a/src/collision.hpp b/src/collision.hpp index 88d1d3fb1..989e140c8 100644 --- a/src/collision.hpp +++ b/src/collision.hpp @@ -18,6 +18,7 @@ real_t entityDist(Entity* my, Entity* your); Entity* entityClicked(); bool entityInsideTile(Entity* entity, int x, int y, int z); bool entityInsideEntity(Entity* entity1, Entity* entity2); +bool EntityInFrontOfEntity(Entity* entity1, Entity* entity2, Sint32 direction); bool entityInsideSomething(Entity* entity); int barony_clear(real_t tx, real_t ty, Entity* my); real_t clipMove(real_t* x, real_t* y, real_t vx, real_t vy, Entity* my); From 603cc9e98bf23f43c469f76d758a0442f60d2df6 Mon Sep 17 00:00:00 2001 From: Lutz Date: Wed, 11 Oct 2017 21:19:49 -0500 Subject: [PATCH 2/7] * Refactored Boulder logic to handle pushing collision issues --- src/actboulder.cpp | 187 ++++++++++++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 63 deletions(-) diff --git a/src/actboulder.cpp b/src/actboulder.cpp index e1da4ba13..db86a2ac3 100644 --- a/src/actboulder.cpp +++ b/src/actboulder.cpp @@ -28,33 +28,60 @@ #define BOULDER_DESTX my->skill[6] #define BOULDER_DESTY my->skill[7] -/*------------------------------------------------------------------------------- - - boulderCheckAgainstEntity - - causes the boulder given in my to crush the object given in entity - or vice versa - --------------------------------------------------------------------------------*/ - -int boulderCheckAgainstEntity(Entity* my, Entity* entity) +/* actboulder.cpp + * @param my - A pointer to the Boulder which is checking for collisions + * @param entity - A pointer to the current Entity being checked against @my + * @param bWasBoulderPushed - True if the Boulder was pushed before calling this function + * @returns true - If the Boulder was destroyed as a result of a collision + * @returns false - Default case + * Checks for collisions against every Entity on the map + * Damages Players or Monsters. Can be destroyed if the Entity survives + * Destroys Doors and stops movement in every other case + */ +bool BoulderCheckAgainstEntity(Entity* const my, Entity* const entity, const bool bWasBoulderPushed) { - if (!my || !entity) + if ( my == nullptr || entity == nullptr ) { - return 0; + printlog("ERROR: BoulderCheckAgainstEntity() - A parameter is null. my = %d, entity = %d.", (my == nullptr), (entity == nullptr)); + return false; } + // Boulders crush or hurt Players and Monsters if ( entity->behavior == &actPlayer || entity->behavior == &actMonster ) { - if ( entityInsideEntity( my, entity ) ) + bool bWasEntityHit = false; + + // Calculate collision differently if the Boulder was pushed + if ( bWasBoulderPushed ) { - Stat* stats = entity->getStats(); - if ( stats ) + // If the Boulder was pushed, check to see if the Entity is in front of the Boulder + if ( EntityInFrontOfEntity(my, entity, BOULDER_ROLLDIR) ) { + bWasEntityHit = true; + } + } + else + { + // Check if the Entity is inside of the Boulder + if ( entityInsideEntity(my, entity) ) + { + bWasEntityHit = true; + } + } + + // If the Entity was hit, damage them TODOR: (also handles killing them) + if ( bWasEntityHit ) + { + Stat* entityStats = entity->getStats(); + if ( entityStats != nullptr ) + { + // If a Player was hit, message them to let them know, and shake the screen if ( entity->behavior == &actPlayer ) { Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0); - messagePlayerColor(entity->skill[2], color, language[455]); + messagePlayerColor(entity->skill[2], color, language[455]); // "You are struck by a boulder!" + + // Shake the screen if ( entity->skill[2] == clientnum ) { camera_shakex += .1; @@ -63,7 +90,7 @@ int boulderCheckAgainstEntity(Entity* my, Entity* entity) else { strcpy((char*)net_packet->data, "SHAK"); - net_packet->data[4] = 10; // turns into .1 + net_packet->data[4] = 10; // Turns into .1 net_packet->data[5] = 10; net_packet->address.host = net_clients[entity->skill[2] - 1].host; net_packet->address.port = net_clients[entity->skill[2] - 1].port; @@ -71,23 +98,29 @@ int boulderCheckAgainstEntity(Entity* my, Entity* entity) sendPacketSafe(net_sock, -1, net_packet, entity->skill[2] - 1); } } - playSoundEntity(my, 181, 128); - playSoundEntity(entity, 28, 64); + + playSoundEntity(my, 181, 128); // "BoulderCrunch.ogg" + playSoundEntity(entity, 28, 64); // "Damage.ogg" + spawnGib(entity); entity->modHP(-80); - entity->setObituary(language[1505]); + entity->setObituary(language[1505]); // "fails to dodge the incoming boulder." + + // If a Player was hit, and killed, attempt to unlock the Steam Achievement "Throw Me The Whip!" if ( entity->behavior == &actPlayer ) - if ( stats->HP <= 0 ) + { + if ( entityStats->HP <= 0 ) { steamAchievementClient(entity->skill[2], "BARONY_ACH_THROW_ME_THE_WHIP"); } - if ( stats->HP > 0 ) - { - // spawn several rock items - int i = 8 + rand() % 4; + } - int c; - for ( c = 0; c < i; c++ ) + // If the Entity survived being hit by the Boulder, destroy the Boulder + if ( entityStats->HP > 0 ) + { + // Spawn several Rock Items + Uint8 numberOfRocks = 8 + rand() % 4; + for ( Uint8 iRockIndex = 0; iRockIndex < numberOfRocks; iRockIndex++ ) { Entity* entity = newEntity(-1, 1, map.entities); entity->flags[INVISIBLE] = true; @@ -103,52 +136,67 @@ int boulderCheckAgainstEntity(Entity* my, Entity* entity) entity->vel_z = -.25 - (rand() % 5) / 10.0; entity->flags[PASSABLE] = true; entity->behavior = &actItem; - entity->flags[USERFLAG1] = true; // no collision: helps performance - entity->skill[10] = GEM_ROCK; // type - entity->skill[11] = WORN; // status - entity->skill[12] = 0; // beatitude - entity->skill[13] = 1; // count - entity->skill[14] = 0; // appearance - entity->skill[15] = false; // identified + entity->flags[USERFLAG1] = true; // No collision, helps performance + entity->skill[10] = GEM_ROCK; // Type + entity->skill[11] = WORN; // Status + entity->skill[12] = 0; // Beatitude + entity->skill[13] = 1; // Count + entity->skill[14] = 0; // Appearance + entity->skill[15] = false; // Identified } - double ox = my->x; - double oy = my->y; - - // destroy the boulder - playSoundEntity(my, 67, 128); - list_RemoveNode(my->mynode); - - // on sokoban, destroying boulders spawns scorpions + // On the Sokoban map, destroying Boulders spawns Scorpions if ( !strcmp(map.name, "Sokoban") ) { - Entity* monster = summonMonster(SCORPION, ox, oy); - if ( monster ) + Entity* sokobanScorpion = summonMonster(SCORPION, static_cast(my->x), static_cast(my->y)); + if ( sokobanScorpion != nullptr ) { - int c; - for ( c = 0; c < MAXPLAYERS; c++ ) + // Message all Players that a Scorpion was summoned + for ( Uint8 iPlayerIndex = 0; iPlayerIndex < MAXPLAYERS; iPlayerIndex++ ) { + // Don't message Players that are not connected + if ( client_disconnected[iPlayerIndex] ) + { + continue; + } + Uint32 color = SDL_MapRGB(mainsurface->format, 255, 128, 0); - messagePlayerColor(c, color, language[406]); + messagePlayerColor(iPlayerIndex, color, language[406]); // "You have angered the gods of Sokoban!" } } } - return 1; + // Destroy the Boulder + playSoundEntity(my, 67, 128); // "BustWall.ogg" + list_RemoveNode(my->mynode); + + return true; } } + else + { + printlog("ERROR: BoulderCheckAgainstEntity() - Entity Stats are null."); + return false; + } } + + return false; } + + // Boulders stop moving when hitting anything but Players, Monsters, and Doors else if ( entity->behavior == &actGate || entity->behavior == &actBoulder || entity->behavior == &actChest || entity->behavior == &actHeadstone || entity->behavior == &actFountain || entity->behavior == &actSink ) { + // Passable Entities like open Gates wont block a Boulder if ( !entity->flags[PASSABLE] ) { - if ( entityInsideEntity( my, entity ) ) + if ( entityInsideEntity(my, entity) ) { - // stop the boulder + // Stop the Boulder BOULDER_STOPPED = 1; BOULDER_ROLLING = 0; - playSoundEntity(my, 181, 128); + playSoundEntity(my, 181, 128); // "BoulderCrunch.ogg" + + // Make the Boulder impassable if ( my->flags[PASSABLE] ) { my->flags[PASSABLE] = false; @@ -159,26 +207,37 @@ int boulderCheckAgainstEntity(Entity* my, Entity* entity) } } } + + return false; } + + // Boulders destroy Doors else if ( entity->behavior == &actDoor ) { - if ( entityInsideEntity( my, entity ) ) + if ( entityInsideEntity(my, entity) ) { - playSoundEntity(entity, 28, 64); - entity->skill[4] = 0; - if ( !entity->skill[0] ) + playSoundEntity(entity, 28, 64); // "Damage.ogg" + + entity->skill[4] = 0; // DOOR_HEALTH + + // TODOR: What does this do? + if ( !entity->skill[0] ) // DOOR_DIR { - entity->skill[6] = (my->x > entity->x); + entity->skill[6] = (my->x > entity->x); // DOOR_SMACKED } else { - entity->skill[6] = (my->y < entity->y); + entity->skill[6] = (my->y < entity->y); // DOOR_SMACKED } - playSoundEntity(my, 181, 128); + + playSoundEntity(my, 181, 128); // "BoulderCrunch.ogg" } + + return false; } - return 0; -} + + return false; +} // BoulderCheckAgainstEntity() /*------------------------------------------------------------------------------- @@ -208,10 +267,12 @@ void actBoulder(Entity* my) // gravity bool nobounce = true; if ( !BOULDER_NOGROUND ) + { if ( noground ) { BOULDER_NOGROUND = true; } + } if ( my->z < 0 || BOULDER_NOGROUND ) { my->vel_z = std::min(my->vel_z + .1, 3.0); @@ -235,7 +296,7 @@ void actBoulder(Entity* my) { continue; } - if ( boulderCheckAgainstEntity(my, entity) ) + if ( BoulderCheckAgainstEntity(my, entity) ) { return; } @@ -338,7 +399,7 @@ void actBoulder(Entity* my) { continue; } - if ( boulderCheckAgainstEntity(my, entity) ) + if ( BoulderCheckAgainstEntity(my, entity) ) { return; } @@ -507,7 +568,7 @@ void actBoulder(Entity* my) { continue; } - if ( boulderCheckAgainstEntity(my, entity) ) + if ( BoulderCheckAgainstEntity(my, entity) ) { return; } From b7fe576507588b7517925e28ea43e0e407c12f99 Mon Sep 17 00:00:00 2001 From: Lutz Date: Fri, 13 Oct 2017 20:44:28 -0500 Subject: [PATCH 3/7] * Added the missing third parameter to calls to 'BoulderCheckAgainstEntity()' --- src/actboulder.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/actboulder.cpp b/src/actboulder.cpp index db86a2ac3..a7caede56 100644 --- a/src/actboulder.cpp +++ b/src/actboulder.cpp @@ -296,7 +296,8 @@ void actBoulder(Entity* my) { continue; } - if ( BoulderCheckAgainstEntity(my, entity) ) + + if ( BoulderCheckAgainstEntity(my, entity, false) ) { return; } @@ -399,7 +400,8 @@ void actBoulder(Entity* my) { continue; } - if ( BoulderCheckAgainstEntity(my, entity) ) + + if ( BoulderCheckAgainstEntity(my, entity, false) ) { return; } @@ -568,7 +570,7 @@ void actBoulder(Entity* my) { continue; } - if ( BoulderCheckAgainstEntity(my, entity) ) + if ( BoulderCheckAgainstEntity(my, entity, true) ) { return; } From 9cf1700a94332153d033ad3126da2950a08c2346 Mon Sep 17 00:00:00 2001 From: Lutz Date: Fri, 20 Oct 2017 18:25:47 -0500 Subject: [PATCH 4/7] - Removed redundant 'break' statements --- src/collision.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/collision.cpp b/src/collision.cpp index f0b173725..b18bd93d6 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -284,7 +284,6 @@ bool EntityInFrontOfEntity(Entity* entity1, Entity* entity2, Sint32 direction) } } return false; - break; case 1: // South if ( (entity1->y + entity1->sizey) > (entity2->y - entity2->sizey) ) // Check everything South (+Y) Is the Entity in front of the boulder? { @@ -300,7 +299,6 @@ bool EntityInFrontOfEntity(Entity* entity1, Entity* entity2, Sint32 direction) } } return false; - break; case 2: // West if ( (entity1->x - entity1->sizex) < (entity2->x + entity2->sizex) ) // Check everything West (-X) Is the Entity in front of the boulder? { @@ -316,7 +314,6 @@ bool EntityInFrontOfEntity(Entity* entity1, Entity* entity2, Sint32 direction) } } return false; - break; case 3: // North if ( (entity1->y - entity1->sizey) < (entity2->y + entity2->sizey) ) // Check everything North (-Y) Is the Entity in front of the boulder? { @@ -332,7 +329,6 @@ bool EntityInFrontOfEntity(Entity* entity1, Entity* entity2, Sint32 direction) } } return false; - break; default: return false; // Should never happen } } From fcca10f1eecd63bc57d24562a26066d65c19afee Mon Sep 17 00:00:00 2001 From: Lutz Date: Mon, 23 Oct 2017 21:19:34 -0500 Subject: [PATCH 5/7] * Changed the radius of Boulders to be the correct size (7 -> 8) This fixes the massive jump boulders will make when pushing them into entities --- src/actmonster.cpp | 12 ++++++------ src/maps.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/actmonster.cpp b/src/actmonster.cpp index 0f4d80910..5bce33b8e 100644 --- a/src/actmonster.cpp +++ b/src/actmonster.cpp @@ -3783,8 +3783,8 @@ void actMonster(Entity* my) } entity->z = -64; entity->yaw = angle * (PI / 2.f); - entity->sizex = 7; - entity->sizey = 7; + entity->sizex = 8; + entity->sizey = 8; entity->behavior = &actBoulder; entity->flags[UPDATENEEDED] = true; entity->flags[PASSABLE] = true; @@ -3833,8 +3833,8 @@ void actMonster(Entity* my) } entity->z = -64; entity->yaw = angle * (PI / 2.f); - entity->sizex = 7; - entity->sizey = 7; + entity->sizex = 8; + entity->sizey = 8; entity->behavior = &actBoulder; entity->flags[UPDATENEEDED] = true; entity->flags[PASSABLE] = true; @@ -3883,8 +3883,8 @@ void actMonster(Entity* my) } entity->z = -64; entity->yaw = angle * (PI / 2.f); - entity->sizex = 7; - entity->sizey = 7; + entity->sizex = 8; + entity->sizey = 8; entity->behavior = &actBoulder; entity->flags[UPDATENEEDED] = true; entity->flags[PASSABLE] = true; diff --git a/src/maps.cpp b/src/maps.cpp index 327c35783..732ffd65b 100644 --- a/src/maps.cpp +++ b/src/maps.cpp @@ -2445,8 +2445,8 @@ void assignActions(map_t* map) entity->x += 8; entity->y += 8; entity->sprite = 245; - entity->sizex = 7; - entity->sizey = 7; + entity->sizex = 8; + entity->sizey = 8; entity->behavior = &actBoulder; entity->skill[0] = 1; // BOULDER_STOPPED break; From 94d5ef888380f3c6cd9eab402bb0934eaec9ec63 Mon Sep 17 00:00:00 2001 From: Lutz Date: Mon, 23 Oct 2017 21:21:52 -0500 Subject: [PATCH 6/7] * Changed the radius of Boulders to be the correct size (7 -> 8) --- src/actboulder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/actboulder.cpp b/src/actboulder.cpp index a7caede56..d5b3efa1a 100644 --- a/src/actboulder.cpp +++ b/src/actboulder.cpp @@ -674,8 +674,8 @@ void actBoulderTrap(Entity* my) entity->y = (y << 4) + 8; entity->z = -64; entity->yaw = c * (PI / 2.f); - entity->sizex = 7; - entity->sizey = 7; + entity->sizex = 8; + entity->sizey = 8; if ( checkObstacle( entity->x + cos(entity->yaw) * 16, entity->y + sin(entity->yaw) * 16, entity, NULL ) ) { entity->yaw += PI * (rand() % 2) - PI / 2; From 190d72f003e0d7b5afc28a39adeffb9371fde5d7 Mon Sep 17 00:00:00 2001 From: Lutz Date: Tue, 24 Oct 2017 16:00:09 -0500 Subject: [PATCH 7/7] * Implemented a system to undo the movement to place boulder back in a solid tile Note: It is very likely and possible that this could increase/cause the clipping issues where a player will be partially inside of a boulder. That issue has to be solved separately. This commit fixes issues where the boulder is in a semi-moved state. --- src/actboulder.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/actboulder.cpp b/src/actboulder.cpp index d5b3efa1a..bbc6a0b40 100644 --- a/src/actboulder.cpp +++ b/src/actboulder.cpp @@ -194,6 +194,25 @@ bool BoulderCheckAgainstEntity(Entity* const my, Entity* const entity, const boo // Stop the Boulder BOULDER_STOPPED = 1; BOULDER_ROLLING = 0; + + // Move the Boulder back to a solid tile + switch ( BOULDER_ROLLDIR ) + { + case 0: + my->x += ((BOULDER_DESTX - 16) - my->x); + break; + case 1: + my->y += ((BOULDER_DESTY - 16) - my->y); + break; + case 2: + my->x += ((BOULDER_DESTX + 16) - my->x); + break; + case 3: + my->y += ((BOULDER_DESTY + 16) - my->y); + break; + default: printlog("ERROR: BoulderCheckAgainstEntity() - BOULDER_ROLLDIR is out of bounds."); break; + } + playSoundEntity(my, 181, 128); // "BoulderCrunch.ogg" // Make the Boulder impassable