Document what the functions actually do instead of that they can fail
The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,15 @@
|
||||
/**
|
||||
* @file character.h
|
||||
* @brief Declares the public character API.
|
||||
* @brief The reusable half of an actor: movement constants and a state-to-sprite map.
|
||||
*
|
||||
* A character is the template; an akgl_Actor is the instance. Everything that is
|
||||
* the same for every goblin on the map -- top speed, acceleration, frame dwell
|
||||
* time, and which sprite to draw for which combination of actor-state bits --
|
||||
* lives here and is shared by pointer, so a hundred actors cost one character.
|
||||
*
|
||||
* Characters are pool objects (akgl_heap_next_character) and are published in
|
||||
* the #AKGL_REGISTRY_CHARACTER property registry under their name, which is how
|
||||
* akgl_actor_set_character finds them. akgl_registry_init must have run first.
|
||||
*/
|
||||
|
||||
#ifndef _AKGL_CHARACTER_H_
|
||||
@@ -15,64 +24,140 @@
|
||||
|
||||
/** @brief Defines reusable movement parameters and actor-state sprite bindings. */
|
||||
typedef struct akgl_Character {
|
||||
uint8_t refcount;
|
||||
char name[AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH];
|
||||
SDL_PropertiesID state_sprites;
|
||||
uint64_t speedtime;
|
||||
float32_t ax;
|
||||
float32_t ay;
|
||||
float32_t az;
|
||||
float32_t sx;
|
||||
float32_t sy;
|
||||
float32_t sz;
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_add)(struct akgl_Character *, akgl_Sprite *, int);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_get)(struct akgl_Character *, int, akgl_Sprite **);
|
||||
uint8_t refcount; /**< Pool bookkeeping; 0 means the slot is free. */
|
||||
char name[AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH]; /**< Registry key. Truncated, not rejected, if the source name is longer. */
|
||||
SDL_PropertiesID state_sprites; /**< State bitmask (decimal, as a string) -> akgl_Sprite *. */
|
||||
uint64_t speedtime; /**< Nanoseconds one sprite frame is held before advancing. Read from JSON in milliseconds and scaled by #AKGL_TIME_ONESEC_MS, which despite its name is nanoseconds-per-millisecond. TODO.md item 6. */
|
||||
float32_t ax; /**< Acceleration along x, world units per second squared. Copied into an actor by akgl_actor_set_character. */
|
||||
float32_t ay; /**< Acceleration along y. */
|
||||
float32_t az; /**< Acceleration along z. Not read from JSON; stays 0 unless set by hand. */
|
||||
float32_t sx; /**< Maximum speed along x, world units per second. */
|
||||
float32_t sy; /**< Maximum speed along y. */
|
||||
float32_t sz; /**< Maximum speed along z. Not read from JSON; stays 0 unless set by hand. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_add)(struct akgl_Character *, akgl_Sprite *, int); /**< Bound to akgl_character_sprite_add by akgl_character_initialize. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_get)(struct akgl_Character *, int, akgl_Sprite **); /**< Bound to akgl_character_sprite_get by akgl_character_initialize. */
|
||||
} akgl_Character;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Character initialize.
|
||||
* @param basechar Character whose state-to-sprite map is accessed.
|
||||
* @param name Registry key or human-readable object name.
|
||||
* @brief Zero a pooled character, name it, and publish it in the character registry.
|
||||
*
|
||||
* Wipes the struct, copies @p name into it, creates the empty state-to-sprite
|
||||
* property set, binds the `sprite_add`/`sprite_get` function pointers, inserts
|
||||
* the character into #AKGL_REGISTRY_CHARACTER under @p name, and takes the first
|
||||
* reference. Everything numeric (speeds, accelerations, `speedtime`) is left at
|
||||
* zero for the caller -- or akgl_character_load_json -- to fill in.
|
||||
*
|
||||
* @param basechar Pooled character to initialize, normally straight from
|
||||
* akgl_heap_next_character. Required. Any previous contents are
|
||||
* discarded without releasing the sprites they referenced.
|
||||
* @param name Registry key, NUL-terminated. Required. Truncated at
|
||||
* #AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH. A name already in the
|
||||
* registry is silently replaced, and the character it displaced
|
||||
* becomes unreachable rather than being released.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_KEY When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p basechar or @p name is `NULL`, or if SDL
|
||||
* cannot create the state-to-sprite property set (out of memory).
|
||||
* @throws AKERR_KEY If the character cannot be written into
|
||||
* #AKGL_REGISTRY_CHARACTER -- in practice, because akgl_registry_init
|
||||
* has not run and the registry id is still 0.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_initialize(akgl_Character *basechar, char *name);
|
||||
/**
|
||||
* @brief Character sprite add.
|
||||
* @param basechar Character whose state-to-sprite map is accessed.
|
||||
* @param ref Sprite reference to associate with the character.
|
||||
* @param state Actor-state bit mask used as a sprite-map key.
|
||||
* @brief Bind a sprite to one exact combination of actor-state bits.
|
||||
*
|
||||
* The map key is the decimal spelling of @p state, so lookups match the *whole*
|
||||
* value: a sprite added for `FACE_LEFT|MOVING_LEFT` is not found by a lookup for
|
||||
* `FACE_LEFT` alone. Callers are expected to register every combination they
|
||||
* intend to draw. Adding a sprite takes a reference on it.
|
||||
*
|
||||
* @param basechar Character to add the mapping to. Required. Must already have
|
||||
* been through akgl_character_initialize.
|
||||
* @param ref Sprite to draw for @p state. Required. Its `refcount` is
|
||||
* incremented, so the character keeps it alive.
|
||||
* @param state The exact actor-state bitmask (`AKGL_ACTOR_STATE_*`) this
|
||||
* sprite is for. 0 is accepted and is a usable key.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p basechar or @p ref is `NULL`.
|
||||
*
|
||||
* @note Re-adding a different sprite for a @p state that is already mapped
|
||||
* replaces the entry without releasing the sprite it displaced, so the
|
||||
* displaced sprite's reference is never given back.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_sprite_add(akgl_Character *basechar, akgl_Sprite *ref, int state);
|
||||
/**
|
||||
* @brief Character sprite get.
|
||||
* @param basechar Character whose state-to-sprite map is accessed.
|
||||
* @param state Actor-state bit mask used as a sprite-map key.
|
||||
* @param dest Output destination populated by the function.
|
||||
* @brief Look up the sprite bound to one exact combination of actor-state bits.
|
||||
*
|
||||
* The counterpart to akgl_character_sprite_add, and matched the same way: an
|
||||
* exact match on the whole @p state value, with no fallback to a subset of the
|
||||
* bits and no default sprite. Unlike most lookups in this codebase, *not finding
|
||||
* one is an error* rather than a successful "nothing here" -- an actor with no
|
||||
* sprite for its current state cannot be drawn.
|
||||
*
|
||||
* @param basechar Character to search. Required.
|
||||
* @param state The exact actor-state bitmask to look up.
|
||||
* @param dest Receives the mapped sprite. Required -- the return value is
|
||||
* spoken for by the error context. Set to `NULL` when there is
|
||||
* no mapping, alongside the AKERR_KEY error.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_KEY When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p basechar or @p dest is `NULL`.
|
||||
* @throws AKERR_KEY If no sprite is bound to exactly that @p state. The message
|
||||
* carries the state both as a decimal and as a bit pattern.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_sprite_get(akgl_Character *basechar, int state, akgl_Sprite **dest);
|
||||
|
||||
// This is an SDL iterator so we can't return our error state from it.
|
||||
/**
|
||||
* @brief Character state sprites iterate.
|
||||
* @param userdata Caller data supplied to the SDL property iterator.
|
||||
* @param props SDL property collection being iterated.
|
||||
* @param name Registry key or human-readable object name.
|
||||
* @brief `SDL_EnumerateProperties` callback that applies an akgl_Iterator to one state-to-sprite entry.
|
||||
*
|
||||
* Currently implements exactly one operation: with #AKGL_ITERATOR_OP_RELEASE
|
||||
* set, it hands the mapped sprite back to its heap layer. Every other flag is
|
||||
* ignored here. Used to tear a character's sprite map down entry by entry.
|
||||
*
|
||||
* @param userdata The akgl_Iterator carrying the operation flags, passed through
|
||||
* by `SDL_EnumerateProperties`. Required despite the `void *` --
|
||||
* a `NULL` here is an error, not "no operations".
|
||||
* @param props The character's `state_sprites` set, supplied by SDL.
|
||||
* @param name The property key: one state bitmask in decimal. Required.
|
||||
*
|
||||
* @warning This is an SDL callback, so it returns `void` and has nowhere to
|
||||
* propagate an error to. It ends in `FINISH_NORETURN`, which logs the
|
||||
* stack trace and then calls libakerror's unhandled-error handler --
|
||||
* whose default implementation **exits the process** with the error
|
||||
* status. A missing sprite or a `NULL` @p userdata therefore terminates
|
||||
* the game rather than skipping an entry. Install your own
|
||||
* `akerr_handler_unhandled_error` if that is not what you want.
|
||||
*/
|
||||
void akgl_character_state_sprites_iterate(void *userdata, SDL_PropertiesID props, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Character load json.
|
||||
* @param filename Path to the source asset or JSON document.
|
||||
* @brief Build a character from a JSON definition file and register it.
|
||||
*
|
||||
* Claims a character from the pool, then reads: `name` (the registry key),
|
||||
* `sprite_mappings` (an array of `{ "sprite": <name>, "state": [<state names>] }`
|
||||
* objects), `speedtime` in seconds, `speed_x`, `speed_y`, `acceleration_x`, and
|
||||
* `acceleration_y`. Each mapping's `state` array is OR-ed together into one
|
||||
* bitmask by looking every name up in #AKGL_REGISTRY_ACTOR_STATE_STRINGS, so
|
||||
* `["FACE_LEFT", "MOVING_LEFT"]` becomes a single key.
|
||||
*
|
||||
* Every referenced sprite must already be in #AKGL_REGISTRY_SPRITE: this loads
|
||||
* characters, not sprites, so akgl_sprite_load_json runs first. On any failure
|
||||
* the pooled character is released again.
|
||||
*
|
||||
* @param filename Path to the JSON document. Required. Used verbatim -- it is
|
||||
* not resolved against `SDL_GetBasePath()`.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p filename is `NULL`; if the file cannot be
|
||||
* opened or does not parse (the message carries jansson's line number
|
||||
* and text); or if a mapping names a sprite that is not in the sprite
|
||||
* registry.
|
||||
* @throws AKERR_KEY If a `state` array contains a name that is not a known
|
||||
* actor state, if a required top-level key is absent, or if the
|
||||
* character cannot be added to the registry.
|
||||
* @throws AKERR_TYPE If a key is present but holds the wrong JSON type -- for
|
||||
* example `speedtime` as a string. The message names the key.
|
||||
* @throws AKERR_OUTOFBOUNDS If a `state` array is indexed past its end.
|
||||
* @throws AKGL_ERR_HEAP If the character pool or the string pool is exhausted.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_load_json(char *filename);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user