diff --git a/assets/blood_splatter-256.png b/assets/blood_splatter-256.png new file mode 100644 index 0000000..d80a65f Binary files /dev/null and b/assets/blood_splatter-256.png differ diff --git a/assets/config.json b/assets/config.json index 0127a77..d86ab30 100644 --- a/assets/config.json +++ b/assets/config.json @@ -16,7 +16,8 @@ "rope_vines_up": "assets/rope_vines_up-256.png", "tripwire_trap": "assets/tripwire_trap-256.png", "cinqueda": "assets/cinqueda_1-256.png", - "left_gui": "assets/left_gui.png" + "left_gui": "assets/left_gui.png", + "blood_splatter": "assets/blood_splatter-256.png" }, "enemy": { "HEARING_DISTANCE": 20 @@ -24,7 +25,7 @@ "player": { }, "worldgen": { - "enemy_probability": 20, + "enemy_probability": 80, "empty_room_probability": 10, "device_probability": 30 } diff --git a/assets/enemies.json b/assets/enemies.json index f900bd8..19f79a1 100644 --- a/assets/enemies.json +++ b/assets/enemies.json @@ -6,7 +6,7 @@ "foreground": [255, 200, 125], "background": [30, 20, 75] }, - {"_type": "Combat", "hp": 200, "damage": 15, "dead": false}, + {"_type": "Combat", "hp": 200, "max_hp": 200, "damage": 15, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "LightSource", "strength": 60, "radius": 1.8}, {"_type": "EnemyConfig", "hearing_distance": 5}, @@ -19,7 +19,7 @@ "foreground": [131, 213, 238], "background": [30, 20, 75] }, - {"_type": "Combat", "hp": 20, "damage": 1, "dead": false}, + {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "LightSource", "strength": 40, "radius": 1.2}, {"_type": "EnemyConfig", "hearing_distance": 5}, @@ -32,7 +32,7 @@ "foreground": [205, 164, 246], "background": [30, 20, 75] }, - {"_type": "Combat", "hp": 50, "damage": 10, "dead": false}, + {"_type": "Combat", "hp": 50, "max_hp": 20, "damage": 50, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 10}, {"_type": "Sprite", "name": "evil_eye"} diff --git a/assets/items.json b/assets/items.json index 9116efc..c7cb5f1 100644 --- a/assets/items.json +++ b/assets/items.json @@ -65,7 +65,7 @@ "foreground": [255, 205, 189], "background": [255, 205, 189] }, - {"_type": "Curative", "hp": 20}, + {"_type": "Curative", "hp": 200}, {"_type": "Sprite", "name": "healing_potion_small"} ] } diff --git a/components.cpp b/components.cpp index a390365..e41a78b 100644 --- a/components.cpp +++ b/components.cpp @@ -8,7 +8,7 @@ namespace components { ENROLL_COMPONENT(Curative, hp); ENROLL_COMPONENT(EnemyConfig, hearing_distance); ENROLL_COMPONENT(Motion, dx, dy, random); - ENROLL_COMPONENT(Combat, hp, damage, dead); + ENROLL_COMPONENT(Combat, hp, max_hp, damage, dead); ENROLL_COMPONENT(LightSource, strength, radius); ENROLL_COMPONENT(Device, config, events); ENROLL_COMPONENT(Sprite, name); diff --git a/components.hpp b/components.hpp index af1ec91..ffd349d 100644 --- a/components.hpp +++ b/components.hpp @@ -58,6 +58,7 @@ namespace components { struct Combat { int hp; + int max_hp; int damage; /* NOTE: This is used to _mark_ entities as dead, to detect ones that have just died. Don't make attack automatically set it.*/ diff --git a/constants.hpp b/constants.hpp index c6725b6..8d5b995 100644 --- a/constants.hpp +++ b/constants.hpp @@ -13,6 +13,7 @@ constexpr const int RAY_VIEW_Y=0; constexpr const bool VSYNC=false; constexpr const int FRAME_LIMIT=60; constexpr const int NUM_SPRITES=1; +constexpr const int MAX_LOG_MESSAGES=20; #ifdef NDEBUG constexpr const bool DEBUG_BUILD=false; diff --git a/gui.cpp b/gui.cpp index 6cd434c..ba184ca 100644 --- a/gui.cpp +++ b/gui.cpp @@ -6,6 +6,7 @@ #include "components.hpp" #include #include "systems.hpp" +#include "events.hpp" namespace gui { @@ -50,6 +51,7 @@ namespace gui { $rayview.position_camera($player.x + 0.5, $player.y + 0.5); $status_view.create_render(); + $status_view.log("Welcome to the game!"); $renderer.init_terminal(); $map_view.create_render(); @@ -143,6 +145,7 @@ namespace gui { state(State::MAPPING); break; case ATTACK: + $status_view.log("You attack!"); $rotation = -30.0f; state(State::ATTACKING); break; @@ -215,7 +218,10 @@ namespace gui { auto& debug = $level.world->get_the(); debug.FPS = !debug.FPS; debug.PATHS = !debug.PATHS; - } break; + auto player = $level.world->get_the(); + auto& player_combat = $level.world->get(player.entity); + player_combat.hp = player_combat.max_hp; + } break; default: break; // ignored } @@ -253,6 +259,18 @@ namespace gui { $window.draw($text); } + void FSM::draw_blood() { + auto player = $level.world->get_the(); + auto player_combat = $level.world->get(player.entity); + + if(float(player_combat.hp) / float(player_combat.max_hp) < 0.5) { + auto blood = $textures.sprite_textures.at("blood_splatter").sprite; + blood->setPosition({RAY_VIEW_X,0}); + blood->setScale({3.0, 3.0}); + $window.draw(*blood); + } + } + void FSM::draw_gui() { sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, SCREEN_HEIGHT}); rect.setPosition({0,0}); @@ -290,6 +308,7 @@ namespace gui { $stats.sample(1/elapsed.count()); draw_gui(); + draw_blood(); draw_weapon(); } @@ -314,9 +333,49 @@ namespace gui { System::motion($level); System::lighting($level); System::death($level); + handle_world_events(); } bool FSM::active() { return !in_state(State::END); } + + void FSM::handle_world_events() { + using eGUI = Events::GUI; + auto& world = *$level.world; + + while(world.has_event()) { + auto [evt, entity, data] = world.recv(); + + switch(evt) { + case eGUI::COMBAT: { + auto &damage = std::any_cast(data); + auto enemy_combat = world.get(entity); + + if(damage.enemy_did > 0) { + $status_view.log(fmt::format("Enemy HIT YOU for {} damage!", damage.enemy_did)); + $status_view.log(fmt::format("-- Enemy has {} HP left.", enemy_combat.hp)); + } else { + $status_view.log("Enemy MISSED YOU."); + } + + if(damage.player_did > 0) { + $status_view.log(fmt::format("You HIT enemy for {} damage!", damage.player_did)); + } else { + $status_view.log("You MISSED the enemy."); + } + } + break; + case eGUI::LOOT: { + // auto &item = std::any_cast(data); + // $status_view.log(fmt::format("You picked up a {}.", + // std::string(item.data["name"]))); + $status_view.log("You picked up an item."); + } + break; + default: + $status_view.log(fmt::format("INVALID EVENT! {},{}", evt, entity)); + } + } + } } diff --git a/gui.hpp b/gui.hpp index 13a5026..58d0ade 100644 --- a/gui.hpp +++ b/gui.hpp @@ -74,10 +74,12 @@ namespace gui { void draw_weapon(); void draw_stats(); void draw_gui(); + void draw_blood(); void render(); void mouse(); void generate_map(); bool active(); void run_systems(); + void handle_world_events(); }; } diff --git a/status_ui.cpp b/status_ui.cpp index 0c17daf..5d4cd60 100644 --- a/status_ui.cpp +++ b/status_ui.cpp @@ -25,10 +25,7 @@ namespace gui { const auto& player_combat = $level.world->get(player.entity); const auto& combat = $level.world->get(player.entity); - std::vector log_list; - log_list.push_back(text("Log messages here.")); - - auto log_box = vbox(log_list) | yflex_grow; + auto log_box = vbox($log_list) | yflex_grow | border; return hbox({ hflow( @@ -45,4 +42,16 @@ namespace gui { set_renderer(status_rend); } + + void StatusUI::log(std::string msg) { + $messages.push_front(msg); + if($messages.size() > MAX_LOG_MESSAGES) { + $messages.pop_back(); + } + + $log_list.clear(); + for(auto msg : $messages) { + $log_list.push_back(text(msg)); + } + } } diff --git a/status_ui.hpp b/status_ui.hpp index 1763db9..698893b 100644 --- a/status_ui.hpp +++ b/status_ui.hpp @@ -2,13 +2,17 @@ #include "panel.hpp" #include "levelmanager.hpp" #include "constants.hpp" +#include namespace gui { class StatusUI : public Panel { public: + std::vector $log_list; + std::deque $messages; GameLevel $level; StatusUI(GameLevel level); void create_render(); void update_level(GameLevel &level) { $level = level; } + void log(std::string msg); }; } diff --git a/systems.cpp b/systems.cpp index a16e20d..d4280e2 100644 --- a/systems.cpp +++ b/systems.cpp @@ -180,7 +180,7 @@ void System::collision(GameLevel &level) { if(world.has(entity)) { auto& cure = world.get(entity); - player_combat.hp += cure.hp; + player_combat.hp = std::min(player_combat.hp + cure.hp, player_combat.max_hp); world.remove(entity); }