128 lines
4.6 KiB
C++
128 lines
4.6 KiB
C++
#include "game/entities/concrete/whip_attack.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "game/entities/entity_factory.hpp"
|
|
|
|
namespace {
|
|
|
|
// Llig una entrada `sprites.<dir>: { path, size: [w, h] }` del YAML.
|
|
// Si manquen camps, retorna un DirSprite buit (queda invalidat al
|
|
// configure() per `surface_key.empty()`).
|
|
auto readDirSprite(const fkyaml::node& s) -> WhipAttack::DirSprite {
|
|
WhipAttack::DirSprite sprite;
|
|
if (s.contains("path")) {
|
|
const auto REL = s["path"].get_value<std::string>();
|
|
sprite.surface_key = std::filesystem::path(REL).filename().string();
|
|
}
|
|
if (s.contains("size")) {
|
|
const auto& sz = s["size"];
|
|
if (sz.is_sequence() && sz.size() == 2) {
|
|
sprite.frame_w = static_cast<float>(sz[0].get_value<int>());
|
|
sprite.frame_h = static_cast<float>(sz[1].get_value<int>());
|
|
}
|
|
}
|
|
return sprite;
|
|
}
|
|
|
|
// Construeix l'`AnimationComponent` del swing a partir del bloc
|
|
// `animations.swing`. Convenció igual que la resta d'entitats:
|
|
// - `frames` : llista d'índexs dins l'spritesheet (obligatori).
|
|
// - `frame_ms` : durada uniforme per frame (ms). Alternativament `fps`.
|
|
// - `loop` : per defecte true; al látigo el volem false (mor en
|
|
// completar).
|
|
auto buildSwingAnim(const fkyaml::node& yaml, std::vector<int>& active_out)
|
|
-> AnimationComponent {
|
|
std::vector<int> frames = {0};
|
|
float fps = 8.0F;
|
|
bool loop = false;
|
|
if (yaml.contains("animations") && yaml["animations"].contains("swing")) {
|
|
const auto& sw = yaml["animations"]["swing"];
|
|
if (sw.contains("frames")) {
|
|
frames.clear();
|
|
for (const auto& f : sw["frames"]) {
|
|
// cppcheck-suppress useStlAlgorithm
|
|
frames.push_back(f.get_value<int>());
|
|
}
|
|
if (frames.empty()) { frames.push_back(0); }
|
|
}
|
|
if (sw.contains("frame_ms")) {
|
|
const auto MS = static_cast<float>(sw["frame_ms"].get_value<int>());
|
|
if (MS > 0.0F) { fps = 1000.0F / MS; }
|
|
} else if (sw.contains("fps")) {
|
|
fps = sw["fps"].get_value<float>();
|
|
}
|
|
if (sw.contains("loop")) { loop = sw["loop"].get_value<bool>(); }
|
|
if (sw.contains("active_frames")) {
|
|
for (const auto& f : sw["active_frames"]) {
|
|
// cppcheck-suppress useStlAlgorithm
|
|
active_out.push_back(f.get_value<int>());
|
|
}
|
|
}
|
|
}
|
|
return {frames, fps, loop};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
WhipAttack::WhipAttack(const fkyaml::node& yaml) {
|
|
name_ = yaml.contains("name") ? yaml["name"].get_value<std::string>() : std::string("whip_attack");
|
|
collides_ = false;
|
|
interactable_ = false;
|
|
|
|
if (yaml.contains("sprites")) {
|
|
const auto& s = yaml["sprites"];
|
|
if (s.contains("north")) { sprite_north_ = readDirSprite(s["north"]); }
|
|
if (s.contains("south")) { sprite_south_ = readDirSprite(s["south"]); }
|
|
if (s.contains("east")) { sprite_east_ = readDirSprite(s["east"]); }
|
|
if (s.contains("west")) { sprite_west_ = readDirSprite(s["west"]); }
|
|
}
|
|
|
|
anim_swing_ = buildSwingAnim(yaml, active_frames_);
|
|
anim_ = anim_swing_;
|
|
}
|
|
|
|
void WhipAttack::configure(Direction dir, float spawn_x, float spawn_y, float range_px) {
|
|
direction_ = dir;
|
|
range_px_ = range_px;
|
|
|
|
const auto& chosen = spriteFor(dir);
|
|
setPixelRect({.x = spawn_x, .y = spawn_y, .w = chosen.frame_w, .h = chosen.frame_h});
|
|
|
|
if (anim_.has_value()) { anim_->reset(); }
|
|
}
|
|
|
|
auto WhipAttack::getSurfaceKey() const -> const std::string& {
|
|
return spriteFor(direction_).surface_key;
|
|
}
|
|
|
|
auto WhipAttack::spriteFor(Direction dir) const -> const DirSprite& {
|
|
switch (dir) {
|
|
case Direction::NORTH:
|
|
return sprite_north_;
|
|
case Direction::SOUTH:
|
|
return sprite_south_;
|
|
case Direction::EAST:
|
|
return sprite_east_;
|
|
case Direction::WEST:
|
|
return sprite_west_;
|
|
}
|
|
return sprite_east_;
|
|
}
|
|
|
|
void WhipAttack::update(float delta_time) {
|
|
Entity::update(delta_time);
|
|
if (anim_.has_value() && anim_->completed()) { markDead(); }
|
|
}
|
|
|
|
auto WhipAttack::isFrameActive() const -> bool {
|
|
if (!anim_.has_value()) { return false; }
|
|
const int FRAME = anim_->currentFrame();
|
|
return std::ranges::any_of(active_frames_, [FRAME](int f) { return f == FRAME; });
|
|
}
|
|
|
|
REGISTER_ENTITY("whip_attack", WhipAttack)
|