The attack is now fully configurable in attacks.json and the name for each attack is generated from the ritual crafting.
This commit is contained in:
parent
adcfbb883e
commit
09b61f17a7
11 changed files with 77 additions and 45 deletions
28
assets/attacks.json
Normal file
28
assets/attacks.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"MAGICK_FIRE": {
|
||||||
|
"animation": "burning_animation",
|
||||||
|
"shader": "flame",
|
||||||
|
"components": [
|
||||||
|
{"_type": "Sprite", "name": "burning_animation", "scale": 1.0},
|
||||||
|
{"_type": "LightSource", "strength": 25, "radius": 2.0},
|
||||||
|
{"_type": "Temporary", "is": true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"MAGICK_LIGHTNING": {
|
||||||
|
"animation": "lightning_animation",
|
||||||
|
"shader": "lightning",
|
||||||
|
"components": [
|
||||||
|
{"_type": "Sprite", "name": "lightning_animation", "scale": 1.0},
|
||||||
|
{"_type": "LightSource", "strength": 35, "radius": 2.5},
|
||||||
|
{"_type": "Temporary", "is": true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"PHYSICAL_NONE": {
|
||||||
|
"animation": "lightning_animation",
|
||||||
|
"shader": "flame",
|
||||||
|
"components": [
|
||||||
|
{"_type": "Sprite", "name": "lightning_animation", "scale": 1.0},
|
||||||
|
{"_type": "Temporary", "is": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
"foreground": "enemies/fg:player",
|
"foreground": "enemies/fg:player",
|
||||||
"background": "color:transparent"
|
"background": "color:transparent"
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 200, "max_hp": 200, "ap": 0, "max_ap": 12, "ap_delta": 6, "damage": 50, "dead": false, "attack_rating": 0.60, "toughness_rating": 0.0},
|
{"_type": "Combat", "hp": 200, "max_hp": 200, "ap": 0, "max_ap": 12, "ap_delta": 6, "damage": 500, "dead": false, "attack_rating": 1.0, "toughness_rating": 0.0},
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||||
{"_type": "Collision", "has": true},
|
{"_type": "Collision", "has": true},
|
||||||
{"_type": "EnemyConfig", "ai_script": "Host::actions", "ai_start_name": "Host::initial_state", "ai_goal_name": "Host::final_state"},
|
{"_type": "EnemyConfig", "ai_script": "Host::actions", "ai_start_name": "Host::initial_state", "ai_goal_name": "Host::final_state"},
|
||||||
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
||||||
{"_type": "LightSource", "strength": 35, "radius": 2.0}
|
{"_type": "LightSource", "strength": 20, "radius": 1.0}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"GOLD_SAVIOR": {
|
"GOLD_SAVIOR": {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ namespace components {
|
||||||
components::enroll<Sprite>(MAP);
|
components::enroll<Sprite>(MAP);
|
||||||
components::enroll<Sound>(MAP);
|
components::enroll<Sound>(MAP);
|
||||||
components::enroll<Collision>(MAP);
|
components::enroll<Collision>(MAP);
|
||||||
|
components::enroll<Temporary>(MAP);
|
||||||
MAP_configured = true;
|
MAP_configured = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ namespace components {
|
||||||
settings::Config rituals;
|
settings::Config rituals;
|
||||||
settings::Config stories;
|
settings::Config stories;
|
||||||
settings::Config scenes;
|
settings::Config scenes;
|
||||||
|
settings::Config attacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Personality {
|
struct Personality {
|
||||||
|
|
@ -174,6 +175,7 @@ namespace components {
|
||||||
ENROLL_COMPONENT(Storyboard, image, audio, layout, beats);
|
ENROLL_COMPONENT(Storyboard, image, audio, layout, beats);
|
||||||
ENROLL_COMPONENT(Sound, attack, death);
|
ENROLL_COMPONENT(Sound, attack, death);
|
||||||
ENROLL_COMPONENT(Collision, has);
|
ENROLL_COMPONENT(Collision, has);
|
||||||
|
ENROLL_COMPONENT(Temporary, is);
|
||||||
|
|
||||||
template<typename COMPONENT> COMPONENT convert(nlohmann::json &data) {
|
template<typename COMPONENT> COMPONENT convert(nlohmann::json &data) {
|
||||||
COMPONENT result;
|
COMPONENT result;
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ namespace GameDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_configs(DinkyECS::World &world) {
|
void load_configs(DinkyECS::World &world) {
|
||||||
|
// TODO: Could have a simple meta-config file that loads these....
|
||||||
world.set_the<GameConfig>({
|
world.set_the<GameConfig>({
|
||||||
settings::get("config"),
|
settings::get("config"),
|
||||||
settings::get("enemies"),
|
settings::get("enemies"),
|
||||||
|
|
@ -139,7 +140,8 @@ namespace GameDB {
|
||||||
settings::get("bosses"),
|
settings::get("bosses"),
|
||||||
settings::get("rituals"),
|
settings::get("rituals"),
|
||||||
settings::get("stories"),
|
settings::get("stories"),
|
||||||
settings::get("scenes")
|
settings::get("scenes"),
|
||||||
|
settings::get("attacks")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "game/rituals.hpp"
|
#include "game/rituals.hpp"
|
||||||
#include "ai/ai_debug.hpp"
|
#include "ai/ai_debug.hpp"
|
||||||
#include "ai/ai.hpp"
|
#include "ai/ai.hpp"
|
||||||
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
namespace ritual {
|
namespace ritual {
|
||||||
Engine::Engine(std::string config_path) :
|
Engine::Engine(std::string config_path) :
|
||||||
|
|
@ -129,6 +130,12 @@ namespace ritual {
|
||||||
fmt::println("\n");
|
fmt::println("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Action::name() {
|
||||||
|
return fmt::format("{}_{}",
|
||||||
|
magic_enum::enum_name(kind),
|
||||||
|
magic_enum::enum_name(element));
|
||||||
|
}
|
||||||
|
|
||||||
Action& Belt::get(int index) {
|
Action& Belt::get(int index) {
|
||||||
return equipped.at(index);
|
return equipped.at(index);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ namespace ritual {
|
||||||
std::vector<std::string> names;
|
std::vector<std::string> names;
|
||||||
|
|
||||||
void dump();
|
void dump();
|
||||||
|
std::string name();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Engine {
|
struct Engine {
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ void System::death() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::combat(int attack_id) {
|
void System::combat(int belt_id) {
|
||||||
auto& level = GameDB::current_level();
|
auto& level = GameDB::current_level();
|
||||||
auto& collider = *level.collision;
|
auto& collider = *level.collision;
|
||||||
auto& world = *level.world;
|
auto& world = *level.world;
|
||||||
|
|
@ -285,8 +285,9 @@ void System::combat(int attack_id) {
|
||||||
.enemy_did=0
|
.enemy_did=0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Need a "miss" animation/sound
|
||||||
if(result.player_did > 0) {
|
if(result.player_did > 0) {
|
||||||
spawn_attack(world, attack_id, enemy.entity);
|
spawn_attack(world, belt_id, enemy.entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(enemy_action == "kill_enemy") {
|
if(enemy_action == "kill_enemy") {
|
||||||
|
|
@ -666,31 +667,34 @@ void System::clear_attack() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::spawn_attack(World& world, int attack_id, DinkyECS::Entity enemy) {
|
void System::spawn_attack(World& world, int belt_id, DinkyECS::Entity enemy) {
|
||||||
|
auto& config = world.get_the<GameConfig>().attacks;
|
||||||
|
|
||||||
using enum ritual::Element; // for FIRE vs LIGHTNING
|
using enum ritual::Element; // for FIRE vs LIGHTNING
|
||||||
auto& the_belt = world.get_the<ritual::Belt>();
|
auto& the_belt = world.get_the<ritual::Belt>();
|
||||||
|
|
||||||
dbc::check(the_belt.has(attack_id), "STOP passing invalid attack IDs to the system.");
|
dbc::check(the_belt.has(belt_id), "STOP passing invalid attack IDs to the system.");
|
||||||
|
|
||||||
auto& ritual = the_belt.get(attack_id);
|
auto& ritual = the_belt.get(belt_id);
|
||||||
|
fmt::println("RITUAL NAME: {}", ritual.name());
|
||||||
|
|
||||||
auto effect = ritual.element == FIRE ? "burning_animation" : "lightning_animation";
|
auto attack_id = world.entity();
|
||||||
|
auto& attack_config = config[ritual.name()];
|
||||||
|
|
||||||
auto effect_id = world.entity();
|
components::configure_entity(world, attack_id, attack_config["components"]);
|
||||||
world.set<Sprite>(effect_id, {effect, 1.0f});
|
|
||||||
world.set<Temporary>(effect_id, {true});
|
|
||||||
|
|
||||||
auto shader = shaders::get(ritual.element == FIRE ? "flame" : "lightning");
|
auto shader = shaders::get(attack_config["shader"]);
|
||||||
world.set<SpriteEffect>(effect_id, {100, shader});
|
world.set<SpriteEffect>(attack_id, {100, shader});
|
||||||
|
|
||||||
// also add the same effect to the enemy
|
// also add the same effect to the enemy
|
||||||
world.set<SpriteEffect>(enemy, {50, shader});
|
world.set<SpriteEffect>(enemy, {50, shader});
|
||||||
|
|
||||||
auto anim = animation::load("assets/animation.json", effect);
|
auto anim = animation::load("assets/animation.json", attack_config["animation"]);
|
||||||
anim.play();
|
anim.play();
|
||||||
world.set<animation::Animation>(effect_id, anim);
|
world.set<animation::Animation>(attack_id, anim);
|
||||||
|
|
||||||
drop_item(effect_id);
|
//BUG: THIS IS BULLSHIT! Should have a generic place item thing.
|
||||||
|
drop_item(attack_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init(Registry& reg) {
|
void System::init(Registry& reg) {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace System {
|
||||||
void drop_item(Entity item);
|
void drop_item(Entity item);
|
||||||
|
|
||||||
void enemy_ai();
|
void enemy_ai();
|
||||||
void combat(int attack_id);
|
void combat(int belt_id);
|
||||||
|
|
||||||
std::shared_ptr<sf::Shader> sprite_effect(Entity entity);
|
std::shared_ptr<sf::Shader> sprite_effect(Entity entity);
|
||||||
void player_status();
|
void player_status();
|
||||||
|
|
@ -70,7 +70,7 @@ namespace System {
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_attack();
|
void clear_attack();
|
||||||
void spawn_attack(World& world, int attack_id, DinkyECS::Entity enemy);
|
void spawn_attack(World& world, int belt_id, DinkyECS::Entity enemy);
|
||||||
void init(Registry& reg);
|
void init(Registry& reg);
|
||||||
void restart();
|
void restart();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#define FSM_DEBUG 1
|
|
||||||
#include "gui/fsm.hpp"
|
#include "gui/fsm.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
@ -14,6 +13,7 @@
|
||||||
#include "gui/guecstra.hpp"
|
#include "gui/guecstra.hpp"
|
||||||
#include "game/level.hpp"
|
#include "game/level.hpp"
|
||||||
#include "boss/system.hpp"
|
#include "boss/system.hpp"
|
||||||
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
using namespace components;
|
using namespace components;
|
||||||
|
|
@ -60,8 +60,6 @@ namespace gui {
|
||||||
$map_ui.init();
|
$map_ui.init();
|
||||||
$map_ui.log(L"Welcome to the game!");
|
$map_ui.log(L"Welcome to the game!");
|
||||||
|
|
||||||
run_systems();
|
|
||||||
|
|
||||||
state(State::IDLE);
|
state(State::IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +67,6 @@ namespace gui {
|
||||||
// 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()) {
|
||||||
$systems.runMoving(*move_to);
|
$systems.runMoving(*move_to);
|
||||||
run_systems();
|
|
||||||
$main_ui.dirty();
|
$main_ui.dirty();
|
||||||
state(State::IDLE);
|
state(State::IDLE);
|
||||||
}
|
}
|
||||||
|
|
@ -90,8 +87,6 @@ namespace gui {
|
||||||
something_died(std::any_cast<DinkyECS::Entity>(data));
|
something_died(std::any_cast<DinkyECS::Entity>(data));
|
||||||
break;
|
break;
|
||||||
case TICK:
|
case TICK:
|
||||||
run_systems();
|
|
||||||
|
|
||||||
if(!$main_ui.hands_playing()) {
|
if(!$main_ui.hands_playing()) {
|
||||||
// run combat one more time
|
// run combat one more time
|
||||||
state(State::IDLE);
|
state(State::IDLE);
|
||||||
|
|
@ -159,11 +154,7 @@ namespace gui {
|
||||||
$map_open = !$map_open;
|
$map_open = !$map_open;
|
||||||
break;
|
break;
|
||||||
case ATTACK:
|
case ATTACK:
|
||||||
$main_ui.play_hands();
|
play_attack(std::any_cast<int>(data));
|
||||||
$main_ui.dirty();
|
|
||||||
$systems.runCombat(0);
|
|
||||||
run_systems();
|
|
||||||
$status_ui.update();
|
|
||||||
state(State::ATTACKING);
|
state(State::ATTACKING);
|
||||||
break;
|
break;
|
||||||
case CLOSE:
|
case CLOSE:
|
||||||
|
|
@ -238,7 +229,6 @@ namespace gui {
|
||||||
auto& sprite = world->get<components::Sprite>(entity);
|
auto& sprite = world->get<components::Sprite>(entity);
|
||||||
$main_ui.$rayview->update_sprite(entity, sprite);
|
$main_ui.$rayview->update_sprite(entity, sprite);
|
||||||
$main_ui.dirty();
|
$main_ui.dirty();
|
||||||
run_systems();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSM::something_died(DinkyECS::Entity entity) {
|
void FSM::something_died(DinkyECS::Entity entity) {
|
||||||
|
|
@ -254,6 +244,13 @@ namespace gui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FSM::play_attack(int attack_id) {
|
||||||
|
$main_ui.play_hands();
|
||||||
|
$main_ui.dirty();
|
||||||
|
$systems.runCombat(attack_id);
|
||||||
|
$status_ui.update();
|
||||||
|
}
|
||||||
|
|
||||||
void FSM::CUT_SCENE_PLAYING(Event ev, std::any data) {
|
void FSM::CUT_SCENE_PLAYING(Event ev, std::any data) {
|
||||||
if($boss_scene->playing()) {
|
if($boss_scene->playing()) {
|
||||||
if(ev == game::Event::MOUSE_CLICK) {
|
if(ev == game::Event::MOUSE_CLICK) {
|
||||||
|
|
@ -404,22 +401,18 @@ namespace gui {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSM::update() {
|
void FSM::update() {
|
||||||
if(in_state(State::BOSS_FIGHT)) {
|
$systems.runUpdate();
|
||||||
$boss_fight->update();
|
|
||||||
} else if(in_state(State::CUT_SCENE_PLAYING)) {
|
|
||||||
$boss_scene->update();
|
|
||||||
} else {
|
|
||||||
// BUG: look at draw_gui for the update for here
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSM::render() {
|
void FSM::render() {
|
||||||
$window.clear();
|
$window.clear();
|
||||||
|
|
||||||
if(in_state(State::BOSS_FIGHT)) {
|
if(in_state(State::BOSS_FIGHT)) {
|
||||||
|
$boss_fight->update();
|
||||||
$boss_fight->render($window);
|
$boss_fight->render($window);
|
||||||
// this clears any attack animations, like fire
|
// this clears any attack animations, like fire
|
||||||
} else if(in_state(State::CUT_SCENE_PLAYING)) {
|
} else if(in_state(State::CUT_SCENE_PLAYING)) {
|
||||||
|
$boss_scene->update();
|
||||||
$boss_scene->render($window);
|
$boss_scene->render($window);
|
||||||
} else {
|
} else {
|
||||||
// this clears any attack animations, like fire
|
// this clears any attack animations, like fire
|
||||||
|
|
@ -431,10 +424,6 @@ namespace gui {
|
||||||
$window.display();
|
$window.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSM::run_systems() {
|
|
||||||
$systems.runUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FSM::active() {
|
bool FSM::active() {
|
||||||
return !in_state(State::END);
|
return !in_state(State::END);
|
||||||
}
|
}
|
||||||
|
|
@ -487,7 +476,5 @@ namespace gui {
|
||||||
$main_ui.update_level();
|
$main_ui.update_level();
|
||||||
$loot_ui.update_level();
|
$loot_ui.update_level();
|
||||||
}
|
}
|
||||||
|
|
||||||
run_systems();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ namespace gui {
|
||||||
void update();
|
void update();
|
||||||
void render();
|
void render();
|
||||||
bool active();
|
bool active();
|
||||||
void run_systems();
|
|
||||||
void handle_events();
|
void handle_events();
|
||||||
void handle_boss_fight_events();
|
void handle_boss_fight_events();
|
||||||
void next_level(bool bossfight);
|
void next_level(bool bossfight);
|
||||||
|
|
@ -82,5 +81,6 @@ namespace gui {
|
||||||
void take_screenshot();
|
void take_screenshot();
|
||||||
void something_died(DinkyECS::Entity entity);
|
void something_died(DinkyECS::Entity entity);
|
||||||
void entity_spawned(DinkyECS::Entity entity);
|
void entity_spawned(DinkyECS::Entity entity);
|
||||||
|
void play_attack(int attack_id);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue