Porytiles
Loading...
Searching...
No Matches
animation.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <optional>
5#include <ranges>
6#include <set>
7#include <string>
8#include <string_view>
9#include <vector>
10
17
18namespace porytiles {
19
20namespace anim {
21
22constexpr std::string g_tileset_anims_prefix = "gTilesetAnims_";
23constexpr std::string s_tileset_anims_prefix = "sTilesetAnims_";
24constexpr std::string porytiles_managed_prefix = "PorytilesManaged_";
25
26// "InitTilesetAnim_PorytilesManaged_": prefix of the init callback symbol Porytiles generates for a managed
27// tileset (i.e. "InitTilesetAnim_" + porytiles_managed_prefix). Used for both constructing and detecting it.
28constexpr std::string_view porytiles_managed_callback_prefix = "InitTilesetAnim_PorytilesManaged_";
29
30// Builds the Porytiles-managed init-callback symbol name for a tileset, e.g.
31// "gTileset_velvet_forest" -> "InitTilesetAnim_PorytilesManaged_VelvetForest".
32[[nodiscard]] inline std::string managed_callback_name(const std::string &tileset_name)
33{
35}
36
37[[nodiscard]] inline std::string message_header(
38 const TextFormatter &format,
39 const std::string &anim_name,
40 const std::string &frame_name,
41 std::size_t internal_tile_index,
42 std::size_t subtile_row,
43 std::size_t subtile_col)
44{
45 return format.format(
46 "{} '{}' | frame '{}' | subtile {} | pixel {},{}",
47 FormatParam{"anim"},
48 FormatParam{anim_name},
49 FormatParam{frame_name},
50 FormatParam{std::to_string(internal_tile_index)},
51 FormatParam{std::to_string(subtile_row)},
52 FormatParam{std::to_string(subtile_col)});
53}
54
55[[nodiscard]] inline std::string message_header(
56 const TextFormatter &format,
57 const std::string &anim_name,
58 const std::string &frame_name,
59 std::size_t internal_tile_index)
60{
61 return format.format(
62 "{} '{}' | frame '{}' | subtile {}",
63 FormatParam{"anim"},
64 FormatParam{anim_name},
65 FormatParam{frame_name},
66 FormatParam{std::to_string(internal_tile_index)});
67}
68
69} // namespace anim
70
88template <SupportsTransparency PixelType>
89class Animation {
90 public:
91 Animation() = default;
92
93 explicit Animation(std::string name) : name_{std::move(name)} {}
94
95 Animation(std::string name, AnimParams params) : name_{std::move(name)}, params_{std::move(params)} {}
96
97 [[nodiscard]] const std::string &name() const
98 {
99 return name_;
100 }
101
102 [[nodiscard]] const AnimParams &params() const
103 {
104 return params_;
105 }
106
108 {
109 params_ = std::move(params);
110 }
111
115 [[nodiscard]] bool has_key_frame() const
116 {
117 return key_frame_.has_value();
118 }
119
128 [[nodiscard]] const AnimFrame<PixelType> &key_frame() const
129 {
130 if (!key_frame_.has_value()) {
131 panic("key_frame() called on Animation with no key frame set");
132 }
133 return *key_frame_;
134 }
135
137 {
138 key_frame_ = std::move(key_frame);
139 }
140
141 [[nodiscard]] const std::map<std::string, AnimFrame<PixelType>> &frames() const
142 {
143 return frames_;
144 }
145
146 [[nodiscard]] std::map<std::string, AnimFrame<PixelType>> &frames()
147 {
148 return frames_;
149 }
150
151 [[nodiscard]] std::vector<AnimFrame<PixelType>> frames_values() const
152 {
153 return frames_ | std::views::values | std::ranges::to<std::vector>();
154 }
155
159 [[nodiscard]] bool has_frames() const
160 {
161 return !frames_.empty();
162 }
163
167 [[nodiscard]] std::size_t frame_count() const
168 {
169 return frames_.size();
170 }
171
172 bool has_frame(const std::string &frame_name)
173 {
174 return frames_.contains(frame_name);
175 }
176
182 [[nodiscard]] const AnimFrame<PixelType> &frame_for_name(const std::string &frame_name) const
183 {
184 if (!frames_.contains(frame_name)) {
185 panic("anim '" + name_ + "' has no such frame '" + frame_name + "'");
186 }
187 return frames_.at(frame_name);
188 }
189
194 void put_frame(const std::string &name, AnimFrame<PixelType> frame)
195 {
196 frames_.emplace(name, std::move(frame));
197 }
198
217 [[nodiscard]] AnimFrame<PixelType> composite_frame(const PixelType &extrinsic_transparency) const
218 requires requires(const PixelType &p) { p.is_transparent(p); }
219 {
220 // Note: this method currently only supports extrinsic transparency (Rgba32). If a future caller needs the same
221 // logic for IndexPixel (intrinsic transparency), this would need to be extended — but no caller exists today,
222 // so the function is constrained via the requires-clause to the Rgba32 case.
223
224 // Determine tile count from key frame or first regular frame
225 std::size_t tile_count = 0;
226 if (has_key_frame()) {
227 tile_count = key_frame().tile_count();
228 }
229 else if (has_frames()) {
230 tile_count = frames_.begin()->second.tile_count();
231 }
232 else {
233 panic("composite_frame() called on Animation with no key frame and no frames");
234 }
235
236 AnimFrame<PixelType> composite{"composite"};
237
238 for (std::size_t tile_idx = 0; tile_idx < tile_count; ++tile_idx) {
239 std::set<PixelType> all_colors;
240
241 // Collect from key frame (if present)
242 if (has_key_frame()) {
243 auto key_colors = key_frame().tile_at(tile_idx).unique_nontransparent_colors(extrinsic_transparency);
244 all_colors.merge(key_colors);
245 }
246
247 // Collect from regular frames
248 for (const auto &[name, frame] : frames_) {
249 auto colors = frame.tile_at(tile_idx).unique_nontransparent_colors(extrinsic_transparency);
250 all_colors.merge(colors);
251 }
252
253 // Create composite tile with all colors packed arbitrarily, first pixel is guaranteed extrinsic_transparent
254 PixelTile<PixelType> composite_tile{extrinsic_transparency};
255 std::size_t pix_idx = 1;
256 for (const auto &color : all_colors) {
257 if (pix_idx >= tile::size_pix) {
258 panic("pix_idx exceeded tile size in pixels");
259 }
260 composite_tile.set(pix_idx++, color);
261 }
262
263 if (composite_tile.unique_nontransparent_colors(extrinsic_transparency).size() >= palette::max_size) {
264 panic("composite tile had >= 16 colors");
265 }
266
267 composite.add_tile(std::move(composite_tile));
268 }
269
270 return composite;
271 }
272
273 private:
274 std::string name_;
275 AnimParams params_;
276 std::optional<AnimFrame<PixelType>> key_frame_;
277 std::map<std::string, AnimFrame<PixelType>> frames_;
278};
279
280} // namespace porytiles
Represents a single frame of an animation, containing tiles and a frame name.
Configuration parameters for a single tileset animation.
A complete tileset animation with name, configuration, and frame data.
Definition animation.hpp:89
const AnimParams & params() const
AnimFrame< PixelType > composite_frame(const PixelType &extrinsic_transparency) const
Returns the "composite" frame for this animation.
Animation(std::string name)
Definition animation.hpp:93
const AnimFrame< PixelType > & frame_for_name(const std::string &frame_name) const
Returns the frame with the specified name.
void params(AnimParams params)
bool has_frames() const
Checks if this animation has any frames.
const std::string & name() const
Definition animation.hpp:97
void key_frame(AnimFrame< PixelType > key_frame)
bool has_key_frame() const
Checks if this animation has a key frame set.
Animation(std::string name, AnimParams params)
Definition animation.hpp:95
const AnimFrame< PixelType > & key_frame() const
Returns the key frame of this animation.
void put_frame(const std::string &name, AnimFrame< PixelType > frame)
Adds a frame to this Animation frame map.
std::map< std::string, AnimFrame< PixelType > > & frames()
bool has_frame(const std::string &frame_name)
std::vector< AnimFrame< PixelType > > frames_values() const
const std::map< std::string, AnimFrame< PixelType > > & frames() const
std::size_t frame_count() const
Returns the number of frames in this animation.
std::string to_pascal_case() const
Outputs all words flattened and joined in PascalCase (each word capitalized, no separators).
A text parameter with associated styling for formatted output.
Abstract base class for applying text styling with context-aware formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > &params) const
Formats a string with styled parameters using fmtlib syntax.
std::string managed_callback_name(const std::string &tileset_name)
Definition animation.hpp:32
constexpr std::string porytiles_managed_prefix
Definition animation.hpp:24
constexpr std::string_view porytiles_managed_callback_prefix
Definition animation.hpp:28
constexpr std::string g_tileset_anims_prefix
Definition animation.hpp:22
constexpr std::string s_tileset_anims_prefix
Definition animation.hpp:23
std::string message_header(const TextFormatter &format, const std::string &anim_name, const std::string &frame_name, std::size_t internal_tile_index, std::size_t subtile_row, std::size_t subtile_col)
Definition animation.hpp:37
constexpr std::size_t max_size
Definition palette.hpp:19
constexpr std::size_t size_pix
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43
DynamicCasedName extract_tileset_cased_name(const std::string &tileset_name)
Extracts the tileset short name and wraps it in a DynamicCasedName.
Utility functions for string manipulation and formatting.