Mostly working spatical map with 2 level collision/space structure. Not the best implementation but this is the idea.

master
Zed A. Shaw 7 days ago
parent fd53f92fe6
commit d6326c9e41
  1. 3
      gui/main_ui.cpp
  2. 2
      levelmanager.cpp
  3. 33
      spatialmap.cpp
  4. 7
      spatialmap.hpp
  5. 13
      systems.cpp
  6. 24
      tests/spatialmap.cpp
  7. 9
      worldbuilder.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;

@ -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;
}

@ -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<>());

@ -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;

@ -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<InventoryItem>(junk_entity, {inventory_count, entity_data});
world.set<Position>(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>(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<Position>(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<Position>(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>(Events::GUI::ENTITY_SPAWN, item, {});

@ -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<size_t>(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);

@ -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<Position>(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<InventoryItem>(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;
}

Loading…
Cancel
Save