diff --git a/README.md b/README.md index 48a1e8a..e947f4d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,16 @@ game: * If you receive a map with custom doodads, you can **install** the doodads into your copy of the game and use them in your own maps. +# Developer Documentation + +In case you are reading this from the game's open source repository, take a +look in the `docs/` folder or the `*.md` files in the root of the repository. + +Some to start with: + +* [Building](Building.md) the game (tl;dr. run bootstrap.py) +* [Tour of the Code](docs/Tour%20of%20the%20Code.md) + # Keybindings Global Keybindings: @@ -134,52 +144,6 @@ four directions but characters with gravity only move left and right and have the dedicated "Jump" button. This differs from regular keyboard controls where the Up arrow is to Jump. -# Built-In Doodads - -A brief introduction to the built-in doodads available so far: - -- **Characters** - - Blue Azulian: this is used as the player character for now. If - dragged into a level, it doesn't do anything but is affected - by gravity. - - Red Azulian: an example mobile mob for now. It walks back and - forth, changing directions only when it comes across an - obstacle and can't proceed any further. -- **Doors and Keys** - - Colored Doors: these act like solid barriers until the player or - another doodad collects the matching colored key. - - Colored Keys: these are collectable items that allow the player or - another doodad to open the door of matching color. Note that - inventories are doodad-specific, so other mobs besides the - player can steal a key before the player gets it! (For now) - - Electric Door: this mechanical door stays closed and only - opens when it receives a power signal from a linked button. - - Trapdoor: this door allows one-way access, but once it's closed - behind you, you can't pass through it from that side! -- **Buttons** - - Button: while pressed by a player or other doodad, the button - emits a power signal to any doodad it is linked to. Link a button - to an electric door, and the door will open while the button is - pressed and close when the button is released. - - Sticky Button: this button will stay pressed once activated and - will not release unless it receives a power signal from another - linked doodad. For example, one Button that links to a Sticky - Button will release the sticky button if pressed. -- **Switches** - - Switch: when touched by the player or other doodad, the switch will - toggle its state from "OFF" to "ON" or vice versa. Link it to an - Electric Door to open/close the door. Link switches _to each other_ as - well as to a door, and all switches will stay in sync with their ON/OFF - state when any switch is pressed. -- **Crumbly Floor** - - This rocky floor will break and fall away after being stepped on. -- **Two State Blocks** - - Blue and orange blocks that will toggle between solid and pass-thru - whenever the corresponding ON/OFF block is hit. -- **Start and Exit Flags** - - The "Go" flag lets you pick a spawn point for the player character. - - The "Exit" flag marks the level goal. - # Developer Console Press `Enter` at any time to open the developer console. The console diff --git a/docs/Tour of the Code.md b/docs/Tour of the Code.md new file mode 100644 index 0000000..23b927b --- /dev/null +++ b/docs/Tour of the Code.md @@ -0,0 +1,57 @@ +# Tour of the Code + +Here is a brief tour of where to find important parts of the game's code. + +## Basic App Structure + +**cmd/doodle/main.go** is the main function for Sketchy Maze and handles the command line flags and bootstrapping the game. + +**pkg/doodle.go** holds the root Doodle (`d`) struct, the base object for the meat of the program - its setup funcs and MainLoop are here. + +### Scenes + +The gameplay is divided into scenes, each in a **pkg/\*_scene.go** file: + +* MainScene is the title screen (default) +* PlayScene is play mode +* EditScene is the level editor, etc. + +**pkg/scene.go** defines the interface for Scene itself and the `d.Goto(scene)` function. + +The game changes scenes with Goto and the MainLoop calls the active scene's functions (Loop and Draw). + +## Doodad JavaScript API + +### pkg/scripting + +This package provides the generic JavaScript API used by the developer console and doodads. + +**pkg/scripting/js_api.go** defines global and generic JavaScript functions to doodads' scope, e.g.: + +* console.log and friends +* Sound.Play +* RGBA, Point, and Vector +* Flash, GetTick +* time.Now() and friends +* Events +* setTimeout, setInterval.. +* Self: provided externally. + +### pkg/uix/scripting.go + +The home of **MakeScriptAPI()** which installs various Play Mode objects into the script's environment, e.g. + +* Actors and Level API +* Self: a surface of uix.Actor capabilities that point to the local actor. + +## Collision Detection + +### pkg/collision + +The algorithmic stuff is here - the collision package focused on just the math and general types to handle logic for: + +* Collision with level geometry ('pixel perfect') +* Collision between actors +* BoundingRect and collision boxes + +### pkg/uix/actor_collision.go \ No newline at end of file diff --git a/pkg/uix/scripting.go b/pkg/uix/scripting.go index ffab91b..435ab55 100644 --- a/pkg/uix/scripting.go +++ b/pkg/uix/scripting.go @@ -93,60 +93,10 @@ func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} { "Filename": actor.Doodad().Filename, "Title": actor.Doodad().Title, - // functions - "ID": actor.ID, - "Size": actor.Size, - "GetTag": actor.Doodad().Tag, - "Options": actor.Options, - "GetOption": func(name string) interface{} { - opt := actor.GetOption(name) - if opt == nil { - return nil - } - return opt.Value - }, - "Position": actor.Position, - "MoveTo": func(p render.Point) { - actor.MoveTo(p) - actor.SetGrounded(false) - }, - "MoveBy": func(p render.Point) { - actor.MoveBy(p) - actor.SetGrounded(false) - }, - "IsOnScreen": actor.IsOnScreen, - // // TODO: passing this to actor.IsOnScreen didn't work? - // return actor.Position().Inside(actor.Canvas.ViewportRelative()) - // }, - "Grounded": actor.Grounded, - "SetHitbox": actor.SetHitbox, - "Hitbox": actor.Hitbox, - "SetVelocity": actor.SetVelocity, - "GetVelocity": actor.Velocity, - "SetMobile": actor.SetMobile, - "SetInventory": actor.SetInventory, - "HasInventory": actor.HasInventory, - "SetGravity": actor.SetGravity, - "Invulnerable": actor.Invulnerable, - "SetInvulnerable": actor.SetInvulnerable, - "AddAnimation": actor.AddAnimation, - "IsAnimating": actor.IsAnimating, - "IsPlayer": actor.IsPlayer, - "PlayAnimation": actor.PlayAnimation, - "StopAnimation": actor.StopAnimation, - "ShowLayer": actor.ShowLayer, - "ShowLayerNamed": actor.ShowLayerNamed, - "Inventory": actor.Inventory, - "AddItem": actor.AddItem, - "RemoveItem": actor.RemoveItem, - "HasItem": actor.HasItem, - "ClearInventory": actor.ClearInventory, - "Destroy": actor.Destroy, - "Freeze": actor.Freeze, - "Unfreeze": actor.Unfreeze, - "IsWet": actor.IsWet, - "Hide": actor.Hide, - "Show": actor.Show, + /* + Unique options to the Self API over regular Actors. + */ + "GetLinks": func() []map[string]interface{} { var result = []map[string]interface{}{} for _, linked := range w.GetLinkedActors(actor) { @@ -160,5 +110,80 @@ func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} { // Update the doodad that the camera should focus on. w.FollowActor = actor.ID() }, + + // functions + + "GetTag": actor.Doodad().Tag, + + /* + Expose the uix.Actor API surface area, in the same order + that these functions are defined in their respective files + to keep it easy to maintain. + */ + + // actor.go + "ID": actor.ID, + "Doodad": actor.Doodad, + "SetGravity": actor.SetGravity, + "SetMobile": actor.SetMobile, + "SetInventory": actor.SetInventory, + "IsMobile": actor.IsMobile, + "IsPlayer": actor.IsPlayer, + "HasInventory": actor.HasInventory, + "HasGravity": actor.HasGravity, + "Invulnerable": actor.Invulnerable, + "SetInvulnerable": actor.SetInvulnerable, + "IsWet": actor.IsWet, + "IsOnScreen": actor.IsOnScreen, + "SetWet": actor.SetWet, + "Size": actor.Size, + "Velocity": actor.Velocity, + "GetVelocity": actor.Velocity, // TODO: deprecated, old Self.GetVelocity inconsistency + "SetVelocity": actor.SetVelocity, + "Position": actor.Position, + "MoveTo": func(p render.Point) { + actor.MoveTo(p) + actor.SetGrounded(false) + }, + "MoveBy": func(p render.Point) { + actor.MoveBy(p) + actor.SetGrounded(false) + }, + "Grounded": actor.Grounded, + "SetGrounded": actor.SetGrounded, + "IsCoyoteTime": actor.IsCoyoteTime, + "Hide": actor.Hide, + "Show": actor.Show, + "Freeze": actor.Freeze, + "Unfreeze": actor.Unfreeze, + "IsFrozen": actor.IsFrozen, + // Not: SetUsing, SetNoclip + "AddItem": actor.AddItem, + "RemoveItem": actor.RemoveItem, + "ClearInventory": actor.ClearInventory, + "HasItem": actor.HasItem, + "ListItems": actor.ListItems, + "Inventory": actor.Inventory, + "GetBoundingRect": actor.GetBoundingRect, + "SetHitbox": actor.SetHitbox, + "Hitbox": actor.Hitbox, + "Options": actor.Options, + "GetOption": func(name string) interface{} { + opt := actor.GetOption(name) + if opt == nil { + return nil + } + return opt.Value + }, + "LayerCount": actor.LayerCount, + "ShowLayer": actor.ShowLayer, + "ShowLayerNamed": actor.ShowLayerNamed, + "Destroy": actor.Destroy, + + // actor_animation.go + "AddAnimation": actor.AddAnimation, + "PlayAnimation": actor.PlayAnimation, + "IsAnimating": actor.IsAnimating, + "StopAnimation": actor.StopAnimation, } }