Add Hugo website and rewrite docs to match current engine

New Hugo site in website/ with prosperon.dev theme (blue/gold/castle
aesthetic), docs sidebar navigation, and content pages. Rewrote all
doc files to align with the actual codebase: compositor+film2d
rendering, use() modules (no global prosperon object), Pit language,
script+JSON entity model. Added entities.md, front matter to all
70+ API docs, and updated API index for current module architecture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 18:09:55 -06:00
parent 1619122a58
commit 83b798e365
106 changed files with 2749 additions and 492 deletions

View File

@@ -1,3 +1,7 @@
---
draft: true
---
This was a good camera system for third person games. Recreate it eventually in cell.
// void ThirdPersonFollow::_ready() {

View File

@@ -1,3 +1,8 @@
---
title: "actor"
type: docs
---
# actor
### toString() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "console"
type: docs
---
# console
The console object provides various logging, debugging, and output methods.

View File

@@ -1,5 +1,62 @@
# Appendix B - api
---
title: "API Reference"
type: docs
---
This is a complete list of accessible functions and parameters that are built into Prosperon. For the most part, developers will concern themselves with the modules, all of which can be imported with `use`.
# API Reference
Types document particular javascript objects with a specific object in their prototype chain, which can allow access to an underlying C data structure. A lot of these are used only internally by Prosperon, but brave developers can pick around in the module internals to see how they're used and do their own thing if they want!
Prosperon's API is a set of modules imported with `use()`. There is no global engine object — import only what you need.
## Game-Facing Modules
These are the modules you use to build games:
| Module | Import | Purpose |
|--------|--------|---------|
| **core** | `use('core')` | Main loop, window, GPU initialization |
| **sprite** | `use('sprite')` | Create and manage sprites |
| **compositor** | `use('compositor')` | Build render plans from scene configs |
| **camera** | `use('camera')` | Viewport into the world |
| **input** | `use('input')` | Action mapping, device routing, control stacks |
| **sound** | `use('sound')` | Audio playback and mixing |
| **world** | `use('world')` | Entity management |
| **text2d** | `use('text2d')` | Text rendering |
| **shape2d** | `use('shape2d')` | SDF-rendered shapes (rect, circle, ellipse, pill) |
| **tilemap2d** | `use('tilemap2d')` | Grid-based tile rendering |
| **line2d** | `use('line2d')` | Line rendering |
| **particles2d** | `use('particles2d')` | Particle emitters |
| **tween** | `use('tween')` | Value interpolation over time |
| **ease** | `use('ease')` | Easing functions |
| **color** | `use('color')` | Color utilities |
| **resources** | `use('resources')` | Asset path resolution |
| **action** | `use('action')` | Action definitions |
| **draw2d** | `use('draw2d')` | Immediate-mode 2D drawing |
| **clay** | `use('clay')` | UI layout |
| **point** | `use('point')` | Point/vector utilities |
## Actor Primitives
These are built into the Pit language runtime and available in all `.ce` programs:
| Primitive | Purpose |
|-----------|---------|
| `$start(callback, path)` | Spawn a child actor from a program |
| `$receiver(callback)` | Register a message handler |
| `send(target, message)` | Send a message to an actor |
| `$delay(callback, seconds)` | Schedule a one-shot callback |
| `$clock(callback, interval)` | Schedule a repeating callback |
| `$stop()` | Terminate the current actor |
## Globals
| Name | Purpose |
|------|---------|
| `use(path)` | Import a module |
| `log.console(msg)` | Print to console |
| `meme(proto, data)` | Create object with prototype and overrides |
## Internal Modules
These are used by the engine internally and not intended for direct game use:
`film2d`, `effects`, `sdl_gpu`, `graphics`, `rasterize`, `fx_graph`, `debug_imgui`, `device`, `input/backends/*`, `input/bindings`, `input/devices`, `input/router`

View File

@@ -1,3 +1,8 @@
---
title: "actor"
type: docs
---
# actor

View File

@@ -1,3 +1,8 @@
---
title: "camera"
type: docs
---
# camera
### list() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "cmd"
type: docs
---
# cmd
### length <sub>number</sub>

View File

@@ -1,3 +1,8 @@
---
title: "color"
type: docs
---
# color
### Color <sub>object</sub>

View File

@@ -1,3 +1,8 @@
---
title: "debug"
type: docs
---
# debug
### stack_depth() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "dmon"
type: docs
---
# dmon
### watch() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "doc"
type: docs
---
# doc

View File

@@ -1,3 +1,8 @@
---
title: "draw2d"
type: docs
---
# draw2d

View File

@@ -1,3 +1,8 @@
---
title: "enet"
type: docs
---
# enet
### initialize() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "event"
type: docs
---
# event
### push_event(event) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "geometry"
type: docs
---
# geometry

View File

@@ -1,3 +1,8 @@
---
title: "graphics"
type: docs
---
# graphics

View File

@@ -1,3 +1,8 @@
---
title: "imgui"
type: docs
---
# imgui
### windowpos() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "input"
type: docs
---
# input
### mouse_show(show) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "io"
type: docs
---
# io
### rm(path) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "js"
type: docs
---
# js

View File

@@ -1,3 +1,8 @@
---
title: "json"
type: docs
---
# json
### encode(val,space,replacer,whitelist) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "loop"
type: docs
---
# loop
### step() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "math"
type: docs
---
# math
### dot() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "miniz"
type: docs
---
# miniz
### read(data) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "nota"
type: docs
---
# nota
### encode(value) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "os"
type: docs
---
# os
### make_transform() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "packer"
type: docs
---
# packer
### getAllFiles(dir) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "render"
type: docs
---
# render
### _main <sub>object</sub>

View File

@@ -1,3 +1,8 @@
---
title: "resources"
type: docs
---
# resources
### scripts <sub>object</sub>

View File

@@ -1,3 +1,8 @@
---
title: "sound"
type: docs
---
# sound
### undefined <sub>string</sub>

View File

@@ -1,3 +1,8 @@
---
title: "spline"
type: docs
---
# spline
### catmull() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "time"
type: docs
---
# time
The main time object, handling date/time utilities in earth-seconds.

View File

@@ -1,3 +1,8 @@
---
title: "tween"
type: docs
---
# tween
### Tween <sub>object</sub>

View File

@@ -1,3 +1,8 @@
---
title: "util"
type: docs
---
# util

View File

@@ -1,3 +1,8 @@
---
title: "video"
type: docs
---
# video
### make_video() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "prosperon"
type: docs
---
# prosperon
### c_types <sub>object</sub>

View File

@@ -1,3 +1,8 @@
---
title: "PHYSFS_File"
type: docs
---
# PHYSFS_File
A file handle opened via PhysFS for writing or reading. Freed automatically when references go away.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_Camera"
type: docs
---
# SDL_Camera
A handle to a physical camera device. Freed when references drop or camera is closed.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_Cursor"
type: docs
---
# SDL_Cursor
An SDL cursor handle. Freed automatically on GC. No direct methods.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUBuffer"
type: docs
---
# SDL_GPUBuffer
### name() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUCommandBuffer"
type: docs
---
# SDL_GPUCommandBuffer
A command buffer that accumulates rendering, copy, and compute operations. Freed after submission or GC.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUComputePass"
type: docs
---
# SDL_GPUComputePass
A compute pass for dispatching compute pipelines. Freed after end() or GC.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUComputePipeline"
type: docs
---
# SDL_GPUComputePipeline
Encapsulates a compute shader program plus associated resource layouts.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUCopyPass"
type: docs
---
# SDL_GPUCopyPass
A pass for CPU<->GPU or GPU<->GPU copy operations. No direct JS API besides internal usage.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUDevice"
type: docs
---
# SDL_GPUDevice
A handle for low-level GPU operations via SDL GPU. Freed on GC.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUFence"
type: docs
---
# SDL_GPUFence
A GPU fence for synchronization. Created upon commandBuffer.submit().

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUGraphicsPipeline"
type: docs
---
# SDL_GPUGraphicsPipeline
Encapsulates vertex+fragment shaders, blend/cull states, and vertex attribute layouts.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPURenderPass"
type: docs
---
# SDL_GPURenderPass
A single pass of drawing commands with color/depth attachments. Freed after end() or GC.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUSampler"
type: docs
---
# SDL_GPUSampler
Defines how a texture is sampled (filter mode, address mode, anisotropy, compare op, etc.).

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUShader"
type: docs
---
# SDL_GPUShader
A single compiled shader (vertex or fragment) in a GPU-friendly format

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUTexture"
type: docs
---
# SDL_GPUTexture
### name() <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "SDL_GPUTransferBuffer"
type: docs
---
# SDL_GPUTransferBuffer
A staging buffer used for copying data to or from GPU buffers/textures. Typically

View File

@@ -1,3 +1,8 @@
---
title: "SDL_Renderer"
type: docs
---
# SDL_Renderer
A 2D rendering context using the SDL renderer API. Freed automatically.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_Surface"
type: docs
---
# SDL_Surface
A software (CPU) image in memory. Freed when references vanish. Typically converted

View File

@@ -1,3 +1,8 @@
---
title: "SDL_Texture"
type: docs
---
# SDL_Texture
A 2D GPU-accelerated texture for rendering with SDL_Renderer. Freed automatically.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_Thread"
type: docs
---
# SDL_Thread
A handle to an SDL-created thread. Freed on GC after join.

View File

@@ -1,3 +1,8 @@
---
title: "SDL_Window"
type: docs
---
# SDL_Window
An application window, created via prosperon.engine_start or SDL calls. Freed on GC.

View File

@@ -1,3 +1,8 @@
---
title: "datastream"
type: docs
---
# datastream
A streaming media handle, typically for MPEG video. Freed automatically.

View File

@@ -1,3 +1,8 @@
---
title: "enet_host"
type: docs
---
# enet_host
### service(callback, timeout) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "enet_peer"
type: docs
---
# enet_peer
### send(data) <sub>function</sub>

View File

@@ -1,3 +1,8 @@
---
title: "font"
type: docs
---
# font
A bitmap or TTF-based font object storing glyph data.

View File

@@ -1,3 +1,8 @@
---
title: "rtree"
type: docs
---
# rtree
An R-tree for spatial lookups. Insert bounding boxes, query by bounding box, etc.

View File

@@ -1,3 +1,8 @@
---
title: "sprite"
type: docs
---
# sprite
A 'sprite' is a simple struct for 2D drawing. It stores a rectangle (pos + size),

View File

@@ -1,3 +1,8 @@
---
title: "timer"
type: docs
---
# timer
A scheduled callback or countdown. Freed automatically once no longer referenced

View File

@@ -1,3 +1,8 @@
---
title: "transform"
type: docs
---
# transform
A hierarchical transform storing 3D or 2D position, rotation (as a quaternion),

View File

@@ -1,3 +1,8 @@
---
title: "use"
type: docs
---
# use
### length <sub>number</sub>

121
docs/entities.md Normal file
View File

@@ -0,0 +1,121 @@
---
title: "Entities"
type: docs
---
# Entities and the World
Prosperon uses a **script + overrides** model for entities, similar to Source engine entities. An entity type is defined by a script (`.cm` module), and instances are records that override specific attributes.
## Entity Types
An entity type is a module that returns a prototype object:
```javascript
// entities/goblin.cm
var sprite = use('sprite')
return {
health: 100,
speed: 2,
image: "goblin.png",
init: function() {
this.sprite = sprite({
image: this.image,
pos: this.pos,
width: 32,
height: 32,
layer: 5
})
},
on_destroy: function() {
this.sprite.destroy()
}
}
```
The return value defines every valid field with a default. This prototype IS the schema — the editor and tooling can introspect it to know what fields exist and what their defaults are.
## Creating Entities
Use the `world` module to create entity instances:
```javascript
var world = use('world')
var goblin_proto = use('entities/goblin')
var goblin = world.add_entity(goblin_proto, {
pos: {x: 100, y: 200},
health: 50
})
```
The second argument overrides specific fields from the prototype. Unspecified fields keep their defaults.
## Entity Lifecycle
Entities have two lifecycle hooks:
- `init()` — called when the entity is created (after overrides are applied)
- `on_destroy()` — called when the entity is removed from the world
```javascript
world.destroy_entity(goblin)
```
## Levels as Data
A level is a collection of entity instances stored as JSON — each entry is a script path plus attribute overrides:
```json
[
{"script": "entities/goblin", "pos": {"x": 100, "y": 200}, "health": 50},
{"script": "entities/goblin", "pos": {"x": 300, "y": 200}},
{"script": "entities/tree", "pos": {"x": 200, "y": 150}},
{"script": "entities/player_spawn", "pos": {"x": 50, "y": 50}}
]
```
The world loads this file, creates each entity from its prototype with the overrides applied. Since levels are JSON, they're easy to diff in source control and simple to generate from an editor.
## Composition via Modules
Entity behavior comes from composition, not inheritance. A goblin that patrols and has health uses separate modules:
```javascript
// entities/patrol_goblin.cm
var health = use('systems/health')
var patrol = use('systems/patrol')
return {
health: 100,
patrol_speed: 1.5,
patrol_points: [],
init: function() {
health.attach(this)
patrol.attach(this, this.patrol_points)
}
}
```
Each system module provides behavior that operates on the entity's data. No class trees, no diamond inheritance — just modules that read and write fields.
## Querying Entities
The world module lets you find entities:
```javascript
var world = use('world')
// All entities are tracked internally
// Query patterns are still being developed
```
## Override Rules
Overrides replace fields at the top level. If the prototype has `pos: {x: 0, y: 0}` and the override has `pos: {x: 50, y: 100}`, the entire `pos` is replaced. This is predictable and avoids ambiguity about partial nested merges.
Overrides should be pure data — field values only. No expressions, no conditionals, no logic. The script defines behavior; the override only tweaks data.

View File

@@ -1,152 +1,189 @@
# Graphics drawing
---
title: "Graphics"
type: docs
---
## Terminology
Texture - a set of bytes on the GPU, not directly accessible
Surface - a set of bytes in RAM, modifiable
rect - a rectangle of {x,y,width,height}
Image - combination of a texture and rect, where the rect defines the UV coordinates on the texture to draw
# Graphics Concepts
# Drawing, cameras, viewports, logical size, and so on
This page covers the fundamental graphics concepts in Prosperon.
A camera is a view into the game world. A camera can be "rendered", which means it renders the world, and what it can see in the world. A camera may draw to a surface, or to the main window. Objects in the world will render so that if their position is equal to the camera position, that is in the center of the screen. HUD functions always render so [0,0] is the bottom left of the camera's view.
## Textures and Images
Cameras always draw to their own render target. Then, they draw that render target to the framebuffer.
A **texture** is a set of bytes on the GPU — not directly accessible from script code.
# COORDINATES
Screen coordinates start in the upper left corner at [0,0] and extend to the bottom right, in pixels. Raw mouse coordinates are in these.
An **image** is a texture combined with a UV rect that specifies which region to draw:
# RENDERING PIPELINE
```
image = {
texture: GPU texture handle,
rect: {x, y, width, height} // UV coordinates
}
```
In prosperon, you call graphics rendering functions at well defined hook points. These are interleaved as necessary with predefined created objects, like sprites, 3d world models, and so on.
This means multiple images can share one texture (a sprite sheet or atlas), each referencing a different region.
The engine stores a command buffer. When you issue "draw" commands, they are recorded into the command buffer. These are batched as much as they can be; if there is no significant state change between, the draw commands can be coalesced into one. Then, for each camera, the draw commands are executed.
## Supported Image Formats
# RENDERING COMPONENTS
## MATERIALS
A material defines the inputs to a shader.
| Format | Notes |
|--------|-------|
| PNG | Standard, lossless |
| QOI | Fast decode, lossless |
| GIF | Animated frames supported |
| JPG/JPEG | Lossy |
| Aseprite (.ase) | Frames, tags, slices, durations, pivots |
## PIPELINES
Pipelines are how the rendering engine is set up. Switching pipelines can be done for special effects.
Aseprite files are fully parsed — named animation tags, per-frame durations, pivot points, and slice data are all available.
## SPECIAL EFFECTS
Sometimes you want a special effect. While there are many objects in prosperon you can create and have the engine handle for you, a special effect typically requires a bit of code.
## Sprites
# LAYERS
All things that draw have a layer. If no layer is set, the implicit layer is "0". Even draw and hud functions have a layer. To draw a draw function on a specific layer, set that function's "layer". ie,
A sprite is the fundamental drawable. Create one with the `sprite` module:
this.draw = function() { render.rect(); }
this.draw.layer = -5;
```javascript
var sprite = use('sprite')
Now that layer will draw at the -5 layer.
var s = sprite({
image: "player.png",
pos: {x: 100, y: 200},
width: 32,
height: null, // derived from aspect ratio
anchor_x: 0.5, // center horizontally
anchor_y: 0, // bottom edge
layer: 5,
rotation: 0,
flip: {x: false, y: false},
color: {r: 1, g: 1, b: 1, a: 1},
opacity: 1,
tint: {r: 1, g: 1, b: 1, a: 1},
filter: 'nearest',
plane: 'default',
groups: [],
visible: true
})
```
# CAMERAS
Everything is drawn via cameras. Cameras can draw directly to the screen, or they can draw to an offscreen render target. By default, everything is drawn to all cameras. There will eventually be a tag that lets you filter what is drawn to specifc cameras.
### Fit Modes
Cameras have a resolution they draw at, "size".
When both `width` and `height` are specified, the `fit` property controls how the texture maps into the rectangle:
## TEXTURES
| Fit | Behavior |
|-----|----------|
| `none` | Use the texture's native pixel size |
| `fill` | Stretch to exactly fill the rectangle (may distort) |
| `contain` | Fit inside the rectangle, preserving aspect ratio |
| `cover` | Fill the rectangle, preserving aspect ratio (crops via UV) |
Anatomy of rendpering an image render.image(path)
Path can be a file like "toad"
If this is a gif, this would display the entire range of the animation
It can be a frame of animation, like "frog.0"
If it's an aseprite, it can have multiple animations, like "frog.walk.0"
file^ frame^ idx
If only one dimension is given (e.g. `width: 32, height: null`), the other is derived from the texture's aspect ratio.
render.image("frog.walk.0",
game.image("frog.walk.0") ==> retrieve
### Anchors
image = {
texture: "spritesheet.png",
rect: [x,y,w,h],
time: 100
},
The anchor determines which point of the sprite sits at `pos`:
frames: {
toad: {
x: 4,
y: 5,
w: 10,
h: 10
},
frog: {
walk: [
{ texture: spritesheet.png, x: 10, y:10, w:6,h:6, time: 100 },
{ texture: spritesheet.png, x:16,y:10,w:6,h:6,time:100} <--- two frame walk animation
],
},
},
}
texture frog {
texture: {"frog.png"}, <--- this is the actual thing to send to the gpu
x:0,
y:0,
w:10,
h:10
},
- `anchor_x: 0, anchor_y: 0` — top-left at pos
- `anchor_x: 0.5, anchor_y: 0.5` — center at pos
- `anchor_x: 0.5, anchor_y: 0` — bottom-center at pos (good for characters)
### UV Mapping
## RENDER MODES
/* rendering modes
ps1
gouraud
diffuse // 16 bit color, 5-5-5
7 dynamic lights, 1 ambient
textures are affine
no vertex skinning
256x256 texture max (generally 128x128)
320x240, variable up to 640x480
For sprite sheets, control which region of the texture is drawn:
n64
gouraud
diffuse
combiner // a secondary texture sometimes used to combine
7 dynamic lights, 1 ambient
320x240, or 640x480
```javascript
sprite({
image: spritesheet_texture,
uv: {offset: {x: 0.25, y: 0}, scale: {x: 0.25, y: 0.5}}
})
```
sega saturn
gouraud
diffuse
320x240 or 640x480
## Text
ps2
phong
diffuse
combiner // second texture for modulation of diffuse
combine_mode // int for how to combine
Create text with the `text2d` module:
dreamcast
phong
diffuse
combiner // second texture; could be an environment map, or emboss bump mapping
fog
640x480
640x448, special mode to 1280x1024
```javascript
var text2d = use('text2d')
gamecube
phong
diffuse
+7 textures // wow!
8 dynamic lights
640x480
var label = text2d({
text: "Hello World",
font: "myfont.ttf",
size: 24,
pos: {x: 100, y: 100},
layer: 10
})
```
*/
## Shapes
/* meshes
position (float3)
color (rgba)
uv
*/
SDF-rendered shapes via the `shape2d` module:
/* materials, modern pbr
any object can act as a "material". The engine expects some standardized things:
diffuse - base color texture
bump - a normal map for dot3 bump maping used in phong shading
height - a grayscale heightmap
occlusion - ambient occlusion texture
emission - texture for where model emits light
bump2 - a second normal map for detail
metallic - a metal/smoothness map
specular - specular map, alternative for the metallic workflow
*/
```javascript
var shape2d = use('shape2d')
var box = shape2d.rect({
pos: {x: 50, y: 50},
width: 100,
height: 80,
color: {r: 1, g: 0, b: 0, a: 1},
layer: 3
})
var ball = shape2d.circle({
pos: {x: 200, y: 200},
radius: 25,
color: {r: 0, g: 1, b: 0, a: 1}
})
```
Available shapes: `rect`, `circle`, `ellipse`, `pill`.
## Tilemaps
Grid-based tile rendering:
```javascript
var tilemap2d = use('tilemap2d')
var map = tilemap2d({
tiles: tiles_2d_array,
tile_width: 16,
tile_height: 16,
layer: 0
})
```
Tiles can be flipped, and the entire tilemap can have an affine transform applied. Camera culling for large maps is planned.
## Particles
Particle emitters produce sprite-like particles:
```javascript
var particles2d = use('particles2d')
var emitter = particles2d({
texture: "spark.png",
pos: {x: 100, y: 100},
layer: 8
})
```
## Coordinate System
Prosperon uses a Y-up coordinate system:
- **X+** goes right
- **Y+** goes up
- `[0, 0]` in world space is where the camera starts
- `[0, 0]` in HUD space is the bottom-left of the screen
## Camera
A camera defines the viewport into the world. Its `width` and `height` set the game's internal resolution. Its `pos` determines what world coordinate is at the center of the screen.
```javascript
var camera = use('camera')
var cam = camera.make({
pos: {x: 0, y: 0},
width: 320,
height: 240
})
```
Everything in world space is drawn relative to the camera's position. HUD space is always screen-relative.

View File

@@ -1,32 +1,59 @@
# Preface: The Prosperon Vision
---
title: "Prosperon Manual"
type: docs
---
![image](wizard.png)
# Prosperon
Prosperon is based around a Javascript-like language, DullJS, termed henceforth "dull", engineered to be the quickest way to make computer games. "Dull" will be explored in more detail, but for all intents and purposes it **is** Javascript, with a handful of ES6 features removed. We've given it its own name to crush from the outset any idea that what you're dealing with a web styled Javascript environment!
Prosperon is a 2D game programming environment built on [Pit](https://crumbpit.org), an actor-based language designed for safe, sandboxed scripting.
## The Vision
The less lines of code it takes to do something, the less your program does. The less your program does, the faster it is, and the fewer bugs it has. And, the less the developer needs to keep in their head, the less bugs and more understandable a program is.
You write game logic in Pit — a simplified JavaScript with actor primitives — and Prosperon handles rendering, input, audio, and asset management. Games are composed from small, focused modules. No class hierarchies, no giant scene trees.
Prosperon is designed to achieve a minimal number of lines of code when creating a game. Prosperon is designed to be the **best** way to make computer games, quickly.
## How It Works
1. **Flexible API**
A lot of the API usage is informed from 'duck typing'. If something "looks" like a camera - it can be used like one! If something "looks" like a sprite, it can be used like one! This means that if you create an enemy in the game, you may be able to pass it in to render, while in other more strict languages, you might need to create an intermediate "sprite" object.
A Prosperon game is a collection of **modules** (`.cm`) and **programs** (`.ce`).
2. **Uniformity**
Javascript has the brilliant object idea, which is used and abused everywhere in prosperon. Objects allow for a huge variety of ideas to be expressed, and act as an ideal transferring tool between the disparate parts of a computer game. Dull, our dumbed down javascript, uses objects and closures to a greater extent than modern javascript, **without** losing any of the features. There is less syntax to remember, meaning it is quicker to get code down.
- **Modules** return a frozen value — an API, a config, a data table. Import them with `use()`.
- **Programs** are actor entry points. They run top-to-bottom, register message handlers, and spawn children.
3. **Highly reflective**
Built in tools to give an overview of your game. Prosperon can tell you where and how many certain entities are created, how much space they use, and so on. Prosperon can generate an textual overview of your game, resulting in documentation.
The engine provides modules for everything a 2D game needs:
4. **Text based**
Prosperon is totally text based. It uses a script based scene structure, where scripts spawn each other and concatenate onto each other to create objects in a scene in a flexible way. This allows for a first class experience with SCM tools, and allows for easy multi user collaboration.
| Module | Purpose |
|--------|---------|
| `sprite` | Create and register sprites |
| `draw2d` | High-level factories for sprites, text, shapes, tilemaps |
| `camera` | Create cameras with transform and projection |
| `input` | Multi-device action mapping with control stacks |
| `sound` | Audio playback with PCM caching and voice mixing |
| `tween` | Fire-and-forget animation with easing |
| `world` | Entity management and lifecycle |
| `resources` | Asset discovery and path resolution |
| `core` | Main loop, window, frame scheduling |
4. **Gradual Performance**
There are fast paths on nearly everything for well defined objects. For most use cases, the flexible, duck typing works. But in specific circumstances, where speed is required, it's possible.
You create sprites, poke their positions, and the engine handles everything else — batching, sorting, effects, presentation scaling. Like programming sprites on a GBA, but with modern hardware behind it.
5. **Interactive**
Whenever there is a question of feature, Prosperon chooses the interactive path. Games are interactive. That means you want it to be totally dynamic and fast. Prosperon is designed to favor, for example, generated MIDI music, which you might be able to sync to goblin death animations, over static MP3 files.
## Design Principles
## Installation
**Minimal code.** The fewer lines it takes to do something, the fewer bugs it has, the faster it runs, and the less you need to keep in your head.
Just grab the prosperon build for your platform, drop it in a folder, and run it. Prosperon is a tiny executable, so it's recommended to version it with your project.
**Duck typing.** If an object looks like a sprite — has a position, an image, a layer — it works as a sprite. No base classes to inherit from.
**Text-based.** Every file in a Prosperon project is text. Scripts, configs, levels. Git diffs are readable, merges are clean.
**Modules, not globals.** Everything is accessed via `use()`. No global engine object. The world, camera, input — all imported as modules.
**Data-driven rendering.** Your game logic never touches the GPU. You declare drawables and their properties. The rendering pipeline sorts, batches, applies effects, and draws.
## Guides
- [Quickstart](quickstart/) — Run your first program
- [Tutorial](tutorial/) — Build a game step by step
- [Entities](entities/) — The world and entity model
- [Rendering](rendering/) — Sprites, cameras, and the rendering pipeline
- [Graphics](graphics/) — Textures, images, and coordinate systems
- [Input](input/) — Action mapping, devices, and control stacks
- [Resources](resources/) — Asset loading and discovery
## API Reference
- [API Index](api/) — All modules and types

View File

@@ -1,54 +1,148 @@
# Program events and an example input system
---
title: "Input"
type: docs
---
Prosperon provides a handy `input` module. Input is done in a highly generic and customizable manner. *players* can take control of any object (actor or otherwise) in Prosperon, after which it is referred to as a *pawn* of a player. If the object has a defined *input* object, it is a valid pawn. One player can have many pawns, but each pawn may have only one player.
# Input
Pawns are added as a stack, with the newest ones getting priority, and handled first. It is possible for pawns to block input to lower pawns on the stack.
Prosperon's input system normalizes keyboard, mouse, gamepad, and touch into **named actions**. You define what actions your game uses, bind them to physical inputs, and the engine routes events to the right place.
```
*newest*
car <== When a key is pressed, this is the first pawn to handle input
player
ui <== /block/ is set to true here, so editor recieves no input!
editor
*oldest*
```
## Actions
The default player can be obtained with `Player.players[0]`. Players are all local, and the highest number is determined by platform.
An action is a named game event like `"jump"`, `"attack"`, or `"ui_up"`. Multiple physical inputs can trigger the same action:
The **input** object defines a number of keys or actions, with their values being functions.
## Editor input
The editor input style defines keystrokes. It is good for custom editors, or any sort of game that requires many hotkeys. Keystrokes are case sensitive and can be augmented with auxiliary keys.
| symbol | key |
|--------|-------|
| C | ctrl |
| M | alt |
| S | super |
```
```javascript
var input = use('input')
var orc = this.spawn('orc');
orc.inputs = {};
orc.inputs.a = function() { ... };
orc.inputs.A = function() { ... }; /* This is only called with a capital A! */
orc.inputs['C-a'] = function() { ... }; /* Control-a */
input.players[0].control(orc); /* player 0 is now in control of the orc */
input.configure({
action_map: {
jump: ['space', 'gamepad_a'],
attack: ['mouse_button_left', 'gamepad_x'],
move_left: ['a', 'left', 'gamepad_dpleft'],
move_right: ['d', 'right', 'gamepad_dpright']
}
})
```
The input object can be modified to customize how it handles input.
Default actions are provided for common UI navigation (`ui_up`, `ui_down`, `ui_left`, `ui_right`, `confirm`, `cancel`, `menu`).
| property | type | effect |
|----------------|----------|--------------------------------------|
| post | function | called after any input is processed |
| =release_post= | function | called after any input is released |
| fallthru | bool | false if input should stop with this |
| block | bool | true if input should stop with this |
## Players and Users
The input can be modified by setting properties on the associated function.
The input system supports multiple players. Each **user** has:
| property | type | effect |
|----------|----------|--------------------------------------------------------|
| released | function | Called when the input is released |
| rep | bool | true if holding the input should repeatedly trigger it |
| down | function | called while the input is down |
- Paired devices (keyboard, specific gamepad, etc.)
- An action map (bindings can differ per player)
- A **control stack** of possessed entities
```javascript
var p1 = input.player1()
```
### Device Pairing
By default, pairing is `'last_used'` — all input goes to player 1, and the active device switches when a button is pressed. For local multiplayer, use `'explicit'` pairing where each player is assigned specific devices.
```javascript
input.configure({
max_users: 2,
pairing: 'explicit'
})
```
## Control Stack (Possession)
Players **possess** entities to route input to them. The control stack is ordered — the topmost entity gets input first.
```javascript
var p1 = input.player1()
// Take control of the player character
p1.possess(player_entity)
// Push a menu on top — it gets input priority
p1.push(pause_menu)
// Pop the menu — player character gets input again
p1.pop()
```
The possessed entity must have an `on_input` method:
```javascript
var player = {
on_input: function(action, data) {
if (action == 'jump' && data.pressed) {
// jump logic
}
if (action == 'move_right') {
// data.pressed, data.released, data.time
}
}
}
```
## Device Detection
The input system tracks what kind of device is active, so you can show appropriate button prompts:
```javascript
var p1 = input.player1()
var kind = p1.device_kind() // 'keyboard', 'mouse', 'gamepad'
var type = p1.gamepad_type() // 'xbox', 'playstation', etc.
```
Get the display name or icon for an action based on the current device:
```javascript
var icon = p1.get_icon_for_action('jump')
var binding = p1.get_primary_binding('jump')
```
## Emacs-Style Keybindings
For editors or complex hotkey systems, Prosperon supports modifier notation:
| Prefix | Key |
|--------|-----|
| `C-` | Ctrl |
| `M-` | Alt |
| `S-` | Super |
So `C-a` means Ctrl+A, `C-M-s` means Ctrl+Alt+S. Case is preserved — `a` and `A` are different bindings.
The emacs module is enabled by default but can be disabled:
```javascript
input.configure({ emacs: false })
```
## Gestures
Touch gesture recognition is built in:
- Swipe detection (with configurable distance and time thresholds)
- Pinch detection
```javascript
input.configure({
gestures: true,
swipe_min_dist: 50,
swipe_max_time: 0.5
})
```
## Saved Bindings
Player bindings are automatically saved and loaded, so remapped controls persist across sessions.
## Raw Events
For debugging or special cases, the raw SDL event can be accessed via the config callback:
```javascript
core.start({
input: function(raw_event) {
// raw SDL event object
}
})
```

View File

@@ -1,186 +1,170 @@
# RENDERING PIPELINE
The basic flow for developing graphics here:
---
title: "Compositor"
type: docs
---
1) develop a render graph
2) decide what to draw
# The Compositor
The render graph is the "big idea" of how the data flows through a render; inside the execution, you utilize "what to draw".
The compositor is Prosperon's rendering orchestrator. You describe your scene — planes, layers, effects — and the compositor figures out what render passes are needed and executes them.
Prosperon provides you with functions to facilitate the creation of rendering pipelines. For example, you could use "shadow_vol" function to create buffer geometry with shadow volume data.
## The Basic Idea
Unity has a "graphics.rendermesh" function that you can call, and that unity automatically calls for renderer components. It is the same here. But there are a handful of other types to draw, particularly for 2d.
You don't issue draw calls. You create sprites and set their properties. The compositor does the rest:
## 2D
### Anatomy of a 2d renderer
Traditionally, 2d rendering is a mix of tilemaps and sprites. Today, it is still more cost effective to render tilemaps, but we have a lot more flexibility.
1. Queries all registered drawables
2. Organizes them by plane and layer
3. Applies effects to tagged groups
4. Composites everything to the screen
NES
Nes had 1 tilemap and up to 8 sprites per scanline.
Think of it like a GBA: you declare "sprite here, sprite there," get handles back, and poke their positions. The hardware (in this case, the compositor + GPU backend) handles the actual rendering.
SNES
Up to 4 tilemap backgrounds, with priority, and flipping capability. 32 sprites per scanline, and by setting the priority correctly, they could appear behind background layers.
## Scene Configuration
GB
One background layer, 10 sprites per scanline/40 per frame.
A scene is described as a config object passed to the compositor:
GBA
Up to 4 layers, sprites with affine transforms!
```javascript
var compositor = use('compositor')
DS
Up to 4 layers; many sprites; and a 3d layer!
var plan = compositor.compile({
clear: {r: 0, g: 0, b: 0, a: 1},
planes: [
{
name: 'background',
plane: 'background',
resolution: {width: 320, height: 240},
camera: bg_camera,
presentation: 'integer_scale'
},
{
name: 'game',
plane: 'default',
resolution: {width: 320, height: 240},
camera: game_camera,
layer_sort: {'5': 'y'},
presentation: 'integer_scale'
},
{
name: 'hud',
plane: 'hud',
resolution: {width: 1280, height: 720},
camera: hud_camera,
presentation: 'stretch'
}
]
})
```
Sega saturn
This and everything else with generic vertex processing could do as many background layers and sprites as desired. This is what you get with prosperon on most modern computers. For more limited hardware, your options become limited too!
Each plane renders independently at its own resolution and composites onto the screen.
### Prosperon rendering
Layers
Every drawable 2d thing has a layer. This is an integer that goes from -9223372036854775808 to 9223372036854775808.
## Planes
!!! On hardware that supports only a limited number of layers, this value must go from 0 to (layer #).
A **plane** is a named rendering group. Sprites belong to a plane via their `plane` property (default: `'default'`). Each plane in the compositor config:
Layer sort
Within a layer, objects are sorted based on a given criteria. By default, this is nothing, and the engine may reorder the draws to optimize for performance. Instead, you can choose to sort by their y axis position, for example.
- Has its own **resolution** (low-res pixel art, native UI, etc.)
- Has its own **camera**
- Has its own **layer sorting** rules
- Has a **presentation mode** (how it maps to the window)
Parallax
Layers can have a defined parallax value, set at the engine level. Anything on that layer will move with the provided parallax. Each layer has an implicit parallax value of "1", which means it moves "as expected". Below 1 makes it move slower (0 makes it not move at all), 2 makes it move twice as fast, etc.
Planes composite in order — later planes draw on top of earlier ones.
Tilemaps
These are highly efficient and work just like tilemaps on old consoles. When you submit one of these to draw, Prosperon can efficientally cull what can't be seen by the camera. You can have massive levels with these without any concern for performance. A tilemap is all on its own layer.
## Layer Sorting
Tiles can be flipped; and the entire tilemap can have an affine transformation applied to it.
Within a plane, drawables are sorted by `layer` (integer). Within each layer, you can choose a sorting mode:
Sprites each have their own layer and affine transform. Tilemaps are just like a large sprite.
In addition to all of this, objects can have a "draw" event, wherein you can issue direct drawing commands like "render.sprite", "render.text", "render.circle", and so on. This can be useful for special effects, like multi draw passes (set stencil -> draw -> revert stencil). In this case, it is the draw event itself with the layer setting.
## 3D
3d models are like 3d sprites. Add them to the world, and then the engine handles drawing them. If you want special effects, its "draw" command can be overridden.
As sprites and 3d models are sent to render, they are added to a list; sorted; and then finally rendered.
## THE RENDERER
## Fully scriptable
The render layer is where you do larger scale organizing. For example, for a single outline, you might have an object's draw method be the standard:
- draw the model, setting stencil
- draw a scaled up model with a single color
But, since each object doing this won't merge their outlines, you need a larger order solution, wherein you draw *all* models that will be outlined, and then draw *all* scaled up models with a single color. The render graph is how you could do that. The render graph calls draw and render functions; so with a tag system, you can essentially choose to draw whatever you want. You can add new shadow passes; whatever. Of course, prosperon is packed with some standard render graphs to utilize right away.
Each graphical drawing command has a specific pipeline. A pipeline is a static object that defines every rendering detail of a drawing command.
A drawing command is composed of:
- a model
- a material
- a pipeline
The engine handles sorting these and rendering them effectively. There exist helper functions, like "render.image" which will in turn create a material and use the correct model.
You execute a list of drawing commands onto a render target. This might be the computer screen; it might be an offscreen target.
The material's properties are copied into the shader on a given pipeline; they also can have extra properties like "castshadows", "getshadows", and so on.
An *image* is a struct {
texture: GPU texture
rect: UV coordinates
```javascript
layer_sort: {
'0': 'explicit', // engine may reorder for batching efficiency
'5': 'y' // sort by Y position (top-down game depth)
}
```
## 2D drawing commands
The 2d drawing commands ultimately interface with a VERY limited subset of backend knowledge, and so are easily adaptable for a wide variety of hardware and screen APIs.
The basic 2D drawing techniques are:
Sprite - arbitrarily blit a bitmap to the screen with a given affine transformation and color
Tiles - Uniform squares in a grid pattern, drawn all on a single layer
Text - Generates whatever is needed to display text wrapped in a particular way at a particular coordinate
Particles - a higher order construction
Geometry - programmer called for circles or any other arbitrary shape. Might be slow!
Y-sorting is essential for top-down games where objects lower on screen should appear in front.
## Effects
An "effect" is essentially a sequence of render commands. Typically, a sprite draws itself to a screen. It may have a unique pipeline for a special effect. But it might also have an "effect", which is actually a sequence of draw instructions. An example might be an outline scenario, where the sprite draws a black version of it scaled 1.1x, and then draws with the typical pipeline afterwards.
## A frame
During a frame, the engine finds everything that needs rendered. This includes enabled models, enabled sprites, tilemaps, etc. This also includes programmer directions inside of the draw() and hud() functions.
Effects are applied to **groups** of sprites. Tag sprites with group names, then define effects for those groups:
This high level commands are culled down, accounting for off screen sprites, etc, into a more compact command queue. This command queue is then rendered in whichever way the backend sees fit. Each "command queue" maps roughly into a "render pass" in vulkan. Once you submit a command queue, the data is sorted, required data is uploaded, and a render pass draws it to the specified frame.
```javascript
var player = sprite({
image: "player.png",
groups: ['glow_objects']
})
A command is kicked off with a "batch" command.
var plan = compositor.compile({
planes: [{
name: 'game',
plane: 'default',
camera: cam,
resolution: {width: 320, height: 240}
}],
group_effects: {
glow_objects: {
effects: [
{type: 'bloom', threshold: 0.5, intensity: 1.5, blur_passes: 2}
]
}
}
})
```
var batch = render.batch(target, clearcolor) // target is the target buffer to draw onto
target must be known when the batch starts because it must ensure the pipelines fed into it are compatible. If clearcolor is undefined, it does not erase what is present on the target before drawing. To disable depth, simply do not include a depth attachment in the target.
### Available Effects
batch.draw(mesh, material, pipeline)
This is the most fundamental draw command you can do. In modern parlance, the pipeline sets up the GPU completely for rendering (stencil, blend, shaders, etc); the material plugs data into the pipeline, via reflection; the mesh determines the geometry that is drawn. A mesh defines everything that's needed to kick of a draw call, including if the buffers are indexed or not, the number of indices to draw, and the first index to draw from.
**Bloom** — extracts bright areas, blurs them, composites back:
```javascript
{type: 'bloom', threshold: 0.8, intensity: 1.0, blur_passes: 2}
```
batch.viewport()
**Mask** — uses sprites in a mask group as a stencil:
```javascript
{type: 'mask', mask_group: 'mask_shapes', channel: 'alpha', invert: false}
```
batch.sprite
Sprites in the `mask_group` are not drawn directly — they only serve as the mask shape.
## Presentation Modes
batch.text // a text object. faster than doing each letter as a sprite, but less flexible
// etc
batch.render(camera)
Each plane specifies how its render target maps to the window:
Batches can be saved to be executed again and again. So, one set of batches can be created, and then drawn from many cameras' perspectives. batch.render must take a camera
| Mode | Behavior |
|------|----------|
| `stretch` | Fill the window exactly (may distort aspect ratio) |
| `letterbox` | Fit inside window, preserving aspect ratio, with black bars |
| `integer_scale` | Scale by whole numbers only for pixel-perfect rendering |
Behind the scenes, a batch tries to merge geometry, and does reordering for minimum pipeline changes behind the scenes.
`integer_scale` automatically uses nearest-neighbor filtering for crisp pixels.
Each render command can use its own unique pipeline, which entails its own shader, stencil buffer setup, everything. It is extremely flexible. Sprites can have their own pipeline.
## Execution
ULTIMATELY:::
This is a much more functional style than what is typically presented from graphics APIs. Behind the scenes these are all translated to OpenGL or whatever; being functional at this level helps to optimize.
The compositor produces a **plan** — a list of render passes. The plan is executed by the GPU backend:
IMPORTANT NOTE:
Optimization only happens at the object level. If you have two pipelines with the exact same characteristics, they will not be batched. Use the exact same pipeline object to batch.
```javascript
var result = compositor.execute(plan)
// result.commands is sent to the GPU backend
```
## SCENARIOS
In practice, `core.start({render})` handles this for you. Your render callback returns the compositor result and the engine executes it.
BLOOM BULLETS
You want to draw a background; some ships; and some bullets that have glow to them. This amounts to two ideas:
1) draw the background and ships
2) draw bullets to a texture
3) apply bloom on the bullet
4) draw bullets+bloom over the background and ships
## Manual Drawables
Steps 1, and 2-3, can be done in parallel. They constitute their own command queues. When both are done, the composite can then happen.
You can inject drawables that aren't registered with film2d by adding them directly to a plane config:
var bg_batch = render.batch(surf1, camera);
bg_batch.draw(background)
bg_batch.draw(ships)
bg_batch.end()
```javascript
planes: [{
name: 'game',
plane: 'default',
camera: cam,
drawables: [
{type: 'sprite', image: tex, pos: {x: 0, y: 0}, width: 32, height: 32, layer: 0}
]
}]
```
var bullet_batch = render.batch(surf2, camera);
bullet_batch.draw(bullets)
bullet_batch.end()
These are merged with the registered drawables for that plane.
var bloom = render.batch(surf3, postcam)
bloom.draw(bullet_batch.color, bloom_pipeline)
bloom.end()
## Render Targets
var final = render.batch(swapchain)
final.draw(bg_batch.color)
final.draw(bloom.color)
final.end()
The compositor automatically manages intermediate render targets for effects. You don't need to create or manage GPU textures — the compositor allocates and reuses them as needed.
When 'batch.end' is called, it reorders as needed, uploads data, and then does a render pass.
## Debug
3D GAME WITH DIRECTIONAL LIGHT SHADOW MAP
var shadow_batch = render.batch(shadow_surf, dir_T)
shadow_batch.draw(scene, depth_mat) // scene returns a list of non culled 3d obejcts; we force it to use depth_mat
shadow_batch.end()
base_mat.shadowmap = shadow_batch.color;
var main_batch = render.batch(swapchain, camera)
main_batch.draw(scene)
main_batch.end()
FIERY LETTERS
This pseudo code draws a "hello world" cutout, with fire behind it, and then draws the game's sprites over that
var main = render.batch(swapchain, 2dcam)
main.draw("hello world", undefined, stencil_pipeline)
main.draw(fire)
main.draw(fullscreen, undefined, stencil_reset)
main.draw(game)
main.end()
Pass `debug: 'graph'` to `core.start` config to log the compiled render plan as JSON. Pass `debug: 'cmd'` to log the final command list sent to the GPU.

View File

@@ -1,9 +1,47 @@
---
title: "About Prosperon"
type: docs
---
# Prosperon
Prosperon is a game programming environment. It's a descriptive language for making a game, which can have many engines as its backend; it ships with its own.
Prosperon is a 2D game programming environment built on [Pit](https://crumbpit.org), an actor-based language designed for safe, sandboxed scripting.
It is primarily 2D focused; think of it like a 2d S&Box. Its aim is to make it easy to make a 2d game that works anywhere.
Games are written as small, focused modules in Pit — a simplified JavaScript with actor primitives. Prosperon handles rendering, input, audio, and asset management. No class hierarchies, no giant scene trees.
Key to it is how you use and define sprites.
## Design Goals
Prosperon games are written in cell. Cell and prosperon are designed for simple AI usage. An AI can add elements to the game directly, it can code it, etc; cell and prosperon both are designed to have many small files with simple dependencies, which allow LLMs to utilize them easily.
- **Minimal API surface.** A handful of modules cover sprites, input, sound, and composition. Each does one thing.
- **Data-driven rendering.** You create sprites and set properties. The compositor handles batching, sorting, and GPU submission.
- **Composition over inheritance.** Entity behavior comes from modules, not class trees. An entity type is a script that returns a prototype object.
- **AI-friendly.** Many small files with simple dependencies. An LLM can read a module, understand it, and modify it.
- **Text-first.** Assets are referenced as string paths. Levels are JSON. Everything diffs cleanly in source control.
## Architecture
Prosperon is not a monolithic engine with a global state object. It is a collection of modules:
| Module | Purpose |
|--------|---------|
| `core` | Main loop, window, GPU init |
| `sprite` | Create and manage sprites |
| `compositor` | Build render plans from scene configs |
| `input` | Action mapping, device routing |
| `sound` | Audio playback |
| `world` | Entity management |
| `camera` | Viewport into the world |
| `text2d` | Text rendering |
| `shape2d` | SDF shapes |
| `tilemap2d` | Grid-based tile rendering |
| `tween` | Value interpolation |
| `resources` | Asset path resolution |
Import what you need with `use()`. If you don't use a module, it doesn't load.
## The Language
Pit is an actor-based language. Programs (`.ce` files) are actors that run top-to-bottom, can register message handlers, and spawn child actors. Modules (`.cm` files) return frozen values and are cached.
Actor primitives: `$start`, `$receiver`, `send`, `$delay`, `$clock`, `$stop`.
See [Quickstart](../quickstart/) for a hands-on introduction.

View File

@@ -1,47 +1,19 @@
---
title: "Quickstart"
type: docs
---
# Quickstart
This quickstart walks through running Cell programs, exploring examples, and creating your first module and program. It assumes the `./cell` executable is present at the repo root (already built or downloaded).
This quickstart walks through creating modules, programs, and running examples.
## 1) Initialize a Project Shop (.cell)
## Your First Module
Create the project structure used for module management and bytecode cache:
A module (`.cm`) returns a frozen value — an API, a config, data.
- `./cell init`
This creates `.cell/` with `cell.toml`, `lock.toml`, `modules/`, `build/`, and `patches/`.
## 2) Run Builtin Examples
Examples under `examples/` are programs (`*.ce`) you can run directly:
- NAT portal server (introduction service):
- `./cell examples/nat`
- NAT client (contacts a portal):
- `./cell examples/nat_client`
- Nonblocking HTTP download actor:
- `./cell examples/http_download_actor`
Tip: Use these as references for portals, contacts, and nonblocking I/O.
## 3) Run the Accio Game
Accio lives under `accio/`. The program is `accio/accio.ce` and supports modes via arguments:
- Start menu: `./cell accio start`
- Load a level: `./cell accio level game/1.json`
- Analyze assets: `./cell accio analyze`
- Clean level data: `./cell accio clean`
Arguments after the program path are available inside the program via `arg` (e.g., `arg[0] == 'start'`).
## 4) Your First Module (.cm) and Program (.ce)
Create a module that returns a frozen API object, and a program that uses it.
- File: `examples/hello.cm` (module)
Create `hello.cm`:
```javascript
// Returns a value (frozen by the engine)
return {
greet: function(name) {
return `Hello, ${name}!`
@@ -49,39 +21,43 @@ return {
}
```
- File: `examples/hello.ce` (program)
## Your First Program
A program (`.ce`) is an entry point. It runs top-to-bottom and can register handlers.
Create `hello.ce`:
```javascript
var hello = use('examples/hello')
var hello = use('hello')
log.console(hello.greet('Cell'))
log.console(hello.greet('Prosperon'))
$receiver(function(msg) {
if (msg.type == 'ping') {
send(msg, {type:'pong'})
send(msg, {type: 'pong'})
}
})
$delay(_ => $stop(), 0.1)
```
Run it:
## Modules vs Programs
- `./cell examples/hello`
| | Modules (`.cm`) | Programs (`.ce`) |
|---|---|---|
| Must return a value | Yes | No |
| Return value frozen | Yes | N/A |
| Can register handlers | No | Yes (`$receiver`) |
| Can spawn children | No | Yes (`$start`) |
| Cached after first load | Yes | N/A |
Notes:
- Modules are `*.cm` and must return a value. The engine deepfreezes return values, so mutate via new objects or closures rather than inplace.
- Programs are `*.ce` and must not return a value. They run toptobottom when spawned and can register handlers via `$receiver()` and schedule work via `$delay()` or `$clock()`.
## Spawning Actors
## 5) Spawning Child Programs (Actors)
Programs can spawn other programs and receive lifecycle events.
- File: `examples/spawner.ce`
Programs can spawn other programs as child actors:
```javascript
$receiver(function(e) {
if (e.type == 'greet' && e.actor) {
if (e.type == 'greet') {
log.console('Child greeted me')
}
})
@@ -90,44 +66,46 @@ $start(function(info) {
if (info.type == 'greet') {
log.console('Spawned child actor')
}
}, 'examples/hello.ce')
}, 'hello.ce')
$delay(_ => $stop(), 0.5)
```
Run it:
## Actor Primitives
- `./cell examples/spawner`
| Primitive | Purpose |
|-----------|---------|
| `$start(callback, path)` | Spawn a child actor from a program |
| `$receiver(callback)` | Register a message handler |
| `send(target, message)` | Send a message to an actor |
| `$delay(callback, seconds)` | Schedule a one-shot callback |
| `$clock(callback, interval)` | Schedule a repeating callback |
| `$stop()` | Terminate the current actor |
## 6) Module Shop Basics
## Module Resolution
The module shop manages vendored dependencies under `.cell/modules/` and caches compiled bytecode under `.cell/build/`.
`use('path')` looks for modules in this order:
Common commands (all are programs under `scripts/`):
1. The current module's directory for `path.cm`
2. The mounted roots (the program's directory is mounted) for `path.cm`
3. Embedded/native modules if no script file is found
- Initialize (if you havent already):
- `./cell init`
- See available commands:
- `./cell help`
- Configure system/actor settings:
- `./cell config list`
- `./cell config set system.reply_timeout 60`
- `./cell config actor prosperon/_sdl_video set resolution 1280x720`
- Download or vendor dependencies (from `.cell/cell.toml`):
- `./cell mod download`
Modules compile to bytecode (`.o` files) and are cached for fast reloading.
## 7) How Module Resolution Works
## Example Games
- `use('path')` checks:
1) The current modules directory for `path.cm` while loading.
2) The mounted roots (the programs directory is mounted) for `path.cm`.
3) Embedded/native modules if no script file is found.
- Modules compile to `.cell/build/<canonical-path>.o` and reuse the cache if newer than the source.
- Scripted modules can extend embedded modules via prototype; if the script returns nothing, the embedded module is used asis.
Prosperon ships with example games:
## 8) Next Steps
- **Pong** — two-player paddle game
- **Snake** — growing snake on a grid
- **Tetris** — falling block puzzle
- **Chess** — full chess with move validation
- **Bunnymark** — sprite rendering benchmark
- Language details: `docs/cell.md`
- Actors, programs, and messaging: `docs/actors.md`
- Rendering, input, and resources: `docs/rendering.md`, `docs/input.md`, `docs/resources.md`
- Full API reference: `docs/api/`
## Next Steps
- [Tutorial](tutorial/) — Build a game step by step
- [Entities](entities/) — The world and entity model
- [Rendering](rendering/) — How drawing works
- [Input](input/) — Handling player input
- [API Reference](api/) — Full module documentation

View File

@@ -1,36 +1,113 @@
# Rendering, the camera & the world
---
title: "Rendering"
type: docs
---
Prosperon is rendered via a single camera, defined on `prosperon.camera`. Examine the `camera` object in the API for detail about its properties.
# Rendering
The camera has a width and a height, which defines the resolution of the rendered game. The camera also has a transform `prosperon.camera.transform`, which should be used to move it. The camera's position can also be set via `prosperon.camera.pos`.
Prosperon's rendering is **data-driven**. You create sprites and other drawables, set their properties, and the engine handles drawing them. You never touch the GPU directly.
## The Model
Think of it like programming sprites on a classic console:
1. **Create** a sprite (or text, shape, tilemap, particle emitter)
2. **Set** its position, image, layer, and visual properties
3. The engine **draws** everything each frame, sorted and batched automatically
```javascript
var sprite = use('sprite')
var player = sprite({
image: "player.png",
pos: {x: 100, y: 200},
layer: 5
})
// Move it later
player.pos.x = 150
player.pos.y = 250
```
That's it. The sprite is registered with the engine when created and drawn every frame until destroyed.
## Coordinate System
There are two important coordinate systems in Prosperon: world and hud. Both coordinate systems are measured in pixels, with X+ going to the right, and Y+ going up.
**World space** is the game world, and is what the camera "sees". The center of the camera is the coordinate it is viewing; so, for example, if the camera is at position [100,100], the pixel at the center of the screen is whatever pixel is at world space [100,100].
There are two coordinate spaces:
**Hud space** can be thought of as the screen itself, or the lens of the camera. The bottom left of the viewing window is [0,0], and the top right is [camera.width, camera.height].
**World space** is the game world. The camera's position determines what's visible. If the camera is at `[100, 100]`, that world coordinate is at the center of the screen. X+ goes right, Y+ goes up.
## The render module & queues
Internally, prosperon keeps a rendering queue as draw commands are issued throughout the frame. It keeps two: 'draw' and 'hud'. At the end of the frame, draw commands are sorted and issued to the GPU: first the 'draw' queue, and then the 'hud' queue, so that it overlays.
**HUD space** is the screen itself. `[0, 0]` is the bottom-left corner, `[camera.width, camera.height]` is the top-right. HUD elements don't move with the camera.
The render module contains functionality for low level rendering. It includes commands like 'scissor' and 'viewport', it is more recommended to use the 'draw2d' module to do the majority of the drawing.
## Layers
## Draw2d
The easiest way to draw with prosperon is to use the 'draw2d' module. Prosperon defines two events via `prosperon.on`: `draw` and `hud`. During a frame, the `render` module sets its internal queue to `draw`, and then the `draw` event is issued. Then it does the same for `hud`. The 'draw2d' module is a high level way to issue drawing commands to the current queue. So, a simple example:
Every drawable has a `layer` — an integer that determines draw order. Lower layers draw first (behind), higher layers draw last (in front).
```
var draw = use('draw2d')
// main.js
var rect = {x:0,y:0,width:100,height:100} // a 100x100 square at [0,0]
this.draw = function() {
draw.rectangle(rect) // this will draw with the bottom left corner at the center of the screen
}
Within a layer, objects can be sorted by different criteria:
this.hud = function() {
draw.rectangle(rect) // this draws a rectangle at the botomm left corner of the screen
}
- **explicit** (default) — engine may reorder for performance
- **y-sort** — sort by Y position, useful for top-down games where "lower" objects appear in front
## Planes
Drawables are organized into **planes**. A plane is a named group that can have its own resolution, camera, and effects. By default, everything goes into the `'default'` plane.
Planes render independently and composite onto the screen. This lets you have a game world at 320x240 pixel-art resolution and a HUD at native resolution, for example.
## Effects
Planes and groups of sprites can have effects applied:
- **Bloom** — threshold + multi-pass Gaussian blur
- **Mask** — stencil masking using other sprites as the mask shape
Effects are declared in the compositor config and applied automatically. You tag sprites with groups, then apply effects to those groups.
## Presentation Modes
The final composited frame is presented to the window in one of three modes:
| Mode | Behavior |
|------|----------|
| `stretch` | Fill the window, may distort |
| `letterbox` | Fit inside window, preserve aspect ratio, black bars |
| `integer_scale` | Scale by whole numbers only, nearest-neighbor, pixel-perfect |
## Camera
A camera defines the viewport into the world:
```javascript
var camera = use('camera')
var cam = camera.make({
pos: {x: 0, y: 0},
width: 320,
height: 240
})
```
## Imgui
Prosperon includes imgui. Another event is the `imgui` event, where you can issue imguie commands via the 'imgui' module. Examine the 'imgui' API for more info.
The camera's `width` and `height` set the game's internal resolution. The `pos` determines what world coordinate is at the center of the screen.
## Drawable Types
| Type | Module | Description |
|------|--------|-------------|
| Sprite | `sprite` | Textured quad with transform, tint, UV mapping |
| Text | `text2d` | Rendered text with font, size, wrapping |
| Shape | `shape2d` | SDF-rendered rectangles, circles, ellipses, pills |
| Tilemap | `tilemap2d` | Grid of tiles on a single layer |
| Particles | `particles2d` | Particle emitter producing sprite-like particles |
| Line | `line2d` | Polylines and line segments as triangle meshes |
All drawable types share common properties: `pos`, `layer`, `plane`, `groups`, `visible`, `opacity`, `tint`.
## How It Works Internally
For those curious about the pipeline (you don't need to know this to use Prosperon):
1. Drawables register with `film2d` — the internal sprite registry
2. Each frame, the **compositor** queries film2d for all visible drawables
3. Drawables are sorted by layer, batched by texture/material
4. Effect groups are rendered to intermediate targets, effects applied
5. Planes composite to the screen via the **SDL GPU backend**
6. The backend uses Metal, Vulkan, or DirectX depending on platform

View File

@@ -1,17 +1,48 @@
# Resources & Data
---
title: "Resources"
type: docs
---
Prosperon, being text based, encourages you to reference assets - images, sounds, etc - as strings.
# Resources
## Module Loading and Discovery
Prosperon uses string paths to reference assets — images, sounds, fonts, scripts. The resource system resolves these paths and caches loaded assets.
Prosperon uses a set of **search paths** to locate modules. Each path in this list is consulted in order, and the first matching module file is loaded.
**prosperon.PATH** is an array which is a list of directories in which it looks for resources. Developers can add or remove entries from this list, controlling where the engine will search. Assets are searched from `prosperon.PATH[0]` first.
## Module Resolution
**Extension Matching:** Assets should be referenced without a file extension. Based on the context of what asset is being searched for, a list of file extensions are iterated through to find the asset. When setting a sprite's texture, for example, first a 'png' may be searched for, then a 'gif', and so on. If a specific extension is requested, that specific extension - and only that one - will be searched for. it's very useful to not specify extensions at all, because that enables prosperon to optimize assets and replace them with backend specific ones, and the game will still work.
`use('path')` loads a module by searching for `path.cm` in this order:
Prosperon **caches** each loaded assets. When a sprite references a 'png', if another one does, it uses the same GPU texture.
1. The current module's directory
2. Mounted roots (the program's directory is always mounted)
3. Embedded/native modules if no script file is found
Modules compile to bytecode (`.o` files) and are cached after first load. Subsequent `use()` calls return the cached value.
## Asset Resolution
When you reference an asset like `"player.png"`, the resource system searches mounted paths for a match. Assets should generally be referenced **without** a file extension — the engine tries appropriate extensions based on context:
- **Images:** png, qoi, gif, jpg, jpeg, ase
- **Sounds:** wav, ogg, mp3
- **Fonts:** ttf
Omitting extensions lets the engine swap optimized formats transparently. If you specify an extension, only that exact file is searched for.
## Caching
All loaded assets are cached. When two sprites reference the same image path, they share one GPU texture. Sound data, fonts, and bytecode are similarly deduplicated.
## Mounts
Prosperon uses the idea of file mounting for all io operations. To start with, the write directory is set to the folder prosperon is ran from. That same folder is also mounted as a read directory, as well as the executable itself, which contains a zip file of the engine's core assets.
Folders or archives like zip records can be mounted, allowing for an easy way to overwrite specific files with new ones. Because prosperon deals with file systems and strings for its operations, this makes modding easy. For example, a mod `mod.zip` may have a file `sprites/bug.png`. Once mounted, when your game loads `bug` as a sprite, that would be grabbed instead of your game's natives `bug`.
Prosperon uses a virtual filesystem. The write directory is set to the folder the engine runs from. That same folder is mounted as a read directory, along with the executable itself (which contains a zip archive of core engine assets).
Additional folders or zip archives can be mounted, allowing mods to override specific files:
```javascript
// A mod zip containing sprites/bug.png will override the game's bug.png
```
Because all asset references are string paths resolved through the mount system, modding is straightforward — mount a new archive and its files take priority.
## .prosperonignore
A `.prosperonignore` file in a mounted directory excludes files from resolution, similar to `.gitignore`. This is useful for keeping source assets (e.g., Aseprite working files) out of the build.

View File

@@ -1,67 +1,225 @@
---
title: "Tutorial"
type: docs
---
# A Tutorial Introduction
Let's make your first game. When prosperon is launched, it looks in the folder it's in for a ```config.js``` and ```main.js```. These are your first two examples of a module and a program.
This tutorial walks through building a simple game from scratch. By the end, you'll understand modules, programs, rendering, and input.
A module is a file that returns a single value. It could be any value: a number, a function, a string. Usually, it's an object. Values are **frozen** before returning, so they cannot be modified. A module is ran once and cached, so any program that accesses a module subsequently is simply getting the already cached object. Modules can import other modules. Circular references are not allowed.
## How Prosperon Starts
The ```config.js``` module must return a single object that describes your game. It sets up the window name, starting size, etc. Below is the default config, demonstrating all of the values that can be set on it.
Prosperon runs a `.ce` program file as the root actor. This is your game's entry point. There is no `config.js` or `main.js` — everything is modules you import with `use()`.
| Key | Default Value | Type | Description |
|--------------------|----------------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------|
| **title** | `Prosperon [\${prosperon.version}-\${prosperon.revision}]` | string | Title of the game window, typically including version information. |
| **width** | `1280` | number | Initial width of the game window. |
| **height** | `720` | number | Initial height of the game window. |
| **icon** | `graphics.make_texture(io.slurp('icons/moon.gif'))` | object | Icon texture for the game window, loaded from the provided file. |
| **high_dpi** | `0` | number | Enables (1) or disables (0) High DPI support for the window. |
| **alpha** | `1` | number | Alpha channel setting for the window (0 to disable, 1 to enable transparency). |
| **fullscreen** | `0` | number | Sets whether the window should launch in fullscreen (1) or windowed (0). |
| **sample_count** | `1` | number | Multisampling count for rendering. Increasing this can improve image quality at the cost of performance. |
| **enable_clipboard** | `true` | boolean | Enables clipboard functionality within the application. |
| **enable_dragndrop** | `true` | boolean | Enables drag-and-drop functionality for files into the window. |
| **max_dropped_files** | `1` | number | Maximum number of files that can be dropped into the window at once. |
| **swap_interval** | `1` | number | Controls vertical synchronization (VSync). Commonly 1 for enabling or 0 for disabling VSync. |
| **name** | `"Prosperon"` | string | Human-readable name of the application. |
| **version** | `prosperon.version + "-" + prosperon.revision` | string | Version string for the application, dynamically built from Prosperon's version info. |
| **identifier** | `"world.pockle.prosperon"` | string | Package or bundle identifier for your game/application. |
| **creator** | `"Pockle World LLC"` | string | Name of the creator or organization behind the application. |
| **copyright** | `"Copyright Pockle World 2025"` | string | Copyright declaration for the application. |
| **type** | `"application"` | string | The general content type or category of this project. |
| **url** | `"https://github.com/johnbrethauer/prosperon"` | string | URL link associated with the project, such as a repository or official homepage. |
## Step 1: A Window
Create `game.ce`:
With the engine configured, prosperon starts ```main.js``` as the first **actor** of the game. Actors are created from files that **do not** return a value. An actor executes the statements in its script. Initialization should happen here. An actor can pull in other chunks of code by importing modules. Modules are imported with the ```use``` statement. ```use``` returns the value the module returned, and it can be assigned to any variable. To use the internal ```io``` module, for example, you might say ```var io = use('cellfs')```.
Actors exist until they are explicitly killed, by invoking their ```kill``` function. Because the actor created from ```main.js``` is the root actor for the entire game, when it is killed, the program exits. In an actor script file, ```this``` is set to the actor being spawned from the script.
## Our first program
With that out of the way, we can establish our first simple program.
In the folder with your ```prosperon``` executable, create a ```config.js``` and set it to be the following:
```javascript
var core = use('core')
core.start({
title: "My Game",
width: 1280,
height: 720
})
```
Run it with `prosperon game.ce`. You get a window. `core.start()` initializes the GPU backend, opens the window, and starts the main loop.
## Step 2: Drawing a Sprite
Sprites are the fundamental drawable. Create one and it appears on screen — you don't issue draw calls.
```javascript
var core = use('core')
var sprite = use('sprite')
var compositor = use('compositor')
var camera = use('camera')
var cam = camera.make({
pos: {x: 0, y: 0},
width: 320,
height: 240
})
var player = sprite({
image: "player.png",
pos: {x: 0, y: 0},
width: 32,
layer: 5
})
var plan = compositor.compile({
planes: [{
name: 'game',
plane: 'default',
resolution: {width: 320, height: 240},
camera: cam,
presentation: 'integer_scale'
}]
})
core.start({
title: "Sprite Test",
width: 1280,
height: 720,
render: function() {
return compositor.execute(plan)
}
})
```
Key ideas:
- `sprite()` creates a sprite and registers it with the engine automatically
- The **compositor** describes how to render the scene: which planes, at what resolution, with what camera
- Your `render` callback returns the compositor result — the engine sends it to the GPU
## Step 3: Moving the Sprite
Add an `update` callback to move things each frame:
```javascript
core.start({
title: "Moving Sprite",
width: 1280,
height: 720,
update: function(dt) {
player.pos.x += 60 * dt
},
render: function() {
return compositor.execute(plan)
}
})
```
`dt` is the time elapsed since the last frame, in seconds. Multiply speeds by `dt` for frame-rate-independent movement.
## Step 4: Handling Input
Use the `input` module to map physical keys to named actions, then route those actions to your game objects:
```javascript
var input = use('input')
input.configure({
action_map: {
move_left: ['a', 'left'],
move_right: ['d', 'right'],
jump: ['space']
}
})
var player_entity = {
on_input: function(action, data) {
if (action == 'move_left' && data.pressed) {
player.pos.x -= 16
}
if (action == 'move_right' && data.pressed) {
player.pos.x += 16
}
}
}
var p1 = input.player1()
p1.possess(player_entity)
```
The input system handles keyboard, gamepad, and touch. You define actions once and bind them to any physical input.
## Step 5: Adding Sound
```javascript
var sound = use('sound')
sound.play("jump.wav")
```
Sounds are loaded by path. The engine caches audio data so loading the same sound twice reuses the existing data.
## Step 6: Multiple Planes
Real games often need separate rendering layers — pixel art at low res, HUD at native res:
```javascript
var plan = compositor.compile({
clear: {r: 0.1, g: 0.1, b: 0.2, a: 1},
planes: [
{
name: 'game',
plane: 'default',
resolution: {width: 320, height: 240},
camera: cam,
layer_sort: {'5': 'y'},
presentation: 'integer_scale'
},
{
name: 'hud',
plane: 'hud',
resolution: {width: 1280, height: 720},
camera: hud_cam,
presentation: 'stretch'
}
]
})
```
Sprites belong to a plane via their `plane` property. Each plane renders at its own resolution with its own camera, then composites onto the screen in order.
## Step 7: Entities
For anything more than a few sprites, use the entity system. Define entity types as modules, create instances with overrides:
```javascript
// entities/coin.cm
var sprite = use('sprite')
return {
title: "Hello World",
width: 500,
height:500
value: 1,
image: "coin.png",
init: function() {
this.sprite = sprite({
image: this.image,
pos: this.pos,
width: 16,
layer: 3
})
},
on_destroy: function() {
this.sprite.destroy()
}
}
```
This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
```javascript
// In your game program
var world = use('world')
var coin_proto = use('entities/coin')
```
log.console("Hello world")
this.delay(_ => {
this.kill();
}, 3)
var c = world.add_entity(coin_proto, {
pos: {x: 50, y: 80},
value: 5
})
```
**delay** is a function on all actors in prosperon. It executes a given function after a number of seconds. In this case, after 3 seconds, the root actor kills itself, closing the window.
The entity's `init()` runs after overrides are applied. The prototype defines every valid field with a default — it is the schema.
## The prosperon global
The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
## Putting It Together
## Getting help
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `cell.DOC(prosperon)` in your `main.js`.
A complete minimal game has:
Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.
1. A `.ce` program that imports modules and calls `core.start()`
2. Sprite and entity modules (`.cm`) that define game objects
3. A compositor config that describes the rendering setup
4. Input configuration that maps keys to actions
The engine does the rest: batching sprites, sorting layers, managing GPU resources, polling events, scheduling frames.
## Next Steps
- [Graphics](../graphics/) — Sprites, text, shapes, tilemaps
- [Compositor](../ops/) — Planes, effects, presentation modes
- [Input](../input/) — Actions, players, control stacks
- [Entities](../entities/) — The entity model and world system

2
website/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
public/
.hugo_build.lock

165
website/content/_index.html Normal file
View File

@@ -0,0 +1,165 @@
---
title: "Prosperon Engine"
---
<!-- Hero -->
<section class="hero">
<a class="hero-image" href="/"><img src="/images/prosperon_load.gif" alt="Prosperon Engine" /></a>
<p>
A minimal, fast, and flexible <strong>2D game engine</strong> built on <a href="https://crumbpit.org">Pit</a>&mdash;an actor-based language designed for safe, sandboxed scripting. Prosperon gives you a compositor-driven rendering pipeline, multi-device input, and reflective tooling, all in a tiny executable you version with your project.
</p>
</section>
<!-- What Is Prosperon -->
<section class="section">
<h2>What Is Prosperon?</h2>
<p>
Prosperon is a <strong>game programming environment</strong> for 2D games. Think of it like a 2D S&amp;Box: you write game logic in Pit (a simplified, sandboxed JavaScript), and Prosperon handles rendering, input, audio, and asset management. Games are written as small, focused modules that compose together&mdash;no class hierarchies, no giant scene trees.
</p>
<p>
The engine ships as a single executable. Drop it in a folder, write your scripts, and run. Your entire project is text files&mdash;friendly to source control, easy for collaborators, and readable by AI tools.
</p>
</section>
<!-- Key Features -->
<section id="features" class="section">
<h2>Key Features</h2>
<div class="feature-grid">
<div class="feature-card">
<h3>Compositor Rendering</h3>
<p>Describe your scene as planes, layers, and effects. The compositor builds a render plan&mdash;bloom, masks, shader passes&mdash;and the SDL GPU backend executes it. You declare what to draw; the engine decides how.</p>
</div>
<div class="feature-card">
<h3>Duck-Typed Sprites</h3>
<p>If an object looks like a sprite, it <em>is</em> a sprite. Register any object with <code>film2d</code> and it draws. No base classes, no component systems to configure. Just data.</p>
</div>
<div class="feature-card">
<h3>Actor-Based Scripting</h3>
<p>Game logic runs in Pit&mdash;an actor language where each program has its own heap and message queue. Spawn actors, send messages, schedule work with <code>$delay</code> and <code>$clock</code>. Clean isolation without threads.</p>
</div>
<div class="feature-card">
<h3>Multi-Device Input</h3>
<p>Keyboard, mouse, gamepad, and touch&mdash;normalized into named actions. Supports user pairing, control stacks (possess/push/pop), emacs-style keybindings, and gesture recognition out of the box.</p>
</div>
<div class="feature-card">
<h3>Source-Control Friendly</h3>
<p>Every game file is text. Modules are <code>.cm</code> files, programs are <code>.ce</code> files, configs are plain objects. No binary scene files until you ship. Git merges just work.</p>
</div>
<div class="feature-card">
<h3>Tiny &amp; Self-Contained</h3>
<p>Prosperon is a single executable. No installer, no SDK, no build tools. Version it alongside your project. Supports Windows, Mac, and Linux.</p>
</div>
</div>
</section>
<!-- How It Works -->
<section class="section">
<h2>How It Works</h2>
<p>A Prosperon game is a collection of <strong>modules</strong> (<code>.cm</code>) and <strong>programs</strong> (<code>.ce</code>). Modules return frozen values&mdash;APIs, configs, data. Programs are entry points that run top-to-bottom and can spawn child actors.</p>
<p>The engine provides core modules for everything you need:</p>
<ul>
<li><strong>core</strong> &mdash; main loop, window management, frame scheduling</li>
<li><strong>film2d</strong> &mdash; drawable registry (sprites, shapes, text, tilemaps, particles)</li>
<li><strong>compositor</strong> &mdash; builds multi-pass render plans from scene descriptions</li>
<li><strong>input</strong> &mdash; multi-device action mapping with control stacks</li>
<li><strong>sound</strong> &mdash; audio playback with PCM caching and voice mixing</li>
<li><strong>resources</strong> &mdash; asset discovery and path resolution</li>
<li><strong>draw2d</strong> &mdash; high-level 2D drawing (points, lines, rectangles, circles)</li>
<li><strong>tween</strong> &mdash; fire-and-forget animation with easing functions</li>
</ul>
<p>You compose these into your game. A typical frame:</p>
<pre><code>// Your game registers sprites with film2d
// The compositor queries film2d for drawables
// It builds render passes (clear, draw, effects, blit)
// The GPU backend executes the command list
// Input events are normalized and dispatched to your pawns
// Audio pumps in the background via $delay</code></pre>
</section>
<!-- Rendering -->
<section class="section">
<h2>The Rendering Pipeline</h2>
<p>Prosperon's rendering is <strong>data-driven</strong>. Your game logic never touches the GPU. Instead:</p>
<ol>
<li>Register drawables (sprites, shapes, text) with <code>film2d</code></li>
<li>Describe your scene as planes with optional effects (bloom, masks)</li>
<li>The <strong>compositor</strong> queries film2d, sorts by layer, builds render passes</li>
<li>The <strong>SDL GPU backend</strong> executes the command list on Metal/Vulkan/DirectX</li>
<li>Final output is blitted to screen with letterbox, integer scale, or stretch</li>
</ol>
<p>
This means you can inspect the entire render plan as data. Debug it, log it, swap backends. The renderer supports sprite batching, y-sorting, effect pipelines (threshold + blur for bloom, mask rendering), and multiple presentation modes for pixel-perfect scaling.
</p>
</section>
<!-- The Language -->
<section class="section">
<h2>The Language: Pit</h2>
<p>
Prosperon games are written in <a href="https://crumbpit.org">Pit</a>, an actor-based language that looks like simplified JavaScript. Pit is designed for <strong>safe, sandboxed execution</strong>&mdash;each actor runs in its own heap with hard memory and time limits.
</p>
<p>Key differences from JavaScript:</p>
<ul>
<li><strong>Modules</strong> (<code>.cm</code>) return frozen values&mdash;no accidental mutation</li>
<li><strong>Programs</strong> (<code>.ce</code>) are actor entry points with message handlers</li>
<li><strong>No classes</strong>&mdash;prototypal objects via <code>meme()</code> and closures</li>
<li><strong>Actor primitives</strong>&mdash;<code>$start</code>, <code>$receiver</code>, <code>send</code>, <code>$delay</code>, <code>$stop</code></li>
<li><strong>Capability-based</strong>&mdash;actors start with zero permissions, granted explicitly</li>
</ul>
<p>Pit compiles to bytecode and runs on a QuickJS-derived VM. Modules are cached as <code>.o</code> files for fast loading.</p>
</section>
<!-- Pricing -->
<section id="pricing" class="section">
<h2>Price</h2>
<p>
Prosperon is currently <strong>$25</strong> in pre-release (50% off the eventual $50 price). That one-time fee grants you:
</p>
<ul>
<li>Use of Prosperon for commercial or personal games (no royalties)</li>
<li>All updates through <strong>v1.x</strong></li>
<li>Support for <strong>Windows, Mac, and Linux</strong></li>
<li>Account on the <a href="https://discourse.prosperon.dev">official forum</a></li>
</ul>
</section>
<!-- Subscriptions -->
<section id="subscriptions" class="section">
<div class="support-subscriptions">
<div class="subscription-box purchase-box">
<h3>Plus Support</h3>
<span class="support-price">$30/year</span>
<ul>
<li>Access to support forum</li>
<li>Vote on <strong>1 feature</strong> for upcoming releases</li>
</ul>
</div>
<div class="subscription-box purchase-box">
<h3>Pro Support</h3>
<span class="support-price">$80/year</span>
<ul>
<li>Access to support forum</li>
<li>Vote on <strong>up to 5 features</strong></li>
<li>Early access to new features</li>
</ul>
</div>
</div>
</section>
<!-- Learn -->
<section class="section">
<h2>Learn</h2>
<p>
Read the <a href="/docs/">Prosperon manual</a> to learn about the module system, rendering pipeline, input handling, and deploying your game. Prosperon also ships with example games (pong, snake, chess, tetris, bunnymark) to help you learn fast.
</p>
</section>
<!-- License -->
<section id="license" class="section">
<h2>License</h2>
<p>
All Prosperon licenses are commercial. You can distribute Prosperon-based games with no royalties and freely ship the Prosperon runtime. For full details, visit the <a href="/license/">license page</a>.
</p>
</section>

View File

@@ -0,0 +1,7 @@
---
title: "Changelog"
---
Release notes are published on the [Gitea repository](https://gitea.pockle.world/john/prosperon/releases).
Check back here for updates as new versions are released.

View File

@@ -0,0 +1,27 @@
---
title: "Prosperon Manual"
type: "docs"
---
Welcome to the Prosperon documentation. This manual covers everything from getting started to the full API reference.
## Getting Started
- [Quickstart](/docs/quickstart/) — Modules, programs, and actor primitives
- [Tutorial](/docs/tutorial/) — Build your first game step by step
## Concepts
- [Rendering](/docs/rendering/) — The data-driven rendering model
- [Graphics](/docs/graphics/) — Sprites, text, shapes, tilemaps, particles
- [Compositor](/docs/ops/) — Planes, effects, and presentation modes
- [Entities](/docs/entities/) — The script + overrides entity model
- [Input](/docs/input/) — Actions, players, and control stacks
- [Resources](/docs/resources/) — Asset loading, discovery, and caching
- [About Prosperon](/docs/prosperon/) — Architecture and design goals
## API Reference
- [API Index](/docs/api/) — All modules, primitives, and types
Browse the full reference in the sidebar.

View File

@@ -0,0 +1,81 @@
---
title: "Features"
---
## Compositor-Driven Rendering
Prosperon separates *what to draw* from *how to draw it*. Your game registers sprites, shapes, text, tilemaps, and particles with `film2d`. At render time, the **compositor** queries these drawables, organizes them into planes and layers, applies effects, and produces a command list that the GPU backend executes.
This means:
- **Automatic batching** — sprites sharing a texture and material are drawn together
- **Layer sorting** — explicit ordering, y-sorting, or engine-optimized ordering per layer
- **Effects pipeline** — bloom (threshold + multi-pass blur), masks, custom shader passes
- **Presentation modes** — letterbox, integer scale, or stretch to window
- **Inspectable** — the render plan is plain data you can log and debug
## Duck-Typed Everything
Prosperon's API uses duck typing throughout. If an object has `type: 'sprite'`, a position, and an image, it's a valid sprite. No base classes to inherit from, no interfaces to implement.
This extends to the entire engine: anything that "looks like" a camera can be used as one. Anything with an `on_input` method can be possessed by a player. This makes prototyping extremely fast — you can test ideas with plain objects before committing to any structure.
## Actor-Based Game Logic
Games are written in [Pit](https://crumbpit.org), an actor language where:
- Each **program** (`.ce`) runs as an actor with its own heap
- Actors communicate via **message passing** (`send`, `$receiver`)
- Work is scheduled with `$delay` (one-shot) and `$clock` (periodic)
- Actors spawn children with `$start` and terminate with `$stop`
- **Modules** (`.cm`) return frozen values — shared, immutable APIs
This gives you concurrency without threads, isolation without overhead, and a natural way to structure game systems.
## Multi-Device Input
The input system normalizes keyboard, mouse, gamepad, and touch into **named actions**. A default action map handles common UI actions (up/down/confirm/cancel), and you define game-specific actions on top.
Features:
- **User pairing** — automatically pair devices to players, or explicit assignment
- **Control stacks** — possess entities, push/pop UI layers, block input propagation
- **Emacs-style keybindings** — `C-a` for ctrl+a, `M-x` for alt+x
- **Gesture recognition** — swipe detection on touch devices
- **Device-aware icons** — show the right button prompts for the current input device
## Asset Management
Prosperon finds and loads assets by name. Ask for `"slime"` and it searches the path for `slime.png`, `slime.qoi`, `slime.ase`, etc. Supported formats:
| Category | Formats |
|----------|---------|
| Images | PNG, QOI, GIF, JPG, Aseprite |
| Audio | WAV, FLAC, MP3, QOA |
| Fonts | TTF |
Assets are cached after first load. The virtual filesystem (PhysFS) supports mounting ZIP archives for mods and DLC.
## Audio
Simple audio playback with PCM caching and voice mixing:
```javascript
var audio = use('sound')
var voice = audio.play("explosion.wav")
voice.vol = 0.5
voice.loop = true
voice.stopped = true // stop it
```
Decodes to a uniform sample rate on load. Multiple voices mix independently.
## Text-Based Everything
Prosperon projects are entirely text:
- Game logic: `.cm` and `.ce` script files
- Config: plain JavaScript objects
- No binary scene files until you ship
This means git diffs are readable, merges are clean, and AI tools can understand your entire project.
## Tiny Footprint
Prosperon is a single executable. No installer, no SDK, no 2GB download. Drop it in your project folder and run. Version it alongside your code. Build on any machine with the same binary.

View File

@@ -0,0 +1,39 @@
---
title: "Prosperon Commercial License"
---
**Last Updated:** February 11, 2025
1. **Grant of License**
Upon purchasing a Prosperon license, Pockle World LLC (*"Licensor"*) grants you (*"Licensee"*) a non-exclusive, worldwide, commercial license to use the Prosperon game engine (the *"Software"*) to develop, publish, and distribute one or more video game or interactive software products (the *"Products"*).
2. **Closed-Source Restriction**
The Software is currently closed-source. Licensee shall not redistribute or disclose the Prosperon engine source code, in whole or in part, unless expressly permitted by Pockle World LLC.
3. **Permitted Usage**
Licensee may create and distribute compiled or packaged Products that incorporate or depend upon the Software, for commercial or non-commercial purposes, without owing royalties or additional fees to Licensor (beyond the initial purchase).
4. **Ownership and Copyright**
The Software is owned by Pockle World LLC. All rights not expressly granted in this License are reserved by Pockle World LLC.
5. **Modifications**
You may modify the scripts of your own Projects, but you may not share or distribute any modifications to the Prosperon engine's proprietary binaries, libraries, or source code itself.
6. **No Warranty**
THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
7. **Limitation of Liability**
IN NO EVENT SHALL LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
8. **Term and Termination**
This License shall remain in effect perpetually, unless you fail to comply with any term of this License. In such event, your rights under this License will terminate immediately without notice from Pockle World LLC.
9. **Support and Updates**
Purchase of the Software includes access to all updates through Prosperon v1.X. Additional support plans may be purchased separately.
10. **Governing Law**
This License shall be governed by and construed in accordance with the laws of the jurisdiction in which Pockle World LLC is located, without regard to conflict-of-law principles.
---
**Contact:** For any questions about this License, please contact Pockle World LLC at [support@pockle.world](mailto:support@pockle.world).

View File

@@ -0,0 +1,43 @@
---
title: "Privacy Policy"
---
**Last Updated:** February 11, 2025
This Privacy Policy explains how Pockle World LLC ("we," "our," or "us") collects, uses, and discloses your personal information in relation to the Prosperon game engine and its associated websites, forums, and services (collectively, the "Services").
## 1. Information We Collect
- **Personal Data:** We may collect your name, email address, and/or payment details when you purchase a Prosperon license or sign up for our support subscriptions.
- **Usage Data:** We may gather anonymized analytics data about feature usage, engine error logs, or user interactions to help improve Prosperon.
## 2. How We Use Your Information
- To process license purchases, manage subscriptions, and provide customer support.
- To communicate product updates, new features, or important changes.
- To improve our Services through analytics and usage metrics.
## 3. Sharing Your Information
- We do not sell your personal information to third parties.
- We may share essential data with payment processors or partners who facilitate license fulfillment, under strict confidentiality obligations.
## 4. Data Security
We employ industry-standard measures to protect your information. However, no data transmission or storage can be guaranteed 100% secure.
## 5. Retention
We retain personal data only as long as necessary for the purposes outlined in this Policy or as required by law.
## 6. International Transfers
Your information may be transferred to and stored in servers outside your country of residence. We take steps to ensure compliance with relevant data protection laws.
## 7. Changes to This Policy
We may occasionally update this Privacy Policy to reflect changes in our practices. The updated version will be posted on this page with a revised "Last Updated" date.
## 8. Contact Us
If you have any questions or concerns about this Privacy Policy, please contact us at [support@pockle.world](mailto:support@pockle.world).

View File

@@ -0,0 +1,108 @@
---
title: "Get Started"
---
## Installation
Grab the Prosperon executable for your platform and drop it in a project folder. Prosperon is a single binary — no installer required.
## Create a Project
Initialize the module system:
```
./cell init
```
This creates `.cell/` with the module shop, bytecode cache, and configuration.
## Your First Module
Create `hello.cm` — a module that returns a frozen API:
```javascript
return {
greet: function(name) {
return `Hello, ${name}!`
}
}
```
## Your First Program
Create `hello.ce` — a program that uses the module:
```javascript
var hello = use('hello')
log.console(hello.greet('Prosperon'))
$delay(_ => $stop(), 0.1)
```
Run it:
```
./cell hello
```
## A Simple Game
Create `config.cm` for window settings:
```javascript
return {
title: "My Game",
width: 1280,
height: 720
}
```
Create `main.ce` as your entry point:
```javascript
var core = use('prosperon/core')
var sprite = use('sprite')
var input = use('input')
// Create a sprite
var player = sprite({
image: "player.png",
pos: {x: 100, y: 100},
width: 32,
height: 32
})
core.start({
width: 1280,
height: 720,
title: "My Game",
update: function(dt) {
// Game logic here
},
render: function() {
// The compositor handles rendering registered sprites
}
})
```
## Example Games
Prosperon ships with several examples:
- **Pong** — classic two-player pong
- **Snake** — snake game with growing tail
- **Tetris** — falling block puzzle
- **Chess** — full chess with move validation
- **Bunnymark** — sprite rendering benchmark
Run any example:
```
./cell examples/pong
```
## Next Steps
- [Read the manual](/docs/) for the full API reference
- [Rendering guide](/docs/rendering/) to understand the compositor
- [Input guide](/docs/input/) for multi-device input handling
- [Join the forum](https://discourse.prosperon.dev) to connect with other developers

3
website/deploy.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
cd "$(dirname "$0")"
hugo --gc --minify

58
website/hugo.toml Normal file
View File

@@ -0,0 +1,58 @@
baseURL = 'https://prosperon.dev/'
languageCode = 'en-us'
title = 'Prosperon Engine'
theme = 'prosperon'
[markup]
[markup.highlight]
noClasses = false
style = 'monokailight'
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true
[menus]
[[menus.main]]
name = 'Features'
pageRef = '/features/'
weight = 10
[[menus.main]]
name = 'Docs'
pageRef = '/docs/'
weight = 20
[[menus.main]]
name = 'Get Started'
pageRef = '/start/'
weight = 30
[[menus.main]]
name = 'Changelog'
pageRef = '/changelog/'
weight = 40
[[menus.main]]
name = 'Forum'
url = 'https://discourse.prosperon.dev'
weight = 50
[menus.main.params]
external = true
[module]
[[module.mounts]]
source = "content"
target = "content"
[[module.mounts]]
source = "../docs"
target = "content/docs"
excludeFiles = "index.md"
[cascade]
[cascade._target]
path = '/docs/**'
[cascade.params]
type = 'docs'
[params]
description = 'A minimal, fast, and flexible 2D game engine built on Pit. Rapid iteration, reflective tooling, and a compositor-driven rendering pipeline.'
email = 'press@pockle.world'
twitter = 'pockleworld'
forum = 'https://discourse.prosperon.dev'
gitea = 'https://gitea.pockle.world'

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
<head>
{{ partial "head.html" . }}
</head>
<body>
{{ partial "header.html" . }}
<main>
{{ block "main" . }}{{ end }}
</main>
{{ partial "footer.html" . }}
</body>
</html>

View File

@@ -0,0 +1,8 @@
{{ define "main" }}
<div class="page-wrapper no-sidebar">
<div class="main-content">
<h1>{{ .Title }}</h1>
{{ .Content }}
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,8 @@
{{ define "main" }}
<div class="page-wrapper no-sidebar">
<div class="main-content">
<h1>{{ .Title }}</h1>
{{ .Content }}
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,53 @@
{{ define "main" }}
<div class="docs-wrapper">
<nav class="docs-sidebar">
<h3><a href="/docs/">Manual</a></h3>
<div class="nav-section-title">Guides</div>
<ul>
<li><a href="/docs/"{{ if eq .RelPermalink "/docs/" }} class="active"{{ end }}>Overview</a></li>
<li><a href="/docs/quickstart/"{{ if eq .RelPermalink "/docs/quickstart/" }} class="active"{{ end }}>Quickstart</a></li>
<li><a href="/docs/tutorial/"{{ if eq .RelPermalink "/docs/tutorial/" }} class="active"{{ end }}>Tutorial</a></li>
</ul>
<div class="nav-section-title">Concepts</div>
<ul>
<li><a href="/docs/rendering/"{{ if eq .RelPermalink "/docs/rendering/" }} class="active"{{ end }}>Rendering</a></li>
<li><a href="/docs/graphics/"{{ if eq .RelPermalink "/docs/graphics/" }} class="active"{{ end }}>Graphics</a></li>
<li><a href="/docs/ops/"{{ if eq .RelPermalink "/docs/ops/" }} class="active"{{ end }}>Compositor</a></li>
<li><a href="/docs/entities/"{{ if eq .RelPermalink "/docs/entities/" }} class="active"{{ end }}>Entities</a></li>
<li><a href="/docs/input/"{{ if eq .RelPermalink "/docs/input/" }} class="active"{{ end }}>Input</a></li>
<li><a href="/docs/resources/"{{ if eq .RelPermalink "/docs/resources/" }} class="active"{{ end }}>Resources</a></li>
<li><a href="/docs/prosperon/"{{ if eq .RelPermalink "/docs/prosperon/" }} class="active"{{ end }}>About Prosperon</a></li>
</ul>
<div class="nav-section-title">API Reference</div>
<ul>
<li><a href="/docs/api/"{{ if eq .RelPermalink "/docs/api/" }} class="active"{{ end }}>API Index</a></li>
</ul>
<div class="nav-section-title">Modules</div>
<ul>
{{ range (where .Site.RegularPages "Section" "docs") }}
{{ if hasPrefix .RelPermalink "/docs/api/modules/" }}
<li><a href="{{ .RelPermalink }}"{{ if eq $.RelPermalink .RelPermalink }} class="active"{{ end }}>{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ul>
<div class="nav-section-title">Types</div>
<ul>
{{ range (where .Site.RegularPages "Section" "docs") }}
{{ if hasPrefix .RelPermalink "/docs/api/types/" }}
<li><a href="{{ .RelPermalink }}"{{ if eq $.RelPermalink .RelPermalink }} class="active"{{ end }}>{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ul>
</nav>
<article class="docs-body">
{{ if .Title }}<h1>{{ .Title }}</h1>{{ end }}
{{ .Content }}
</article>
</div>
{{ end }}

View File

@@ -0,0 +1,53 @@
{{ define "main" }}
<div class="docs-wrapper">
<nav class="docs-sidebar">
<h3><a href="/docs/">Manual</a></h3>
<div class="nav-section-title">Guides</div>
<ul>
<li><a href="/docs/"{{ if eq .RelPermalink "/docs/" }} class="active"{{ end }}>Overview</a></li>
<li><a href="/docs/quickstart/"{{ if eq .RelPermalink "/docs/quickstart/" }} class="active"{{ end }}>Quickstart</a></li>
<li><a href="/docs/tutorial/"{{ if eq .RelPermalink "/docs/tutorial/" }} class="active"{{ end }}>Tutorial</a></li>
</ul>
<div class="nav-section-title">Concepts</div>
<ul>
<li><a href="/docs/rendering/"{{ if eq .RelPermalink "/docs/rendering/" }} class="active"{{ end }}>Rendering</a></li>
<li><a href="/docs/graphics/"{{ if eq .RelPermalink "/docs/graphics/" }} class="active"{{ end }}>Graphics</a></li>
<li><a href="/docs/ops/"{{ if eq .RelPermalink "/docs/ops/" }} class="active"{{ end }}>Compositor</a></li>
<li><a href="/docs/entities/"{{ if eq .RelPermalink "/docs/entities/" }} class="active"{{ end }}>Entities</a></li>
<li><a href="/docs/input/"{{ if eq .RelPermalink "/docs/input/" }} class="active"{{ end }}>Input</a></li>
<li><a href="/docs/resources/"{{ if eq .RelPermalink "/docs/resources/" }} class="active"{{ end }}>Resources</a></li>
<li><a href="/docs/prosperon/"{{ if eq .RelPermalink "/docs/prosperon/" }} class="active"{{ end }}>About Prosperon</a></li>
</ul>
<div class="nav-section-title">API Reference</div>
<ul>
<li><a href="/docs/api/"{{ if eq .RelPermalink "/docs/api/" }} class="active"{{ end }}>API Index</a></li>
</ul>
<div class="nav-section-title">Modules</div>
<ul>
{{ range (where .Site.RegularPages "Section" "docs") }}
{{ if hasPrefix .RelPermalink "/docs/api/modules/" }}
<li><a href="{{ .RelPermalink }}"{{ if eq $.RelPermalink .RelPermalink }} class="active"{{ end }}>{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ul>
<div class="nav-section-title">Types</div>
<ul>
{{ range (where .Site.RegularPages "Section" "docs") }}
{{ if hasPrefix .RelPermalink "/docs/api/types/" }}
<li><a href="{{ .RelPermalink }}"{{ if eq $.RelPermalink .RelPermalink }} class="active"{{ end }}>{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ul>
</nav>
<article class="docs-body">
{{ if .Title }}<h1>{{ .Title }}</h1>{{ end }}
{{ .Content }}
</article>
</div>
{{ end }}

View File

@@ -0,0 +1,18 @@
{{ define "main" }}
<div class="page-wrapper">
<div class="purchase-box">
<img src="/images/prosperon-square.png" alt="Prosperon" />
<span class="support-price">$25</span>
<ul>
<li>No royalties</li>
<li>All updates through v1.x</li>
<li>Account on the Discourse</li>
</ul>
<button class="buy_button">Checkout</button>
</div>
<div class="main-content">
{{ .Content }}
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,7 @@
<footer class="footer-bar">
&copy;{{ now.Year }} <a href="https://pockle.world">Pockle</a>
| <a href="/privacy/">Privacy Policy</a>
| <a href="/license/">License</a>
<br>
<a href="mailto:support@pockle.world">support@pockle.world</a>
</footer>

View File

@@ -0,0 +1,14 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} — {{ .Site.Title }}{{ end }}</title>
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
<link rel="icon" href="/images/favicon.gif" type="image/gif">
<link rel="stylesheet" href="/css/style.css">
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{ .Site.Title }}">
<meta property="og:title" content="{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }}{{ end }}">
<meta property="og:description" content="{{ .Site.Params.description }}">
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:image" content="{{ .Site.BaseURL }}images/prosperon-square.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@{{ .Site.Params.twitter }}">

View File

@@ -0,0 +1,44 @@
<header class="main-header">
<div class="header-content">
<div class="logo-line">
<a class="logo-link" href="/">
<img src="/images/prosperon-square.png" alt="Prosperon Logo">
</a>
<span>by</span>
<a class="logo-link" href="https://pockle.world">
<img src="/images/pockle_logo_small.png" alt="Pockle Logo">
</a>
</div>
<input type="checkbox" id="menu-toggle" />
<label for="menu-toggle" class="hamburger">
<span></span>
<span></span>
<span></span>
</label>
<nav class="header-nav" id="nav-links">
{{ range .Site.Menus.main }}
{{ if .Params.external }}
<a href="{{ .URL }}" target="_blank" rel="noopener">{{ .Name }}</a>
{{ else }}
<a href="{{ .URL }}"{{ if $.IsMenuCurrent "main" . }} class="active"{{ end }}>{{ .Name }}</a>
{{ end }}
{{ end }}
<button class="buy_button">Checkout</button>
</nav>
</div>
</header>
<script type="text/javascript" src="https://static.itch.io/api.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
var buyButtons = document.querySelectorAll('.buy_button');
buyButtons.forEach(function(button) {
Itch.attachBuyButton(button, {
user: "pockleworld",
game: "prosperon"
});
});
});
</script>

View File

@@ -0,0 +1,580 @@
/* =========================================
Custom Fonts
========================================= */
@font-face {
font-family: 'Toolkit';
src: url('/fonts/toolkit.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Grotesk';
src: url('/fonts/overusedgrotesk.woff') format('woff');
font-weight: normal;
font-style: normal;
}
/* =========================================
Global / Body Styles
========================================= */
*, *::before, *::after {
box-sizing: border-box;
}
html body {
min-height: 100vh;
}
body {
background: url("/images/castle.png"), linear-gradient(to bottom, #1678b2, #64d2ff) repeat-x;
background-repeat: repeat-x;
background-position: bottom;
color: #ffffff;
font-family: 'Grotesk', "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
padding-bottom: 200px;
font-size: 18px;
}
/* Links */
a {
color: #ffd600;
text-decoration: underline;
transition: color 0.2s, text-shadow 0.2s;
}
a:hover {
color: #fffa80;
text-shadow: 0 0 5px rgba(255,255,255,0.8);
}
/* Global button */
button, .btn {
display: inline-block;
background-color: #28c0ff;
color: #ffffff;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
transition: background-color 0.3s;
font-size: 18px;
font-family: 'Toolkit';
text-decoration: none;
}
button:hover, .btn:hover {
background-color: #00b5ff;
color: #ffffff;
text-decoration: none;
text-shadow: none;
}
.btn-gold {
background-color: #ffd600;
color: #1a1a1a;
}
.btn-gold:hover {
background-color: #fffa80;
color: #1a1a1a;
}
/* =========================================
Header (Fixed Nav)
========================================= */
.main-header {
position: fixed;
top: 0;
left: 0;
right: 0;
background-image: url("/images/tac.png");
z-index: 1000;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
padding: 2px 0;
display: flex;
align-items: center;
background-repeat: repeat;
animation: scrollBg 240s linear infinite;
}
@keyframes scrollBg {
0% { background-position: 0 0; }
100% { background-position: 100% 1000%; }
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
width: 100%;
}
.logo-line {
display: flex;
align-items: center;
gap: 5px;
}
.logo-line img {
max-height: 50px;
}
.logo-line span {
font-size: 14px;
opacity: 0.7;
}
/* =========================================
Navigation
========================================= */
.header-nav {
display: flex;
position: static;
flex-direction: row;
gap: 8px;
align-items: center;
}
.header-nav a {
display: inline-block;
color: #ffd600;
text-decoration: none;
padding: 4px 16px;
border-radius: 4px;
transition: background-color 0.3s, color 0.3s;
}
.header-nav a:hover {
background-color: #ffd600;
color: #333;
text-shadow: none;
}
.header-nav a.active {
background-color: rgba(255, 214, 0, 0.2);
}
/* =========================================
Page Wrapper
========================================= */
.page-wrapper {
margin-top: 60px;
padding: 20px;
max-width: 900px;
margin-left: auto;
margin-right: auto;
display: grid;
grid-template-columns: 640px auto;
grid-gap: 20px;
grid-template-areas: "main purchase";
align-items: start;
justify-items: stretch;
}
.page-wrapper.no-sidebar {
display: block;
max-width: 800px;
}
.main-content {
grid-area: main;
}
/* =========================================
Purchase Box
========================================= */
.purchase-box {
background-image: url("/images/rect.png");
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
margin-bottom: 0;
max-width: 300px;
font-size: 16px;
grid-area: purchase;
position: sticky;
top: 80px;
}
.purchase-box ul {
list-style: disc inside;
margin: 0;
padding: 0;
margin-bottom: 10px;
}
.purchase-box li {
margin-bottom: 0;
}
.purchase-box img {
max-width: 100%;
height: auto;
display: block;
margin: auto;
}
.purchase-box h3 {
text-align: center;
margin: auto;
}
.support-price {
display: block;
text-align: center;
color: #ffd600;
font-size: 1.8em;
line-height: 1.5em;
}
/* =========================================
Support Subscriptions
========================================= */
.support-subscriptions {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 40px;
}
.subscription-box {
background-color: #1678b2;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
box-sizing: border-box;
flex: 1 1 100%;
line-height: 1em;
}
.subscription-box ul {
list-style: disc inside;
margin: 0;
padding: 0;
margin-bottom: 10px;
}
/* =========================================
Typography
========================================= */
h1, h2, h3, h4 {
color: #ffffff;
margin: 0.5em 0;
line-height: 1.3;
font-family: 'Toolkit', Helvetica, Arial, sans-serif;
}
h1 { font-size: 3em; }
h2 { font-size: 2em; }
h3 { font-size: 1.5em; }
h4 { font-size: 1.2em; }
p, li {
color: #ffffff;
margin-bottom: 1em;
line-height: 1.4;
}
/* Hero & Sections */
.hero {
margin-bottom: 40px;
text-align: left;
}
.hero img {
max-width: 100%;
height: auto;
display: block;
margin-bottom: 20px;
}
.section {
margin-bottom: 40px;
}
hr {
border: none;
border-top: 1px solid #ffd600;
}
/* Code */
code {
font-family: 'Courier New', Courier, monospace;
font-size: 0.9em;
background: rgba(0, 0, 0, 0.3);
padding: 0.15em 0.4em;
border-radius: 3px;
}
pre {
background: rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 214, 0, 0.2);
border-radius: 6px;
padding: 1rem 1.25rem;
overflow-x: auto;
margin: 1rem 0 1.5rem;
line-height: 1.5;
}
pre code {
background: none;
padding: 0;
font-size: 0.85rem;
}
/* Tables */
table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0 1.5rem;
font-size: 0.95rem;
}
th, td {
padding: 0.5rem 0.75rem;
text-align: left;
border-bottom: 1px solid rgba(255, 214, 0, 0.3);
}
th {
font-weight: bold;
border-bottom: 2px solid #ffd600;
font-family: 'Toolkit';
}
/* =========================================
Feature Cards
========================================= */
.feature-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin: 30px 0;
}
.feature-card {
background-image: url("/images/rect.png");
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
.feature-card h3 {
margin-top: 0;
font-size: 1.3em;
color: #ffd600;
}
.feature-card p {
font-size: 0.95em;
line-height: 1.4;
}
/* =========================================
Architecture Diagram
========================================= */
.arch-box {
background: rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 214, 0, 0.3);
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.6;
overflow-x: auto;
white-space: pre;
}
/* =========================================
Footer
========================================= */
.footer-bar {
text-align: center;
padding: 20px 0;
font-size: 14px;
}
/* =========================================
Changelog
========================================= */
.changelog-entry {
margin-bottom: 1.5rem;
}
.changelog-header {
display: flex;
align-items: baseline;
font-weight: bold;
margin-bottom: 0.5rem;
gap: 10px;
}
.changelog-header .dashed-line {
flex: 1;
border: none;
border-top: 2px dashed #ffd600;
margin: 0;
}
.changelog-header .date {
color: #ffd600;
font-size: 0.9em;
}
/* =========================================
Docs Layout
========================================= */
.docs-wrapper {
margin-top: 60px;
padding: 20px;
max-width: 1100px;
margin-left: auto;
margin-right: auto;
display: flex;
gap: 30px;
}
.docs-sidebar {
width: 220px;
flex-shrink: 0;
position: sticky;
top: 80px;
max-height: calc(100vh - 100px);
overflow-y: auto;
}
.docs-sidebar h3 {
font-size: 1em;
margin-bottom: 0.5em;
color: #ffd600;
}
.docs-sidebar ul {
list-style: none;
padding: 0;
margin: 0 0 1em 0;
}
.docs-sidebar li {
margin-bottom: 0;
}
.docs-sidebar a {
display: block;
padding: 3px 0;
font-size: 0.9em;
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: color 0.15s;
}
.docs-sidebar a:hover {
color: #ffd600;
text-shadow: none;
}
.docs-sidebar a.active {
color: #ffd600;
font-weight: bold;
}
.docs-sidebar .nav-section-title {
font-size: 0.75em;
text-transform: uppercase;
letter-spacing: 0.05em;
color: rgba(255, 214, 0, 0.6);
margin-top: 1em;
margin-bottom: 0.25em;
}
.docs-body {
flex: 1;
min-width: 0;
}
.docs-body h1 {
font-size: 2.2em;
margin-bottom: 0.5em;
}
.docs-body h2 {
font-size: 1.5em;
margin-top: 2em;
padding-bottom: 0.3em;
border-bottom: 1px solid rgba(255, 214, 0, 0.3);
}
.docs-body h3 {
font-size: 1.2em;
margin-top: 1.5em;
}
.docs-body img {
max-width: 100%;
height: auto;
}
/* =========================================
Hamburger Menu (Hidden on Desktop)
========================================= */
#menu-toggle,
.hamburger {
display: none;
}
#menu-toggle:checked ~ .header-nav {
display: flex;
}
/* =========================================
Mobile Overrides (max-width: 899px)
========================================= */
@media (max-width: 899px) {
.page-wrapper {
display: block;
}
.purchase-box {
margin: 0 auto 20px;
position: static;
}
.hero-image {
display: none;
}
.feature-grid {
grid-template-columns: 1fr;
}
/* Hamburger nav */
.header-nav {
display: none;
flex-direction: column;
padding: 10px;
position: absolute;
right: 20px;
top: 60px;
background-image: url("/images/rect.png");
border-radius: 8px;
}
#menu-toggle {
display: none;
}
.hamburger {
display: inline-block;
cursor: pointer;
width: 30px;
height: 24px;
margin-left: 10px;
}
.hamburger span {
display: block;
height: 3px;
margin-bottom: 5px;
background: #ffd600;
transition: 0.3s ease;
}
/* Docs sidebar */
.docs-wrapper {
flex-direction: column;
}
.docs-sidebar {
width: 100%;
position: static;
max-height: none;
border-bottom: 1px solid rgba(255, 214, 0, 0.3);
padding-bottom: 15px;
margin-bottom: 15px;
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Some files were not shown because too many files have changed in this diff Show More