diff --git a/gui/main_ui.cpp b/gui/main_ui.cpp index 45970c3..676ed17 100644 --- a/gui/main_ui.cpp +++ b/gui/main_ui.cpp @@ -31,7 +31,8 @@ namespace gui { } DinkyECS::Entity MainUI::camera_aim() { - if($level.collision->occupied($rayview.aiming_at)) { + // what happens if there's two things at that spot + if($level.collision->something_there($rayview.aiming_at)) { return $level.collision->get($rayview.aiming_at); } else { return 0; diff --git a/levelmanager.cpp b/levelmanager.cpp index 0edec64..a9745f1 100644 --- a/levelmanager.cpp +++ b/levelmanager.cpp @@ -85,7 +85,7 @@ DinkyECS::Entity LevelManager::spawn_enemy(std::string named) { } } - level.collision->insert(entity_pos.location, entity_id); + level.collision->insert(entity_pos.location, entity_id, true); return entity_id; } diff --git a/spatialmap.cpp b/spatialmap.cpp index fb5f846..2c11aa0 100644 --- a/spatialmap.cpp +++ b/spatialmap.cpp @@ -5,25 +5,43 @@ using namespace fmt; using DinkyECS::Entity; -void SpatialMap::insert(Point pos, Entity ent) { - yes_collision.insert_or_assign(pos, ent); +void SpatialMap::insert(Point pos, Entity ent, bool has_collision) { + if(has_collision) { + yes_collision.insert_or_assign(pos, ent); + } else { + no_collision.insert_or_assign(pos, ent); + } } -void SpatialMap::remove(Point pos) { - yes_collision.erase(pos); +bool SpatialMap::remove(Point pos) { + if(yes_collision.contains(pos)) { + yes_collision.erase(pos); + return true; + } else { + no_collision.erase(pos); + return false; + } } void SpatialMap::move(Point from, Point to, Entity ent) { - remove(from); - insert(to, ent); + bool has_collision = remove(from); + insert(to, ent, has_collision); } bool SpatialMap::occupied(Point at) const { return yes_collision.contains(at); } +bool SpatialMap::something_there(Point at) const { + return yes_collision.contains(at) || no_collision.contains(at); +} + Entity SpatialMap::get(Point at) const { - return yes_collision.at(at); + if(yes_collision.contains(at)) { + return yes_collision.at(at); + } else { + return no_collision.at(at); + } } /* @@ -81,6 +99,7 @@ SortedEntities SpatialMap::distance_sorted(Point from, int max_dist) { SortedEntities sprite_distance; update_sorted(sprite_distance, yes_collision, from, max_dist); + update_sorted(sprite_distance, no_collision, from, max_dist); std::sort(sprite_distance.begin(), sprite_distance.end(), std::greater<>()); diff --git a/spatialmap.hpp b/spatialmap.hpp index 85908da..db975cb 100644 --- a/spatialmap.hpp +++ b/spatialmap.hpp @@ -20,11 +20,14 @@ class SpatialMap { public: SpatialMap() {} PointEntityMap yes_collision; + PointEntityMap no_collision; - void insert(Point pos, DinkyECS::Entity obj); + void insert(Point pos, DinkyECS::Entity obj, bool has_collision); void move(Point from, Point to, DinkyECS::Entity ent); - void remove(Point pos); + // return value is whether the removed thing has collision + bool remove(Point pos); bool occupied(Point pos) const; + bool something_there(Point at) const; DinkyECS::Entity get(Point at) const; FoundEntities neighbors(Point position, bool diag=false) const; diff --git a/systems.cpp b/systems.cpp index 7238ab5..d78ac34 100644 --- a/systems.cpp +++ b/systems.cpp @@ -163,11 +163,10 @@ void System::distribute_loot(GameLevel &level, Position target_pos) { // BUG: inventory_count here isn't really used to remove it world.set(junk_entity, {inventory_count, entity_data}); world.set(junk_entity, target_pos); - level.collision->insert(target_pos.location, junk_entity); + level.collision->insert(target_pos.location, junk_entity, false); level.world->send(Events::GUI::ENTITY_SPAWN, junk_entity, {}); } else { dbc::log("DEAD BODY NOT IMPLEMENTED, for now just removing enemy"); - level.collision->remove(target_pos.location); // BUG: should maybe add a component to the world for "dead thing no loot" that // has no collision or goes away after some kind of animation // Something like: @@ -213,9 +212,11 @@ void System::death(GameLevel &level) { } auto pos = world.get(ent); - // NOTE: distribute loot is responsible for either removing or replacing - // the collision for this entity. It has to do this since it's determining - // if a junkpile goes there or nothing + + // need to remove _after_ getting the position + level.collision->remove(pos.location); + + // distribute_loot is then responsible for putting something there System::distribute_loot(level, pos); world.destroy(ent); @@ -454,7 +455,7 @@ bool System::drop_item(GameLevel& level, Entity item) { if(map.can_move(pos.location) && !collision.occupied(pos.location)) { world.set(item, pos); - collision.insert(pos.location, item); + collision.insert(pos.location, item, false); // BUG: really there should be another system that handles loot->inv moves if(player_inv.has(item)) player_inv.remove(item); level.world->send(Events::GUI::ENTITY_SPAWN, item, {}); diff --git a/tests/spatialmap.cpp b/tests/spatialmap.cpp index 93bb250..04d018a 100644 --- a/tests/spatialmap.cpp +++ b/tests/spatialmap.cpp @@ -23,8 +23,8 @@ TEST_CASE("confirm basic collision operations", "[collision]") { Entity enemy = world.entity(); SpatialMap collider; - collider.insert({11,11}, player); - collider.insert({21,21}, enemy); + collider.insert({11,11}, player, true); + collider.insert({21,21}, enemy, true); { // not found auto [found, nearby] = collider.neighbors({1,1}); @@ -43,7 +43,7 @@ TEST_CASE("confirm basic collision operations", "[collision]") { REQUIRE(nearby.empty()); } - collider.insert({11,11}, player); // setup for the move test + collider.insert({11,11}, player, true); // setup for the move test { // moving, not found collider.move({11,11}, {12, 12}, player); auto [found, nearby] = collider.neighbors({10,10}, true); @@ -72,10 +72,10 @@ TEST_CASE("confirm multiple entities moving", "[collision]") { Entity e3 = world.entity(); SpatialMap collider; - collider.insert({11,11}, player); - collider.insert({10,10}, e2); - collider.insert({11,10}, e3); - collider.insert({21,21}, e1); + collider.insert({11,11}, player, true); + collider.insert({10,10}, e2, true); + collider.insert({11,10}, e3, true); + collider.insert({21,21}, e1, true); EntityList nearby = require_found(collider, {11,11}, false, 1); REQUIRE(nearby[0] == e3); @@ -95,10 +95,10 @@ TEST_CASE("test edge cases that might crash", "[collision]") { Entity enemy = world.entity(); SpatialMap collider; - collider.insert({0,0}, player); + collider.insert({0,0}, player, true); Point enemy_at = {1, 0}; - collider.insert(enemy_at, enemy); + collider.insert(enemy_at, enemy, true); EntityList nearby = require_found(collider, {0,0}, true, 1); @@ -118,10 +118,10 @@ TEST_CASE("check all diagonal works", "[collision]") { SpatialMap collider; Point player_at = {1,1}; - collider.insert(player_at, player); + collider.insert(player_at, player, true); Point enemy_at = {1, 0}; - collider.insert(enemy_at, enemy); + collider.insert(enemy_at, enemy, true); for(size_t x = 0; x <= 2; x++) { for(size_t y = 0; y <= 2; y++) { @@ -150,7 +150,7 @@ TEST_CASE("confirm can iterate through all", "[spatialmap-sort]") { size_t y = Random::uniform(0, 251); Entity ent = world.entity(); - collider.insert({x,y}, ent); + collider.insert({x,y}, ent, true); } auto sprite_distance = collider.distance_sorted(player, 1000); diff --git a/worldbuilder.cpp b/worldbuilder.cpp index 01525eb..84b98ec 100644 --- a/worldbuilder.cpp +++ b/worldbuilder.cpp @@ -91,15 +91,20 @@ DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, j // NOTE: aiming_at is set by the rayview since it knows that world.set(item, {pos.x, pos.y}); - if(entity_data["inventory_count"] > 0) { + // BUG: See #72, but this will change to a setting for collision + int inv_count = entity_data.contains("inventory_count") ? (int)entity_data["inventory_count"] : 0; + bool has_collision = true; + + if(inv_count > 0) { world.set(item, {entity_data["inventory_count"], entity_data}); + has_collision = false; } if(entity_data.contains("components")) { components::configure_entity(world, item, entity_data["components"]); } - $collision->insert(pos, item); + $collision->insert(pos, item, has_collision); return item; }