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,32 @@
|
||||
/**
|
||||
* @file actor.h
|
||||
* @brief Declares the public actor API.
|
||||
* @brief A live thing in the world: state bits, motion, hierarchy, and behaviour hooks.
|
||||
*
|
||||
* An actor is an instance; the akgl_Character it points at is its template. The
|
||||
* actor carries only what is unique to it -- where it is, which way it is
|
||||
* moving, which animation frame it is on -- and borrows speed, acceleration and
|
||||
* the state-to-sprite map from the character. That is what makes a hundred of
|
||||
* one kind of thing cheap.
|
||||
*
|
||||
* **State is a bitmask**, not an enum. An actor is facing left *and* moving left
|
||||
* *and* alive at the same time, and the whole combination is the key that
|
||||
* selects a sprite (see akgl_character_sprite_get). Adding a state means adding
|
||||
* a bit here *and* a name in `src/actor_state_string_names.c`, or character JSON
|
||||
* cannot refer to it.
|
||||
*
|
||||
* **Behaviour attaches as function pointers**, not by inheritance. Every actor
|
||||
* gets the library's default `updatefunc`, `renderfunc`, `facefunc`,
|
||||
* `movementlogicfunc`, `changeframefunc` and `addchild` from
|
||||
* akgl_actor_initialize; a game that wants a different flavour of anything
|
||||
* replaces the pointer on that one actor.
|
||||
*
|
||||
* **Motion is split four ways** -- thrust, environmental, acceleration, and the
|
||||
* velocity that is their sum -- so that a character's own walking and the
|
||||
* world's gravity and drag can be reasoned about separately. See physics.h for
|
||||
* the model and which fields mean what.
|
||||
*
|
||||
* Actors are pool objects (akgl_heap_next_actor) published in
|
||||
* #AKGL_REGISTRY_ACTOR under their name.
|
||||
*/
|
||||
|
||||
#ifndef _AKGL_ACTOR_H_
|
||||
@@ -48,214 +74,385 @@
|
||||
#define AKGL_ACTOR_STATE_UNDEFINED_30 1 << 30 // 1073741824 0100 0000 0000 0000
|
||||
#define AKGL_ACTOR_STATE_UNDEFINED_31 1 << 31 // 2147483648 1000 0000 0000 0000
|
||||
|
||||
/** @brief Bits in an actor's state word. Fixed by the width of `int32_t state`. */
|
||||
#define AKGL_ACTOR_MAX_STATES 32
|
||||
|
||||
// This is an array of strings equal to actor states from 1-32.
|
||||
// This is built by a utility script and not kept in git, see
|
||||
// the Makefile for lib_src/actor_state_string_names.c
|
||||
/** @brief Maps actor-state bit positions to symbolic names. */
|
||||
/**
|
||||
* @brief Bit position -> state name, as text. Index `i` names the bit `1 << i`.
|
||||
*
|
||||
* akgl_registry_init_actor_state_strings builds
|
||||
* #AKGL_REGISTRY_ACTOR_STATE_STRINGS out of this, which is what lets character
|
||||
* JSON write `"AKGL_ACTOR_STATE_FACE_LEFT"` instead of `2`. A bit whose name
|
||||
* here does not match its `#define` above cannot be referred to from JSON at
|
||||
* all.
|
||||
*
|
||||
* @warning Three known defects, all tracked in TODO.md items 24-26: the
|
||||
* definition in `src/actor_state_string_names.c` is 32 entries while
|
||||
* this declares 33, so reading index 32 reads past the object; bits 11
|
||||
* and 12 are named `UNDEFINED_11`/`UNDEFINED_12` rather than
|
||||
* `MOVING_IN`/`MOVING_OUT`; and the comment above about a generator
|
||||
* script is stale -- there is no such script, and the file is tracked
|
||||
* in git and maintained by hand.
|
||||
*/
|
||||
extern char *AKGL_ACTOR_STATE_STRING_NAMES[AKGL_ACTOR_MAX_STATES+1];
|
||||
|
||||
/** @brief Every facing bit. Clear this before setting one, so an actor faces exactly one way. */
|
||||
#define AKGL_ACTOR_STATE_FACE_ALL (AKGL_ACTOR_STATE_FACE_DOWN | AKGL_ACTOR_STATE_FACE_LEFT | AKGL_ACTOR_STATE_FACE_RIGHT | AKGL_ACTOR_STATE_FACE_UP)
|
||||
/** @brief Every planar movement bit. The two depth bits, MOVING_IN and MOVING_OUT, are not in it. */
|
||||
#define AKGL_ACTOR_STATE_MOVING_ALL (AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_MOVING_UP | AKGL_ACTOR_STATE_MOVING_DOWN)
|
||||
|
||||
/** @brief Longest actor name, including the terminator. Names are truncated, not rejected. */
|
||||
#define AKGL_ACTOR_MAX_NAME_LENGTH 128
|
||||
/** @brief Children one actor can carry. A child moves with its parent rather than simulating. */
|
||||
#define AKGL_ACTOR_MAX_CHILDREN 8
|
||||
|
||||
/** @brief Actors in the pool. Override before including heap.h to change it; see heap.h. */
|
||||
#define AKGL_MAX_HEAP_ACTOR 64
|
||||
|
||||
/** @brief Represents a live actor, including state, motion, hierarchy, and behavior callbacks. */
|
||||
typedef struct akgl_Actor {
|
||||
uint8_t refcount;
|
||||
char name[AKGL_ACTOR_MAX_NAME_LENGTH];
|
||||
akgl_Character *basechar;
|
||||
uint8_t curSpriteFrameId;
|
||||
SDL_Time curSpriteFrameTimer;
|
||||
bool curSpriteReversing;
|
||||
uint32_t layer;
|
||||
int32_t state;
|
||||
bool movement_controls_face;
|
||||
void *actorData;
|
||||
bool visible;
|
||||
SDL_Time movetimer;
|
||||
uint8_t refcount; /**< Pool bookkeeping; 0 means the slot is free. One reference per parent, plus one for the registry. */
|
||||
char name[AKGL_ACTOR_MAX_NAME_LENGTH]; /**< Registry key. Truncated, not rejected, if the source name is longer. */
|
||||
akgl_Character *basechar; /**< The template this actor instantiates. Borrowed; no reference is taken. Required before the actor can update, render, or simulate. */
|
||||
uint8_t curSpriteFrameId; /**< Index into the current sprite's `frameids`, not a frame number on the sheet. */
|
||||
SDL_Time curSpriteFrameTimer; /**< When the current frame was shown. The next frame is due once the sprite's `speed` has elapsed. */
|
||||
bool curSpriteReversing; /**< Walking the animation backwards, for a sprite with `loopReverse` set. */
|
||||
uint32_t layer; /**< Which tilemap layer the actor is drawn and simulated on. Set from the object layer it was placed in. */
|
||||
int32_t state; /**< The `AKGL_ACTOR_STATE_*` bitmask. The whole value is the key that selects a sprite. */
|
||||
bool movement_controls_face; /**< When set, the default `facefunc` turns the actor to face whichever way it is moving. Clear it for an actor that aims independently. */
|
||||
void *actorData; /**< The game's own per-actor data. Never read or freed by the library. */
|
||||
bool visible; /**< Whether to draw at all. An actor off-camera is skipped separately, so this is for deliberate hiding. */
|
||||
SDL_Time movetimer; /**< Unused. Nothing in the library reads or writes it. */
|
||||
// Velocity. Combined effect of all forces acting on the actor resulting
|
||||
// in energy along an axis.
|
||||
float32_t vx;
|
||||
float32_t vy;
|
||||
float32_t vz;
|
||||
float32_t vx; /**< Velocity along x, world units per second. Recomputed each step as `ex + tx`; writing it directly is overwritten. */
|
||||
float32_t vy; /**< Velocity along y. On a child actor this is read as an offset from the parent instead. */
|
||||
float32_t vz; /**< Velocity along z. */
|
||||
// Environmental velocity. These are the forces acting on the actor by the
|
||||
// environment (such as gravity and atmospheric drag)
|
||||
float32_t ex;
|
||||
float32_t ey;
|
||||
float32_t ez;
|
||||
float32_t ex; /**< Environmental velocity along x: what gravity and drag have done. Accumulates across steps. */
|
||||
float32_t ey; /**< Environmental velocity along y. This is what carries a falling actor. */
|
||||
float32_t ez; /**< Environmental velocity along z. */
|
||||
// Thrust. Energy originating only from the actor's own acceleration on a
|
||||
// given axis, before the effects of gravity and drag.
|
||||
float32_t tx;
|
||||
float32_t ty;
|
||||
float32_t tz;
|
||||
float32_t tx; /**< Thrust along x: the actor's own effort. Capped at `sx`, which is why gravity can outrun top speed and walking cannot. */
|
||||
float32_t ty; /**< Thrust along y, capped at `sy`. */
|
||||
float32_t tz; /**< Thrust along z, capped at `sz`. */
|
||||
// Acceleration. These are borrowed from the base character object.
|
||||
// A given axis resets to 0 when the actor stops moving in a given axis.
|
||||
float32_t ax;
|
||||
float32_t ay;
|
||||
float32_t az;
|
||||
float32_t ax; /**< Acceleration along x, signed by the direction of travel. Copied from the character each step by the default movement logic. */
|
||||
float32_t ay; /**< Acceleration along y, signed the same way. */
|
||||
float32_t az; /**< Acceleration along z. Not set by the default movement logic. */
|
||||
// Max speed. These are borrowed from the base character object.
|
||||
float32_t sx;
|
||||
float32_t sy;
|
||||
float32_t sz;
|
||||
float32_t sx; /**< Maximum thrust along x, copied from the character. */
|
||||
float32_t sy; /**< Maximum thrust along y. */
|
||||
float32_t sz; /**< Maximum thrust along z. */
|
||||
// Position.
|
||||
float32_t x;
|
||||
float32_t y;
|
||||
float32_t z;
|
||||
float32_t scale;
|
||||
struct akgl_Actor *children[AKGL_ACTOR_MAX_CHILDREN];
|
||||
struct akgl_Actor *parent;
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, float32_t dt);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*addchild)(struct akgl_Actor *obj, struct akgl_Actor *child);
|
||||
float32_t x; /**< Position in map pixels. For a child, an offset from the parent. */
|
||||
float32_t y; /**< Position in map pixels. Also what the tilemap's perspective band scales against. */
|
||||
float32_t z; /**< Depth. Carried through the simulation but not used at draw time. */
|
||||
float32_t scale; /**< Draw scale, 1.0 for natural size. Written by akgl_tilemap_scale_actor, or forced to 1.0 when tilemap scaling is off. */
|
||||
struct akgl_Actor *children[AKGL_ACTOR_MAX_CHILDREN]; /**< Attached actors, `NULL` in unused slots. Each holds a reference and is released with the parent. */
|
||||
struct akgl_Actor *parent; /**< The actor this one is attached to, or `NULL`. A child is positioned relative to it and does not simulate on its own. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj); /**< Per-frame logic: facing, then animation frame. Defaults to akgl_actor_update. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj); /**< Per-frame draw. Defaults to akgl_actor_render. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj); /**< Chooses the facing bits. Defaults to akgl_actor_automatic_face. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, float32_t dt); /**< Called by the physics step before gravity. Defaults to akgl_actor_logic_movement. May raise AKGL_ERR_LOGICINTERRUPT to skip the rest of the step. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems); /**< Advances the animation. Defaults to akgl_actor_logic_changeframe. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*addchild)(struct akgl_Actor *obj, struct akgl_Actor *child); /**< Attaches a child. Defaults to akgl_actor_add_child. */
|
||||
} akgl_Actor;
|
||||
|
||||
/**
|
||||
* @brief Actor initialize.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param name Registry key or human-readable object name.
|
||||
* @brief Zero a pooled actor, name it, wire up its default behaviour, and register it.
|
||||
*
|
||||
* Sets `scale` to 1.0 and `movement_controls_face` to true, installs all six
|
||||
* default function pointers, publishes the actor in #AKGL_REGISTRY_ACTOR, and
|
||||
* takes the first reference. It does *not* set a character: the actor cannot
|
||||
* update, render, or simulate until akgl_actor_set_character has run.
|
||||
*
|
||||
* @param obj Pooled actor to initialize, normally from akgl_heap_next_actor.
|
||||
* Required. Any previous contents are discarded -- including its
|
||||
* child list, so release an actor rather than reinitializing it.
|
||||
* @param name Registry key, NUL-terminated. Required. Truncated at
|
||||
* #AKGL_ACTOR_MAX_NAME_LENGTH. An existing entry with the same name
|
||||
* is silently replaced, and the actor 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 obj or @p name is `NULL`.
|
||||
* @throws AKERR_KEY If the actor cannot be written into #AKGL_REGISTRY_ACTOR --
|
||||
* in practice, because akgl_registry_init has not run.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_initialize(akgl_Actor *obj, char *name);
|
||||
/**
|
||||
* @brief Actor set character.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param basecharname Registry name of the character to assign.
|
||||
* @brief Bind an actor to a registered character, and copy its top speeds.
|
||||
*
|
||||
* Looks the character up by name and takes its `sx`/`sy` as this actor's speed
|
||||
* limits, zeroing `ax` and `ay` so the actor starts from rest. The character is
|
||||
* borrowed, not referenced: releasing it out from under a live actor leaves a
|
||||
* dangling pointer.
|
||||
*
|
||||
* @param obj The actor to bind. Required.
|
||||
* @param basecharname Registry name of the character. Required. Rebinding an
|
||||
* actor mid-life is allowed, and is how a character swaps
|
||||
* its whole sprite set at once.
|
||||
* @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 obj or @p basecharname is `NULL`, or if no
|
||||
* character is registered under that name. The last case is a lookup
|
||||
* miss reported with a pointer status rather than AKERR_KEY.
|
||||
*
|
||||
* @note `sz` is not copied and `az` is not zeroed, so an actor's depth speed
|
||||
* keeps whatever it had. The default movement logic re-copies all three
|
||||
* each step, which papers over it for anything that simulates.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_set_character(akgl_Actor *obj, char *basecharname);
|
||||
/**
|
||||
* @brief Actor render.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @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_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @brief Draw an actor's current animation frame, if it is on camera.
|
||||
*
|
||||
* Selects the sprite for the actor's current state, checks it against the
|
||||
* camera, and blits the frame at the actor's position translated into screen
|
||||
* space. A child actor is drawn at its parent's position plus its own, which is
|
||||
* what makes an offset mean an offset.
|
||||
*
|
||||
* Several things are skipped rather than reported, because a frame is not the
|
||||
* place to fail: an actor with no sprite for its state, one off camera, one with
|
||||
* `visible` clear, and one whose frame index has run past its current sprite
|
||||
* are all simply not drawn.
|
||||
*
|
||||
* @param obj The actor to draw. Required, along with its `basechar`.
|
||||
* @return `NULL` on success -- including every skipped case above -- otherwise
|
||||
* an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER If @p obj or `obj->basechar` is `NULL`.
|
||||
* @throws AKERR_* Whatever akgl_sprite_sheet_coords_for_frame or the renderer's
|
||||
* `draw_texture` raises.
|
||||
*
|
||||
* @note The destination height is computed from the sprite's *width*, so a
|
||||
* non-square sprite is drawn square.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_render(akgl_Actor *obj);
|
||||
/**
|
||||
* @brief Actor update.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @brief Per-frame logic: turn the actor to face its direction, then advance its animation.
|
||||
*
|
||||
* Calls the actor's `facefunc`, then selects the sprite for the resulting state
|
||||
* and calls `changeframefunc` if that sprite's frame time has elapsed. An actor
|
||||
* with no sprite registered for its current state is left alone and reported as
|
||||
* success -- states change faster than art gets drawn, and a missing sprite
|
||||
* should not stop the frame.
|
||||
*
|
||||
* @param obj The actor to update. Required, along with its `basechar`.
|
||||
* @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 obj or `obj->basechar` is `NULL`.
|
||||
* @throws AKERR_* Whatever `facefunc` or `changeframefunc` raises. Note that an
|
||||
* AKERR_KEY from either is swallowed along with the missing-sprite case.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_update(akgl_Actor *obj);
|
||||
/**
|
||||
* @brief Actor logic movement.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param dt Elapsed simulation time in seconds.
|
||||
* @brief The default `movementlogicfunc`: turn movement bits into signed acceleration.
|
||||
*
|
||||
* Re-copies the character's speed limits onto the actor -- so a change to the
|
||||
* character takes effect next step -- and sets `ax`/`ay` to plus or minus the
|
||||
* character's acceleration according to which movement bits are set. It does not
|
||||
* integrate anything; the physics backend does that.
|
||||
*
|
||||
* Opposing bits do not cancel: `MOVING_LEFT` wins over `MOVING_RIGHT` because it
|
||||
* is tested first. An actor with no movement bits keeps its previous
|
||||
* acceleration rather than being zeroed, which is why the input handlers clear
|
||||
* it themselves on key release.
|
||||
*
|
||||
* @param obj The actor to compute acceleration for. Required, along with its
|
||||
* `basechar`.
|
||||
* @param dt Seconds since the previous step. Accepted for the hook's signature;
|
||||
* this implementation does not use it, since it sets an acceleration
|
||||
* rather than integrating one.
|
||||
* @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 obj or `obj->basechar` is `NULL`.
|
||||
*
|
||||
* @note A replacement for this hook may raise AKGL_ERR_LOGICINTERRUPT to tell
|
||||
* the physics step to skip the rest of this actor's tick. This
|
||||
* implementation never does.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_movement(akgl_Actor *obj, float32_t dt);
|
||||
/**
|
||||
* @brief Actor logic changeframe.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param curSprite Sprite currently selected for the actor.
|
||||
* @param curtimems Current frame-clock value.
|
||||
* @brief The default `changeframefunc`: step to the next animation frame.
|
||||
*
|
||||
* Walks forward through the sprite's frames; at the last one it either wraps to
|
||||
* 0, or -- for a sprite with both `loop` and `loopReverse` -- turns round and
|
||||
* walks back down, turning again at frame 0. A sprite with `loop` clear also
|
||||
* wraps to 0 rather than holding the final frame.
|
||||
*
|
||||
* @param obj The actor whose frame index to advance. Required.
|
||||
* @param curSprite The sprite currently selected for the actor, whose frame
|
||||
* count and loop flags decide what happens at the end.
|
||||
* Required in practice, and **not** checked -- a `NULL` here is
|
||||
* a crash, not an error.
|
||||
* @param curtimems The current clock reading. Accepted for the hook's signature;
|
||||
* this implementation does not use it, because the caller has
|
||||
* already decided the frame is due.
|
||||
* @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 obj is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems);
|
||||
/**
|
||||
* @brief Actor automatic face.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @brief The default `facefunc`: turn the actor to face whichever way it is moving.
|
||||
*
|
||||
* Clears every facing bit and sets the one matching the first movement bit it
|
||||
* finds, in the order left, right, up, down -- so an actor moving diagonally
|
||||
* faces the horizontal component. An actor with `movement_controls_face` clear
|
||||
* is left alone entirely, which is the hook for something that aims
|
||||
* independently of the way it walks.
|
||||
*
|
||||
* @param obj The actor to turn. Required.
|
||||
* @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 obj is `NULL`.
|
||||
*
|
||||
* @note An actor that has stopped moving is left with no facing bit at all,
|
||||
* rather than keeping the way it was last facing -- so its state no longer
|
||||
* matches any "standing still facing left" sprite. The implementation
|
||||
* carries a TODO saying as much.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_automatic_face(akgl_Actor *obj);
|
||||
/**
|
||||
* @brief Actor add child.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param child Actor to attach as a child.
|
||||
* @brief Attach one actor to another so it moves with it.
|
||||
*
|
||||
* A child is positioned relative to its parent rather than simulated: the
|
||||
* physics step snaps it to the parent's position plus its own velocity fields,
|
||||
* used here as a fixed offset. That is what a carried lantern or a turret on a
|
||||
* tank wants.
|
||||
*
|
||||
* The parent takes a reference on the child, and releasing the parent releases
|
||||
* every child with it.
|
||||
*
|
||||
* @param obj The parent. Required.
|
||||
* @param child The actor to attach. Required. Must not already have a parent --
|
||||
* this builds a tree, not a graph. Its `x`/`y` become an offset
|
||||
* from the parent's position rather than a world position.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_RELATIONSHIP When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p obj or @p child is `NULL`.
|
||||
* @throws AKERR_RELATIONSHIP If @p child already has a parent. Detach it first --
|
||||
* though note there is no detach function, so in practice this means
|
||||
* releasing and rebuilding it.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p obj already has #AKGL_ACTOR_MAX_CHILDREN
|
||||
* children.
|
||||
*
|
||||
* @warning Nothing checks for a cycle. Making an actor its own ancestor makes
|
||||
* akgl_heap_release_actor recurse until the stack runs out.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child);
|
||||
|
||||
/*
|
||||
* The control-map handler functions ("cmhf"). These are what
|
||||
* akgl_controller_default binds the arrow keys and the D-pad to, and what a
|
||||
* game's own akgl_Control bindings can point at. They are called with the
|
||||
* control map's target actor, not with a global "player", so they work for any
|
||||
* number of locally controlled actors.
|
||||
*
|
||||
* Each pair is symmetric: the `_on` handler starts movement in a direction and
|
||||
* turns the actor to face it, and the `_off` handler stops it dead -- zeroing
|
||||
* acceleration, thrust, environmental velocity and velocity on that axis, so
|
||||
* there is no coasting. That is an arcade feel rather than a physical one, and
|
||||
* it is deliberate; a game wanting momentum binds its own handlers instead.
|
||||
*
|
||||
* An `_on` handler clears *every* facing and movement bit before setting its
|
||||
* own, so holding two directions at once moves in whichever was pressed last
|
||||
* rather than diagonally.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Actor cmhf left on.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Start the actor moving left, and turn it to face left.
|
||||
* @param obj The actor to move, supplied by the control map. Required, along
|
||||
* with its `basechar`, whose acceleration is negated onto `ax`.
|
||||
* @param event The event that triggered this. Required, but not read -- the
|
||||
* binding has already decided what the event means.
|
||||
* @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 obj, @p event, or `obj->basechar` is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event);
|
||||
/**
|
||||
* @brief Actor cmhf left off.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Stop the actor moving left, zeroing everything on the x axis.
|
||||
* @param obj The actor to stop. Required. `basechar` is not needed here.
|
||||
* @param event The event that triggered this. Required, but not read.
|
||||
* @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 obj or @p event is `NULL`.
|
||||
* @note It clears the x axis whichever way the actor was going, so releasing
|
||||
* left while holding right also stops the rightward movement.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_off(akgl_Actor *obj, SDL_Event *event);
|
||||
/**
|
||||
* @brief Actor cmhf right on.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Start the actor moving right, and turn it to face right.
|
||||
* @param obj The actor to move. Required, along with its `basechar`.
|
||||
* @param event The event that triggered this. Required, but not read.
|
||||
* @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 obj, @p event, or `obj->basechar` is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_on(akgl_Actor *obj, SDL_Event *event);
|
||||
/**
|
||||
* @brief Actor cmhf right off.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Stop the actor moving right, zeroing everything on the x axis.
|
||||
* @param obj The actor to stop. Required.
|
||||
* @param event The event that triggered this. Required, but not read.
|
||||
* @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 obj or @p event is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_off(akgl_Actor *obj, SDL_Event *event);
|
||||
/**
|
||||
* @brief Actor cmhf up on.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Start the actor moving up the screen, and turn it to face up.
|
||||
* @param obj The actor to move. Required, along with its `basechar`, whose y
|
||||
* acceleration is negated -- y grows downward.
|
||||
* @param event The event that triggered this. Required, but not read.
|
||||
* @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 obj, @p event, or `obj->basechar` is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_on(akgl_Actor *obj, SDL_Event *event);
|
||||
/**
|
||||
* @brief Actor cmhf up off.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Stop the actor moving up, zeroing everything on the y axis.
|
||||
* @param obj The actor to stop. Required.
|
||||
* @param event The event that triggered this. Required, but not read.
|
||||
* @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 obj or @p event is `NULL`.
|
||||
* @note This also zeroes `ey`, so an actor under gravity has its accumulated
|
||||
* fall cancelled by releasing a movement key.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_off(akgl_Actor *obj, SDL_Event *event);
|
||||
/**
|
||||
* @brief Actor cmhf down on.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Start the actor moving down the screen, and turn it to face down.
|
||||
* @param obj The actor to move. Required, along with its `basechar`.
|
||||
* @param event The event that triggered this. Required, but not read.
|
||||
* @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 obj, @p event, or `obj->basechar` is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_on(akgl_Actor *obj, SDL_Event *event);
|
||||
/**
|
||||
* @brief Actor cmhf down off.
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param event SDL input event to process.
|
||||
* @brief Stop the actor moving down, zeroing everything on the y axis.
|
||||
* @param obj The actor to stop. Required.
|
||||
* @param event The event that triggered this. Required, but not read.
|
||||
* @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 obj or @p event is `NULL`.
|
||||
* @note Zeroes `ey` as well; see akgl_Actor_cmhf_up_off.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_off(akgl_Actor *obj, SDL_Event *event);
|
||||
|
||||
/**
|
||||
* @brief Registry iterate actor.
|
||||
* @param userdata Caller data supplied to the SDL property iterator.
|
||||
* @param registry SDL property registry being queried.
|
||||
* @param name Registry key or human-readable object name.
|
||||
* @brief `SDL_EnumerateProperties` callback that applies an akgl_Iterator to one registered actor.
|
||||
*
|
||||
* Runs the operations the iterator asks for, in a fixed order regardless of the
|
||||
* order the bits were set: layer filter, update, tilemap scaling, render. With
|
||||
* #AKGL_ITERATOR_OP_LAYERMASK set, an actor on any other layer is skipped
|
||||
* entirely; with #AKGL_ITERATOR_OP_TILEMAPSCALE clear, `scale` is forced to 1.0
|
||||
* rather than left alone.
|
||||
*
|
||||
* @param userdata The akgl_Iterator carrying the operation flags, passed through
|
||||
* by `SDL_EnumerateProperties`. Required despite the `void *`.
|
||||
* @param registry #AKGL_REGISTRY_ACTOR, supplied by SDL.
|
||||
* @param name The actor's registry key. 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. An actor whose `updatefunc` fails therefore terminates the
|
||||
* game rather than skipping a frame. Install your own
|
||||
* `akerr_handler_unhandled_error` if that is not what you want.
|
||||
*/
|
||||
void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user