Animations are refactored to let me spawn in an 'attack animation' but I think the data model is wrong. Rather than spawning in an animation every time I can probably just make one, reposition it, then tell it to play. I'll have to try it.
This commit is contained in:
parent
8384b11993
commit
ad3e580495
15 changed files with 109 additions and 56 deletions
|
@ -70,18 +70,17 @@ namespace animation {
|
|||
static AnimationManager MGR;
|
||||
static bool initialized = false;
|
||||
|
||||
bool apply(Animation& anim, SpriteTexture& target) {
|
||||
auto size = target.texture->getSize();
|
||||
anim.frame_width = int(size.x) / (unsigned int)anim.frames;
|
||||
sf::IntRect rect{{0,0}, {anim.frame_width, int(size.y)}};
|
||||
bool apply(Animation& anim, sf::Sprite& sprite) {
|
||||
sf::IntRect rect{{0,0}, {anim.frame_width, anim.frame_height}};
|
||||
sf::Vector2f scale{1.0, 1.0};
|
||||
sf::Vector2f pos{0, 0};
|
||||
|
||||
anim.step(scale, pos, rect);
|
||||
|
||||
target.sprite->setTextureRect(rect);
|
||||
target.sprite->setPosition(pos);
|
||||
target.sprite->setScale(scale);
|
||||
sprite.setTextureRect(rect);
|
||||
sprite.setPosition(pos);
|
||||
sprite.setScale(scale);
|
||||
|
||||
|
||||
return anim.playing;
|
||||
}
|
||||
|
@ -99,11 +98,27 @@ namespace animation {
|
|||
|
||||
void init() {
|
||||
if(!initialized) {
|
||||
Config config("assets/animations.json");
|
||||
Config animations("assets/animations.json");
|
||||
Config config("assets/config.json");
|
||||
auto& sprites = config["sprites"];
|
||||
|
||||
for(auto& [name, data] : config.json().items()) {
|
||||
auto anim = components::convert<Animation>(data);
|
||||
MGR.animations.insert_or_assign(name, anim);
|
||||
for(auto& [name, data] : animations.json().items()) {
|
||||
try {
|
||||
auto anim = components::convert<Animation>(data);
|
||||
auto& sprite_config = sprites[name];
|
||||
|
||||
anim.frame_width = sprite_config["frame_width"];
|
||||
anim.frame_height = sprite_config["frame_height"];
|
||||
|
||||
dbc::check(anim.frame_width > 0 && anim.frame_height > 0,
|
||||
fmt::format("invalid frame width/height for animation: {}",
|
||||
name));
|
||||
|
||||
MGR.animations.insert_or_assign(name, anim);
|
||||
} catch(...) {
|
||||
dbc::log(fmt::format("error in sprite config: {}", name));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
#include "easings.hpp"
|
||||
#include "dinkyecs.hpp"
|
||||
#include <SFML/Graphics/Rect.hpp>
|
||||
|
||||
#include <SFML/Graphics/Sprite.hpp>
|
||||
|
||||
namespace animation {
|
||||
struct AnimationManager {
|
||||
std::unordered_map<std::string, components::Animation> animations;
|
||||
};
|
||||
|
||||
bool apply(components::Animation& anim, textures::SpriteTexture& target);
|
||||
bool apply(components::Animation& anim, sf::Sprite& target);
|
||||
void rotate(sf::Sprite& target, float degrees);
|
||||
void center(sf::Sprite& target, sf::Vector2f pos);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"burning_effect": {
|
||||
"burning_animation": {
|
||||
"_type": "Animation",
|
||||
"easing": 0,
|
||||
"ease_rate": 0.5,
|
||||
|
@ -9,7 +9,17 @@
|
|||
"speed": 0.1,
|
||||
"stationary": false
|
||||
},
|
||||
"ritual_blanket": {
|
||||
"lightning_animation": {
|
||||
"_type": "Animation",
|
||||
"easing": 0,
|
||||
"ease_rate": 0.5,
|
||||
"scale": 1.0,
|
||||
"simple": false,
|
||||
"frames": 5,
|
||||
"speed": 0.5,
|
||||
"stationary": false
|
||||
},
|
||||
"ritual_crafting_area": {
|
||||
"_type": "Animation",
|
||||
"easing": 0,
|
||||
"ease_rate": 0.5,
|
||||
|
|
|
@ -30,8 +30,13 @@
|
|||
"hp_status_00": "assets/sounds/hp_status_00.ogg"
|
||||
},
|
||||
"sprites": {
|
||||
"burning_effect":
|
||||
{"path": "assets/sprites/burning_effect.png",
|
||||
"burning_animation":
|
||||
{"path": "assets/sprites/burning_animation.png",
|
||||
"frame_width": 256,
|
||||
"frame_height": 256
|
||||
},
|
||||
"lightning_animation":
|
||||
{"path": "assets/sprites/lightning_animation.png",
|
||||
"frame_width": 256,
|
||||
"frame_height": 256
|
||||
},
|
||||
|
|
|
@ -12,21 +12,6 @@
|
|||
{"_type": "LightSource", "strength": 35, "radius": 2.0}
|
||||
]
|
||||
},
|
||||
"FLAME_BLOB": {
|
||||
"components": [
|
||||
{"_type": "Tile", "display": 10899,
|
||||
"foreground": "enemies/fg:gold_savior",
|
||||
"background": "color:transparent"
|
||||
},
|
||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false},
|
||||
{"_type": "Collision", "has": true},
|
||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
||||
{"_type": "Sprite", "name": "burning_effect", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
|
||||
{"_type": "Sound", "attack": "fireball_01", "death": "fireball_01"}
|
||||
]
|
||||
},
|
||||
"GOLD_SAVIOR": {
|
||||
"components": [
|
||||
{"_type": "Tile", "display": 42586,
|
||||
|
|
BIN
assets/sprites/burning_animation.png
Normal file
BIN
assets/sprites/burning_animation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
BIN
assets/sprites/lightning_animation.png
Normal file
BIN
assets/sprites/lightning_animation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
|
@ -26,6 +26,10 @@ namespace components {
|
|||
std::shared_ptr<sf::Shader> effect;
|
||||
};
|
||||
|
||||
struct Temporary {
|
||||
bool is = true;
|
||||
};
|
||||
|
||||
struct Collision {
|
||||
bool has = true;
|
||||
};
|
||||
|
@ -124,7 +128,8 @@ namespace components {
|
|||
float subframe = 0.0f;
|
||||
bool looped = false;
|
||||
// BUG: this is weirdly not used in most animations but also named wrong should be frame_width
|
||||
int frame_width = TEXTURE_WIDTH;
|
||||
int frame_width = -1;
|
||||
int frame_height = -1;
|
||||
|
||||
void play();
|
||||
float twitching();
|
||||
|
|
|
@ -412,6 +412,7 @@ namespace gui {
|
|||
draw_gui();
|
||||
}
|
||||
|
||||
System::clear_attack();
|
||||
$window.display();
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace gui {
|
|||
$ritual_ui = textures::get_sprite("ritual_crafting_area");
|
||||
$ritual_ui.sprite->setPosition($gui.get_position());
|
||||
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
|
||||
$ritual_anim = animation::load("ritual_blanket");
|
||||
$ritual_anim = animation::load("ritual_crafting_area");
|
||||
|
||||
auto open_close_toggle = $gui.entity("ritual_ui");
|
||||
$gui.set<Clickable>(open_close_toggle, {
|
||||
|
@ -84,7 +84,6 @@ namespace gui {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void UI::CLOSED(Event ev) {
|
||||
if(ev == Event::TOGGLE) {
|
||||
$ritual_anim.play();
|
||||
|
@ -95,7 +94,7 @@ namespace gui {
|
|||
|
||||
void UI::OPENING(Event ev) {
|
||||
if(ev == Event::TICK) {
|
||||
if(!animation::apply($ritual_anim, $ritual_ui)) {
|
||||
if(!animation::apply($ritual_anim, *$ritual_ui.sprite)) {
|
||||
state(State::OPENED);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,6 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) {
|
|||
auto world = $level.world;
|
||||
$level.collision->distance_sorted($sprite_order, {(size_t)$pos_x, (size_t)$pos_y}, RENDER_DISTANCE);
|
||||
|
||||
|
||||
// after sorting the sprites, do the projection
|
||||
for(auto& rec : $sprite_order) {
|
||||
if(!$sprites.contains(rec.entity)) continue;
|
||||
|
@ -184,6 +183,10 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) {
|
|||
int d = y * texture_height - $height * half_height + sprite_height * half_height;
|
||||
int tex_y = ((d * texture_height) / sprite_height) / texture_height;
|
||||
|
||||
// BUG: this data could be put into the world
|
||||
// as frame data, then just have a system that
|
||||
// constantly applies this to any sprite that
|
||||
// has an animation and is visible
|
||||
sf::Vector2f origin{texture_width / 2.0f, texture_height / 2.0f};
|
||||
sf::Vector2f scale{sprite_scale_w, sprite_scale_h};
|
||||
sf::Vector2f position{x + origin.x * scale.x, y + origin.y * scale.y};
|
||||
|
|
|
@ -25,9 +25,9 @@ function Build-Images {
|
|||
}
|
||||
}
|
||||
|
||||
Build-Images -Source "Textures" -pixel_count 12
|
||||
Build-Images -Source "Sprites" -pixel_count 6
|
||||
Build-Images -Source "Items" -pixel_count 2
|
||||
# Build-Images -Source "Textures" -pixel_count 12
|
||||
# Build-Images -Source "Sprites" -pixel_count 6
|
||||
# Build-Images -Source "Items" -pixel_count 2
|
||||
Build-Images -Source "Animations" -pixel_count 6
|
||||
|
||||
cp -recurse -force C:\Users\lcthw\Pictures\Games\Renders\Raycaster\UI assets\ui
|
||||
|
|
55
systems.cpp
55
systems.cpp
|
@ -27,8 +27,6 @@ using namespace DinkyECS;
|
|||
using lighting::LightSource;
|
||||
|
||||
void System::set_position(World& world, SpatialMap& collision, Entity entity, Position pos) {
|
||||
dbc::check(world.has<Tile>(entity), "entity doesn't have tile");
|
||||
|
||||
world.set<Position>(entity, pos);
|
||||
bool has_collision = world.has<Collision>(entity);
|
||||
collision.insert(pos.location, entity, has_collision);
|
||||
|
@ -238,11 +236,6 @@ void System::combat(int attack_id) {
|
|||
auto& level = GameDB::current_level();
|
||||
auto& collider = *level.collision;
|
||||
auto& world = *level.world;
|
||||
auto& the_belt = world.get_the<ritual::Belt>();
|
||||
|
||||
if(!the_belt.has(attack_id)) return;
|
||||
|
||||
auto& ritual = the_belt.get(attack_id);
|
||||
const auto& player_pos = GameDB::player_position();
|
||||
auto& player_combat = world.get<Combat>(level.player);
|
||||
|
||||
|
@ -272,13 +265,7 @@ void System::combat(int attack_id) {
|
|||
};
|
||||
|
||||
if(result.player_did > 0) {
|
||||
using enum ritual::Element;
|
||||
|
||||
if(ritual.element == FIRE || ritual.element == LIGHTNING) {
|
||||
auto effect = shaders::get(
|
||||
ritual.element == FIRE ? "flame" : "lightning");
|
||||
world.set<SpriteEffect>(enemy.entity, {100, effect});
|
||||
}
|
||||
spawn_attack(world, attack_id);
|
||||
}
|
||||
|
||||
if(enemy_action == combat::BattleAction::ATTACK) {
|
||||
|
@ -656,3 +643,43 @@ gui::Event System::shortest_rotate(Point player_at, Point aiming_at, Point targe
|
|||
|
||||
return normalized < 180.0 ? gui::Event::ROTATE_LEFT : gui::Event::ROTATE_RIGHT;
|
||||
}
|
||||
|
||||
void System::clear_attack() {
|
||||
auto world = GameDB::current_world();
|
||||
std::vector<Entity> dead_anim;
|
||||
|
||||
world->query<Animation, Temporary>([&](auto ent, auto& anim, auto&) {
|
||||
if(!anim.playing) dead_anim.push_back(ent);
|
||||
});
|
||||
|
||||
for(auto ent : dead_anim) {
|
||||
world->remove<Sprite>(ent);
|
||||
world->remove<Animation>(ent);
|
||||
world->remove<SpriteEffect>(ent);
|
||||
remove_from_world(ent);
|
||||
}
|
||||
}
|
||||
|
||||
void System::spawn_attack(World& world, int attack_id) {
|
||||
using enum ritual::Element; // for FIRE vs LIGHTNING
|
||||
auto& the_belt = world.get_the<ritual::Belt>();
|
||||
|
||||
dbc::check(the_belt.has(attack_id), "STOP passing invalid attack IDs to the system.");
|
||||
|
||||
auto& ritual = the_belt.get(attack_id);
|
||||
|
||||
auto effect = ritual.element == FIRE ? "burning_animation" : "lightning_animation";
|
||||
|
||||
auto effect_id = world.entity();
|
||||
world.set<Sprite>(effect_id, {effect, 256, 256, 1.0});
|
||||
world.set<Temporary>(effect_id, {true});
|
||||
|
||||
auto shader = shaders::get(ritual.element == FIRE ? "flame" : "lightning");
|
||||
world.set<SpriteEffect>(effect_id, {100, shader});
|
||||
|
||||
auto anim = animation::load(effect);
|
||||
anim.play();
|
||||
world.set<Animation>(effect_id, anim);
|
||||
|
||||
drop_item(effect_id);
|
||||
}
|
||||
|
|
|
@ -71,4 +71,7 @@ namespace System {
|
|||
|
||||
paths.compute_paths(walls);
|
||||
}
|
||||
|
||||
void clear_attack();
|
||||
void spawn_attack(World& world, int attack_id);
|
||||
}
|
||||
|
|
|
@ -43,11 +43,11 @@ TEST_CASE("animation utility API", "[animation]") {
|
|||
animation::init();
|
||||
|
||||
auto blanket = textures::get_sprite("ritual_crafting_area");
|
||||
auto anim = animation::load("ritual_blanket");
|
||||
auto anim = animation::load("ritual_crafting_area");
|
||||
|
||||
anim.play();
|
||||
|
||||
while(animation::apply(anim, blanket)) {
|
||||
while(animation::apply(anim, *blanket.sprite)) {
|
||||
fmt::println("animation: {}", anim.subframe);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue