aboutsummaryrefslogtreecommitdiff
path: root/docs/scenes.org
diff options
context:
space:
mode:
Diffstat (limited to 'docs/scenes.org')
-rw-r--r--docs/scenes.org432
1 files changed, 432 insertions, 0 deletions
diff --git a/docs/scenes.org b/docs/scenes.org
new file mode 100644
index 0000000..da62291
--- /dev/null
+++ b/docs/scenes.org
@@ -0,0 +1,432 @@
+#+TITLE: Scenes, Cameras, and Game States
+#+AUTHOR: Downstroke Project
+
+* Overview
+
+A /scene/ in Downstroke is the immutable record that answers the question "what should the engine simulate and draw right now?". It bundles the entity list, an optional tilemap (for TMX-backed levels), an optional tileset (for sprite-only games that still want tile-sheet rendering), a camera, a background clear color, and a choice of which physics pipeline to run. Each frame the engine reads the current scene from the ~game~ struct, runs an engine-update pass on its entities, lets your ~update:~ hook produce a /new/ scene, snaps the camera to a tagged target if asked, and then hands the new scene to the renderer.
+
+Everything about scenes is built around swapping one immutable value for another: constructors like ~make-scene~ and ~make-sprite-scene~ build them; accessors like ~scene-entities~ and ~scene-camera~ read them; functional transformers like ~scene-map-entities~ and ~scene-add-entity~ return new scenes; and ~game-scene-set!~ is the single point where the engine sees the new state. If you also want more than one "mode" — a title screen and a play screen, say, or a paused state and a gameplay state — Downstroke layers a lightweight /game state/ system on top, so each state gets its own update and render hooks while sharing the same scene or building its own on demand.
+
+* The minimum you need
+
+The smallest scene is a sprite-only one with a single entity, built inside the ~create:~ hook:
+
+#+begin_src scheme
+(import scheme
+ (chicken base)
+ (only (list-utils alist) plist->alist)
+ downstroke-engine
+ downstroke-world
+ downstroke-entity
+ downstroke-scene-loader)
+
+(define (make-player)
+ (plist->alist
+ (list #:type 'player #:x 100 #:y 100
+ #:width 32 #:height 32
+ #:color '(100 160 255))))
+
+(game-run!
+ (make-game
+ title: "Minimal Scene"
+ width: 320 height: 240
+ create: (lambda (game)
+ (game-scene-set! game
+ (make-sprite-scene
+ entities: (list (make-player))
+ background: '(20 22 30))))))
+#+end_src
+
+That is all that is required to put a blue square on a dark background. ~make-sprite-scene~ fills in sensible defaults for everything you have not specified — no tilemap, a camera at ~(0, 0)~, no camera target, the default (physics) engine update — and ~game-scene-set!~ installs the result on the game. From here on, every section in this document describes a field you can fill in or a function you can compose to reshape what is on screen.
+
+* Core concepts
+
+** The scene record
+
+~scene~ is a record type defined with ~defstruct~ in ~downstroke-world~. Its fields map one-to-one onto the things the engine needs to know about the current frame:
+
+| Field | Type | Purpose |
+|-------------------+--------------------------------+--------------------------------------------------------------------------------------------------|
+| ~entities~ | list of entity alists | Every entity the engine simulates and draws this frame. |
+| ~tilemap~ | ~tilemap~ struct or ~#f~ | Optional TMX-loaded tilemap (layers + embedded tileset). ~#f~ for sprite-only scenes. |
+| ~tileset~ | ~tileset~ struct or ~#f~ | Optional tileset used for sprite rect math when ~tilemap~ is ~#f~. |
+| ~camera~ | ~camera~ struct | The viewport offset (see /Cameras/ below). |
+| ~tileset-texture~ | SDL2 texture or ~#f~ | GPU texture used to draw tiles and any entity with a ~#:tile-id~. ~#f~ is allowed (see note). |
+| ~camera-target~ | symbol or ~#f~ | A tag the engine will auto-follow with the camera. ~#f~ disables auto-follow. |
+| ~background~ | ~#f~ or ~(r g b)~ / ~(r g b a)~ | Framebuffer clear color. ~#f~ means plain black. |
+| ~engine-update~ | ~#f~, procedure, or ~'none~ | Per-scene physics pipeline choice (see below). |
+
+The raw ~make-scene~ constructor is auto-generated by ~defstruct~ and accepts every field as a keyword argument. It does /not/ provide defaults — if you call ~make-scene~ directly you must pass every field, or wrap it with ~make-sprite-scene~ (below) which does the defaulting for you. The paired functional copier ~update-scene~ takes an existing scene and returns a new one with only the named fields replaced:
+
+#+begin_src scheme
+(update-scene scene
+ entities: (list new-player)
+ camera-target: 'player)
+#+end_src
+
+*** What each "optional" field actually means
+
+- ~tilemap: #f~ — sprite-only scenes. No tilemap rendering, no tile collisions (the physics pipeline tile-collision steps no-op when there is no tilemap). Used by all the shmup, topdown, tween, scaling, and sandbox demos when they want full control over layout.
+- ~tileset: #f~ — fine when the scene has a ~tilemap~ (the renderer falls back to ~(tilemap-tileset tilemap)~ for rect math) /or/ when no entity has a ~#:tile-id~. Set it explicitly only when you have sprites but no tilemap and still want tile-sheet rendering (the ~animation~ and ~sandbox~ demos do this).
+- ~tileset-texture: #f~ — allowed. The renderer's ~draw-entity~ guards on the texture being present and falls back to ~#:color~ rendering when it is missing, so sprite entities are /not/ silently dropped on ~#f~ — they draw as solid colored rectangles. Tilemap layers are simply skipped if either ~tilemap~ or ~tileset-texture~ is ~#f~.
+- ~background: #f~ — the renderer clears the framebuffer with opaque black every frame. Any non-~#f~ value must be a list of at least three 0–255 integers; a four-element list includes alpha.
+- ~engine-update:~ — three forms are accepted:
+ - ~#f~ (the default) means "inherit": the engine runs ~default-engine-update~, which is the built-in physics pipeline (tweens → acceleration → gravity → velocity → tile collisions → ground detection → entity collisions → group sync → animation).
+ - A procedure ~(lambda (game dt) ...)~ replaces the pipeline entirely for this scene. You are fully responsible for whatever transformations of ~(game-scene game)~ should happen each frame.
+ - The symbol ~'none~ disables engine-update altogether — nothing runs before your ~update:~ hook. Used by the shmup and scaling demos which hand-roll all motion.
+- ~camera-target:~ — a symbol that will be looked up via ~scene-find-tagged~ in each entity's ~#:tags~ list. When set, the engine centers the camera on that entity on every frame and clamps the result to ~x ≥ 0~ and ~y ≥ 0~. ~#f~ disables auto-follow (you can still move ~scene-camera~ yourself).
+
+All eight fields are plain slots: read them with ~scene-entities~, ~scene-tilemap~, ~scene-camera~, ~scene-camera-target~, ~scene-background~, ~scene-engine-update~, and so on.
+
+** Constructing a scene
+
+Downstroke offers three escalating ways to build a scene, from full manual control to "load a whole level off disk in one call".
+
+*** ~make-scene~ — full control
+
+The auto-generated constructor accepts every slot as a keyword and does no defaulting. You will use it when you want to build a scene by hand with some exotic combination of fields, most often because you are bypassing the physics pipeline or embedding a procedurally built tilemap:
+
+#+begin_src scheme
+(make-scene
+ entities: (list (make-player))
+ tilemap: #f
+ tileset: #f
+ camera: (make-camera x: 0 y: 0)
+ tileset-texture: #f
+ camera-target: #f
+ engine-update: 'none)
+#+end_src
+
+This is the form the ~shmup~ and ~scaling~ demos use to turn off the physics pipeline and drive everything from their own ~update:~ hook. If you omit a field it will be unbound on the struct; prefer ~make-sprite-scene~ below unless you actually need that degree of control.
+
+*** ~make-sprite-scene~ — convenient sprite-only scenes
+
+~make-sprite-scene~ lives in ~downstroke-scene-loader~ and wraps ~make-scene~ with sensible defaults for sprite-only games:
+
+#+begin_src scheme
+(make-sprite-scene
+ entities: (list (make-player))
+ background: '(20 22 30))
+#+end_src
+
+It always passes ~tilemap: #f~, and supplies these defaults for anything you leave out:
+
+| Keyword | Default |
+|--------------------+-----------------------------|
+| ~entities:~ | ~'()~ |
+| ~tileset:~ | ~#f~ |
+| ~tileset-texture:~ | ~#f~ |
+| ~camera:~ | ~(make-camera x: 0 y: 0)~ |
+| ~camera-target:~ | ~#f~ |
+| ~background:~ | ~#f~ |
+| ~engine-update:~ | ~#f~ (inherit = run physics) |
+
+Use this whenever your game has no TMX map — see ~demo/getting-started.scm~ for the canonical example.
+
+*** ~game-load-scene!~ — load a TMX level
+
+When you have a Tiled (TMX) map on disk, ~game-load-scene!~ wires up the entire pipeline in one call:
+
+#+begin_src scheme
+(game-load-scene! game "demo/assets/level-0.tmx")
+#+end_src
+
+Internally it performs four steps:
+
+1. Calls ~game-load-tilemap!~ which invokes ~load-tilemap~ to parse the TMX (via expat) and the linked TSX tileset, also loading the tileset's PNG image. The resulting ~tilemap~ struct is stored in the game's asset registry under the key ~'tilemap~.
+2. Calls ~create-tileset-texture~ (a thin wrapper around ~create-texture-from-tileset~) to upload the tileset image as an SDL2 texture via the game's renderer.
+3. Builds a ~scene~ with ~entities: '()~, the parsed ~tilemap~, the fresh tileset texture, a camera at the origin, and no camera target.
+4. Installs the scene on the game with ~game-scene-set!~ and returns it.
+
+Because it returns the new scene, you can chain additional transformations before the frame begins (see the ~topdown~ and ~platformer~ demos):
+
+#+begin_src scheme
+(let* ((s0 (game-load-scene! game "demo/assets/level-0.tmx"))
+ (s1 (scene-add-entity s0 (make-player)))
+ (s2 (update-scene s1 camera-target: 'player)))
+ (game-scene-set! game s2))
+#+end_src
+
+Note that ~game-load-scene!~ does /not/ automatically populate ~entities~ from the TMX object layer. That conversion is available as a separate function:
+
+#+begin_src scheme
+(tilemap-objects->entities tilemap registry)
+#+end_src
+
+~tilemap-objects->entities~ walks the parsed ~tilemap-objects~ and, for each TMX ~<object>~ whose ~type~ string matches a prefab in your registry, calls ~instantiate-prefab~ with the object's ~(x, y, width, height)~. Objects with no matching prefab are filtered out. You can call it yourself to build the initial entity list from the TMX objects and then feed the result to ~scene-add-entity~ or a fresh ~update-scene~.
+
+*** Related asset loaders
+
+Three smaller helpers register assets into ~(game-assets game)~ keyed by a symbol, so other code can fetch them with ~game-asset~:
+
+- ~(game-load-tilemap! game key filename)~ — parse a ~.tmx~ and store the ~tilemap~ struct.
+- ~(game-load-tileset! game key filename)~ — parse a ~.tsx~ and store the ~tileset~ struct.
+- ~(game-load-font! game key filename size)~ — open a TTF font at a given point size and store it.
+
+Each returns the loaded value so you can bind it directly:
+
+#+begin_src scheme
+(let* ((ts (game-load-tileset! game 'tileset "demo/assets/monochrome_transparent.tsx"))
+ (tex (create-texture-from-tileset (game-renderer game) ts)))
+ ...)
+#+end_src
+
+This pattern — load once in ~preload:~ (or early in ~create:~), retrieve many times via ~game-asset~ — is how the ~animation~ and ~sandbox~ demos share one tileset across multiple prefabs and scenes.
+
+** Manipulating scene entities
+
+All scene transformers in ~downstroke-world~ are /functional/: they take a scene and return a new one. Nothing is mutated; the idiomatic pattern inside an ~update:~ hook is always
+
+#+begin_src scheme
+(game-scene-set! game (<transformer> (game-scene game) ...))
+#+end_src
+
+or, when composing multiple transformations, a ~chain~ form from ~srfi-197~.
+
+*** ~scene-add-entity~
+
+~(scene-add-entity scene entity)~ appends one entity to the entity list. Use it after ~game-load-scene!~ to drop the player into a freshly loaded TMX level:
+
+#+begin_src scheme
+(chain (game-load-scene! game "demo/assets/level-0.tmx")
+ (scene-add-entity _ (make-player))
+ (update-scene _ camera-target: 'player))
+#+end_src
+
+*** ~scene-map-entities~
+
+~(scene-map-entities scene proc ...)~ applies each ~proc~ in sequence to every entity in the scene. Each ~proc~ has the signature ~(lambda (scene entity) ...) → entity~ — it receives the /current/ scene (read-only, snapshot at the start of the call) and one entity, and must return a replacement entity. Multiple procs are applied like successive ~map~ passes, in argument order.
+
+This is the workhorse of the physics pipeline; see ~default-engine-update~ in ~downstroke-engine~ for how it is chained to apply acceleration, gravity, velocity, and so on. In game code you use it for per-entity updates that do not need to see each other:
+
+#+begin_src scheme
+(scene-map-entities scene
+ (lambda (scene_ e)
+ (if (eq? (entity-type e) 'demo-bot)
+ (update-demo-bot e dt)
+ e)))
+#+end_src
+
+*** ~scene-filter-entities~
+
+~(scene-filter-entities scene pred)~ keeps only entities for which ~pred~ returns truthy. Use it to remove dead/expired/off-screen entities each frame:
+
+#+begin_src scheme
+(scene-filter-entities scene
+ (lambda (e) (or (eq? (entity-type e) 'player) (in-bounds? e))))
+#+end_src
+
+*** ~scene-transform-entities~
+
+~(scene-transform-entities scene proc)~ hands /the whole entity list/ to ~proc~ and expects a replacement list back. Use this when an operation needs to see all entities at once — resolving pairwise entity collisions, for instance, or synchronising grouped entities to their origin:
+
+#+begin_src scheme
+(scene-transform-entities scene resolve-entity-collisions)
+(scene-transform-entities scene sync-groups)
+#+end_src
+
+The engine's default pipeline uses ~scene-transform-entities~ for exactly these two steps, because they are inherently N-to-N.
+
+*** Tagged lookups
+
+Two helpers let you find entities by tag without iterating yourself:
+
+- ~(scene-find-tagged scene tag)~ returns the /first/ entity whose ~#:tags~ list contains ~tag~, or ~#f~.
+- ~(scene-find-all-tagged scene tag)~ returns every such entity as a list.
+
+Both are O(n) scans and intended for coarse queries — finding the player, all enemies of a type, the camera target — not for every-frame loops over hundreds of entities. The engine itself uses ~scene-find-tagged~ internally to resolve ~camera-target~.
+
+*** The update idiom
+
+Putting it together, the typical shape of an ~update:~ hook is either a single transformer:
+
+#+begin_src scheme
+update: (lambda (game dt)
+ (let* ((input (game-input game))
+ (scene (game-scene game))
+ (player (update-player (car (scene-entities scene)) input)))
+ (game-scene-set! game
+ (update-scene scene entities: (list player)))))
+#+end_src
+
+or a chain of them, when multiple pipelines compose:
+
+#+begin_src scheme
+(game-scene-set! game
+ (chain (update-scene scene entities: all)
+ (scene-map-entities _
+ (lambda (scene_ e) (if (eq? (entity-type e) 'player) e (move-projectile e))))
+ (scene-remove-dead _)
+ (scene-filter-entities _ in-bounds?)))
+#+end_src
+
+In both shapes, ~game-scene-set!~ is called exactly once per frame with the final scene. That single write is what the next frame's engine-update and renderer read from.
+
+** Cameras
+
+A /camera/ is a tiny record with two integer slots, ~x~ and ~y~, holding the top-left pixel coordinate of the viewport in world space. ~make-camera~ is the constructor:
+
+#+begin_src scheme
+(make-camera x: 0 y: 0)
+#+end_src
+
+The renderer subtracts ~camera-x~ and ~camera-y~ from every sprite and tile's world position before drawing, so moving the camera east visually scrolls everything west.
+
+*** ~camera-follow~
+
+~(camera-follow camera entity viewport-w viewport-h)~ returns a /new/ camera struct centered on the entity, clamped so neither ~x~ nor ~y~ goes below zero. It floors both results to integers (SDL2 wants integer rects). You can call it directly from an ~update:~ hook — for example to manually follow an entity without tagging it — but the engine provides an auto-follow mechanism built on top of it.
+
+*** Auto-follow via ~camera-target:~
+
+If a scene has ~camera-target:~ set to a symbol, the engine runs ~update-camera-follow~ /after/ your ~update:~ hook and /before/ rendering. That function looks up the first entity whose ~#:tags~ list contains the target symbol via ~scene-find-tagged~. If found, it replaces the scene's camera with one centered on that entity (using the game's ~width~ and ~height~ as the viewport size); if no tagged entity is found, the camera is left alone. The clamp to ~x ≥ 0~ and ~y ≥ 0~ is inherited from ~camera-follow~, so the camera never reveals negative world coordinates.
+
+To opt in, just tag the entity you want followed and set ~camera-target:~ to that tag:
+
+#+begin_src scheme
+(define (make-player)
+ (plist->alist
+ (list #:type 'player
+ #:x 100 #:y 50
+ #:tags '(player)
+ ...)))
+
+(update-scene scene camera-target: 'player)
+#+end_src
+
+Subsequent frames will keep the camera locked to the player. To stop following, set ~camera-target:~ back to ~#f~ (you keep whatever camera position was most recent).
+
+If you need to animate the camera yourself (shake, parallax, cutscenes), leave ~camera-target:~ at ~#f~ and edit ~scene-camera~ via ~update-scene~ from inside your own update. The engine will not override what you write so long as no target is set.
+
+** Named game states
+
+Many games have more than one "mode": a title menu, a playing state, a game-over screen, maybe a pause overlay. Downstroke's game-state system is a lightweight way to switch update and render hooks between modes without having to build a single mega-update.
+
+A game state is just an alist of lifecycle hooks, built with ~make-game-state~:
+
+#+begin_src scheme
+(make-game-state #:create main-menu-create
+ #:update main-menu-update
+ #:render main-menu-render)
+#+end_src
+
+Each of ~#:create~, ~#:update~, and ~#:render~ is optional (~#f~ by default). The engine uses them as follows:
+
+- ~#:create~ is called by ~game-start-state!~ when the state becomes active. Use it to lazily build the scene for that state (e.g. build the level the first time the player picks "Play").
+- ~#:update~, if present, /replaces/ the game's top-level ~update-hook~ while this state is active. The engine's ~resolve-hooks~ prefers the state hook and falls back to the game-level hook when the state has no ~#:update~.
+- ~#:render~ is handled the same way: state-level wins when present, otherwise the game-level ~render-hook~ runs.
+
+Registration is done by name with ~game-add-state!~:
+
+#+begin_src scheme
+(game-add-state! game 'main-menu
+ (make-game-state #:update main-menu-update
+ #:render main-menu-render))
+(game-add-state! game 'playing
+ (make-game-state #:update playing-update
+ #:render playing-render))
+#+end_src
+
+And transitions happen through ~game-start-state!~, which sets the active state and runs the new state's ~#:create~ hook if present:
+
+#+begin_src scheme
+(game-start-state! game 'main-menu)
+...
+(when (input-pressed? input 'a)
+ (game-start-state! game 'playing))
+#+end_src
+
+Only ~update~ and ~render~ are state-scoped; ~preload~ and ~create~ at the ~make-game~ level still fire once each at boot. The engine's built-in engine-update (the physics pipeline) still runs whenever a scene exists, regardless of which state is active — states only swap which user hook drives the frame.
+
+* Common patterns
+
+** Sprite-only scene with no tilemap
+
+The default choice for small demos and menus: one ~make-sprite-scene~ call, no tilemap, a plain background color.
+
+*Getting started demo* — run with ~bin/demo-getting-started~, source in ~demo/getting-started.scm~. A blue square you push around the screen with the arrow keys; canonical minimal sprite scene.
+
+*Tweens demo* — run with ~bin/demo-tweens~, source in ~demo/tweens.scm~. A grid of easing curves applied to moving boxes; also sprite-only.
+
+** TMX-loaded scene with a player prefab
+
+Load the level off disk, append the player, set up camera follow. One chain per create hook.
+
+*Platformer demo* — run with ~bin/demo-platformer~, source in ~demo/platformer.scm~. Uses ~game-load-scene!~ for the TMX map and then ~scene-add-entity~ and ~update-scene~ to attach the player and enable camera follow.
+
+*Topdown demo* — run with ~bin/demo-topdown~, source in ~demo/topdown.scm~. Same pattern, with ~#:gravity? #f~ on the player so the default engine-update gives four-direction motion.
+
+** Camera following a tagged entity
+
+Tag the entity you want the viewport to track, set ~camera-target:~ to the tag, let the engine take care of the rest:
+
+#+begin_src scheme
+(define (make-player)
+ (plist->alist
+ (list #:type 'player
+ #:x 100 #:y 50
+ #:width 16 #:height 16
+ #:tags '(player)
+ ...)))
+
+;; In create:
+(chain (game-load-scene! game "demo/assets/level-0.tmx")
+ (scene-add-entity _ (make-player))
+ (update-scene _ camera-target: 'player)
+ (game-scene-set! game _))
+#+end_src
+
+Any entity carrying the right tag will be followed, and the camera clamps to non-negative world coordinates every frame. The ~platformer~ and ~topdown~ demos both use ~camera-target: 'player~ exactly this way.
+
+** Switching between a title screen and a play state
+
+Register two named states in ~create:~, each with its own update and render hooks, and kick off the first one. Transitions happen anywhere you have a ~game~ handle, including from inside another state's update.
+
+*Menu demo* — run with ~bin/demo-menu~, source in ~demo/menu.scm~. Registers ~main-menu~ and ~playing~ states, switches to ~playing~ when the player selects the menu entry, and switches back on ~Escape~.
+
+#+begin_src scheme
+create: (lambda (game)
+ (game-add-state! game 'main-menu
+ (make-game-state #:update main-menu-update
+ #:render main-menu-render))
+ (game-add-state! game 'playing
+ (make-game-state #:update playing-update
+ #:render playing-render))
+ (game-start-state! game 'main-menu))
+#+end_src
+
+Each state owns its render surface — in the menu demo both states clear the screen themselves, since there is no scene installed on the game — but nothing stops a state from sharing a scene: the menu state's ~#:create~ could build it, and the play state's ~#:update~ could simply read and transform ~(game-scene game)~.
+
+** Loading multiple assets up front
+
+The ~preload:~ hook is the one place guaranteed to run before the first scene is built, after SDL2 has opened a renderer. Use it to warm up the asset registry so ~create:~ and ~update:~ never block on I/O:
+
+#+begin_src scheme
+preload: (lambda (game)
+ (game-load-tileset! game 'tiles "demo/assets/monochrome_transparent.tsx")
+ (game-load-font! game 'title "demo/assets/DejaVuSans.ttf" 24)
+ (game-load-font! game 'body "demo/assets/DejaVuSans.ttf" 14)
+ (init-audio!)
+ (load-sounds! '((jump . "demo/assets/jump.wav")
+ (coin . "demo/assets/coin.wav"))))
+#+end_src
+
+Inside ~create:~ or ~update:~ you read them back with ~game-asset~:
+
+#+begin_src scheme
+(let ((ts (game-asset game 'tiles))
+ (fnt (game-asset game 'title)))
+ ...)
+#+end_src
+
+Because the registry is a plain hash table, you can store anything — parsed JSON, precomputed tables, your own structs — under any symbol key you like. The three ~game-load-*!~ helpers are there for the file formats the engine itself understands; everything else is ~game-asset-set!~ + ~game-asset~.
+
+* See also
+
+- [[file:guide.org][guide.org]] — step-by-step construction of the getting-started demo, including the first ~make-sprite-scene~ call.
+- [[file:entities.org][entities.org]] — the entity alist, conventional keys like ~#:tags~ and ~#:type~, and how prefabs build entities that the scene-loader instantiates.
+- [[file:physics.org][physics.org]] — what the default engine-update actually does, and how to skip or replace individual pipeline steps per entity.
+- [[file:rendering.org][rendering.org]] — how ~render-scene!~ walks the scene to draw tiles, sprites, and fallback colored rects.
+- [[file:animation.org][animation.org]] — the animation pipeline step, run by ~default-engine-update~ on every entity.
+- [[file:tweens.org][tweens.org]] — per-entity declarative tweens, stepped as the first stage of the default pipeline.
+- [[file:input.org][input.org]] — action mapping and polling, used inside ~update:~ hooks to drive entity changes.
+- [[file:audio.org][audio.org]] — loading and playing sound effects alongside your scene-loading hooks.