Kernel
======

At the very core of Chickadee, in the (chickadee) module, lies an event loop.  This loop, or ``kernel'', is responsible for creating and managing the game window, dispatching input events, ensuring that the game is updated at the desired interval, and rendering graphics. The kernel implements what is known as a ``fixed timestep'' game loop, meaning that the game simulation will be advanced by a fixed interval of time and will never vary from frame to frame, unlike some other styles of game loops.  The appropriately named run-game and abort-game procedures are the entry and exit points to the Chickadee kernel.

On its own, the kernel does not do very much at all.  In order to actually respond to input events, update game state, or draw something to the game window, a hacker with a penchant for game development must latch onto extension points built into the kernel, called ``hooks'', and specify what action ought to be taken for any given event.  For example, the key-press-hook can be used to respond to the a key being pressed by swinging the player's mighty sword. There are many hooks available, so read on to learn about all of them. For information about using Guile's hook API, see Hooks (GNU Guile Reference Manual).

 -- Scheme Procedure: run-game [#:window-title "Chickadee!"]
     [#:window-width 640] [#:window-height 480] [#:window-fullscreen? #f]
     [#:update-hz 60]

     Start the event loop.  This procedure will not return until abort-game is called.

     The update-hook will be run UPDATE-HZ times per second.

     A new graphical window will be opened with WINDOW-WIDTH x WINDOW-HEIGHT as its dimensions, WINDOW-TITLE as its title, and in fullscreen mode if WINDOW-FULLSCREEN? is #t.

 -- Scheme Procedure: abort-game
     Stop the currently running Chickadee event loop.

 -- Scheme Procedure: time
     Return the current game time in milliseconds.

 -- Scheme Variable: load-hook
     A hook that is run once when the event loop boots, before any other hook is run.  This hook is run with zero arguments.

          (add-hook! load-hook (lambda () (display "hello!\n")))

 -- Scheme Variable: update-hook
     A hook that is run every time the game simulation should be advanced. This hook is run with a single argument DT, the fixed timestep that was configured when the event loop was started, in milliseconds.

          (add-hook! update-hook (lambda (dt) (display "tick!\n")))

 -- Scheme Variable: before-draw-hook
     A hook that is run before a frame is rendered.  This hook is run with zero arguments.

          (add-hook! before-draw-hook (lambda () (display "about to draw!\n")))

 -- Scheme Variable: after-draw-hook
     A hook that is run after a frame is rendered.  This hook is run with zero arguments.

          (add-hook! after-draw-hook (lambda () (display "done drawing!\n")))

     Combined with before-draw-hook, one can perform a frames per second calculation to monitor game performance and stability.

 -- Scheme Variable: draw-hook
     A hook that is run each time a frame should be rendered.  This hook is run with a single argument ALPHA, a value in the range [0, 1] which represents how much time has past since the last game state update relative to the upcoming game state update, as a percentage. Because the game state is updated independent of rendering, it is often the case that rendering is occuring between two updates.  If the game is rendered as it was during the last update, a strange side-effect will occur that makes animation appear rough or ``choppy''.  To counter this, the ALPHA value can be used to perfrom a linear interpolation of a moving object between its current position and its previous position.  This odd trick has the pleasing result of making the animation look smooth again, but requires keeping track of previous state.

          (add-hook! draw-hook (lambda (alpha) (display "<(._.<) \n")))

 -- Scheme Variable: quit-hook
     A hook that is run when the user clicks the close button on the game window.  This hook is run with zero arguments.

          (add-hook! quit-hook (lambda () (display "bye!\n")))

 -- Scheme Variable: key-press-hook
     A hook that is run when a key is pressed on the keyboard.  This hook is run with four arguments:

      1. KEY: The symbolic name of the ``virtual'' key that was pressed. For example: backspace.  It's called a virtual key because the operating system may map a physical keyboard key to another key entirely, such as how the author binds the ``caps lock'' key to mean ``control''.

      2. SCANCODE: The symbolic name of the physical key that was pressed.

      3. MODIFIERS: A list of the symbolic names of modifier keys that were being held down when the key was pressed.  Possible values include ctrl, alt, and shift.

      4. REPEAT?: #t if this is a repeated press of the same key.

          (add-hook! key-press-hook
                     (lambda (key scancode modifiers repeat?)
                       (display "pressed key: ")
                       (display key)
                       (newline)))

 -- Scheme Variable: key-release-hook
     A hook that is run when a key is released on the keyboard.  This hook is run with three arguments:

      1. KEY: The symbolic name of the ``virtual'' key that was released.

      2. SCANCODE: The symbolic name of the physical key that was released.

      3. MODIFIERS: A list of the symbolic names of modifier keys that were being held down when the key was released.

 -- Scheme Variable: text-input-hook
     A hook that is run when printable text is typed on the keyboard.  This hook is run with a single argument, TEXT, a string containing the text that was entered.

 -- Scheme Variable: mouse-press-hook
     A hook that is run when a mouse button is pressed.  This hook is run with four arguments:

      1. BUTTON: The symbolic name of the button that was pressed, such as left, middle, or right.

      2. CLICKS: The number of times the button has been clicked in a row.

      3. X: The x coordinate of the mouse cursor.

      4. Y: The y coordinate of the mouse cursor.

 -- Scheme Variable: mouse-release-hook
     A hook that is run when a mouse button is released.  This hook is run with three arguments:

      1. BUTTON: The symbolic name of the button that was released.

      2. X: The x coordinate of the mouse cursor.

      3. Y: The y coordinate of the mouse cursor.

 -- Scheme Variable: mouse-move-hook
     A hook that is run when the mouse is moved.  This hook is run with five arguments:

      1. X: The x coordinate of the mouse cursor.

      2. Y: The y coordinate of the mouse cursor.

      3. DX: The amount the mouse has moved along the x axis since the last mouse move event.

      4. DY: The amount the mouse has moved along the y axis since the last mouse move event.

      5. BUTTONS: A list of the buttons that were pressed down when the mouse was moved.

 -- Scheme Variable: controller-add-hook
     A hook that is run when a game controller is connected.  This hook is run with a single argument, CONTROLLER, the controller that was connected.

 -- Scheme Variable: controller-remove-hook
     A hook that is run when a game controller is disconnected.  This hook is run with a single argument, CONTROLLER, the controller that was disconnected.

 -- Scheme Variable: controller-press-hook
     A hook that is run when a button on a game controller is pressed. This hook is run with two arguments:

      1. CONTROLLER: The controller that triggered the event.

      2. BUTTON: The symbolic name of the button that was pressed. Possible buttons are:

            * a

            * b

            * x

            * y

            * back

            * guide

            * start

            * left-stick

            * right-stick

            * left-shoulder

            * right-shoulder

            * dpad-up

            * dpad-down

            * dpad-left

            * dpad-right

 -- Scheme Variable: controller-release-hook
     A hook that is run when a button on a game controller is released.

     This hook is run with two arguments:

      1. CONTROLLER: The controller that triggered the event.

      2. BUTTON: The symbolic name of the button that was released.

 -- Scheme Variable: controller-move-hook
     A hook that is run when an analog stick or trigger on a game controller is moved.  This hook is run with three arguments

      1. CONTROLLER: The controller that triggered the event.

      2. AXIS: The symbolic name of the axis that was moved.  Possible values are:

            * left-x

            * left-y

            * right-x

            * right-y

            * trigger-left

            * trigger-right

Input
=====

Chickadee can handle input events from the keyboard, mouse, and game controllers.

Keyboard
--------

 -- Scheme Procedure: key-pressed? KEY
     Return #t if KEY is currently being pressed.

 -- Scheme Procedure: key-released? KEY
     Return #t if KEY is not currently being pressed.

Math
====

Chickadee contains data types and procedures for performing the most common computations in video game simulations such as linear algebra with vectors and matrices and axis-aligned bounding box collision detection.

Basics
------

 -- Variable: Scheme Variable pi
     An essential constant for all trigonometry.  π is the ratio of a circle's circumferences to its diameter.  Since π is an irrational number, the PI in Chickadee is a mere floating point approximation that is ``good enough.''

 -- Variable: Scheme Variable pi/2
     Half of PI.

Vectors
-------

Matrices
--------

Rectangles
----------

Easings
-------

 -- Scheme Procedure: linear T

 -- Scheme Procedure: smoothstep T

 -- Scheme Procedure: ease-in-quad T

 -- Scheme Procedure: ease-out-quad T

 -- Scheme Procedure: ease-in-out-quad T

 -- Scheme Procedure: ease-in-cubic T

 -- Scheme Procedure: ease-out-cubic T

 -- Scheme Procedure: ease-in-out-cubic T

 -- Scheme Procedure: ease-in-quart T

 -- Scheme Procedure: ease-out-quart T

 -- Scheme Procedure: ease-in-out-quart T

 -- Scheme Procedure: ease-in-quint T

 -- Scheme Procedure: ease-out-quint T

 -- Scheme Procedure: ease-in-out-quint T

 -- Scheme Procedure: ease-in-sine T

 -- Scheme Procedure: ease-out-sine T

 -- Scheme Procedure: ease-in-out-sine T

Graphics
========

Chickadee aims to make hardware-accelerated graphics rendering as simple and efficient as possible by providing high-level APIs that interact with the low-level OpenGL API under the hood.  Anyone that has worked with OpenGL directly knows that it has a steep learning curve and a lot of effort is needed to render even a single triangle. The Chickadee rendering engine attempts to make it easy to do common tasks like rendering a sprite while also providing all of the building blocks to implement additional rendering techniques.

Rendering Engine
----------------

Chickadee defines rendering using a metaphor familiar to Scheme programmers: procedure application.  A shader (see Shaders) is like a procedure for the GPU to apply.  Shaders are passed arguments: A vertex array containing the geometry to render (see Vertex Arrays) and zero or more keyword arguments that the shader understands.  Similar to how Scheme has apply for calling procedures, Chickadee provides gpu-apply for calling shaders.

Additionally, there is some dynamic state that effects how gpu-apply will behave.  Things like the current viewport, framebuffer, and blend mode are stored as dynamic state because it would be tedious to have to have to specify them each time gpu-apply is called.

The following procedures and syntax can be found in the (chickadee render) module.

 -- Scheme Syntax: gpu-apply SHADER VERTEX-ARRAY
     [#:uniform-key UNIFORM-VALUE ...]
 -- Scheme Syntax: gpu-apply* SHADER VERTEX-ARRAY
     COUNT [#:uniform-key UNIFORM-VALUE ...]

     Render VERTEX-ARRAY using SHADER with the uniform values specified in the following keyword arguments.

     While gpu-apply will draw every vertex in VERTEX-ARRAY, gpu-apply* will only draw COUNT vertices.

 -- Scheme Procedure: current-viewport
     Return the currently bound viewport.  See Viewports for more details about using viewports.

 -- Scheme Procedure: current-framebuffer
     Return the currently bound framebuffer.  See Framebuffers for more details about using framebuffers.

 -- Scheme Procedure: current-blend-mode
     Return the currently bound blend mode.  See Blending and Depth Testing for more details about using blend modes.

 -- Scheme Procedure: current-depth-test
     Return #t if depth testing is currently enabled. See Blending and Depth Testing for more details about using the depth test.

 -- Scheme Procedure: current-texture
     Return the currently bound texture.  See Textures for more details about using textures.

 -- Scheme Procedure: current-projection
     Return the currently bound projection matrix.  See Matrices for more details about matrices.

 -- Scheme Syntax: with-viewport VIEWPORT BODY ...
     Evaluate BODY with the current viewport bound to VIEWPORT.

 -- Scheme Syntax: with-framebuffer FRAMEBUFFER BODY ...
     Evaluate BODY with the current framebuffer bound to FRAMEBUFFER.

 -- Scheme Syntax: with-blend-mode BLEND-MODE BODY ...
     Evaluate BODY with the current blend mode bound to BLEND-MODE.

 -- Scheme Syntax: with-depth-test DEPTH-TEST? BODY ...
     Evaluate BODY with the depth-test disabled if DEPTH-TEST? is #f, or enabled otherwise.

 -- Scheme Syntax: with-texture TEXTURE BODY ...
     Evaluate BODY with the current texture bound to TEXTURE.

 -- Scheme Syntax: with-projection PROJECTION BODY ...
     Evaluate BODY with the current projection matrix bound to PROJECTION.

Textures
--------

 -- Scheme Procedure: load-image FILE [#:min-filter nearest]
     [#:mag-filter nearest] [#:wrap-s repeat] [#:wrap-t repeat]

     Load the image data from FILE and return a new texture object.

     MIN-FILTER and MAG-FILTER describe the method that should be used for minification and magnification when rendering, respectively.  Possible values are nearest and linear.

     WRAP-S and WRAP-T describe how to interpret texture coordinates that are greater than 1.0.  Possible values are repeat, clamp, clamp-to-border, and clamp-to-edge.

Sprites
-------

For those who are new to this game, a sprite is a 2D rectangular bitmap that is rendered to the screen.  For 2D games, sprites are the most essential graphical abstraction.  They are used for drawing maps, players, NPCs, items, particles, text, etc.  In Chickadee, bitmaps are stored in textures (see Textures) and can be used to draw sprites via the draw-sprite procedure.

 -- Scheme Procedure: draw-sprite TEXTURE REGION
     [#:scale] [#:rotation] [#:blend-mode alpha] [#:texture-region]
     [#:shader]

It's not uncommon to need to draw hundreds or thousands of sprites each frame.  However, GPUs (graphics processing units) are tricky beasts that prefer to be sent few, large chunks of data to render rather than many, small chunks.  Using draw-sprite on its own will involve at least one GPU call per sprite, which will quickly lead to poor performance.  To deal with this, a technique known as ``sprite batching'' can be used.  Instead of drawing each sprite immediately, the sprite batch will build up a large of buffer of sprites to draw and defer rendering until the last possible moment. Batching isn't a panacea, though.  Batching only works if the sprites being drawn share as much in common as possible.  Every time you draw a sprite with a different texture or blend mode, the batch will be sent off to the GPU.  Therefore, batching is most useful if you minimize such changes.  A good strategy for reducing texture changes is to stuff many bitmaps into a single image file and create a ``texture atlas'' (see Textures) to access the sub-images within.

Taking advantage of sprite batching in Chickadee is easy, just wrap the code that is calling draw-sprite a lot in the with-batched-sprites form.

 -- Scheme Syntax: with-batched-sprites BODY ...
     Use batched rendering for all draw-sprite calls within BODY.

With a basic sprite abstraction in place, it's possible to build other abstractions on top of it.  One such example is the ``nine patch''.  A nine patch is a sprite that can be rendered at various sizes without becoming distorted.  This is achieved by diving up the sprite into nine regions:

  * the center, which can be scaled horizontally and vertically

  * the four corners, which can never be scaled

  * the left and right sides, which can be scaled vertically

  * the top and bottom sides, which can be scaled horizontally

The one caveat is that the bitmap regions must be designed in such a way so that they are not distorted when stretched along the affected axes.  For example, that means that the top and bottom sides could have varying colored pixels vertically, but not horizontally.

The most common application of this technique is for graphical user interface widgets like buttons and dialog boxes.  By using a nine patch, they can be rendered at any size without unappealing scaling artifacts.

 -- Scheme Procedure: draw-nine-patch TEXTURE REGION
     [#:margin 0] [#:top-margin margin] [#:bottom-margin margin]
     [#:left-margin margin] [#:right-margin margin]
     [#:texture-region] [#:scale] [#:rotation] [#:blend-mode alpha]
     [#:shader]

     Draw a nine patch sprite.  A nine patch sprite renders TEXTURE as a WIDTH x HEIGHT rectangle whose stretchable areas are defined by the given margin measurements TOP-MARGIN, BOTTOM-MARGIN, LEFT-MARGIN, and RIGHT-MARGIN. The MARGIN argument may be used to configure all four margins at once.

     Refer to draw-sprite (see Sprites) for information about the other arguments.

Lines and Shapes
----------------

Sprites are fun, but sometimes simple, untextured lines and polygons are desired.  That's where the (chickadee render shapes) module comes in!

 -- Scheme Procedure: draw-line START END
     [#:thickness 0.5] [#:feather 1.0] [#:cap round] [#:color]
     [#:shader]

     Draw a line segment from START to END. The line will be THICKNESS pixels thick with an antialiased border FEATHER pixels wide.  The line will be colored COLOR. CAP specifies the type of end cap that should be used to terminate the lines, either none, butt, square, round, triangle-in, or triangle-out.  Advanced users may use the SHADER argument to override the built-in line segment shader.

Fonts
-----

Unlike the traditional TrueType font format that many are accustomed to, Chickadee loads and renders bitmap fonts in the Angel Code format (http://www.angelcode.com/products/bmfont/doc/file_format.html).  But why use this seemingly obscure format?  It's easy to find TTFs but not easy to find FNTs (the canonical file extension used for Angel Code fonts) and bitmap fonts don't scale well.  The reason is efficiency.

If all of the glyphs of a font are pre-rendered and packed into an image file then it becomes possible to use a texture atlas (see Textures) and a sprite batch (see Sprites) when rendering, which is a more efficient way to render fonts than using, say, SDL_ttf (https://www.libsdl.org/projects/SDL_ttf/) or other solutions that involve using the FreeType library directly.

Now what about scaling?  In libraries that use TTF fonts, one must choose the size that the glyphs will be rasterized at up front.  To use n sizes of the same font, one must load n variants of that font.  If the size of the text is dynamic, some kind of texture scaling algorithm must be used and the text will inevitably look blurry.  At first glance, using bitmap fonts seem to have an even worse issue.  Instead of just loading the same font n times at different sizes, one would need to generate n image files for each font size needed.  This is where the ``signed distance field'' rendering technique comes in.  Introduced by Valve (http://www.valvesoftware.com/.../2007/SIGGRAPH2007_AlphaTestedMagnification.pdf) in 2007, signed distance field fonts can be efficiently stored in a bitmap and be rendered at arbitrary scale factors with good results.  Chickadee can render both traditional bitmap fonts and signed distance field fonts.  Signed distance field font rendering is not yet available, so be patient.

While Chickadee does not yet offer a tool for converting TTF fonts into FNT fonts, tools such as Hiero (https://github.com/libgdx/libgdx/wiki/Hiero) may be used in the meantime.

The following procedures can be found in the (chickadee render font) module.

 -- Scheme Procedure: load-font FILE
     Load the Angel Code formatted XML document in FILE and return a new font object.

 -- Scheme Procedure: font? OBJ
     Return #t if OBJ is a font object.

 -- Scheme Procedure: font-face FONT
     Return the name of FONT.

 -- Scheme Procedure: font-line-height FONT
     Return the line height of FONT.

 -- Scheme Procedure: font-line-height FONT
     Return the line height of FONT.

 -- Scheme Procedure: font-bold? FONT
     Return #t if FONT is a bold font.

 -- Scheme Procedure: font-italic? FONT
     Return #t if FONT is an italicized font.

 -- Scheme Procedure: draw-text FONT TEXT POSITION
     [#:scale] [#:rotation] [#:blend-mode]

     Draw the string TEXT with the first character starting at POSITION using FONT.

          (draw-text font "Hello, world!" (vec2 128.0 128.0))

     Refer to draw-sprite (see Sprites) for information about the other arguments.

Blending and Depth Testing
--------------------------

Vertex Arrays
-------------

Shaders
-------

Shaders are programs for the GPU to evaluate.  They are written in the OpenGL Shading Language, or GLSL.  Chickadee does not currently provide a Scheme-like domain specific language for writing shaders. Since shaders must be written in GLSL and not Scheme, they are considered an advanced feature.

Framebuffers
------------

Viewports
---------

Audio
=====

Chickadee has two data types for audio: samples and music.  Samples are for short sound effects like explosions.  Music is for, well, uh..., music.

Supported file formats include WAV and OGG.

 -- Scheme Procedure: load-sample FILE
     Load audio sample from FILE.

 -- Scheme Procedure: set-sample-volume! VOLUME
     Set the volume that all samples are played at to VOLUME, an integer value between 0 and 128.

 -- Scheme Procedure: play-sample SAMPLE
     Play SAMPLE.  Pretty straightforward!

 -- Scheme Procedure: load-music FILE
     Load music from FILE.

 -- Scheme Procedure: music-volume
     Return the volume level for music, an integer value between 0 and 128.

 -- Scheme Procedure: set-music-volume! VOLUME
     Set the volume that music is played at to VOLUME, an integer value between 0 and 128.

 -- Scheme Procedure: play-music MUSIC [LOOP?]
     Play MUSIC.  If LOOP?, play it over and over and over and over and...

 -- Scheme Procedure: pause-music
     Pause the current music track.

 -- Scheme Procedure: resume-music
     Resume the current music track.

 -- Scheme Procedure: rewind-music
     estart the current music track from the beginning.

 -- Scheme Procedure: stop-music
     Stop playing the current music track.

 -- Scheme Procedure: music-playing?
     Return #t if music is currently playing.

 -- Scheme Procedure: music-paused?
     Return #t if music is currently paused.

Buffers
=======

Games are big state machines and they can get complex quickly, thus we need tools to manage that complexity.  One useful technique is to separate the many ``screens'' of a game (such as the main menu, player select screen, high score table, game over screen, etc.) from each other, so that it is possible to program the logic for one section of the game independent of another.  In Chickadee, these ``screens'' are called ``buffers''.  In other game engines this same construct may be called a ``scene'' or ``room''.

A buffer knows how to update its state, render, handle input events, etc.  Only one buffer is ever active at a time, and the current buffer can be changed with procedures like push-buffer, replace-buffer, and pop-buffer.

Buffers are implemented using Guile's object oriented programming system (see GOOPS (GNU Guile Reference Manual)).  Each type of buffer is represented as a subclass of the <buffer> class.  Once a new buffer class has been created, there are many methods that can be specialized for that class.

To install buffer support and set the initial buffer, call the use-buffers! procedure.

Let's take a look at a simple example to see how it all comes together:

     (use-modules (chickadee)
                  (chickadee buffer)
                  (chickadee math vector)
                  (chickadee render sprite)
                  (chickadee render texture)
                  (oop goops))

     ;; Define a new buffer type.
     (define-class <splash-screen> (<buffer>)
       (chickadee #:accessor chickadee #:init-value #f))

     ;; Define the logic for when the buffer is first activated.
     (define-method (start (splash <splash-screen>))
       (set! (chickadee splash) (load-image "images/chickadee.png")))

     ;; Define how the buffer should be rendered.
     (define-method (draw (splash <splash-screen>) alpha)
       (draw-sprite (chickadee splash) (vec2 256.0 176.0)))

     ;; Hook into the game engine and make the splash screen the initial buffer.
     (use-buffers! (make <splash-screen>))

     ;; Start the game!
     (run-game)

 -- Class: <buffer>
     The parent class for all buffers.

 -- Method: started? BUFFER
     Return #t if BUFFER has been activated by the game engine.

 -- Method: start BUFFER
     Called when BUFFER becomes the current buffer for the first time.

 -- Method: stop BUFFER
     Called when BUFFER is no longer in use.

 -- Method: pause BUFFER
     Called when BUFFER is no longer the current buffer but still in use.

 -- Method: resume BUFFER
     Called when BUFFER becomes the current buffer after having been suspended.

The following methods are simply wrappers around the hooks described in see Kernel, so see that section for more detail about the arguments to these methods.  The engine calls these methods with the current buffer as the first argument.

 -- Method: update BUFFER DT
     Advance the simulation running in BUFFER by DT units of time.

 -- Method: abort BUFFER
     Called when the user tries to close the game window.

 -- Method: before-draw BUFFER
     Called before BUFFER is rendered.

 -- Method: after-draw BUFFER
     Called after BUFFER is rendered.

 -- Method: draw BUFFER ALPHA
     Render BUFFER.

 -- Method: key-press BUFFER KEY SCANCODE MODIFIERS REPEAT?
     Handle key press event.

 -- Method: key-release BUFFER KEY SCANCODE MODIFIERS
     Handle key release event.

 -- Method: text-input BUFFER TEXT
     Handle text input event.

 -- Method: mouse-press BUFFER BUTTON CLICKS X Y
     Handle mouse press event.

 -- Method: mouse-release BUFFER BUTTON X Y
     Handle mouse release event.

 -- Method: mouse-move BUFFER X Y X-REL Y-REL BUTTONS
     Handle mouse move event.

 -- Method: controller-add BUFFER CONTROLLER
     Handle controller add event.

 -- Method: controller-remove BUFFER CONTROLLER
     Handle controller remove event.

 -- Method: controller-press BUFFER CONTROLLER BUTTON
     Handle controller press event.

 -- Method: controller-release BUFFER CONTROLLER BUTTON
     Handle controller release event.

 -- Method: controller-move BUFFER CONTROLLER AXIS VALUE
     Handle controller move event.

The following procedures are used to manage the buffer stack:

 -- Procedure: use-buffers! INITIAL-BUFFER
     Install buffers into the game engine and set the current buffer to INITIAL-BUFFER.

 -- Procedure: push-buffer! BUFFER
     Pause the current buffer and switch to BUFFER.

 -- Procedure: pop-buffer!
     Stop the current buffer and switch back to the previously active buffer, or terminate the game loop if the buffer stack is empty.

 -- Procedure: replace-buffer! BUFFER
     Stop the current buffer and switch to BUFFER

 -- Procedure: current-buffer
     Return the current buffer.

Scripting
=========

Game logic is a web of asynchronous events that are carefully coordinated to bring the game world to life.  In order to make an enemy follow and attack the player, or move an NPC back and forth in front of the item shop, or do both at the same time, a scripting system is a necessity.  Chickadee comes with an asynchronous programming system in the (chickadee scripting) module. Lightweight, cooperative threads known as ``coroutines'' allow the programmer to write asynchronous code as if it were synchronous, and allow many such ``threads'' to run concurrently.

But before we dig deeper into coroutines, let's discuss the simple act of scheduling tasks.

Agendas
-------

To schedule a task to be performed later, an ``agenda'' is used. There is a default, global agenda that is ready to be used, or additional agendas may be created for different purposes.  The following example prints the text ``hello'' when the agenda has advanced to time unit 10.

     (at 10 (display "hello\n"))

Most of the time it is more convenient to schedule tasks relative to the current time.  This is where after comes in handy:

     (after 10 (display "hello\n"))

Time units in the agenda are in no way connected to real time.  It's up to the programmer to decide what agenda time means.  A simple and effective approach is to map each call of the update hook (see Kernel) to 1 unit of agenda time, like so:

     (add-hook! update-hook (lambda (dt) (update-agenda 1)))

It is important to call update-agenda periodically, otherwise no tasks will ever be run!

In addition to using the global agenda, it is useful to have multiple agendas for different purposes.  For example, the game world can use a different agenda than the user interface, so that pausing the game is a simple matter of not updating the world's agenda while continuing to update the user interface's agenda.  The current agenda is dynamically scoped and can be changed using the with-agenda special form:

     (define game-world-agenda (make-agenda))

     (with-agenda game-world-agenda
       (at 60 (spawn-goblin))
       (at 120 (spawn-goblin))
       (at 240 (spawn-goblin-king)))

 -- Scheme Procedure: make-agenda
     Return a new task scheduler.

 -- Scheme Procedure: agenda? OBJ
     Return #t if OBJ is an agenda.

 -- Scheme Procedure: current-agenda
 -- Scheme Procedure: current-agenda AGENDA
     When called with no arguments, return the current agenda.  When called with one argument, set the current agenda to AGENDA.

 -- Scheme Syntax: with-agenda AGENDA BODY ...
     Evaluate BODY with the current agenda set to AGENDA.

 -- Scheme Procedure: agenda-time
     Return the current agenda time.

 -- Scheme Procedure: update-agenda DT
     Advance the current agenda by DT.

 -- Scheme Procedure: schedule-at TIME THUNK
     Schedule THUNK, a procedure of zero arguments, to be run at TIME.

 -- Scheme Procedure: schedule-after DELAY THUNK
     Schedule THUNK, a procedure of zero arguments, to be run after DELAY.

 -- Scheme Procedure: schedule-every INTERVAL THUNK [N]
     Schedule THUNK, a procedure of zero arguments, to be run every INTERVAL amount of time.  Repeat this N times, or indefinitely if not specified.

 -- Scheme Syntax: at TIME BODY ...
     Schedule BODY to be evaluated at TIME.

 -- Scheme Syntax: after DELAY BODY ...
     Schedule BODY to be evaluated after DELAY.

 -- Scheme Syntax: every INTERVAL BODY ...
 -- Scheme Syntax: every (INTERVAL N) BODY ...
     Schedule BODY to be evaluated every INTERVAL amount of time.  Repeat this N times, or indefinitely if not specified.

Coroutines
----------

Now that we can schedule tasks, let's take things to the next level. It sure would be great if we could make procedures that described a series of actions that happened over time, especially if we could do so without contorting our code into a nest of callback procedures. This is where coroutines come in.  With coroutines we can write code in a linear way, in a manner that appears to be synchronous, but with the ability to suspend periodically in order to let other coroutines have a turn and prevent blocking the game loop.  Building on top of the scheduling that agendas provide, here is a coroutine that models a child trying to get their mother's attention:

     (coroutine
       (while #t
         (display "mom!")
         (newline)
         (wait 60))) ; where 60 = 1 second of real time

This code runs in an endless loop, but the wait procedure suspends the coroutine and schedules it to be run later by the agenda. So, after each iteration of the loop, control is returned back to the game loop and the program is not stuck spinning in a loop that will never exit.  Pretty neat, eh?

Coroutines can suspend to any capable handler, not just the agenda. The yield procedure will suspend the current coroutine and pass its ``continuation'' to a handler procedure.  This handler procedure could do anything.  Perhaps the handler stashes the continuation somewhere where it will be resumed when the user presses a specific key on the keyboard, or maybe it will be resumed when the player picks up an item off of the dungeon floor; the sky is the limit.

Sometimes it is necessary to abruptly terminate a coroutine after it has been started.  For example, when an enemy is defeated their AI routine needs to be shut down.  When a coroutine is spawned, a handle to that coroutine is returned that can be used to cancel it when desired.

     (define co (coroutine (while #t (display "hey\n") (wait 60))))
     ;; sometime later
     (cancel-coroutine co)

 -- Scheme Procedure: spawn-coroutine THUNK
     Apply THUNK as a coroutine and return a handle to it.

 -- Scheme Syntax: coroutine BODY ...
     Evaluate BODY as a coroutine and return a handle to it.

 -- Scheme Procedure: coroutine? OBJ
     Return #t if OBJ is a coroutine handle.

 -- Scheme Procedure: coroutine-cancelled? OBJ
     Return #t if OBJ has been cancelled.

 -- Scheme Procedure: coroutine-running? OBJ
     Return #t if OBJ has not yet terminated or been cancelled.

 -- Scheme Procedure: coroutine-complete? OBJ
     Return #t if OBJ has terminated.

 -- Scheme Procedure: cancel-coroutine CO
     Prevent further execution of the coroutine CO.

 -- Scheme Procedure: yield HANDLER
     Suspend the current coroutine and pass its continuation to the procedure HANDLER.

 -- Scheme Procedure: wait DURATION
     Wait DURATION before resuming the current coroutine.

 -- Scheme Procedure: channel-get CHANNEL
     Wait for a message from CHANNEL.

 -- Scheme Syntax: forever BODY ...
     Evaluate BODY in an endless loop.

Tweening
--------

Tweening is the process of transitioning something from an initial state to a final state over a pre-determined period of time.  In other words, tweening is a way to create animation.  The tween procedure can be used within any coroutine like so:

     (define x 0)
     (coroutine
       ;; 0 to 100 in 60 ticks of the agenda.
       (tween 60 0 100 (lambda (y) (set! x y))))

 -- Scheme Procedure: tween DURATION START END PROC [#:step 1 #:ease smoothstep #:interpolate lerp]
     Transition a value from START to END over DURATION, sending each succesive value to PROC.  STEP controls the amount of time between each update of the animation.

     To control how the animation goes from the initial to final state, an ``easing'' procedure may be specified.  By default, the smoothstep easing is used, which is a more pleasing default than a simplistic linear function.  See Easings for a complete list of available easing procedures.

     The INTERPOLATE procedure computes the values in between START and END.  By default, linear interpolation (``lerp'' for short) is used.

Channels
--------

Channels are a tool for communicating amongst different coroutines. One coroutine can write a value to the channel and another can read from it.  Reading or writing to a channel suspends that coroutine until there is someone on the other end of the line to complete the transaction.

Here's a simplistic example:

     (define c (make-channel))

     (coroutine
      (forever
       (let ((item (channel-get c)))
         (pk 'got item))))

     (coroutine
      (channel-put c 'sword)
      (channel-put c 'shield)
      (channel-put c 'potion))

 -- Scheme Procedure: make-channel
     Return a new channel

 -- Scheme Procedure: channel? OBJ
     Return #t if OBJ is a channel.

 -- Scheme Procedure: channel-get CHANNEL
     Retrieve a value from CHANNEL.  The current coroutine suspends until a value is available.

 -- Scheme Procedure: channel-put CHANNEL DATA
     Send DATA to CHANNEL.  The current coroutine suspends until another coroutine is available to retrieve the value.