The problem with picking up items under a dead body is fixed but now need to fix combat.
This commit is contained in:
parent
97fe02d99d
commit
f84b63f0e6
7 changed files with 93 additions and 43 deletions
2
Makefile
2
Makefile
|
@ -60,7 +60,7 @@ clean:
|
||||||
meson compile --clean -C builddir
|
meson compile --clean -C builddir
|
||||||
|
|
||||||
debug_test: build
|
debug_test: build
|
||||||
gdb --nx -x .gdbinit --ex run --args builddir/runtests -e "[map]"
|
gdb --nx -x .gdbinit --ex run --args builddir/runtests -e
|
||||||
|
|
||||||
win_installer:
|
win_installer:
|
||||||
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'
|
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'
|
||||||
|
|
16
gui/fsm.cpp
16
gui/fsm.cpp
|
@ -74,7 +74,7 @@ namespace gui {
|
||||||
void FSM::MOVING(Event ) {
|
void FSM::MOVING(Event ) {
|
||||||
// this should be an optional that returns a point
|
// this should be an optional that returns a point
|
||||||
if(auto move_to = $main_ui.play_move()) {
|
if(auto move_to = $main_ui.play_move()) {
|
||||||
System::plan_motion(*$level.world, *move_to);
|
System::plan_motion($level, *move_to);
|
||||||
run_systems();
|
run_systems();
|
||||||
$main_ui.dirty();
|
$main_ui.dirty();
|
||||||
state(State::IDLE);
|
state(State::IDLE);
|
||||||
|
@ -198,14 +198,8 @@ namespace gui {
|
||||||
case MOUSE_MOVE:
|
case MOUSE_MOVE:
|
||||||
mouse_action(true);
|
mouse_action(true);
|
||||||
break;
|
break;
|
||||||
case AIM_CLICK: {
|
case AIM_CLICK:
|
||||||
auto aimed_at = $main_ui.camera_aim();
|
System::pickup($level);
|
||||||
|
|
||||||
if(aimed_at) {
|
|
||||||
// this will then send LOOT_ITEM if it's valid
|
|
||||||
System::pickup($level, aimed_at);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
default:
|
default:
|
||||||
break; // ignore everything else
|
break; // ignore everything else
|
||||||
}
|
}
|
||||||
|
@ -357,6 +351,10 @@ namespace gui {
|
||||||
$loot_ui.update();
|
$loot_ui.update();
|
||||||
event(Event::LOOT_OPEN);
|
event(Event::LOOT_OPEN);
|
||||||
break;
|
break;
|
||||||
|
case KEY::Z: {
|
||||||
|
auto& player_pos = System::player_position($level);
|
||||||
|
System::distribute_loot($level, {player_pos.aiming_at});
|
||||||
|
} break;
|
||||||
case KEY::X:
|
case KEY::X:
|
||||||
event(Event::STAIRS_DOWN);
|
event(Event::STAIRS_DOWN);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -66,6 +66,7 @@ inline void find_neighbor(const PointEntityMap &table, EntityList &result, Point
|
||||||
|
|
||||||
Point cell = {at.x + dx, at.y + dy};
|
Point cell = {at.x + dx, at.y + dy};
|
||||||
|
|
||||||
|
// Bug #81, should actually for-loop through these and only add ones with collision
|
||||||
auto it = table.find(cell);
|
auto it = table.find(cell);
|
||||||
if (it != table.end()) {
|
if (it != table.end()) {
|
||||||
result.insert(result.end(), it->second.entity);
|
result.insert(result.end(), it->second.entity);
|
||||||
|
@ -117,6 +118,16 @@ inline void update_sorted(SortedEntities& sprite_distance, PointEntityMap& table
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Entity SpatialMap::find(Point at, std::function<bool(CollisionData)> cb) const {
|
||||||
|
auto [begin, end] = $collision.equal_range(at);
|
||||||
|
|
||||||
|
for(auto it = begin; it != end; ++it) {
|
||||||
|
if(cb(it->second)) return it->second.entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DinkyECS::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
void SpatialMap::distance_sorted(SortedEntities& sprite_distance, Point from, int max_dist) {
|
void SpatialMap::distance_sorted(SortedEntities& sprite_distance, Point from, int max_dist) {
|
||||||
sprite_distance.clear();
|
sprite_distance.clear();
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ class SpatialMap {
|
||||||
bool occupied(Point pos) const;
|
bool occupied(Point pos) const;
|
||||||
bool something_there(Point at) const;
|
bool something_there(Point at) const;
|
||||||
DinkyECS::Entity get(Point at) const;
|
DinkyECS::Entity get(Point at) const;
|
||||||
|
DinkyECS::Entity find(Point at, std::function<bool(CollisionData)> cb) const;
|
||||||
|
|
||||||
FoundEntities neighbors(Point position, bool diag=false) const;
|
FoundEntities neighbors(Point position, bool diag=false) const;
|
||||||
|
|
||||||
void distance_sorted(SortedEntities& sorted_sprites, Point from, int max_distance);
|
void distance_sorted(SortedEntities& sorted_sprites, Point from, int max_distance);
|
||||||
|
|
72
systems.cpp
72
systems.cpp
|
@ -53,10 +53,9 @@ void System::lighting(GameLevel &level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::generate_paths(GameLevel &level) {
|
void System::generate_paths(GameLevel &level) {
|
||||||
auto player = level.world->get_the<Player>();
|
const auto &player_pos = player_position(level);
|
||||||
const auto &player_position = level.world->get<Position>(player.entity);
|
|
||||||
|
|
||||||
level.map->set_target(player_position.location);
|
level.map->set_target(player_pos.location);
|
||||||
level.map->make_paths();
|
level.map->make_paths();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,12 +89,10 @@ void System::enemy_ai_initialize(GameLevel &level) {
|
||||||
void System::enemy_pathing(GameLevel &level) {
|
void System::enemy_pathing(GameLevel &level) {
|
||||||
auto &world = *level.world;
|
auto &world = *level.world;
|
||||||
auto &map = *level.map;
|
auto &map = *level.map;
|
||||||
auto player = world.get_the<Player>();
|
const auto &player_pos = player_position(level);
|
||||||
|
|
||||||
const auto &player_position = world.get<Position>(player.entity);
|
|
||||||
|
|
||||||
world.query<Position, Motion>([&](auto ent, auto &position, auto &motion) {
|
world.query<Position, Motion>([&](auto ent, auto &position, auto &motion) {
|
||||||
if(ent != player.entity) {
|
if(ent != level.player) {
|
||||||
auto& enemy_ai = world.get<ai::EntityAI>(ent);
|
auto& enemy_ai = world.get<ai::EntityAI>(ent);
|
||||||
Point out = position.location; // copy
|
Point out = position.location; // copy
|
||||||
|
|
||||||
|
@ -109,7 +106,7 @@ void System::enemy_pathing(GameLevel &level) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
map.clear_target(player_position.location);
|
map.clear_target(player_pos.location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -240,17 +237,16 @@ inline void animate_entity(World &world, Entity entity) {
|
||||||
void System::combat(GameLevel &level, int attack_id) {
|
void System::combat(GameLevel &level, int attack_id) {
|
||||||
auto &collider = *level.collision;
|
auto &collider = *level.collision;
|
||||||
auto &world = *level.world;
|
auto &world = *level.world;
|
||||||
auto player = world.get_the<Player>();
|
|
||||||
auto& the_belt = world.get_the<ritual::Belt>();
|
auto& the_belt = world.get_the<ritual::Belt>();
|
||||||
|
|
||||||
if(!the_belt.has(attack_id)) return;
|
if(!the_belt.has(attack_id)) return;
|
||||||
|
|
||||||
auto& ritual = the_belt.get(attack_id);
|
auto& ritual = the_belt.get(attack_id);
|
||||||
const auto& player_position = world.get<Position>(player.entity);
|
const auto& player_pos = player_position(level);
|
||||||
auto& player_combat = world.get<Combat>(player.entity);
|
auto& player_combat = world.get<Combat>(level.player);
|
||||||
|
|
||||||
// this is guaranteed to not return the given position
|
// this is guaranteed to not return the given position
|
||||||
auto [found, nearby] = collider.neighbors(player_position.location);
|
auto [found, nearby] = collider.neighbors(player_pos.location);
|
||||||
combat::BattleEngine battle;
|
combat::BattleEngine battle;
|
||||||
|
|
||||||
if(found) {
|
if(found) {
|
||||||
|
@ -297,12 +293,10 @@ void System::combat(GameLevel &level, int attack_id) {
|
||||||
void System::collision(GameLevel &level) {
|
void System::collision(GameLevel &level) {
|
||||||
auto &collider = *level.collision;
|
auto &collider = *level.collision;
|
||||||
auto &world = *level.world;
|
auto &world = *level.world;
|
||||||
auto player = world.get_the<Player>();
|
const auto& player_pos = player_position(level);
|
||||||
|
|
||||||
const auto& player_position = world.get<Position>(player.entity);
|
|
||||||
|
|
||||||
// this is guaranteed to not return the given position
|
// this is guaranteed to not return the given position
|
||||||
auto [found, nearby] = collider.neighbors(player_position.location);
|
auto [found, nearby] = collider.neighbors(player_pos.location);
|
||||||
int combat_count = 0;
|
int combat_count = 0;
|
||||||
|
|
||||||
// AI: I think also this would a possible place to run AI decisions
|
// AI: I think also this would a possible place to run AI decisions
|
||||||
|
@ -320,7 +314,7 @@ void System::collision(GameLevel &level) {
|
||||||
|
|
||||||
if(combat_count == 0) {
|
if(combat_count == 0) {
|
||||||
// BUG: this is probably how we get stuck in combat
|
// BUG: this is probably how we get stuck in combat
|
||||||
world.send<Events::GUI>(Events::GUI::NO_NEIGHBORS, player.entity, player.entity);
|
world.send<Events::GUI>(Events::GUI::NO_NEIGHBORS, level.player, level.player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,13 +331,29 @@ void System::remove_from_world(GameLevel &level, Entity entity) {
|
||||||
level.world->remove<Position>(entity);
|
level.world->remove<Position>(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::pickup(GameLevel &level, Entity entity) {
|
void System::pickup(GameLevel &level) {
|
||||||
auto &world = *level.world;
|
auto &world = *level.world;
|
||||||
auto player = world.get_the<Player>();
|
auto &collision = *level.collision;
|
||||||
|
auto pos = player_position(level);
|
||||||
|
|
||||||
if(world.has<InventoryItem>(entity)) {
|
if(!collision.something_there(pos.aiming_at)) {
|
||||||
|
dbc::log("nothing there");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entity = level.collision->find(pos.aiming_at, [&](auto data) -> bool {
|
||||||
|
return (world.has<InventoryItem>(data.entity) ||
|
||||||
|
world.has<Device>(data.entity));
|
||||||
|
});
|
||||||
|
|
||||||
|
if(entity == DinkyECS::NONE) {
|
||||||
|
dbc::log("no inventory or devices there");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use spatial find to find an item with inventory...
|
||||||
|
if(auto item = world.get_if<InventoryItem>(entity)) {
|
||||||
// NOTE: this might need to be a separate system so that people can leave stuff alone
|
// NOTE: this might need to be a separate system so that people can leave stuff alone
|
||||||
auto item = world.get<InventoryItem>(entity);
|
|
||||||
remove_from_world(level, entity);
|
remove_from_world(level, entity);
|
||||||
|
|
||||||
if(world.has<ritual::JunkPile>(entity)) {
|
if(world.has<ritual::JunkPile>(entity)) {
|
||||||
|
@ -358,14 +368,15 @@ void System::pickup(GameLevel &level, Entity entity) {
|
||||||
// NOTE: chests are different from say a torch, maybe 2 events or the
|
// NOTE: chests are different from say a torch, maybe 2 events or the
|
||||||
// GUI figures out which it is, then when you click either pick it up
|
// GUI figures out which it is, then when you click either pick it up
|
||||||
// and move it or show the loot container UI
|
// and move it or show the loot container UI
|
||||||
world.send<Events::GUI>(Events::GUI::LOOT_ITEM, entity, item);
|
world.send<Events::GUI>(Events::GUI::LOOT_ITEM, entity, *item);
|
||||||
}
|
}
|
||||||
} else if(world.has<Device>(entity)) {
|
} else if(world.has<Device>(entity)) {
|
||||||
System::device(world, player.entity, entity);
|
System::device(world, level.player, entity);
|
||||||
|
} else {
|
||||||
|
// Bug #81 is related to this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void System::device(World &world, Entity actor, Entity item) {
|
void System::device(World &world, Entity actor, Entity item) {
|
||||||
auto& device = world.get<Device>(item);
|
auto& device = world.get<Device>(item);
|
||||||
dbc::log(fmt::format("entity {} INTERACTED WITH DEVICE {}", actor, item));
|
dbc::log(fmt::format("entity {} INTERACTED WITH DEVICE {}", actor, item));
|
||||||
|
@ -387,15 +398,14 @@ void System::device(World &world, Entity actor, Entity item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::plan_motion(World& world, Position move_to) {
|
void System::plan_motion(GameLevel& level, Position move_to) {
|
||||||
auto& player = world.get_the<Player>();
|
auto& player_pos = player_position(level);
|
||||||
auto& player_position = world.get<Position>(player.entity);
|
|
||||||
|
|
||||||
player_position.aiming_at = move_to.aiming_at;
|
player_pos.aiming_at = move_to.aiming_at;
|
||||||
|
|
||||||
auto& motion = world.get<Motion>(player.entity);
|
auto& motion = level.world->get<Motion>(level.player);
|
||||||
motion.dx = move_to.location.x - player_position.location.x;
|
motion.dx = move_to.location.x - player_pos.location.x;
|
||||||
motion.dy = move_to.location.y - player_position.location.y;
|
motion.dy = move_to.location.y - player_pos.location.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace System {
|
||||||
void enemy_ai_initialize(GameLevel &level);
|
void enemy_ai_initialize(GameLevel &level);
|
||||||
|
|
||||||
void device(World &world, Entity actor, Entity item);
|
void device(World &world, Entity actor, Entity item);
|
||||||
void plan_motion(World& world, Position move_to);
|
void plan_motion(GameLevel& level, Position move_to);
|
||||||
Entity spawn_item(World& world, const string& name);
|
Entity spawn_item(World& world, const string& name);
|
||||||
void drop_item(GameLevel& level, Entity item);
|
void drop_item(GameLevel& level, Entity item);
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace System {
|
||||||
void player_status(GameLevel &level);
|
void player_status(GameLevel &level);
|
||||||
void distribute_loot(GameLevel &level, Position target_pos);
|
void distribute_loot(GameLevel &level, Position target_pos);
|
||||||
|
|
||||||
void pickup(GameLevel &level, Entity entity);
|
void pickup(GameLevel &level);
|
||||||
|
|
||||||
bool place_in_container(World& world, Entity cont_id, const string& name, Entity world_entity);
|
bool place_in_container(World& world, Entity cont_id, const string& name, Entity world_entity);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "dinkyecs.hpp"
|
#include "dinkyecs.hpp"
|
||||||
#include "rand.hpp"
|
#include "rand.hpp"
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
using DinkyECS::Entity;
|
using DinkyECS::Entity;
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
|
@ -157,6 +158,34 @@ TEST_CASE("SpatialMap::get", "[spatialmap]") {
|
||||||
REQUIRE(entity == item);
|
REQUIRE(entity == item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("SpatialMap::find", "[spatialmap-find]") {
|
||||||
|
DinkyECS::World world;
|
||||||
|
SpatialMap map;
|
||||||
|
Point at{101, 31};
|
||||||
|
DinkyECS::Entity should_collide = DinkyECS::NONE;
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; i++) {
|
||||||
|
auto ent = world.entity();
|
||||||
|
map.insert(at, ent, i == 8);
|
||||||
|
|
||||||
|
if(i == 8) {
|
||||||
|
should_collide = ent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto collision = map.find(at, [&](auto data) -> bool {
|
||||||
|
return data.collision;
|
||||||
|
});
|
||||||
|
|
||||||
|
REQUIRE(collision == should_collide);
|
||||||
|
|
||||||
|
auto no_collide = map.find(at, [&](auto data) -> bool {
|
||||||
|
return !data.collision;
|
||||||
|
});
|
||||||
|
|
||||||
|
REQUIRE(no_collide != should_collide);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("SpatialMap::neighbors", "[spatialmap]") {
|
TEST_CASE("SpatialMap::neighbors", "[spatialmap]") {
|
||||||
DinkyECS::World world;
|
DinkyECS::World world;
|
||||||
SpatialMap map;
|
SpatialMap map;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue