Compare commits
No commits in common. "main" and "pages" have entirely different histories.
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"version": "0.0.1",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ludic-web",
|
||||
"runtimeExecutable": "python3",
|
||||
"runtimeArgs": ["-m", "http.server", "8123", "-d", "build/web"],
|
||||
"port": 8123
|
||||
},
|
||||
{
|
||||
"name": "ludic-docs",
|
||||
"runtimeExecutable": "python3",
|
||||
"runtimeArgs": ["-m", "http.server", "8124", "-d", "build/pages"],
|
||||
"port": 8124
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# EditorConfig — machine-enforced formatting conventions for the Ludic repo.
|
||||
# Mirrors ludic-fmt (tools/ludic-tools/fmt.ludic), which re-indents with a
|
||||
# 2-space unit and normalises to LF/UTF-8. https://editorconfig.org
|
||||
root = true
|
||||
|
||||
# Ludic is the primary language; 2-space indent, LF, UTF-8, no trailing
|
||||
# whitespace, and a final newline everywhere by default.
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# The docgen/asset tooling is Python — PEP 8's 4-space indent.
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
# Markdown uses two trailing spaces as a hard line break; keep them.
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
# Default owner for everything in the repo. Forgejo requests review from these
|
||||
# owners on matching pull requests. See:
|
||||
# https://forgejo.org/docs/latest/user/code-owners/
|
||||
* @orkun
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
---
|
||||
name: "Bug report"
|
||||
about: "Something in the compiler, runtime, or tooling behaves incorrectly"
|
||||
title: "bug: "
|
||||
labels:
|
||||
- bug
|
||||
---
|
||||
|
||||
## What happened
|
||||
|
||||
<!-- A clear description of the incorrect behaviour. -->
|
||||
|
||||
## Minimal reproduction
|
||||
|
||||
<!-- The smallest .ludic program (or command) that triggers it. -->
|
||||
|
||||
```ludic
|
||||
```
|
||||
|
||||
## Expected vs actual
|
||||
|
||||
- **Expected:**
|
||||
- **Actual:**
|
||||
|
||||
## Environment
|
||||
|
||||
- Command used (e.g. `bin/ludic build foo.ludic --headless`):
|
||||
- Target (native macOS / headless / web-wasm):
|
||||
- Commit (`git rev-parse --short HEAD`):
|
||||
- OS / arch:
|
||||
|
||||
## Notes
|
||||
|
||||
<!-- Stack traces, generated IR, or anything else that helps. -->
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
name: "Cleanup / DX"
|
||||
about: "Repo hygiene, tooling, docs, or developer-experience improvements"
|
||||
title: ""
|
||||
labels:
|
||||
- cleanup
|
||||
- dx
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
<!-- What friction or inconsistency exists today? -->
|
||||
|
||||
## Proposal
|
||||
|
||||
<!-- What to change. Keep it scoped and low-risk. -->
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ]
|
||||
- [ ]
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
name: "Proposal"
|
||||
about: "Propose new language, stdlib, or runtime surface"
|
||||
title: "Proposal: "
|
||||
labels:
|
||||
- proposal
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
<!-- One or two sentences: what should exist that doesn't today. -->
|
||||
|
||||
## Motivation
|
||||
|
||||
<!-- What can't be done cleanly now? Who needs this and why? -->
|
||||
|
||||
## Proposed surface
|
||||
|
||||
<!-- The namespace/API or syntax you propose. Show it in use. -->
|
||||
|
||||
```ludic
|
||||
```
|
||||
|
||||
## Determinism & backends
|
||||
|
||||
<!-- Ludic's runtime is deterministic fixed-point. Does this proposal stay
|
||||
replay-safe? Does it work on native AND web-wasm, or is it host-gated? -->
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
## Open questions
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<!-- Thanks for contributing to Ludic! Please fill in the checklist below. -->
|
||||
|
||||
## What & why
|
||||
|
||||
<!-- What does this change and why? Link the issue: Closes #NN -->
|
||||
|
||||
Closes #
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] `bin/ludic-dev test` passes.
|
||||
- [ ] For compiler/runtime changes: `bin/ludic-dev reseed && bin/ludic-dev bootstrap-cfree`
|
||||
still reaches the self-hosting fixpoint with no C compiler in the loop.
|
||||
- [ ] `ludic-fmt` leaves the touched files unchanged (2-space, LF, UTF-8).
|
||||
- [ ] New/changed stdlib symbols are documented under `docs/language/**` and
|
||||
registered in `tools/docgen/inventory.json`
|
||||
(`bin/ludic-dev docs-gen && bin/ludic-dev docs-check build/pages` passes).
|
||||
- [ ] Commits follow [Conventional Commits](https://www.conventionalcommits.org).
|
||||
- [ ] No new C / Python / JS in tooling (Ludic only), and no generated
|
||||
artifacts committed outside `build/` / `bin/`.
|
||||
|
||||
## Notes for reviewers
|
||||
|
||||
<!-- Anything reviewers should look at closely, or follow-ups deferred. -->
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
name: bootstrap
|
||||
|
||||
# The headline guarantee of a self-hosted, C-free toolchain: assembled from the
|
||||
# checked-in IR seed with clang alone, the compiler recompiles its own source
|
||||
# and reproduces that seed **byte-for-byte**. A regression here (a compiler
|
||||
# change that was never reseeded) is otherwise invisible until someone runs it
|
||||
# locally. This job fails the moment out.ll != seed.ll.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
cfree-fixpoint:
|
||||
runs-on: docker
|
||||
container: node:20-bookworm
|
||||
steps:
|
||||
- name: Install clang-16
|
||||
run: |
|
||||
set -eu
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq --no-install-recommends clang-16 git ca-certificates
|
||||
clang-16 --version | head -1
|
||||
|
||||
- name: Check out the triggering commit
|
||||
env:
|
||||
# the repository that triggered the run, so a fork or a mirror tests itself
|
||||
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
run: |
|
||||
set -eu
|
||||
git config --global --add safe.directory '*'
|
||||
git clone "$REPO_URL" .
|
||||
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
||||
git log --oneline -1
|
||||
# See ci.yml for why the Linux build injects the stdio shim via LUDIC_CC.
|
||||
echo "LUDIC_CC=clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll -lm" >> "$GITHUB_ENV"
|
||||
echo "LUDIC_HOME=$(pwd)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Bootstrap x from the seed
|
||||
run: |
|
||||
set -eu
|
||||
mkdir -p bin
|
||||
clang-16 tools/ci/linux_stdio_shim.ll selfhost/ludicc.seed.ll -o bin/ludicc
|
||||
bin/ludicc tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
||||
|
||||
- name: Rebuild the compiler from the seed and assert byte-identity
|
||||
# `ludic-dev bootstrap-cfree` assembles the seed with clang, has that seed
|
||||
# compiler recompile selfhost.ludic to out.ll, and `cmp`s out.ll against
|
||||
# the checked-in seed. It returns non-zero if they differ — i.e. if the
|
||||
# seed is stale relative to the compiler source.
|
||||
run: bin/ludic-dev bootstrap-cfree
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
name: ci
|
||||
|
||||
# Build the language toolchain from its IR seed and run the regression suites on
|
||||
# every push to main and every pull request. Until this landed the only workflow
|
||||
# was docs.yml, so nothing gated a change on `ludic-dev test` / `ludic-dev test-tools` or on the
|
||||
# compiler even building from the seed. See also bootstrap.yml, which proves the
|
||||
# C-free self-rebuild reproduces the seed byte-for-byte.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
# Same runner label the docs workflow uses — the self-hosted Forgejo runner
|
||||
# advertises `docker`, not the GitHub-ism `ubuntu-latest`.
|
||||
runs-on: docker
|
||||
# Reuse the runner's own base image (Debian bookworm with git + node already
|
||||
# present) and add just the one thing the toolchain needs: a modern clang
|
||||
# (LLVM 16 — the IR uses opaque pointers, so clang 15+ is required). The docs
|
||||
# generator and its guards are now Ludic, so the job carries no Python. A
|
||||
# prebuilt image with clang baked in is the obvious future speed-up (see
|
||||
# issue #33's packaging work).
|
||||
container: node:20-bookworm
|
||||
steps:
|
||||
- name: Install clang-16
|
||||
run: |
|
||||
set -eu
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq --no-install-recommends clang-16 git ca-certificates
|
||||
clang-16 --version | head -1
|
||||
|
||||
- name: Check out the triggering commit
|
||||
env:
|
||||
# the repository that triggered the run, so a fork or a mirror tests itself
|
||||
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
run: |
|
||||
set -eu
|
||||
git config --global --add safe.directory '*'
|
||||
git clone "$REPO_URL" .
|
||||
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
||||
git log --oneline -1
|
||||
# The toolchain is macOS-first; on this Linux runner it links against a
|
||||
# tiny C-free IR shim that supplies the Darwin standard-stream globals
|
||||
# (__stdoutp/__stderrp) over glibc's stdout/stderr. Injected through
|
||||
# LUDIC_CC so every clang invocation — the seed bootstrap, `ludic-dev build`,
|
||||
# and each compiled test program — picks it up. Absolute path so it
|
||||
# still resolves if a step changes directory.
|
||||
echo "LUDIC_CC=clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll -lm" >> "$GITHUB_ENV"
|
||||
echo "LUDIC_HOME=$(pwd)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Bootstrap the toolchain from the IR seed (clang only)
|
||||
run: |
|
||||
set -eu
|
||||
# clang assembles + links the compiler's own checked-in IR seed (plus
|
||||
# the Linux stdio shim), then that seed builds the whole toolchain into
|
||||
# bin/ — ludicc, ludic, x, ludic-fmt, ludic-lsp. No C compiler, no
|
||||
# pre-built binaries: the language builds itself from source + seed.
|
||||
mkdir -p bin
|
||||
clang-16 tools/ci/linux_stdio_shim.ll selfhost/ludicc.seed.ll -o bin/ludicc
|
||||
bin/ludicc tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
||||
bin/ludic-dev build
|
||||
|
||||
- name: Regression suite (ludic-dev test)
|
||||
run: bin/ludic-dev test
|
||||
|
||||
- name: Editor-toolchain suite (ludic-dev test-tools)
|
||||
# Grammar/lexer/vocabulary sync, ludic-fmt idempotence (the project's
|
||||
# formatting contract — hand alignment is deliberately preserved, so the
|
||||
# gate is fmt(fmt(x)) == fmt(x), not fmt(x) == x), and the JSON/XML editor
|
||||
# assets. Cross-file LSP behaviour and the golden renders are macOS-ABI
|
||||
# bound and skip here — visibly — until the runtime's directory walk and
|
||||
# windowing are portable.
|
||||
run: bin/ludic-dev test-tools
|
||||
|
||||
- name: Docs cover the implementation
|
||||
run: |
|
||||
set -eu
|
||||
# The whole docs toolchain is written in Ludic and runs through x —
|
||||
# no Python anywhere. check-impl / check-vocabulary / check-docs guard
|
||||
# the sources; docs-gen builds the site and docs-check is its coverage
|
||||
# + integrity guard. (check-vocabulary also runs in `ludic-dev test-tools`.)
|
||||
bin/ludic-dev check-impl
|
||||
bin/ludic-dev check-vocabulary
|
||||
bin/ludic-dev check-docs
|
||||
bin/ludic-dev docs-gen --out build/pages
|
||||
bin/ludic-dev docs-check build/pages
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
name: commit-lint
|
||||
|
||||
# Enforce Conventional Commits in CI, as a backstop to the local commit-msg hook
|
||||
# (which a contributor only gets after `git config core.hooksPath tools/git-hooks`).
|
||||
# Lints every new commit's summary line against tools/git-hooks/lib.sh — the same
|
||||
# rule the hook uses, so the two can never drift.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
conventional-commits:
|
||||
runs-on: docker
|
||||
container: node:20-bookworm
|
||||
steps:
|
||||
- name: Check out with history
|
||||
env:
|
||||
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
run: |
|
||||
set -eu
|
||||
git config --global --add safe.directory '*'
|
||||
# Full clone so both endpoints of the range are present.
|
||||
git clone "$REPO_URL" .
|
||||
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
||||
|
||||
- name: Lint the new commits
|
||||
env:
|
||||
BEFORE: ${{ github.event.before }}
|
||||
BASE: ${{ github.base_ref }}
|
||||
run: |
|
||||
set -eu
|
||||
# Pick the range of *new* commits to lint:
|
||||
# - pull_request: base branch .. this commit
|
||||
# - push: the pushed range (event.before .. this commit)
|
||||
# - new branch / unknown: just the tip commit
|
||||
# `event.before` is only usable if it still resolves: a force-push
|
||||
# rewrites (and a gc can remove) the commit it names, which made this
|
||||
# job fail with "Invalid revision range" on an otherwise clean push.
|
||||
# Fall back to the tip commit in that case.
|
||||
if [ -n "${BASE:-}" ]; then
|
||||
git fetch --quiet origin "${BASE}" 2>/dev/null || true
|
||||
RANGE="origin/${BASE}..${GITHUB_SHA}"
|
||||
elif [ -n "${BEFORE:-}" ] && ! printf '%s' "$BEFORE" | grep -qE '^0+$' \
|
||||
&& git cat-file -e "${BEFORE}^{commit}" 2>/dev/null; then
|
||||
RANGE="${BEFORE}..${GITHUB_SHA}"
|
||||
else
|
||||
RANGE="${GITHUB_SHA}~1..${GITHUB_SHA}"
|
||||
fi
|
||||
echo "linting range: $RANGE"
|
||||
sh tools/git-hooks/lint-range.sh "$RANGE"
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
name: docs
|
||||
|
||||
# Rebuild the documentation site and publish it to the `pages` branch on every
|
||||
# push to main that touches the docs sources. The site is generated from the
|
||||
# per-symbol source of truth in docs/language/** — nothing is hand-edited on the
|
||||
# pages branch.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'tools/docgen/**'
|
||||
- 'tools/ludic-cli/**'
|
||||
# the site publishes the installer, so a change to it has to redeploy the
|
||||
# site — otherwise a fixed install.sh sits in main while the old one is
|
||||
# still what `curl … | sh` fetches
|
||||
- 'install.sh'
|
||||
- '.forgejo/workflows/docs.yml'
|
||||
workflow_dispatch: {}
|
||||
|
||||
# Deploying is a force-push of an orphan branch, so two runs racing can land out
|
||||
# of order and leave `pages` holding the older build — the site would silently
|
||||
# go backwards with both runs green. Serialise them, and let a newer push cancel
|
||||
# an older one that is still building rather than queue behind it.
|
||||
concurrency:
|
||||
group: pages-deploy
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
# Match the label our Forgejo runner actually advertises. The runner is
|
||||
# Docker-capable (it provides the `container:` below); `ubuntu-latest` is a
|
||||
# GitHub-ism this runner does not register, so a job requesting it sits in
|
||||
# "Waiting" forever with "no online runner found matching this label".
|
||||
runs-on: docker
|
||||
# The generator is now Ludic, so this builds the toolchain from its IR seed
|
||||
# (clang assembles the seed into bin/ludicc, which compiles bin/ludic) exactly
|
||||
# like the ci workflow, then runs `ludic-dev docs-gen`. node:20-bookworm carries git
|
||||
# for the clone + publish; clang-16 is the only extra the bootstrap needs.
|
||||
container: node:20-bookworm
|
||||
steps:
|
||||
- name: Install clang-16
|
||||
run: |
|
||||
set -eu
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq --no-install-recommends clang-16 git ca-certificates
|
||||
clang-16 --version | head -1
|
||||
|
||||
- name: Generate the documentation site
|
||||
env:
|
||||
SOURCE_REF: ${{ github.ref_name }}
|
||||
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
run: |
|
||||
set -eu
|
||||
git config --global --add safe.directory '*'
|
||||
git clone --depth 1 --branch "${SOURCE_REF:-main}" "$REPO_URL" src
|
||||
cd src
|
||||
# The toolchain is macOS-first; on this Linux runner it links against a
|
||||
# tiny C-free IR shim supplying the Darwin stdout/stderr globals over
|
||||
# glibc's, injected through LUDIC_CC. docs-gen is a pure CLI (no
|
||||
# windowing), so the C-free bootstrap is all it needs.
|
||||
export LUDIC_CC="clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll -lm"
|
||||
export LUDIC_HOME="$(pwd)"
|
||||
mkdir -p bin
|
||||
clang-16 tools/ci/linux_stdio_shim.ll selfhost/ludicc.seed.ll -o bin/ludicc
|
||||
bin/ludicc tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
||||
bin/ludic-dev docs-gen --out ../public
|
||||
bin/ludic-dev docs-check ../public
|
||||
cd ..
|
||||
echo "--- generated files ---"
|
||||
ls -la public
|
||||
|
||||
- name: Publish to the pages branch
|
||||
env:
|
||||
PAGES_TOKEN: ${{ secrets.PAGES_TOKEN }}
|
||||
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SOURCE_SHA: ${{ github.sha }}
|
||||
SERVER_URL: ${{ github.server_url }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -eu
|
||||
TOKEN="${PAGES_TOKEN:-${AUTO_TOKEN:-}}"
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "::error::No deploy token. Add a repo secret PAGES_TOKEN (write access) or enable the automatic Actions token for pushes."
|
||||
exit 1
|
||||
fi
|
||||
cd public
|
||||
git init -q -b pages
|
||||
git config user.name "ludic-docs-bot"
|
||||
git config user.email "docs@workshopsoft.io"
|
||||
git add -A
|
||||
git commit -q -m "docs: regenerate site from ${SOURCE_SHA}"
|
||||
# the same server and repository the run came from, with the token spliced in
|
||||
git push -f "${SERVER_URL%%://*}://ludic-docs-bot:${TOKEN}@${SERVER_URL#*://}/${REPO}.git" pages
|
||||
echo "published $(git rev-parse --short HEAD) to pages"
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
name: release
|
||||
|
||||
# Cutting a release is `ludic-dev release` + `git push --tags`; everything after that
|
||||
# happens here. Before this workflow existed the artifacts were built on whatever
|
||||
# machine the maintainer happened to be sitting at, from whatever was in bin/ at
|
||||
# the time, with no checksums and nothing proving the tagged tree even passed its
|
||||
# tests. Now the tag is the trigger and CI is the only thing that publishes.
|
||||
#
|
||||
# The job refuses to publish unless:
|
||||
# * the tag matches the VERSION file in the tagged tree,
|
||||
# * CHANGELOG.md has a section for that version (it becomes the release notes),
|
||||
# * the toolchain builds from the IR seed and the whole suite passes,
|
||||
# * the C-free bootstrap still reproduces the seed byte-for-byte.
|
||||
#
|
||||
# Needs a repository secret FORGEJO_TOKEN with write access to releases.
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag to publish (e.g. v0.4.0)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: docker
|
||||
container: node:20-bookworm
|
||||
steps:
|
||||
- name: Install clang-16
|
||||
run: |
|
||||
set -eu
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq --no-install-recommends clang-16 git ca-certificates curl
|
||||
clang-16 --version | head -1
|
||||
|
||||
- name: Check out the tag
|
||||
env:
|
||||
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
INPUT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
set -eu
|
||||
git config --global --add safe.directory '*'
|
||||
# A full clone: `git archive` needs the tag object, and the tarball is
|
||||
# built from the tag rather than from the working tree.
|
||||
git clone "$REPO_URL" .
|
||||
TAG="${INPUT_TAG:-${GITHUB_REF_NAME}}"
|
||||
git checkout "$TAG"
|
||||
echo "TAG=$TAG" >> "$GITHUB_ENV"
|
||||
# See ci.yml for why the Linux build injects the stdio shim via LUDIC_CC.
|
||||
echo "LUDIC_CC=clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll -lm" >> "$GITHUB_ENV"
|
||||
echo "LUDIC_HOME=$(pwd)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: The tag, VERSION and CHANGELOG must agree
|
||||
run: |
|
||||
set -eu
|
||||
VERSION="$(cat VERSION)"
|
||||
if [ "$TAG" != "v${VERSION}" ]; then
|
||||
echo "::error::tag ${TAG} does not match VERSION (${VERSION})"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "^## v${VERSION} " CHANGELOG.md; then
|
||||
echo "::error::CHANGELOG.md has no '## v${VERSION}' section to use as release notes"
|
||||
exit 1
|
||||
fi
|
||||
echo "publishing ${TAG}"
|
||||
|
||||
- name: Build the toolchain from the IR seed (clang only)
|
||||
run: |
|
||||
set -eu
|
||||
mkdir -p bin
|
||||
clang-16 tools/ci/linux_stdio_shim.ll selfhost/ludicc.seed.ll -o bin/ludicc
|
||||
bin/ludicc tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
||||
bin/ludic-dev build
|
||||
|
||||
- name: The tagged tree must pass its own suites
|
||||
run: |
|
||||
set -eu
|
||||
bin/ludic-dev test
|
||||
bin/ludic-dev test-tools
|
||||
bin/ludic-dev bootstrap-cfree
|
||||
|
||||
- name: Publish the release
|
||||
env:
|
||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
||||
LUDIC_FORGEJO_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
run: |
|
||||
set -eu
|
||||
if [ -z "${FORGEJO_TOKEN:-}" ]; then
|
||||
echo "::error::No FORGEJO_TOKEN secret; cannot create the release."
|
||||
exit 1
|
||||
fi
|
||||
# ludic-dev publish builds dist/ (source tarball from the tag, this host's
|
||||
# toolchain, SHA256SUMS), takes the notes from the CHANGELOG section,
|
||||
# and creates the release. Re-running it only adds missing assets, so
|
||||
# a maintainer can afterwards attach the macOS toolchain from a Mac
|
||||
# with the same command.
|
||||
bin/ludic-dev publish "$TAG"
|
||||
1
.gitattributes
vendored
|
|
@ -1 +0,0 @@
|
|||
packages/*/lib/** filter=lfs diff=lfs merge=lfs -text
|
||||
62
.gitignore
vendored
|
|
@ -1,62 +0,0 @@
|
|||
# Generated build tree: LLVM IR, objects, compiled apps, the headless render
|
||||
# (build/out.ppm) and the docs site all land under build/ (see `bin/ludic-dev build` /
|
||||
# `bin/ludic clean`). Root-anchored so a source dir named "build" elsewhere is never
|
||||
# accidentally ignored. Nothing is written to the repo root any more.
|
||||
/build/
|
||||
|
||||
# Archives are never tracked: the Kenney art is committed *extracted* under
|
||||
# assets/kenney/**/ (PNG/tmx/License), while the original download .zips and any
|
||||
# packaged plugin .zip are local-only build inputs/outputs.
|
||||
*.zip
|
||||
|
||||
# the toolchain binaries (ludicc, ludic, ludic-dev, ludic-fmt, ludic-lsp) — all built
|
||||
# into bin/ by the one-line bootstrap + `bin/ludic-dev build`; never checked in. The
|
||||
# only thing published is the source and the LLVM-IR seed (selfhost/ludicc.seed.ll).
|
||||
/bin/
|
||||
|
||||
# package manager (issue #63): the per-project linked view into the global
|
||||
# content-addressed store, and the optional hermetic copy from `ludic vendor`. Both
|
||||
# are regenerated by `ludic get` / `ludic vendor` — package.ludic + package.lock.ludic
|
||||
# are the tracked source of truth, so these stay out of the tree.
|
||||
ludic_modules/
|
||||
vendor/
|
||||
|
||||
# editor toolchain build artifacts
|
||||
tools/editors/vscode/node_modules/
|
||||
tools/editors/vscode/*.vsix
|
||||
tools/editors/jetbrains/.gradle/
|
||||
tools/editors/jetbrains/build/
|
||||
tools/editors/jetbrains/.kotlin/
|
||||
|
||||
# IntelliJ plugin SDK sandbox (tools/editors/jetbrains)
|
||||
.intellijPlatform/
|
||||
.idea/
|
||||
|
||||
# macOS Finder metadata
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
|
||||
# Python bytecode cache from the docgen / release tooling
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Release artifacts produced by `ludic-dev release`
|
||||
/dist/
|
||||
|
||||
# Build/release tarballs anywhere in the tree. `git -C <repo> archive -o foo.tgz`
|
||||
# resolves -o relative to the repo, not the caller's directory, so a stray
|
||||
# archive lands in the root and a blanket `git add -A` will commit it.
|
||||
*.tar.gz
|
||||
*.tgz
|
||||
|
||||
# The CC0 Poly Haven downloads are fetched, not committed (`ludic-dev fetch-assets`
|
||||
# reads the manifest that ships with the renderer, packages/ludic.render3d/assets.manifest,
|
||||
# so a game outside this repository fetches the same set with `ludic assets`).
|
||||
assets/polyhaven/hdri/
|
||||
assets/polyhaven/textures/
|
||||
assets/polyhaven/models/
|
||||
# `ludic run` beside an example writes its binary into a build/ there
|
||||
examples/**/build/
|
||||
|
||||
# a package native/build.sh writes its objects under the package (phase 15)
|
||||
packages/*/build/
|
||||
1260
CHANGELOG.md
|
|
@ -1,27 +0,0 @@
|
|||
# Code of Conduct
|
||||
|
||||
## Our standard
|
||||
|
||||
Ludic is a small, focused project. Everyone taking part — filing issues,
|
||||
proposing changes, reviewing, or discussing — is expected to keep it a
|
||||
respectful, harassment-free place to collaborate, regardless of background or
|
||||
experience level.
|
||||
|
||||
**Do:** be direct and kind, critique code rather than people, assume good
|
||||
faith, and keep discussion technical and on-topic.
|
||||
|
||||
**Don't:** harass, insult, demean, or discriminate; post others' private
|
||||
information; or derail threads with personal attacks.
|
||||
|
||||
## Scope
|
||||
|
||||
This applies to all project spaces — the issue tracker, pull requests, and any
|
||||
official channels — and to public spaces when someone is representing the
|
||||
project.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Report unacceptable behaviour to the maintainer at **orkun@workshopsoft.io**.
|
||||
Reports are handled confidentially. Maintainers may edit or remove contributions
|
||||
that violate this code, and may temporarily or permanently bar anyone whose
|
||||
behaviour is judged harmful.
|
||||
382
COMPILING.md
|
|
@ -1,382 +0,0 @@
|
|||
# Compiling Ludic
|
||||
|
||||
> **Note:** `ludicc` is **written in Ludic** (`selfhost/*.ludic`) and built from a
|
||||
> checked-in IR seed — the C compiler this document once described has been
|
||||
> deleted. The native pipeline below (Ludic → LLVM IR → object → binary) is
|
||||
> unchanged. `ludicc` drives clang itself (via an `os_system` intrinsic), so
|
||||
> `ludicc app.ludic -o bin/app` and `--emit-llvm` work directly. `--fmt` is
|
||||
> reimplemented as a lex+parse gate (the doc-check hook). The `--target`/
|
||||
> cross-compile and `--shared` paths are still features of the old C driver not
|
||||
> yet re-implemented on the self-hosted toolchain. See the
|
||||
> [Bootstrap deep-dive](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Bootstrap) §5.7 on the wiki.
|
||||
>
|
||||
> Most people never invoke `ludicc` directly: the `ludic` CLI drives it.
|
||||
>
|
||||
> ```bash
|
||||
> curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh # the toolchain, into ~/.ludic
|
||||
> ludic new mygame && cd mygame
|
||||
> ludic run # compile + run
|
||||
> ludic build --headless # compile, deterministic render
|
||||
> ```
|
||||
>
|
||||
> From a clean checkout, the compiler and the CLI come up in two lines and the
|
||||
> CLI does the rest (run it from the repository root):
|
||||
>
|
||||
> ```bash
|
||||
> # one-time bootstrap: clang assembles the seed, then ludicc compiles bin/ludic
|
||||
> mkdir -p bin && clang selfhost/ludicc.seed.ll -o bin/ludicc
|
||||
> bin/ludicc --unsafe --globals tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
||||
> bin/ludic-dev build # the whole toolchain into bin/
|
||||
> # (ludicc, ludic, ludic-fmt, ludic-lsp)
|
||||
> bin/ludicc examples/games/snake.ludic -o bin/snake # the compiler, directly
|
||||
> bin/ludic build examples/games/snake.ludic # or through the CLI
|
||||
> bin/ludic help # every command
|
||||
> ```
|
||||
>
|
||||
> A `.ludic` file with handlers is a game and links windowed by default;
|
||||
> `--headless` and `--windowed` force the mode. The engine runtime
|
||||
> (`runtime/native/cocoa.ll`, the spliced `runtime/native/*.ludic`) and the
|
||||
> bundled `ludic.*` packages are found under the **install root**: `$LUDIC_HOME`
|
||||
> if set, otherwise derived from the binary's own location — the parent of its
|
||||
> `bin/` directory, which is both `~/.ludic` for an install and the repository
|
||||
> root for a checkout. `$LUDIC_CC` overrides the assembler/linker (default
|
||||
> `clang`).
|
||||
|
||||
|
||||
`ludicc` is a compiler, not a translator. It lexes, parses, checks and lowers
|
||||
Ludic to **LLVM IR itself**, then hands that IR to the system toolchain to be
|
||||
assembled and linked. There is no C in the middle: no generated `.c` file, no C
|
||||
runtime compiled alongside your game, and no transpiling step you could inspect
|
||||
and find your program rewritten in another language.
|
||||
|
||||
```
|
||||
app.ludic
|
||||
│ ludicc — lex, parse, lower (selfhost/frontend/*.ludic,
|
||||
▼ selfhost/backend/*.ludic)
|
||||
app.ll LLVM IR: your handlers, your properties, your runtime
|
||||
│ IR assembler (selfhost/main.ludic drives $LUDIC_CC)
|
||||
▼
|
||||
app.o Mach-O / ELF / COFF object code
|
||||
│ system linker
|
||||
▼
|
||||
app or libapp.dylib / .so / .dll
|
||||
```
|
||||
|
||||
`clang` appears in that pipeline twice — as the IR assembler and as the linker
|
||||
driver — which is the same role `rustc` and `swiftc` give it. Set `LUDIC_CC` to
|
||||
point at a different LLVM toolchain if you have one.
|
||||
|
||||
## Artifacts
|
||||
|
||||
| you want | command |
|
||||
| --- | --- |
|
||||
| a windowed native executable | `ludicc game.ludic -o build/game` |
|
||||
| a headless executable | `ludicc game.ludic --headless -o build/game` |
|
||||
| the IR, to read | `ludicc src.ludic --emit-llvm -o src.ll` |
|
||||
| the schema an editor reads (records, registries and their entries, consts) | `ludicc src.ludic --emit-schema schema.json` |
|
||||
| every error, as a JSON array on stdout | `ludicc src.ludic --check --diagnostics=json` |
|
||||
| the same, with an unsaved buffer on stdin standing for one of its files | `ludicc src.ludic --check --diagnostics=json --stdin-file lib/a.ludic < buf` |
|
||||
| a shared library † | `ludicc lib.ludic --shared -o build/liblib.dylib` |
|
||||
| a game that runs in a browser † | `ludicc game.ludic --target wasm32-unknown-unknown -o build/web/game.wasm` |
|
||||
| an object file † | `ludicc src.ludic -c -o src.o` |
|
||||
|
||||
† `--shared`, `--target`/cross-compile, `-c` and the wasm path were features of
|
||||
the old C driver and are **not yet re-implemented** on the self-hosted toolchain
|
||||
(see the note at the top). The rows above the line work today via the
|
||||
self-hosted `ludicc`.
|
||||
|
||||
`bin/ludic build` wraps the common cases:
|
||||
|
||||
```bash
|
||||
bin/ludic build examples/games/snake.ludic # -> build/snake (native)
|
||||
bin/ludic build examples/library/combat.ludic --lib # -> build/libcombat.* (library)
|
||||
bin/ludic build examples/games/snake.ludic --headless # -> build/snake_headless (out.ppm)
|
||||
bin/ludic build examples/games/snake.ludic --web # -> build/web/ (browser)
|
||||
```
|
||||
|
||||
The `--lib` and `--web` targets were part of the old C driver and are **not yet
|
||||
re-implemented** on the self-hosted toolchain — `bin/ludic build` supports the native
|
||||
windowed and `--headless` builds today.
|
||||
|
||||
## Programs and libraries
|
||||
|
||||
> **Not yet on the self-hosted toolchain.** `--shared` and the `nm`/library
|
||||
> workflow below describe the old C driver's behavior; the self-hosted `ludicc`
|
||||
> builds executables only for now. The `@export function` semantics are
|
||||
> unchanged — only the packaging step is pending.
|
||||
|
||||
A source file opens with `program Name { … }`.
|
||||
|
||||
* A program with **handlers** is a game: it gets the phase-ordered frame loop
|
||||
(`Start`, then `Input → FixedUpdate → Update → LateUpdate → Render` each tick).
|
||||
* A program with only an **`entry`** block is a tool: it runs `entry` and exits.
|
||||
* Either kind can be a library: only its `@export function`s become public
|
||||
symbols; everything else stays private.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative: elided body
|
||||
program Combat {
|
||||
@export function damage(attack: int, armour: int, roll: int) -> int { … }
|
||||
function curve(level: int) -> int { … } # private: not a symbol
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
ludicc examples/library/combat.ludic --shared -o build/libcombat.dylib
|
||||
nm -gU build/libcombat.dylib
|
||||
# T _damage T _hits_to_kill T _xp_for (no _curve)
|
||||
```
|
||||
|
||||
Those are ordinary C-ABI symbols, so anything that can call a shared library can
|
||||
call Ludic. To call them from another Ludic program, declare them and link:
|
||||
|
||||
```ludic
|
||||
extern function damage(attack: int, armour: int, roll: int) -> int = "damage"
|
||||
```
|
||||
|
||||
```bash
|
||||
ludicc examples/library/arena.ludic -o build/arena -Lbuild -lcombat
|
||||
```
|
||||
|
||||
Libraries are linked as `@rpath/…` (`$ORIGIN` on Linux) and executables search
|
||||
next to themselves, so a built pair keeps working when you move it.
|
||||
|
||||
## Cross-compilation
|
||||
|
||||
> **Not yet on the self-hosted toolchain.** `--target` and `-c` were old
|
||||
> C-driver flags; the self-hosted `ludicc` builds only for the host today. The
|
||||
> section below records the intended design — object code for ELF, COFF and
|
||||
> Mach-O from one source — which the IR pipeline already supports in principle.
|
||||
|
||||
`--target` takes an LLVM triple and retargets the whole pipeline:
|
||||
|
||||
```bash
|
||||
ludicc game.ludic --target x86_64-unknown-linux-gnu -c -o game-linux.o
|
||||
ludicc game.ludic --target aarch64-unknown-linux-gnu -c -o game-arm64.o
|
||||
ludicc game.ludic --target x86_64-pc-windows-msvc -c -o game-win.o
|
||||
```
|
||||
|
||||
Object code for ELF, COFF and Mach-O comes out of the same source with no
|
||||
per-platform branches in the compiler. Linking a foreign target additionally
|
||||
needs that platform's linker and sysroot, as with any cross toolchain.
|
||||
|
||||
## The runtime is written in Ludic
|
||||
|
||||
`runtime/native/core.ludic` implements the framebuffer, `fill_rect`, the 5×7
|
||||
bitmap text, the registers, the RNG, input and the frame dump — in Ludic. ludicc
|
||||
splices it into every native build, and a builtin call in a game resolves to a
|
||||
runtime function by name: `clear(c)` calls `rt_clear(c)`. Replace that file and
|
||||
you have replaced the runtime; pass `--freestanding` to build without it.
|
||||
|
||||
Underneath the runtime there is exactly one layer, and it is not C: a set of
|
||||
compiler intrinsics that lower to direct calls into the platform ABI.
|
||||
|
||||
| intrinsic | lowers to |
|
||||
| --- | --- |
|
||||
| `mem_alloc(n) -> pointer`, `mem_free`, `mem_copy`, `mem_set` | `malloc`, `free`, `memcpy`, `memset` |
|
||||
| `peek8/peek32(p, i) -> int`, `poke8/poke32(p, i, v)` | `load` / `store` |
|
||||
| `ptr_add(p, n) -> pointer`, `ptr_null()`, `ptr_is_null(p)` | `getelementptr`, `null` |
|
||||
| `file_open(path, mode) -> pointer`, `file_read`, `file_write`, `file_close` | `fopen`, `fread`, `fwrite`, `fclose` |
|
||||
| `read_byte() -> int`, `write_byte(c)`, `print_str(s)`, `print_int(n)` | `getchar`, `putchar`, `printf` |
|
||||
| `str_len(s) -> int`, `os_exit(code)`, `os_time() -> int` | `strlen`, `exit`, `time` |
|
||||
|
||||
That is the operating system's interface — the floor Rust and Swift stand on
|
||||
too. Everything above it, including all the graphics, is Ludic.
|
||||
|
||||
The runtime protocol is four optional functions. Define them (or let the
|
||||
prelude define them) and the entry point calls them:
|
||||
|
||||
| function | when |
|
||||
| --- | --- |
|
||||
| `rt_init()` | once, before the `Start` systems |
|
||||
| `rt_poll() -> int` | once per frame; its result is what `key()` reads |
|
||||
| `rt_running() -> bool` | each frame; false ends the loop |
|
||||
| `rt_shutdown()` | after the loop |
|
||||
|
||||
## The window
|
||||
|
||||
`runtime/native/cocoa.ll` is the macOS platform layer, written in LLVM IR. It
|
||||
talks to the Objective-C runtime through its C ABI — `objc_getClass`,
|
||||
`sel_registerName`, `objc_msgSend` — and to Quartz through CoreGraphics, which
|
||||
is what a compiled `.m` file does anyway; this just skips the `.m`. AppKit
|
||||
paints through `-drawRect:`, so the view class is built at runtime with
|
||||
`objc_allocateClassPair` and an IR function is installed as its IMP.
|
||||
|
||||
ludicc assembles it exactly like the program's own IR and hands both objects to
|
||||
the linker, adding `-framework Cocoa`. A `--headless` build omits it entirely,
|
||||
reads keys from stdin and writes the last frame to `out.ppm`; the `win_*`
|
||||
intrinsics compile to nothing there, so a headless binary never references a
|
||||
symbol the window would have provided.
|
||||
|
||||
Other platforms build headless today. A Win32 or X11 port is another `.ll` file
|
||||
with the same entry points — the window (`win_open`, `win_poll`, `win_present`,
|
||||
`win_running`, `win_close`), keys (`win_held`, `win_held_bit`), the mouse and
|
||||
cursor (`win_mouse`, `win_cursor_mode`, `win_cursor_confine`,
|
||||
`win_cursor_maintain`), gamepad (`win_pad`) and touch (`win_touch`) — and no
|
||||
compiler change.
|
||||
|
||||
## The web
|
||||
|
||||
WebAssembly is a target, not a port. The front end, the type checker, the ECS
|
||||
lowering and the Ludic-written runtime are the same ones a macOS build uses;
|
||||
only the triple changes.
|
||||
|
||||
```
|
||||
game.ludic
|
||||
│ ludicc — the same lex, parse, check and lower
|
||||
▼
|
||||
game.ll LLVM IR, triple wasm32-unknown-unknown
|
||||
│ IR assembler
|
||||
▼
|
||||
game.o + wasm.o (runtime/web/wasm.ll, the platform layer)
|
||||
│ wasm-ld
|
||||
▼
|
||||
game.wasm + index.html + platform.js + assets.json + the assets
|
||||
```
|
||||
|
||||
```bash
|
||||
bin/ludic build examples/games/chronorift.ludic --web
|
||||
python3 -m http.server -d build/web 8000 # then open http://localhost:8000/
|
||||
```
|
||||
|
||||
`build/web/` is self-contained: copy it to any static host — GitHub Pages, S3,
|
||||
itch.io — and the game runs. It needs no server-side anything, and no
|
||||
cross-origin isolation headers.
|
||||
|
||||
**No game logic passes through JavaScript.** The handlers, the queries, the
|
||||
fixed-point arithmetic, the PNG decoder, the TrueType rasteriser and the UI are
|
||||
all compiled Ludic executing as wasm. `platform.js` is 300 lines and implements
|
||||
the same five-function window protocol `cocoa.ll` implements, plus the host
|
||||
services wasm has no OS to ask for. It is the web's Cocoa, not an interpreter.
|
||||
|
||||
### The toolchain
|
||||
|
||||
A wasm build needs an LLVM with the WebAssembly backend and `wasm-ld`. Linux
|
||||
distributions ship both in `clang` and `lld`, so nothing extra is needed there
|
||||
or in CI. Apple's clang is built without the WebAssembly target, so on macOS:
|
||||
|
||||
```bash
|
||||
brew install llvm
|
||||
```
|
||||
|
||||
ludicc looks in `/opt/homebrew/opt/llvm/bin` and `/usr/local/opt/llvm/bin`
|
||||
before falling back to `PATH`. `$LUDIC_CC` and `$LUDIC_WASM_LD` override both,
|
||||
so any LLVM works — a distro one, a downloaded release, `zig cc`, wasi-sdk.
|
||||
|
||||
### Who owns the frame loop
|
||||
|
||||
A native build runs the loop:
|
||||
|
||||
```c
|
||||
ludic_boot(); while (ludic_alive()) ludic_frame(); ludic_teardown();
|
||||
```
|
||||
|
||||
A browser tab cannot be held inside that loop — it would never paint, and the
|
||||
key events the loop is waiting on would never be delivered. So a web build
|
||||
exports those four functions instead of `main`, and `platform.js` calls
|
||||
`ludic_frame` from `requestAnimationFrame`. Both targets emit the four from the
|
||||
same code in `ll_emit_loop_parts`, so the handlers that run, and the phase order
|
||||
they run in, are identical; only the owner of the loop differs.
|
||||
|
||||
### The floor
|
||||
|
||||
`wasm32-unknown-unknown` has no libc, so `runtime/web/wasm.ll` *is* the floor —
|
||||
hand-written LLVM IR, assembled by the same toolchain as everything else:
|
||||
|
||||
| what | how |
|
||||
| --- | --- |
|
||||
| `malloc` / `free` | a first-fit free list over linear memory, growing it with `memory.grow` |
|
||||
| `memcpy` / `memset` | the `memory.copy` / `memory.fill` instructions (`-mbulk-memory`) |
|
||||
| `strlen` | a byte loop |
|
||||
| `fopen` / `fread` / `fwrite` / `fclose` / `fseek` / `ftell` | wasm imports, over a preloaded asset image and `localStorage` |
|
||||
| `getchar` / `putchar` / `print_str` / `time` / `exit` | wasm imports |
|
||||
| `win_open` / `win_poll` / `win_present` / `win_running` / `win_close` | wasm imports, implemented against a `<canvas>` |
|
||||
|
||||
Nothing above that file changes for the web: `core.ludic`, `image.ludic`,
|
||||
`inflate.ludic`, `truetype.ludic` and `ui.ludic` compile to wasm unmodified.
|
||||
|
||||
### Assets and saves
|
||||
|
||||
The browser has no synchronous file access, and `file_open()` is synchronous, so
|
||||
a web build ships an image of its files instead of a filesystem. ludicc records
|
||||
every string literal in the program that names a file existing at compile time,
|
||||
writes the list to `assets.json`, and copies the files into the bundle;
|
||||
`platform.js` fetches them all before the first frame. `file_open()` then
|
||||
resolves exactly the paths it resolves natively.
|
||||
|
||||
That is a heuristic, and a deliberately visible one: a path the compiler never
|
||||
sees written down is a path the browser cannot be told to fetch ahead of time,
|
||||
and a path outside the project (`/System/Library/Fonts/…`) is refused with a
|
||||
warning rather than silently dropped.
|
||||
|
||||
Writes go the other way. `file_open(path, "wb")` buffers and commits to
|
||||
`localStorage` on close, so `save()` / `load()` survive a page reload, and a
|
||||
read prefers a save the player has made over the shipped asset of the same name.
|
||||
|
||||
### Testing a wasm build
|
||||
|
||||
`--headless --target wasm32-unknown-unknown` produces a bare module with no
|
||||
page, driven by a runner instead of a browser:
|
||||
|
||||
```bash
|
||||
node tools/ludic-web/run.mjs build/web/snake_headless.wasm --stdin=ddss
|
||||
```
|
||||
|
||||
Because Ludic is fixed-point and its RNG is seeded, the native headless binary
|
||||
and the wasm one must render byte-identical frames from the same input. `bin/ludic-dev test`
|
||||
asserts exactly that, which is a much stronger check on the backend than
|
||||
"it started".
|
||||
|
||||
## What a build contains
|
||||
|
||||
Everything: properties and models, spawn/despawn, queries with bindings,
|
||||
`where` filters and model filters, `match`, `machine`/`become`,
|
||||
`scene`/`layer`/`enter`, module state (`var`), `const`, int and Q16.16
|
||||
fixed-point arithmetic, control flow, functions, `extern fn` FFI, strings, the
|
||||
entity allocator, save/load snapshots, the frame loop, the window, and the whole
|
||||
graphics stack — framebuffer, PNG decoding, sprites, 9-slice, TrueType text and
|
||||
the retained UI.
|
||||
|
||||
None of it goes through C. `bin/ludic-dev test` asserts that directly: no C source
|
||||
survives in `runtime/`, no C emitter survives in `ludicc`, and the examples all
|
||||
build, run and render from IR alone.
|
||||
|
||||
## Every flag
|
||||
|
||||
The self-hosted `ludicc`/`ludic` (built with `bin/ludic-dev build-cli`) accept:
|
||||
|
||||
```
|
||||
<file.ludic> the program to compile (first non-flag argument)
|
||||
-o <path> output binary; with --emit-llvm, the IR path.
|
||||
Parent directories are created. With no -o and not
|
||||
invoked as `ludic`, the IR is written to stdout.
|
||||
--windowed force a windowed (Cocoa) build
|
||||
--headless force a headless build (stdin input, out.ppm output)
|
||||
--emit-llvm stop at LLVM IR — write it and exit, no clang
|
||||
--check every check a build makes (types, modules, uses, layers, ports, binds); write nothing
|
||||
--fmt lex + parse only; exit 0 if it parses, 1 on a parse error
|
||||
(the check-docs gate; canonical formatting not yet restored)
|
||||
--save-temps keep the intermediate .ll
|
||||
--run compile then run (what `ludic run` uses)
|
||||
(unknown -flags are ignored with a warning, never taken as the input file)
|
||||
|
||||
environment:
|
||||
LUDIC_CC the LLVM that assembles IR and drives the linker (clang)
|
||||
LUDIC_HOME the install root — runtime/, packages/, VERSION
|
||||
(default: the parent of the binary's bin/ directory)
|
||||
LUDIC_MODULES the project's fetched packages (default: ./ludic_modules)
|
||||
```
|
||||
|
||||
Mode is automatic when neither `--windowed` nor `--headless` is given: a program
|
||||
with `system`s (a game) links windowed, anything else headless.
|
||||
|
||||
Not yet re-implemented on the self-hosted toolchain (old C-driver flags):
|
||||
`--shared`, `--emit <kind>`, `-c`, `--target`/cross-compile,
|
||||
`--freestanding`, `-v`, and the explicit link inputs (`-L`/`-l`/`-framework`/
|
||||
`-Wl`). Those, plus `LUDIC_WASM_LD`/`LUDIC_RUNTIME_DIR`/`LUDIC_RUNTIME`, describe
|
||||
the previous driver and are documented here as intended design.
|
||||
|
||||
There is one backend. `ludicc` has no mode that emits C, and no part of a
|
||||
build compiles or links a C translation unit — including the web one, where the
|
||||
platform layer is LLVM IR and the loader is 300 lines of JavaScript that never
|
||||
sees a game rule.
|
||||
288
CONTRIBUTING.md
|
|
@ -1,288 +0,0 @@
|
|||
# Contributing to Ludic
|
||||
|
||||
Thanks for your interest in Ludic — an AoT-**compiled** game language with an
|
||||
ECS core, a deterministic fixed-point runtime, and a native 2D backend. This
|
||||
guide covers the unusual bit: Ludic is **self-hosted**, so the compiler, the
|
||||
runtime, and the tooling are all written in Ludic and built by Ludic.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `clang` (or another C compiler) — used **once** to assemble the checked-in
|
||||
LLVM-IR seed into the first `ludicc`, and thereafter only to assemble IR and
|
||||
link. No C is generated in a build.
|
||||
- LLVM (for the web/wasm target: `brew install llvm lld`).
|
||||
- macOS for the windowed Cocoa backend; headless PPM rendering works anywhere.
|
||||
|
||||
## First build (bootstrap)
|
||||
|
||||
From a clean checkout, one line lifts the toolchain off the seed:
|
||||
|
||||
```bash
|
||||
mkdir -p bin && clang selfhost/ludicc.seed.ll -o bin/ludicc
|
||||
bin/ludicc --unsafe --globals tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
||||
```
|
||||
|
||||
That gives you `bin/ludic-dev`, the contributor tool: it replaces every
|
||||
build/test shell script in the repo and builds everything, including itself and
|
||||
`bin/ludic`. It is deliberately a separate binary from the `ludic` users install
|
||||
— that one carries none of these tasks and is never asked to.
|
||||
|
||||
```bash
|
||||
bin/ludic-dev build # the whole toolchain into bin/ (ludicc, ludic, ludic-dev, ludic-fmt, ludic-lsp)
|
||||
bin/ludic-dev help # every contributor task
|
||||
bin/ludic help # what a user of the language sees
|
||||
```
|
||||
|
||||
Always run `ludic-dev` from the repository root, so `assets/` and `selfhost/`
|
||||
resolve. (A checkout is also an install root: `bin/` beside `runtime/` and
|
||||
`packages/`, exactly the shape `install.sh` lays down under `~/.ludic`, which is
|
||||
why `bin/ludic` behaves there exactly as an installed one does.)
|
||||
|
||||
## The development loop
|
||||
|
||||
When you change the compiler or runtime, prove the self-hosting fixpoint still
|
||||
holds before you push:
|
||||
|
||||
```bash
|
||||
bin/ludic-dev reseed # regenerate selfhost/ludicc.seed.ll after a compiler change
|
||||
bin/ludic-dev bootstrap-cfree # rebuild the compiler from the seed with NO C compiler in the loop
|
||||
bin/ludic-dev test # the full regression suite
|
||||
```
|
||||
|
||||
Other useful targets:
|
||||
|
||||
```bash
|
||||
bin/ludic build <file.ludic> [--headless] # compile a program to a native app in build/
|
||||
bin/ludic-dev selfhost-test # correctness + bootstrap fixpoints
|
||||
bin/ludic-dev test-tools # the editor-toolchain suite (ludic-fmt, ludic-lsp)
|
||||
bin/ludic clean # remove build/, out.ppm and stray artifacts
|
||||
```
|
||||
|
||||
## Adding to the standard library
|
||||
|
||||
The stdlib lives in the runtime (`runtime/`) and is surfaced as namespaces
|
||||
(`Math.*`, `Crypto.*`, `DateTime.*`, `Screen.*`, …). When you add a symbol:
|
||||
|
||||
1. Implement it in the runtime / emitter as appropriate.
|
||||
2. Document it: add one Markdown file per symbol under `docs/language/<namespace>/`
|
||||
and register its id in `tools/docgen/inventory.json`. Each documented
|
||||
namespace gets exactly **one** directory (the docs check enforces this).
|
||||
3. Add or extend an example under `examples/` and a case in the test suite.
|
||||
4. Run `bin/ludic-dev docs-gen --out build/pages && bin/ludic-dev docs-check build/pages` — the
|
||||
check fails if any inventory symbol lacks a page or is still seed text.
|
||||
5. Add a **changeset** for the user-facing change: a small file under
|
||||
[`changes/`](changes/README.md) with a `bump:` level and a one-line summary.
|
||||
The next release folds it into `CHANGELOG.md`.
|
||||
|
||||
## Versioning & releases
|
||||
|
||||
The toolchain is versioned with [SemVer](https://semver.org); `VERSION` is the
|
||||
single source of truth and `ludicc --version` (or `ludic version`) reports it.
|
||||
|
||||
Releases are changeset-driven. Every user-facing change ships with a changeset
|
||||
(step 5 above). Read the next release before cutting it:
|
||||
|
||||
```bash
|
||||
ludic-dev release --dry-run # render the CHANGELOG section, write nothing
|
||||
```
|
||||
|
||||
Then cut it:
|
||||
|
||||
```bash
|
||||
ludic-dev release [major|minor|patch] # omit the level to derive it from the changesets
|
||||
git push origin main --follow-tags
|
||||
```
|
||||
|
||||
`ludic-dev release` aggregates the pending changesets into a new `CHANGELOG.md` section
|
||||
— grouped by change type, with each changeset's markdown kept intact — bumps
|
||||
`VERSION`, commits `chore(release): vX.Y.Z`, and tags it.
|
||||
|
||||
**Pushing the tag is what publishes.** The `release` workflow builds the
|
||||
toolchain from the IR seed, runs `ludic-dev test`, `ludic-dev test-tools` and `ludic-dev bootstrap-cfree`
|
||||
against the tagged tree, and only then creates the Forgejo release — with the
|
||||
source tarball, a Linux toolchain build, a `.sha256` beside each, and that version's
|
||||
`CHANGELOG.md` section as the notes. It refuses to publish if the tag and
|
||||
`VERSION` disagree or the changelog has no section for it.
|
||||
|
||||
Each toolchain artifact is a complete install root — `bin/` beside `runtime/`,
|
||||
`packages/` and `VERSION` — which is exactly what `install.sh` unpacks into
|
||||
`~/.ludic`. A release with no artifact for a platform is not a broken install
|
||||
there: the installer falls back to bootstrapping from the source tarball's IR
|
||||
seed. But the macOS artifacts are the ones most people get, so attach them.
|
||||
|
||||
macOS artifacts cannot be produced on the Linux runner — a `darwin-arm64` build
|
||||
needs a macOS host, and there is no cross-compile path (it would need the Xcode
|
||||
SDK and a Mach-O linker). Attaching one therefore means either registering a
|
||||
macOS runner and giving it a job, or running the same command CI runs from a
|
||||
Mac. Either way it is `ludic-dev publish`, which only adds assets the release is missing:
|
||||
|
||||
```bash
|
||||
FORGEJO_TOKEN=… ludic-dev publish v0.4.0
|
||||
```
|
||||
|
||||
Checksums are one `.sha256` file per artifact rather than a single `SHA256SUMS`,
|
||||
precisely because a release can be assembled from more than one host and an
|
||||
asset that already exists is never overwritten. Verify one with:
|
||||
|
||||
```bash
|
||||
shasum -a 256 -c ludic-0.4.0-src.tar.gz.sha256
|
||||
```
|
||||
|
||||
The tag doubles as the reproducible bootstrap point: the source archive plus its
|
||||
checked-in seed rebuild that exact toolchain.
|
||||
|
||||
## Where the name and the URLs live
|
||||
|
||||
The language may yet be renamed and the project may yet move hosts, so the
|
||||
things that carry a name are kept few and listed here rather than discovered one
|
||||
broken link at a time. Everything host-shaped has an environment override, so a
|
||||
move can be rehearsed before it is committed.
|
||||
|
||||
**Hosts and URLs.** The install one-liner is served from the documentation site,
|
||||
which publishes `install.sh` beside the pages that quote it (`ludic-dev docs-gen`
|
||||
copies it in; `docs-check` fails without it). Change the host in:
|
||||
|
||||
| Where | What |
|
||||
|---|---|
|
||||
| `install.sh` | `REPO_API`, `REPO_URL`, `INSTALL_URL` — each `${LUDIC_…:-default}`, so `LUDIC_REPO_URL=… sh install.sh` tests a move without editing anything |
|
||||
| `tools/ludic-cli/project.ludic` | `install_url()` (`$LUDIC_INSTALL_URL`), used by `ludic upgrade` and `ludic doctor` |
|
||||
| `tools/ludic-cli/forgejo.ludic` | `FORGEJO_API_DEFAULT` (`$LUDIC_FORGEJO_API`), used by `ludic-dev publish` |
|
||||
| `docs/site/site.json` | `repo_url`, the `start.terminal` one-liner, and the doc links in `nav_links` |
|
||||
| Prose | `README.md`, `COMPILING.md`, `tools/editors/README.md`, and the two editor plugins' "server not found" messages |
|
||||
|
||||
**The name itself.** A rename touches, in rough order of blast radius:
|
||||
|
||||
- **The file extension** `.ludic` — the compiler (`strip_ludic`, `do_import`,
|
||||
`is_ludic_file`), every editor asset (`tools/editors/shared/*.json`,
|
||||
`vscode/package.json`, the JetBrains `LudicFileType`), and every source file
|
||||
in the tree.
|
||||
- **The binaries** `ludic`, `ludicc`, `ludic-dev`, `ludic-fmt`, `ludic-lsp` —
|
||||
`cmd_dev_build` in `toolchain.ludic`, the release staging in `release.ludic`,
|
||||
`install.sh`, the editors' executable-name lists. Only the first, third and
|
||||
fourth of those ship: `ludic-dev` is built from a checkout and stays there.
|
||||
- **The install root** `~/.ludic` and the source directories `tools/ludic-cli/`,
|
||||
`tools/ludic-tools/`, `packages/ludic.*`.
|
||||
- **The environment variables** `LUDIC_HOME`, `LUDIC_CC`, `LUDIC_MODULES`,
|
||||
`LUDIC_STORE`, `LUDIC_PKG_PROXY`, `LUDIC_INSTALL_URL`, `LUDIC_KEEP_TMP`,
|
||||
`LUDIC_COVERAGE` — keep the old names working for a release if anyone has them
|
||||
in a script.
|
||||
- **Identifiers that are contracts with other software**: the TextMate scope
|
||||
`source.ludic`, the VS Code language id `ludic`, the JetBrains plugin id
|
||||
`io.ludic.ide`, and the `ludic` code-fence tag understood by the Markdown
|
||||
injection and by `ludic-dev check-docs`.
|
||||
- **The prose**: `README.md`, `LANGUAGE.md`, `COMPILING.md`, `docs/**`, and
|
||||
`docs/site/site.json`'s `brand`/`meta`.
|
||||
|
||||
`ludic-dev test` is the safety net for the mechanical part — it builds the
|
||||
toolchain, stages an install, and runs `new` → `build` → `test` through it, so a
|
||||
half-finished rename fails there rather than in someone's terminal.
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Commits:** [Conventional Commits](https://www.conventionalcommits.org) —
|
||||
`type(scope): summary`, with an optional `!` before the colon for a breaking
|
||||
change. Keep the summary imperative and under ~72 chars. The types in use:
|
||||
|
||||
| type | for |
|
||||
|------|-----|
|
||||
| `feat` | a new user-facing capability (a stdlib namespace, a language feature) |
|
||||
| `fix` | a bug fix |
|
||||
| `refactor` | a change that neither fixes a bug nor adds a feature |
|
||||
| `perf` | a performance improvement |
|
||||
| `docs` | documentation only (`docs/`, README, comments) |
|
||||
| `test` | tests only |
|
||||
| `build` | the build/bootstrap machinery (seed, `bin/ludic`, linking) |
|
||||
| `ci` | CI workflows under `.forgejo/` |
|
||||
| `style` | formatting/whitespace, no behaviour change |
|
||||
| `chore` | routine housekeeping with no other bucket |
|
||||
| `revert` | reverts a previous commit |
|
||||
|
||||
Common scopes: `stdlib`, `lang`, `emit`, `runtime`, `tooling`, `docs`, `repo`.
|
||||
Reference the issue you close with a `Closes #NN` trailer.
|
||||
|
||||
- **Enforcement:** the hooks in [`tools/git-hooks/`](tools/git-hooks) enforce this
|
||||
locally, and the `commit-lint` CI job is the backstop. Turn the hooks on once,
|
||||
per clone:
|
||||
|
||||
```bash
|
||||
git config core.hooksPath tools/git-hooks
|
||||
```
|
||||
|
||||
That activates the `commit-msg` hook (rejects a non-conforming summary) and the
|
||||
`pre-commit` hook (rejects unformatted Ludic). Both read the same rules CI does,
|
||||
so a green local commit is a green CI run.
|
||||
|
||||
- **Formatting:** `ludic-fmt` is the source of truth (2-space indent, LF, UTF-8);
|
||||
the repo `.editorconfig` mirrors it. Run `bin/ludic fmt` on files you touch.
|
||||
The contract CI enforces is *idempotence* — `ludic-fmt` re-run on its own output
|
||||
is a no-op — which leaves deliberate hand alignment in place; it is not a
|
||||
blanket `fmt(x) == x`.
|
||||
- **Code structure:** one job per file. Split large files by concern into
|
||||
subfolders rather than growing a single 500+-line module (see how `selfhost/`
|
||||
and `runtime/` are organised).
|
||||
- **Language of the toolchain:** new runtime and tooling are written in **Ludic**,
|
||||
not C, Python, or JS. The only non-Ludic pieces are the LLVM-IR seed
|
||||
(`selfhost/ludicc.seed.ll`), the hand-written `runtime/native/cocoa.ll` /
|
||||
`runtime/web/wasm.ll` shims, and the legacy Python docgen (being ported).
|
||||
|
||||
## Git history
|
||||
|
||||
The log has two eras: the pre-self-hosting `Phase Nx: …` / `Merge Phase Nx: …`
|
||||
commits, and the Conventional Commits used since. **Decision: the old `Phase`
|
||||
history stays as-is.** Rewriting already-pushed history (`filter-repo`/rebase) is
|
||||
destructive and non-reversible for anyone who has cloned, and it buys little — so
|
||||
we do not rewrite it. The convention is enforced *going forward* by the hook and
|
||||
the `commit-lint` job (both skip merge commits, so the old merges never trip it).
|
||||
|
||||
When the versioning work lands (issue #33), the first release tag doubles as a
|
||||
clean `v0` baseline that brackets the `Phase`-era prefix — the safe,
|
||||
non-destructive version of "tidy the history" without touching a single commit.
|
||||
|
||||
## Pull requests
|
||||
|
||||
- Base your branch on `main`.
|
||||
- Ensure `bin/ludic-dev test` (and `bin/ludic-dev bootstrap-cfree` for compiler/runtime changes)
|
||||
pass, and that `ludic-fmt` leaves your files unchanged.
|
||||
- Fill in the PR template checklist. Reference the issue you close with
|
||||
`Closes #NN` in the description or a commit message.
|
||||
|
||||
## CI (self-hosted runners)
|
||||
|
||||
Every workflow starts by cloning `${{ github.server_url }}/${{ github.repository }}`.
|
||||
On a self-hosted Forgejo runner that URL is usually the instance's *internal*
|
||||
address (e.g. `http://forgejo:3000`), so **the job container must be able to
|
||||
resolve it**. The runner puts each job on a fresh per-job network by default,
|
||||
which the Forgejo container is not attached to — so the clone fails with:
|
||||
|
||||
```
|
||||
fatal: unable to access 'http://forgejo:3000/…': Could not resolve host: forgejo
|
||||
```
|
||||
|
||||
Give the runner a config that pins job containers to a network Forgejo is also
|
||||
on. A dedicated network is better than the general application network, so a CI
|
||||
job cannot reach unrelated services:
|
||||
|
||||
```yaml
|
||||
# the runner's config.yml, passed with: forgejo-runner daemon --config …
|
||||
container:
|
||||
network: forgejo-ci
|
||||
```
|
||||
|
||||
with `forgejo-ci` attached to the Forgejo container as well. Verify it without
|
||||
running a workflow:
|
||||
|
||||
```bash
|
||||
docker run --rm --network forgejo-ci alpine:3 getent hosts forgejo
|
||||
```
|
||||
|
||||
This failure mode is intermittent if left unfixed: Docker forwards names it
|
||||
cannot resolve to the host's resolver, which may answer for the container name
|
||||
often enough that CI looks healthy for a while.
|
||||
|
||||
## Reporting issues
|
||||
|
||||
Use the templates under [`.forgejo/issue_template/`](.forgejo/issue_template):
|
||||
a **bug report**, a **proposal** (new stdlib/language surface), or a
|
||||
**cleanup/DX** task. Choose the one that fits and fill in the sections.
|
||||
|
||||
By participating you agree to the [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
2800
LANGUAGE.md
202
LICENSE
|
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2026 The Ludic Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
200
README.md
|
|
@ -1,200 +0,0 @@
|
|||
# Ludic
|
||||
|
||||
A compiled language for 2D games. The entity-component system is part of the
|
||||
syntax, the runtime is deterministic fixed-point, and `ludicc` lowers Ludic
|
||||
straight to LLVM IR — **no C is generated, compiled or linked in a build.**
|
||||
|
||||
The compiler is written in Ludic. It compiles its own source to a byte-exact
|
||||
fixpoint and rebuilds from a checked-in IR seed with clang alone; CI asserts
|
||||
that on every push.
|
||||
|
||||
- **Documentation:** <https://workshopsoft.pages.workshopsoft.io/ludic/>
|
||||
- **API reference:** <https://workshopsoft.pages.workshopsoft.io/ludic/api.html>
|
||||
- **Issues:** <https://git.workshopsoft.io/workshopsoft/ludic/issues>
|
||||
|
||||
```ludic
|
||||
program Hello {
|
||||
|
||||
property Position { column: int = 0, row: int = 0 }
|
||||
property Velocity { delta_x: int = 0, delta_y: int = 0 }
|
||||
|
||||
handler SpawnEnemies phase Start {
|
||||
spawn Enemy { Position { column: 3, row: 4 }, Velocity { delta_x: 1, delta_y: 0 } }
|
||||
spawn Enemy { Position { column: 10, row: 2 }, Velocity { delta_x: 0, delta_y: 1 } }
|
||||
}
|
||||
|
||||
# a handler declares the entities it touches; the body runs
|
||||
# once per match, with each property bound by name.
|
||||
@Queries(these: [Position, Velocity])
|
||||
handler AdvancePositions phase FixedUpdate {
|
||||
Position.column += Velocity.delta_x
|
||||
Position.row += Velocity.delta_y
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
Install the toolchain — the compiler, the `ludic` CLI, the engine runtime, the
|
||||
formatter and the language server — with one command:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh
|
||||
```
|
||||
|
||||
It installs into `~/.ludic` and puts `~/.ludic/bin` on your `PATH` in every
|
||||
shell — the PATH line lives in `~/.ludic/env`, sourced from `~/.profile`,
|
||||
`~/.zshenv` and your bash or fish config. Nothing else on the machine is touched;
|
||||
uninstalling is `rm -rf ~/.ludic` and deleting those two-line blocks. Where a
|
||||
prebuilt toolchain exists for your platform it is downloaded and verified against
|
||||
a published checksum; where it does not, the installer bootstraps from the
|
||||
compiler's own IR seed with clang. Either way you need clang (or Xcode's Command
|
||||
Line Tools) to link, since Ludic emits LLVM IR and links it natively.
|
||||
|
||||
Then make a game:
|
||||
|
||||
```bash
|
||||
ludic new mygame
|
||||
cd mygame
|
||||
ludic run # compiles src/main.ludic and opens a native window
|
||||
```
|
||||
|
||||
`ludic new` writes a manifest, a program that already moves something on screen,
|
||||
and a test. `ludic build` stops at the binary; `ludic bundle` goes on to the
|
||||
thing you can actually give someone. Rendering is deterministic, so a frame can
|
||||
be produced without a window, which is what CI diffs:
|
||||
|
||||
```bash
|
||||
ludic test
|
||||
ludic build --headless
|
||||
printf 'ddddwww' | ./build/mygame_headless # writes build/out.ppm
|
||||
```
|
||||
|
||||
`ludic help` lists every command, and `ludic doctor` checks the install.
|
||||
[`examples/`](examples/README.md) is a tour grouped by intent: games, rendering,
|
||||
ECS, events, networking, language features and the standard library — compile any
|
||||
of them with `ludic build examples/games/snake.ludic`.
|
||||
|
||||
### Building from a checkout
|
||||
|
||||
Contributors also get `ludic-dev`, a second binary carrying the toolchain's own
|
||||
tasks — building the compiler, the suites, the docs site, releases. It is built
|
||||
from a checkout and is not part of an install, so nothing a user runs is mixed
|
||||
up with it. Bootstrapping is the only step Ludic cannot do for itself, since
|
||||
compiling Ludic needs a compiler — clang assembles the checked-in IR seed, and
|
||||
that compiler builds the rest:
|
||||
|
||||
```bash
|
||||
mkdir -p bin && clang selfhost/ludicc.seed.ll -o bin/ludicc
|
||||
bin/ludicc tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
||||
bin/ludic-dev build # -> bin/{ludicc,ludic,ludic-dev,ludic-fmt,ludic-lsp}
|
||||
bin/ludic-dev test # the regression suite
|
||||
```
|
||||
|
||||
## The language
|
||||
|
||||
- **ECS in the syntax.** `property`, `model` and `handler` are keywords. Query
|
||||
with `for (a, b) in query [A, B, {Tag}] where <expr> { … }`; `spawn` and
|
||||
`despawn` recycle entity slots; `@`-annotations drive lifecycle hooks.
|
||||
- **Deterministic by construction.** Q16.16 `fixed` arithmetic and a seeded RNG
|
||||
give the same frame byte-for-byte on every run — the basis for replays,
|
||||
lockstep netcode and golden-image tests.
|
||||
- **Scenes and state machines.** `scene` / `layer` / `become` model
|
||||
mutually-exclusive game states with enter and exit hooks; `match` / `machine`
|
||||
/ `state` handle dispatch and per-entity FSMs.
|
||||
- **Events and networking.** A cancellable event bus (`event` / `emit` / `@On`)
|
||||
and networking primitives (`@Sync`, ownership, RPCs) over a built-in transport.
|
||||
- **Batteries in the language.** Framebuffer primitives, PNG sprites, TrueType
|
||||
text and a retained `ui` widget tree declared as data, plus a namespaced
|
||||
standard library (`Math`, `Text`, `List`, `Random`, `Crypto`, `Tiled`, …).
|
||||
- **Whole-world snapshots.** `save()` and `load()` serialize every entity,
|
||||
property and program `var` in one call.
|
||||
|
||||
[LANGUAGE.md](LANGUAGE.md) is the full reference; the
|
||||
[API reference](https://workshopsoft.pages.workshopsoft.io/ludic/api.html)
|
||||
documents every symbol on its own page.
|
||||
|
||||
## Shipping
|
||||
|
||||
A built binary is a program, not an application: it opens its assets by a path
|
||||
relative to the working directory, so it runs from the project root and nowhere
|
||||
else, and it wears the generic executable icon.
|
||||
|
||||
```bash
|
||||
ludic pack # every asset the game opens, into one .lpak
|
||||
ludic bundle # ...and that, the binary, an icon and the metadata, as a .app
|
||||
```
|
||||
|
||||
Nothing about how the game is written changes. `gltf_load("assets/kit/hiker",
|
||||
…)` reads a file during development and a run of bytes inside the bundle once
|
||||
shipped, and cannot tell which — the pack is spliced in at `file_open`, the one
|
||||
place every asset in a Ludic program comes through. A bundled game also gets a
|
||||
boot splash it controls (`App.splash_hide()`) and a writable home under
|
||||
Application Support, because Finder starts a `.app` at `/` where no save could
|
||||
be written.
|
||||
|
||||
Without a pack beside it — which is every `ludic run` — nothing mounts and every
|
||||
open goes to the filesystem exactly as before. See [docs/SHIPPING.md](docs/SHIPPING.md).
|
||||
|
||||
## Packages
|
||||
|
||||
Dependencies are identified by URL, resolved with minimal version selection, and
|
||||
cached in a content-addressed store:
|
||||
|
||||
```bash
|
||||
ludic add git.workshopsoft.io/user/pkg # resolve, fetch, link into ludic_modules/
|
||||
ludic get # install from package.ludic, write the lock
|
||||
ludic remove git.workshopsoft.io/user/pkg # the inverse of add
|
||||
ludic verify # check locked packages against the store
|
||||
```
|
||||
|
||||
The `ludic.*` packages — canonical ECS components, the gameplay, platformer,
|
||||
RPG, shooter and NPC-AI modules — ship with the toolchain, so importing one needs
|
||||
no fetch step at all.
|
||||
|
||||
See [`docs/PACKAGES.md`](docs/PACKAGES.md) for the manifest and lockfile model.
|
||||
|
||||
## Editor support
|
||||
|
||||
Editors spawn `ludic lsp`; the server ships with the toolchain, so there is
|
||||
nothing extra to install. It speaks LSP 3.17 over stdio, so one binary serves
|
||||
every editor: completion, diagnostics from the compiler itself, go-to-definition
|
||||
and rename across imports, and comment-preserving formatting. `ludic fmt` runs
|
||||
the same formatter as a CLI, for pre-commit hooks. Both understand
|
||||
```` ```ludic ```` fences in Markdown. Plugins and drop-in config for VS Code, JetBrains, Neovim,
|
||||
Helix, Emacs, Sublime and Zed are in [`tools/editors/`](tools/editors/README.md).
|
||||
|
||||
## Status
|
||||
|
||||
The native 2D backend ships: a Cocoa window on macOS, a headless renderer for
|
||||
CI, and the whole runtime — framebuffer, PNG/DEFLATE decoding, TrueType
|
||||
rasterizer, retained UI, RNG — written in Ludic under
|
||||
[`runtime/native/`](runtime/native/). Only the window seam (`win_*`: window,
|
||||
keys, mouse, cursor, gamepad, touch) is hand-written LLVM IR against the
|
||||
platform ABI, the same floor Rust and Swift stand on.
|
||||
|
||||
The **web/wasm32 backend is not currently available.** The browser platform
|
||||
layer is in-tree under [`runtime/web/`](runtime/web/), but emitting wasm was a
|
||||
capability of the retired C compiler and has not been re-wired on the
|
||||
self-hosted toolchain. `--target` cross-compilation and `--shared` libraries are
|
||||
in the same position. See [COMPILING.md](COMPILING.md).
|
||||
|
||||
Releases follow SemVer and are cut from changesets by `ludic-dev release`, then built
|
||||
and published by CI from the tag; see [CHANGELOG.md](CHANGELOG.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) covers the development loop, the commit and
|
||||
code conventions, how the bootstrap fixpoint works, and what a self-hosted CI
|
||||
runner needs. Issue and pull-request templates are under
|
||||
[`.forgejo/`](.forgejo/).
|
||||
|
||||
## License
|
||||
|
||||
The compiler and runtime are licensed under the
|
||||
[Apache License 2.0](LICENSE) (`SPDX-License-Identifier: Apache-2.0`).
|
||||
|
||||
The bundled [Kenney](https://kenney.nl) art under `assets/kenney/` is
|
||||
third-party and released under **CC0 1.0**; each pack keeps its own
|
||||
`License.txt`. Code and assets are licensed separately — Apache-2.0 covers the
|
||||
source, not the art.
|
||||
1
VERSION
|
|
@ -1 +0,0 @@
|
|||
0.22.0
|
||||
32
angle-add.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.add — Ludic</title>
|
||||
<meta name="description" content="Add two angles (wrapped).">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.add</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.add</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.add(a, b) -> fixed</code>
|
||||
<div class="desc"><p>Adds two angles and wraps the result back into range.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
facing = Angle.add(facing, turnRate)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
32
angle-cos.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.cos — Ludic</title>
|
||||
<meta name="description" content="Cosine of an angle.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.cos</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.cos</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.cos(a) -> fixed</code>
|
||||
<div class="desc"><p>Returns the cosine of the angle, using the deterministic fixed-point trig.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
let dx = Angle.cos(facing)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
32
angle-diff.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.diff — Ludic</title>
|
||||
<meta name="description" content="Shortest signed rotation from a to b.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.diff</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.diff</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.diff(a, b) -> fixed</code>
|
||||
<div class="desc"><p>Returns the shortest signed rotation from <code>a</code> to <code>b</code>, in [-pi, pi) — positive to turn one way, negative the other.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
let steer = Angle.diff(facing, target)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
29
angle-diff_degrees.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.diff_degrees — Ludic</title>
|
||||
<meta name="description" content="The signed difference between two headings in whole degrees.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.diff_degrees</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.diff_degrees</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.diff_degrees(a: int, b: int) -> int</code>
|
||||
<div class="desc"><p><code>b - a</code> wrapped into -180 .. 180, for integer-degree headings such as <code>TopDown.aim_angle</code> and <code>IVec2.heading</code>.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic"># doc-check: skip — illustrative
|
||||
if abs(Angle.diff_degrees(a: aim, b: IVec2.heading(hero, foe))) <= 50 { hit() }</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
32
angle-from_degrees.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.from_degrees — Ludic</title>
|
||||
<meta name="description" content="Degrees to a wrapped radian angle.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.from_degrees</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.from_degrees</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.from_degrees(d) -> fixed</code>
|
||||
<div class="desc"><p>Converts degrees to radians and wraps the result into [-pi, pi). The natural way to author a heading a designer types in degrees.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
let facing = Angle.from_degrees(90.0)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
32
angle-lerp.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.lerp — Ludic</title>
|
||||
<meta name="description" content="Interpolate along the shortest arc.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.lerp</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.lerp</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.lerp(a, b, t) -> fixed</code>
|
||||
<div class="desc"><p>Interpolates from <code>a</code> toward <code>b</code> along the shortest arc (t is a fixed 0..1), so a turn never spins the long way around.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
facing = Angle.lerp(facing, target, 0.1)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
32
angle-sin.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.sin — Ludic</title>
|
||||
<meta name="description" content="Sine of an angle.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.sin</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.sin</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.sin(a) -> fixed</code>
|
||||
<div class="desc"><p>Returns the sine of the angle, using the deterministic fixed-point trig.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
let dy = Angle.sin(facing)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
32
angle-to_degrees.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.to_degrees — Ludic</title>
|
||||
<meta name="description" content="Radians back to degrees.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.to_degrees</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.to_degrees</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.to_degrees(a) -> fixed</code>
|
||||
<div class="desc"><p>Converts a radian angle back to degrees — handy for display or debugging.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
let deg = Angle.to_degrees(facing)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
32
angle-wrap.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Angle.wrap — Ludic</title>
|
||||
<meta name="description" content="Normalize any radian value to [-pi, pi).">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-angle.html">Angle</a> <span>›</span> <span class="here">Angle.wrap</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Angle.wrap</h1>
|
||||
</div>
|
||||
<code class="sig">Angle.wrap(a) -> fixed</code>
|
||||
<div class="desc"><p>Normalizes any radian value into [-pi, pi), so accumulated rotation never runs away.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Step phase Update {
|
||||
heading = Angle.wrap(heading + spin)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
36
anim-cell_x.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.cell_x — Ludic</title>
|
||||
<meta name="description" content="The source x (pixels) of a frame on a grid spritesheet.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.cell_x</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.cell_x</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.cell_x(frame, cols, cell_w) -> int</code>
|
||||
<div class="desc"><p>Turns a frame index into the left pixel of its cell on a spritesheet laid out as a grid of <code>cols</code> columns: <code>(frame mod cols) * cell_w</code>. Combine with <a href="anim-cell_y"><code>Anim.cell_y</code></a> to get the top-left source coordinate to blit from.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-frame"><code class="pname">frame</code><span class="pdesc">the frame index (e.g. from <a href="anim-frame"><code>Anim.frame</code></a>)</span></div><div class="param" id="param-cols"><code class="pname">cols</code><span class="pdesc">columns in the sheet</span></div><div class="param" id="param-cell_w"><code class="pname">cell_w</code><span class="pdesc">cell width in pixels</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Run phase Update {
|
||||
let f = Anim.frame(fixed(1), 12, 8)
|
||||
let sx = Anim.cell_x(f, 4, 16)
|
||||
let sy = Anim.cell_y(f, 4, 16)
|
||||
print(sx)
|
||||
print(sy)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
34
anim-cell_y.html
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.cell_y — Ludic</title>
|
||||
<meta name="description" content="The source y (pixels) of a frame on a grid spritesheet.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.cell_y</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.cell_y</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.cell_y(frame, cols, cell_h) -> int</code>
|
||||
<div class="desc"><p>Turns a frame index into the top pixel of its cell on a spritesheet laid out as a grid of <code>cols</code> columns: <code>(frame / cols) * cell_h</code>. Pair with <a href="anim-cell_x"><code>Anim.cell_x</code></a> for the full source rectangle of the frame.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-frame"><code class="pname">frame</code><span class="pdesc">the frame index (e.g. from <a href="anim-frame"><code>Anim.frame</code></a>)</span></div><div class="param" id="param-cols"><code class="pname">cols</code><span class="pdesc">columns in the sheet</span></div><div class="param" id="param-cell_h"><code class="pname">cell_h</code><span class="pdesc">cell height in pixels</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Run phase Update {
|
||||
let f = Anim.frame(fixed(1), 12, 8)
|
||||
let sy = Anim.cell_y(f, 4, 16)
|
||||
print(sy)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
39
anim-clip.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.clip — Ludic</title>
|
||||
<meta name="description" content="Register a named spritesheet clip so Anim.play can play it by name.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.clip</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.clip</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.clip(name, frames, fps, mode)</code>
|
||||
<div class="desc"><p>Registers (or updates) a spritesheet clip under a <code>name</code>, so gameplay refers to a motion by name — <code>Anim.play(entity, "run")</code> — instead of repeating its frame count and rate everywhere. The clip stores <code>frames</code> (cell count), <code>fps</code> (rate) and <code>mode</code> (0 loop, 1 once, 2 ping-pong). Names compare by identity (a string literal interns to one pointer), and the registry holds up to 32 clips. Registering the same name again overwrites it.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-name"><code class="pname">name</code><span class="pdesc">the clip name (a string)</span></div><div class="param" id="param-frames"><code class="pname">frames</code><span class="pdesc">number of cells in the clip</span></div><div class="param" id="param-fps"><code class="pname">fps</code><span class="pdesc">playback rate in frames per second</span></div><div class="param" id="param-mode"><code class="pname">mode</code><span class="pdesc">0 loop, 1 once (clamp on last), 2 ping-pong</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0 }
|
||||
model Hero { SpriteAnim }
|
||||
entry {
|
||||
Anim.clip("idle", 2, 4, 0) # a slow 2-frame idle loop
|
||||
Anim.clip("run", 6, 12, 0) # a 6-frame run
|
||||
Anim.clip("hit", 3, 15, 1) # a one-shot hit reaction
|
||||
spawn Hero { SpriteAnim { } }
|
||||
Anim.play(World.query_next(World.prop_id("SpriteAnim"), 0), "run")
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
33
anim-duration.html
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.duration — Ludic</title>
|
||||
<meta name="description" content="Seconds for one full cycle of a clip: count / fps.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.duration</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.duration</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.duration(fps, count) -> fixed</code>
|
||||
<div class="desc"><p>Returns the length of one cycle of a clip in seconds as a <code>fixed</code>: <code>count / fps</code>. Handy to schedule the next event, size a progress bar, or line a tween up with an animation.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-fps"><code class="pname">fps</code><span class="pdesc">frames per second</span></div><div class="param" id="param-count"><code class="pname">count</code><span class="pdesc">number of frames in the clip</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
handler Run phase Update {
|
||||
let secs = Anim.duration(12, 4) # 4 frames / 12 fps = 0.333s
|
||||
print(secs)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
35
anim-finished.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.finished — Ludic</title>
|
||||
<meta name="description" content="True once a one-shot clip has run past its last frame.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.finished</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.finished</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.finished(timer, fps, count) -> bool</code>
|
||||
<div class="desc"><p>Returns <code>true</code> once <code>floor(timer * fps) >= count</code> — i.e. a one-shot clip driven by <a href="anim-once"><code>Anim.once</code></a> has played its final frame. Use it to despawn an effect, fire a follow-up, or switch back to an idle clip.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-timer"><code class="pname">timer</code><span class="pdesc">elapsed seconds (a <code>fixed</code>)</span></div><div class="param" id="param-fps"><code class="pname">fps</code><span class="pdesc">frames per second</span></div><div class="param" id="param-count"><code class="pname">count</code><span class="pdesc">number of frames in the clip</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property Blast { timer: fixed = 0.0 }
|
||||
model Boom { Blast }
|
||||
handler Run phase Update {
|
||||
Blast.timer = Blast.timer + Time.delta()
|
||||
if Anim.finished(Blast.timer, 15, 6) { despawn (self()) }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
35
anim-fired.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.fired — Ludic</title>
|
||||
<meta name="description" content="Did the entity's clip land on its armed frame event this tick?">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.fired</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.fired</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.fired(entity) -> bool</code>
|
||||
<div class="desc"><p>Returns whether an entity's <code>SpriteAnim</code> clip landed on the frame armed by <a href="anim-on_frame"><code>Anim.on_frame</code></a> this tick — the read side of a frame event. It is true only on the single tick the clip first reaches that cell, so a game can poll it each frame and turn it into a gameplay action (emit an event, spawn a hitbox, play a footstep). Returns <code>false</code> if the entity has no <code>SpriteAnim</code> or no <code>event_fired</code> field.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-entity"><code class="pname">entity</code><span class="pdesc">the entity carrying <code>SpriteAnim</code></span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0, event_frame: int = 0, event_fired: int = 0 }
|
||||
model Hero { SpriteAnim }
|
||||
handler Step phase Update {
|
||||
let e = World.query_next(World.prop_id("SpriteAnim"), 0)
|
||||
if Anim.fired(e) { print(42) } # react: footstep, hitbox, event…
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
36
anim-frame.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.frame — Ludic</title>
|
||||
<meta name="description" content="The looping frame index for an elapsed timer at a given fps.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.frame</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.frame</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.frame(timer, fps, count) -> int</code>
|
||||
<div class="desc"><p>Returns the current frame of a looping clip: <code>floor(timer * fps)</code> reduced modulo <code>count</code>. <code>timer</code> is elapsed seconds as a <code>fixed</code>, <code>fps</code> the clip's frames per second, and <code>count</code> the number of frames. The clip wraps forever — frame <code>count-1</code> is followed by frame <code>0</code>.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-timer"><code class="pname">timer</code><span class="pdesc">elapsed seconds (a <code>fixed</code>)</span></div><div class="param" id="param-fps"><code class="pname">fps</code><span class="pdesc">frames per second</span></div><div class="param" id="param-count"><code class="pname">count</code><span class="pdesc">number of frames in the clip</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property Sprite { timer: fixed = 0.0 }
|
||||
model Hero { Sprite }
|
||||
handler Run phase Update {
|
||||
Sprite.timer = Sprite.timer + Time.delta()
|
||||
let cell = Anim.frame(Sprite.timer, 12, 4) # 4-frame run at 12 fps
|
||||
print(cell)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
39
anim-on_frame.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.on_frame — Ludic</title>
|
||||
<meta name="description" content="Arm a frame event — the engine flags the tick a clip lands on this frame.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.on_frame</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.on_frame</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.on_frame(entity, frame)</code>
|
||||
<div class="desc"><p>Arms a <strong>frame event</strong> on an entity's <code>SpriteAnim</code>: the engine sets the component's <code>event_fired</code> flag on the tick the clip <em>first lands</em> on <code>frame</code> — a footstep on the contact cell, a hitbox going live mid-swing. Gameplay reads the flag with <a href="anim-fired"><code>Anim.fired</code></a> in its own handler and reacts (emit its own event, spawn, play a sound). The engine detects the boundary; the game owns the reaction, so it stays inside the deterministic, no-runtime-dispatch event model. Requires the component to carry <code>event_frame</code> and <code>event_fired</code> fields.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-entity"><code class="pname">entity</code><span class="pdesc">the entity carrying <code>SpriteAnim</code></span></div><div class="param" id="param-frame"><code class="pname">frame</code><span class="pdesc">the cell index that should fire the event</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0, event_frame: int = 0, event_fired: int = 0 }
|
||||
model Hero { SpriteAnim }
|
||||
entry {
|
||||
spawn Hero { SpriteAnim { fps: 12, frames: 6, mode: 0 } }
|
||||
let e = World.query_next(World.prop_id("SpriteAnim"), 0)
|
||||
Anim.on_frame(e, 3) # fire when the run clip hits its contact frame
|
||||
tick_fixed()
|
||||
if Anim.fired(e) { print(1) }
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
36
anim-once.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.once — Ludic</title>
|
||||
<meta name="description" content="A non-looping frame index that clamps on the last frame.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.once</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.once</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.once(timer, fps, count) -> int</code>
|
||||
<div class="desc"><p>Like <a href="anim-frame"><code>Anim.frame</code></a> but for a one-shot clip: the frame is <code>min(floor(timer * fps), count - 1)</code>, so once it reaches the last frame it stays there instead of wrapping. Use it for a play-once animation like an explosion or a door opening; pair it with <a href="anim-finished"><code>Anim.finished</code></a> to know when it is done.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-timer"><code class="pname">timer</code><span class="pdesc">elapsed seconds (a <code>fixed</code>)</span></div><div class="param" id="param-fps"><code class="pname">fps</code><span class="pdesc">frames per second</span></div><div class="param" id="param-count"><code class="pname">count</code><span class="pdesc">number of frames in the clip</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property Blast { timer: fixed = 0.0 }
|
||||
model Boom { Blast }
|
||||
handler Run phase Update {
|
||||
Blast.timer = Blast.timer + Time.delta()
|
||||
let cell = Anim.once(Blast.timer, 15, 6)
|
||||
print(cell)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
36
anim-pingpong.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.pingpong — Ludic</title>
|
||||
<meta name="description" content="A frame index that bounces 0..count-1..0 and repeats.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.pingpong</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.pingpong</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.pingpong(timer, fps, count) -> int</code>
|
||||
<div class="desc"><p>Returns a frame index that plays forward to <code>count-1</code>, then back to <code>0</code>, then forward again — a triangle wave over the frames. Ideal for a two-way idle bob or a breathing/pulsing loop where a plain wrap would snap.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-timer"><code class="pname">timer</code><span class="pdesc">elapsed seconds (a <code>fixed</code>)</span></div><div class="param" id="param-fps"><code class="pname">fps</code><span class="pdesc">frames per second</span></div><div class="param" id="param-count"><code class="pname">count</code><span class="pdesc">number of frames in the clip</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property Idle { timer: fixed = 0.0 }
|
||||
model Fish { Idle }
|
||||
handler Run phase Update {
|
||||
Idle.timer = Idle.timer + Time.delta()
|
||||
let cell = Anim.pingpong(Idle.timer, 8, 4) # 0 1 2 3 2 1 0 1 ...
|
||||
print(cell)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
39
anim-play.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Anim.play — Ludic</title>
|
||||
<meta name="description" content="Start (or restart) a spritesheet clip on an entity in one call.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-anim.html">Anim</a> <span>›</span> <span class="here">Anim.play</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Anim.play</h1>
|
||||
</div>
|
||||
<code class="sig">Anim.play(entity, clip) | Anim.play(entity, fps, frames, mode)</code>
|
||||
<div class="desc"><p>Starts a spritesheet clip on an entity's <code>SpriteAnim</code> component and rewinds it to its first cell, so gameplay swaps or replays an animation with one call instead of setting five fields by hand. Two forms: <code>Anim.play(entity, "run")</code> plays a <a href="anim-clip"><code>named clip</code></a> registered with <code>Anim.clip</code>; <code>Anim.play(entity, fps, frames, mode)</code> sets the clip directly (<code>mode</code> 0 loop, 1 once, 2 ping-pong). A no-op if the entity has no <code>SpriteAnim</code>. The engine advances the clip from there each tick.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-entity"><code class="pname">entity</code><span class="pdesc">the entity carrying <code>SpriteAnim</code></span></div><div class="param" id="param-clip"><code class="pname">clip</code><span class="pdesc">a registered clip name (2-argument form), **or**</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Demo {
|
||||
property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0 }
|
||||
model Hero { SpriteAnim }
|
||||
entry {
|
||||
Anim.clip("run", 6, 12, 0)
|
||||
spawn Hero { SpriteAnim { } }
|
||||
let e = World.query_next(World.prop_id("SpriteAnim"), 0)
|
||||
Anim.play(e, "run") # by name
|
||||
Anim.play(e, 8, 4, 1) # or directly: 4 frames @ 8fps, once
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
35
annot-clearcolor.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>"@ClearColor" — Ludic</title>
|
||||
<meta name="description" content="Declare a clear colour so the Render phase auto-clears + auto-presents for you.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">"@ClearColor"</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">"@ClearColor"</h1>
|
||||
</div>
|
||||
<code class="sig">"@ClearColor(0xRRGGBB) handler Draw phase Render { … }"</code>
|
||||
<div class="desc"><p><code>@ClearColor(N)</code> declares the framebuffer clear colour (a <code>0x00RRGGBB</code> integer) so the engine owns the per-frame clear and present: at the top of the Render phase it clears the framebuffer to <code>N</code>, and after all Render handlers run it presents the frame — so a game's Render handler carries no <code>Screen.clear</code> / <code>Screen.show</code> boilerplate, and the colour is configured declaratively rather than written in the handler body. It is opt-in: a program with no <code>@ClearColor</code> is byte-for-byte identical (it clears and presents itself, or the light system owns the present). Place it on the program's Render handler (or any declaration — it is program-wide). The argument is any constant expression — a hex literal, a <code>Color.*</code> name, or a <code>const</code> from your palette.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program ClearColorDemo {
|
||||
property Tag { n: int = 0 }
|
||||
model P { Tag }
|
||||
@ClearColor(0x102030)
|
||||
handler Draw phase Render {
|
||||
Screen.fill_rectangle(10, 10, 4, 4, 0xffffff)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
46
annot-computed.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Computed — Ludic</title>
|
||||
<meta name="description" content="A derived field expanded inline wherever it is read, never stored.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Computed</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Computed</h1>
|
||||
</div>
|
||||
<code class="sig">@Computed on a property field</code>
|
||||
<div class="desc"><p><code>@Computed</code> marks a property field as <b>derived</b>: instead of occupying a slot in the world, its expression is expanded inline at every site that reads it, so it is recomputed on demand and never takes storage. Use it for values that are a pure function of other fields — a total, a midpoint, a scaled amount — where storing them would risk going stale. Because a computed field has no backing storage it cannot be assigned to, and it is not something you replicate: it costs nothing on the wire and is simply recomputed on each peer. Reach for it when a value should always be consistent with its inputs and you would otherwise have to remember to recompute it by hand.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program DerivedStats {
|
||||
property Health {
|
||||
current: int = 100,
|
||||
maximum: int = 100,
|
||||
@Computed missing: int = maximum - current # never stored; expanded where read
|
||||
}
|
||||
model Player { Health }
|
||||
|
||||
handler ReportDamage phase Update {
|
||||
for (Health) in query [Health] {
|
||||
Health.current = 70
|
||||
print(Health.missing) # 30 — recomputed from current and maximum
|
||||
}
|
||||
}
|
||||
|
||||
handler SpawnPlayer phase Start {
|
||||
spawn Player { Health { current: 100, maximum: 100 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
45
annot-enginesystem.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@EngineSystem — Ludic</title>
|
||||
<meta name="description" content="Register a package's engine-owned system so the frame loop runs it each phase.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@EngineSystem</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@EngineSystem</h1>
|
||||
</div>
|
||||
<code class="sig">"@EngineSystem(Component, Phase) function esys_name() { … }"</code>
|
||||
<div class="desc"><p><code>@EngineSystem(Component, Phase)</code> marks a function as an engine-owned system: the compiler adds it to the engine-system registry, and the frame loop calls it every <code>Phase</code> — after the game's own handlers — whenever the named <code>Component</code> is present in the program. It is the package-declarable form of the built-in engine systems (the ones that advance <code>SpriteAnim</code>/<code>Motion</code>/<code>Light2D</code>), so a third-party controller ships a system the same way the standard library does, with no compiler edit. The function reads and writes components by name through the reflection ABI, so it stays layout-independent and additive: a build that never declares the component compiles byte-for-byte the same. Phases are <code>Input</code>, <code>FixedUpdate</code>, <code>Update</code>, <code>LateUpdate</code> and <code>Render</code>.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program EngineSystemDemo {
|
||||
property Score { value: int = 0 }
|
||||
model Player { Score }
|
||||
|
||||
@EngineSystem(Score, Update) function esys_score() -> void { # runs every Update
|
||||
let P = world_prop_id("Score")
|
||||
let e = world_query_next(P, 0)
|
||||
if e >= 0 { world_set(e, P, 0, world_get(e, P, 0) + 1) } # advance by name
|
||||
}
|
||||
|
||||
@OnSpawn(Player) handler Init { }
|
||||
handler Boot phase Start { let e = world_spawn(world_model_id("Player")) }
|
||||
handler Run phase Update {
|
||||
let P = world_prop_id("Score")
|
||||
print(world_get(world_query_next(P, 0), P, 0)) # grows each frame
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
40
annot-export.html
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@export — Ludic</title>
|
||||
<meta name="description" content="Expose a function as a native symbol so a host can call it.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@export</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@export</h1>
|
||||
</div>
|
||||
<code class="sig">@export function name(…) -> R</code>
|
||||
<div class="desc"><p><code>@export</code> makes a <code>function</code> visible outside the module as a plain native symbol, so a host program or another linked object can call it by name. Without it, functions are internal to the compiled unit; with it, the emitted symbol is externally linkable, which is how Ludic hands entry points to a runtime seam or a foreign caller. It is the outbound counterpart to <code>extern function</code>, which pulls a foreign symbol in. Keep exported signatures to POD scalars and pointers, since they cross a native ABI boundary where Ludic's richer types do not apply.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program ScoreModule {
|
||||
var running_total: int = 0
|
||||
|
||||
@export function add_points(amount: int) -> int { # callable from a native host
|
||||
running_total = running_total + amount
|
||||
return running_total
|
||||
}
|
||||
|
||||
entry {
|
||||
print(add_points(10)) # 10
|
||||
print(add_points(5)) # 15
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
45
annot-handles.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Handles — Ludic</title>
|
||||
<meta name="description" content="Declarative hint naming the event or subsystem a handler is responsible for.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Handles</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Handles</h1>
|
||||
</div>
|
||||
<code class="sig">@Handles(Event) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@Handles</code> is a declarative annotation that names the event or subsystem a handler (or a whole program) is responsible for. The parser accepts and records it as metadata for analysis and tooling — it does not by itself wire up dispatch, so it documents intent rather than driving execution. When you want a handler to actually run in response to an event, use <code>@On(Event)</code>, which registers the listener; <code>@Handles</code> is the companion label that makes ownership of an event legible across a modular codebase. Keep the named symbol a real event or system so the annotation stays meaningful.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program CombatModule {
|
||||
property Health { current: int = 100 }
|
||||
model Enemy { Health }
|
||||
|
||||
event DamageDealt { amount: int = 0 }
|
||||
|
||||
@Handles(DamageDealt) # documents this handler's responsibility
|
||||
@On(DamageDealt) handler ApplyDamage { # @On does the actual dispatch wiring
|
||||
for (Health) in query [Health, {Enemy}] {
|
||||
Health.current = Health.current - amount
|
||||
}
|
||||
}
|
||||
|
||||
entry {
|
||||
spawn Enemy { Health { current: 100 } }
|
||||
emit DamageDealt(amount: 25)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
36
annot-namespace.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Namespace — Ludic</title>
|
||||
<meta name="description" content="Let a package provide a Name.method(…) namespace that dispatches to name_method.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Namespace</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Namespace</h1>
|
||||
</div>
|
||||
<code class="sig">"@Namespace(Name) function name_method(…) -> T { … }"</code>
|
||||
<div class="desc"><p><code>@Namespace(Name)</code> registers <code>Name</code> as a namespace a package provides, so a caller can write <code>Name.method(…)</code> and the compiler dispatches it to the bare function <code>name_method(…)</code> (the namespace lowercased, an underscore, then the method). It is the package-declarable form of the built-in <code>Foo.*</code> stdlib namespaces (<code>Regex.*</code>, <code>Grid.*</code>, …): a third-party package ships the same call shape without editing the compiler. The alias goes through the same generic call path the core namespaces use and preserves the target function's return type; it only applies after every built-in namespace, so it never shadows a core one. Mark each function that backs a method (the registration is idempotent — one mark is enough to open the namespace, but marking each provider documents the surface).</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program NamespaceDemo {
|
||||
@Namespace(Coach) function coach_bonus() -> int { return 99 }
|
||||
@Namespace(Coach) function coach_double(n: int) -> int { return n + n }
|
||||
|
||||
entry {
|
||||
print(Coach.bonus()) # 99 — dispatches to coach_bonus
|
||||
print(Coach.double(21)) # 42 — dispatches to coach_double
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
43
annot-on.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@On — Ludic</title>
|
||||
<meta name="description" content="Register a compile-time listener that runs whenever an event is emitted.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@On</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@On</h1>
|
||||
</div>
|
||||
<code class="sig">@On(Event) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@On(Event)</code> turns a handler into a listener for a named <code>event</code>: whenever any code runs <code>emit Event(...)</code>, this handler's body runs with the event's fields bound by name. It is how systems talk to each other without referencing one another directly — the emitter never knows who is listening, so gameplay, UI and mods can all react to the same moment independently. Several handlers may listen to one event; they run in declaration order. On a <code>cancellable event</code>, a listener may <code>cancel</code> to veto it, and the emitter sees that outcome. Related lifecycle hooks such as <code>@OnSpawn</code>, <code>@OnEnable</code> and <code>@OnDespawn</code> are specialized event listeners the compiler wires up for you.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Events {
|
||||
property Health { current: int = 100, maximum: int = 100 }
|
||||
model Player { Health }
|
||||
|
||||
event Damaged { amount: int = 0 }
|
||||
|
||||
handler SpawnPlayer phase Start {
|
||||
spawn Player { Health { current: 100, maximum: 100 } }
|
||||
emit Damaged(amount: 25)
|
||||
}
|
||||
|
||||
@On(Damaged)
|
||||
handler ReportDamage {
|
||||
print(amount)
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
45
annot-onattach.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnAttach — Ludic</title>
|
||||
<meta name="description" content="Run a handler when a property is structurally attached to a live instance.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnAttach</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnAttach</h1>
|
||||
</div>
|
||||
<code class="sig">@OnAttach(Property) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnAttach(Property)</code> fires when the named property is <b>structurally added</b> to a live model instance with <code>attach P on e</code> — the property's fields are seeded first, then the hook runs in the context of that instance so it can read and adjust them. This is the structural birth of a property, the counterpart to <code>@OnDetach</code>; it differs from <code>@OnEnable</code>, which only un-pauses a property that already exists. Use it to react when a capability appears on an instance at runtime — a shield goes up, a status effect lands. The property name is a checked reference. Add <code>@Public</code> to also emit a <code>prop_<Property>_attach</code> event.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program AttachHook {
|
||||
property Tag { value: int = 0 }
|
||||
property Shield { amount: int = 0 }
|
||||
model Unit { Tag }
|
||||
|
||||
@OnAttach(Shield) handler RaiseShield { # fires when Shield is attached
|
||||
print(Shield.amount + 10) # 15 — reads the seeded amount
|
||||
}
|
||||
|
||||
handler Seed phase Start { spawn Unit { Tag { value: 1 } } }
|
||||
|
||||
handler AddShield phase Render {
|
||||
for (unit) in query [Unit] {
|
||||
attach Shield on self() { amount: 5 } # triggers @OnAttach
|
||||
}
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
46
annot-ondespawn.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnDespawn — Ludic</title>
|
||||
<meta name="description" content="Run a handler when a model instance is torn down, optionally knowing why.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnDespawn</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnDespawn</h1>
|
||||
</div>
|
||||
<code class="sig">@OnDespawn(Model, reason: r) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnDespawn(Model)</code> is the teardown counterpart to <code>@OnSpawn</code>: it fires once for each instance of the named model as it is removed, whether by an in-world <code>despawn</code> or at program shutdown, and its body still reads the instance's outgoing field values before they are gone. With the optional <code>reason: r</code> binding it becomes reason-carrying teardown — <code>r</code> is bound to an <code>EndReason</code> the compiler passes at each teardown site (an in-world despawn passes <code>EndReason.Despawned</code>, program exit passes <code>EndReason.Quit</code>) so one hook can branch on <b>why</b> the instance is ending, the way Unreal's <code>EndPlay(reason)</code> does. Every still-live instance's hook fires at quit, which makes "no silent deaths" real: drop loot on a real death but skip it when the app is simply closing. Add <code>@Public</code> to also emit a <code>model_<Model>_despawn</code> event.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program DespawnHook {
|
||||
property Health { current: int = 0 }
|
||||
property Loot { gold: int = 0 }
|
||||
model Enemy { Health, Loot }
|
||||
|
||||
@OnDespawn(Enemy, reason: teardown_reason) handler DropLoot {
|
||||
match teardown_reason {
|
||||
EndReason.Quit => { } # app closing — do not drop loot
|
||||
_ => { print(Loot.gold) } # died in-world — award the gold
|
||||
}
|
||||
}
|
||||
|
||||
handler SpawnWave phase Start { spawn Enemy { Health { current: 10 }, Loot { gold: 25 } } }
|
||||
|
||||
handler KillOne phase Render {
|
||||
for (Health) in query [Health, {Enemy}] { despawn self() } # prints 25
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
48
annot-ondetach.html
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnDetach — Ludic</title>
|
||||
<meta name="description" content="Run a handler when a property is structurally detached from a live instance.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnDetach</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnDetach</h1>
|
||||
</div>
|
||||
<code class="sig">@OnDetach(Property) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnDetach(Property)</code> is the teardown counterpart to <code>@OnAttach</code>: it fires when the named property is structurally removed from a live instance with <code>detach P on e</code>. The hook runs <b>before</b> the property's presence flag clears, so its body can still read the outgoing field values one last time — useful for releasing whatever the property was tracking or logging its final state. It is distinct from <code>@OnDisable</code>, which pauses a property while keeping its data; detach actually destroys the property's presence on the instance. Add <code>@Public</code> to also emit a <code>prop_<Property>_detach</code> event.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program DetachHook {
|
||||
property Tag { value: int = 0 }
|
||||
property Shield { amount: int = 0 }
|
||||
model Unit { Tag }
|
||||
|
||||
@OnDetach(Shield) handler DropShield { # fires as Shield is removed
|
||||
print(Shield.amount + 20) # 25 — reads the outgoing amount
|
||||
}
|
||||
|
||||
handler Seed phase Start {
|
||||
spawn Unit { Tag { value: 1 } }
|
||||
for (unit) in query [Unit] { attach Shield on self() { amount: 5 } }
|
||||
}
|
||||
|
||||
handler RemoveShield phase Render {
|
||||
for (shield) in query [Shield] {
|
||||
detach Shield on self() # triggers @OnDetach
|
||||
}
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
45
annot-ondisable.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnDisable — Ludic</title>
|
||||
<meta name="description" content="Run a handler when a property is paused (disabled) on an instance.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnDisable</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnDisable</h1>
|
||||
</div>
|
||||
<code class="sig">@OnDisable(Property) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnDisable(Property)</code> fires when a present property is paused with <code>disable P on e</code>. Disabling keeps the property's data intact and simply stops it from participating — the logical "off" switch — so this hook is the moment a capability goes dormant, not the moment it is destroyed (that is <code>@OnDetach</code>). The body runs in the instance's context and can read the fields before they go quiet, which is handy for cleaning up an effect the property was driving. It pairs with <code>@OnEnable</code> for capabilities that toggle on and off repeatedly. Add <code>@Public</code> to also emit a <code>prop_<Property>_disable</code> event.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program DisableHook {
|
||||
property Tag { value: int = 0 }
|
||||
property Shield { amount: int = 0 }
|
||||
model Unit { Tag, Shield }
|
||||
|
||||
@OnDisable(Shield) handler ShieldDown { # fires when Shield is disabled
|
||||
print(Shield.amount) # 5 — data is retained, just paused
|
||||
}
|
||||
|
||||
handler Seed phase Start { spawn Unit { Tag { value: 1 }, Shield { amount: 5 } } }
|
||||
|
||||
handler Deactivate phase Render {
|
||||
for (unit) in query [Unit] {
|
||||
disable Shield on self() # triggers @OnDisable
|
||||
}
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
46
annot-onenable.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnEnable — Ludic</title>
|
||||
<meta name="description" content="Run a handler when a paused property is re-enabled on an instance.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnEnable</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnEnable</h1>
|
||||
</div>
|
||||
<code class="sig">@OnEnable(Property) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnEnable(Property)</code> fires when a property that is present but disabled is turned back on with <code>enable P on e</code>. Unlike <code>@OnAttach</code>, the property's data was never destroyed — enable/disable only pause and resume a property that already exists, keeping its fields intact — so this hook is about a capability becoming active again rather than being created. The body runs in the instance's context and can read the retained field values. Use the enable/disable pair for a capability that toggles repeatedly (a power-up, a stun) where re-seeding on every toggle would be wrong. Add <code>@Public</code> to also emit a <code>prop_<Property>_enable</code> event.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program EnableHook {
|
||||
property Tag { value: int = 0 }
|
||||
property Shield { amount: int = 0 }
|
||||
model Unit { Tag, Shield }
|
||||
|
||||
@OnEnable(Shield) handler ShieldBackOnline { # fires when Shield is re-enabled
|
||||
print(Shield.amount) # 5 — the retained amount, not re-seeded
|
||||
}
|
||||
|
||||
handler Seed phase Start {
|
||||
spawn Unit { Tag { value: 1 }, Shield { amount: 5 } }
|
||||
for (unit) in query [Unit] { disable Shield on self() }
|
||||
}
|
||||
|
||||
handler Reactivate phase Render {
|
||||
for (unit) in query [Unit] { enable Shield on self() } # triggers @OnEnable
|
||||
quit()
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
39
annot-onquit.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnQuit — Ludic</title>
|
||||
<meta name="description" content="Run a handler once at shutdown, after the last frame.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnQuit</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnQuit</h1>
|
||||
</div>
|
||||
<code class="sig">@OnQuit handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnQuit</code> pins a handler to the program's shutdown moment: it runs exactly once as the program exits, after the final frame. Use it for teardown that must happen once — flushing a save, printing a final tally, releasing a host resource. In the lifecycle timeline it runs after every still-live instance's <code>@OnDespawn</code> hook has fired, so the world has already been torn down by the time <code>@OnQuit</code> sees it. It is the boot-time counterpart of <code>@OnStart</code>; add <code>@Public</code> to also emit a <code>program_quit</code> event other modules can hook.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program QuitHook {
|
||||
property Score { total: int = 0 }
|
||||
model Scoreboard { Score }
|
||||
|
||||
@OnQuit handler ReportFinalScore { # runs once at shutdown
|
||||
for (Score) in query [Score] { print(Score.total) }
|
||||
}
|
||||
|
||||
handler Seed phase Start { spawn Scoreboard { Score { total: 42 } } }
|
||||
|
||||
handler Finish phase Render { quit() } # triggers shutdown -> prints 42
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
40
annot-onspawn.html
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnSpawn — Ludic</title>
|
||||
<meta name="description" content="Run a handler once each time an instance of a model is spawned.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnSpawn</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnSpawn</h1>
|
||||
</div>
|
||||
<code class="sig">@OnSpawn(Model) handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnSpawn(Model)</code> turns a handler into a birth hook: it fires once for each newly <code>spawn</code>ed instance of the named model, right after its properties are seeded. The body runs in the context of that fresh instance, so it can read and adjust the instance's fields by property name to finish initialization — seed derived state, register the instance, play a spawn effect. The model name is a checked reference, so a typo is a compile error rather than a silently dead hook. Its teardown counterpart is <code>@OnDespawn</code>; add <code>@Public</code> to also emit a <code>model_<Model>_spawn</code> event other modules can listen for.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program SpawnHook {
|
||||
property Health { current: int = 0, maximum: int = 100 }
|
||||
model Enemy { Health }
|
||||
|
||||
@OnSpawn(Enemy) handler InitEnemy { # fires per new Enemy instance
|
||||
Health.current = Health.maximum # start every enemy at full health
|
||||
print(Health.current) # 100
|
||||
}
|
||||
|
||||
handler SpawnWave phase Start {
|
||||
spawn Enemy { Health { current: 0, maximum: 100 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
40
annot-onstart.html
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@OnStart — Ludic</title>
|
||||
<meta name="description" content="Run a handler once at boot instead of assigning it a frame phase.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@OnStart</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@OnStart</h1>
|
||||
</div>
|
||||
<code class="sig">@OnStart handler Name { … }</code>
|
||||
<div class="desc"><p><code>@OnStart</code> pins a handler to the program's boot moment: it runs exactly once when the program starts, before the per-frame phases begin, instead of being placed in a recurring phase. Use it for one-time setup — spawning the initial world, seeding program vars, loading a scene. It overrides the handler's phase, so you write <code>@OnStart handler Name { … }</code> rather than <code>handler Name phase Start</code> and get the same once-at-boot placement through the annotation channel. Its shutdown counterpart is <code>@OnQuit</code>; add <code>@Public</code> to also emit a <code>program_start</code> event other modules can hook.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program BootHook {
|
||||
property Health { current: int = 0, maximum: int = 100 }
|
||||
model Player { Health }
|
||||
|
||||
var elapsed_frames: int = 0
|
||||
|
||||
@OnStart handler CreateWorld { # runs once at boot
|
||||
spawn Player { Health { current: 100, maximum: 100 } }
|
||||
elapsed_frames = 0
|
||||
}
|
||||
|
||||
handler CountFrames phase Update { elapsed_frames = elapsed_frames + 1 }
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
41
annot-owned.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Owned — Ludic</title>
|
||||
<meta name="description" content="Give a model a network owner slot so its instances can be assigned to a peer.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Owned</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Owned</h1>
|
||||
</div>
|
||||
<code class="sig">@Owned model Name { … }</code>
|
||||
<div class="desc"><p><code>@Owned</code> declares that every instance of a model carries a network owner — it adds an owner slot to the model (the runtime's <code>@L_owner</code> array), which the builtins <code>owner</code>, <code>set_owner</code>, and <code>is_owner</code> read and assign. A fresh instance starts unowned (<code>-1</code>) until the authority assigns it. Ownership is what gates who may write <code>@Sync(to: owner)</code> fields and who runs <code>@Predicted</code> control handlers, and it is stored in the world snapshot so it round-trips through replication and rollback. Mark the models that represent a player's avatar or units; leave shared scenery unowned.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program OwnedModel {
|
||||
@Sync property Position { column: int = 0, row: int = 0 }
|
||||
@Owned model Player { @Sync Position } # each Player instance has an owner slot
|
||||
|
||||
entry {
|
||||
spawn Player { Position { column: 5, row: 6 } }
|
||||
for (Position) in query [Position, {Player}] {
|
||||
let player_id = self()
|
||||
print(owner(player_id)) # -1 — unowned until assigned
|
||||
set_owner(player_id, 0)
|
||||
print(is_owner(player_id)) # 1 — the local peer now owns it
|
||||
}
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
42
annot-predicted.html
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Predicted — Ludic</title>
|
||||
<meta name="description" content="Run a control handler on the owning client speculatively and on the server authoritatively.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Predicted</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Predicted</h1>
|
||||
</div>
|
||||
<code class="sig">@Predicted handler Name { … }</code>
|
||||
<div class="desc"><p><code>@Predicted</code> marks a control handler that runs in two places: speculatively on the client that <b>owns</b> the affected instance, so local input feels instant, and authoritatively on the server, whose result reconciles the client if the two diverge. It is the responsive-control role from Quake-style client-prediction, named for the netcode behavior (owner-predicts plus server-authoritative plus reconcile) rather than the machine, and it matches Unity's <code>GhostMode.Predicted</code> so the concept transfers. Prediction is explicit — Ludic never silently predicts — and it only applies to instances of an <code>@Owned</code> model, since the dispatch reads <code>is_owner</code> to decide whether the local client should run it. Use it for the owning player's movement and actions; leave authority-only rules on <code>@Server</code>.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program PredictedMovement {
|
||||
@Sync property Position { column: int = 0, row: int = 0 }
|
||||
@Owned model Player { @Sync Position }
|
||||
|
||||
@Predicted handler MovePlayer phase Input { # owner predicts, server reconciles
|
||||
let pressed = Input.key()
|
||||
for (Position) in query [Position, {Player}] {
|
||||
if pressed == 'd' { Position.column = Position.column + 1 }
|
||||
}
|
||||
}
|
||||
|
||||
handler SpawnPlayer phase Start {
|
||||
spawn Player { Position { column: 0, row: 0 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
43
annot-public.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Public — Ludic</title>
|
||||
<meta name="description" content="Promote a lifecycle hook to a public event other modules can listen for.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Public</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Public</h1>
|
||||
</div>
|
||||
<code class="sig">@Public @OnSpawn(Model) handler … { … }</code>
|
||||
<div class="desc"><p><code>@Public</code> stacks in front of a lifecycle hook to also emit a well-named event at the same moment the hook fires, so other modules (or mods) can react without touching the original code. It turns a private hook into a public seam: <code>@Public @OnSpawn(Model)</code> emits <code>model_<Model>_spawn</code>, <code>@OnDespawn</code> emits <code>model_<Model>_despawn</code>, the property hooks emit <code>prop_<Property>_attach</code>/<code>_detach</code>/<code>_enable</code>/<code>_disable</code>, and <code>@OnStart</code>/<code>@OnQuit</code> emit <code>program_start</code>/<code>program_quit</code>. This is the same lowering the event system uses, so anyone can <code>@On</code> the generated event to extend behavior at that lifecycle point. Add it when a hook marks an extension point you want to keep open across a modular codebase.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program PublicSpawn {
|
||||
property Health { current: int = 0, maximum: int = 100 }
|
||||
model Enemy { Health }
|
||||
|
||||
@Public @OnSpawn(Enemy) handler InitEnemy { # also emits model_Enemy_spawn
|
||||
Health.current = Health.maximum
|
||||
}
|
||||
|
||||
@On(model_Enemy_spawn) handler AnnounceEnemy { # another module hooks the public event
|
||||
print(1) # 1 — reacts to every Enemy spawn
|
||||
}
|
||||
|
||||
handler SpawnWave phase Start {
|
||||
spawn Enemy { Health { current: 0, maximum: 100 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
42
annot-queries.html
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Queries — Ludic</title>
|
||||
<meta name="description" content="Declare the properties a handler operates on, binding their fields by name in the body.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Queries</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Queries</h1>
|
||||
</div>
|
||||
<code class="sig">@Queries(these: [Position, Velocity])</code>
|
||||
<div class="desc"><p><code>@Queries</code> lets a handler declare the properties it operates on up front, and the compiler wraps the whole body in a query loop over the matching model instances. The body then runs once per match with each listed property bound by name — you write <code>Position.column</code> directly instead of opening a <code>for (…) in query […]</code> yourself — and <code>self()</code> gives the current instance. It takes the same shape as a manual query: <code>these: [ … ]</code> lists the required properties (each may carry a filter like <code>Health{current <= 0}</code>), and an optional <code>on: Model</code> restricts the match to instances of one model. Reach for it when a handler's entire job is "for every matching instance, do this"; drop to an explicit <code>query</code> when you need finer control inside the loop.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program AdvanceMovement {
|
||||
property Position { column: int = 0, row: int = 0 }
|
||||
property Velocity { delta_x: int = 0, delta_y: int = 0 }
|
||||
model Player { Position, Velocity }
|
||||
|
||||
@Queries(these: [Position, Velocity], on: Player) # body runs once per matching Player
|
||||
handler AdvancePositions phase FixedUpdate {
|
||||
Position.column = Position.column + Velocity.delta_x
|
||||
Position.row = Position.row + Velocity.delta_y
|
||||
}
|
||||
|
||||
handler SpawnPlayer phase Start {
|
||||
spawn Player { Position { column: 0, row: 0 }, Velocity { delta_x: 1, delta_y: 0 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
43
annot-reads.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Reads — Ludic</title>
|
||||
<meta name="description" content="Declare that a handler reads a property — an analysis and scheduling hint.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Reads</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Reads</h1>
|
||||
</div>
|
||||
<code class="sig">@Reads(Property)</code>
|
||||
<div class="desc"><p><code>@Reads(Property)</code> declares that a handler reads the named property. It is an analysis hint: it documents a handler's data dependencies so tooling and scheduling can reason about which handlers touch which state — for example, to tell whether two handlers can run without conflict. It does not itself bind fields the way <code>@Queries</code> does, nor does it grant access; it annotates intent alongside the handler's actual query. Pair it with <code>@Writes</code> to spell out the full read/write footprint of a handler. Keep the named property real so the declared footprint matches what the body does.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program ReadFootprint {
|
||||
property Health { current: int = 100 }
|
||||
property Score { total: int = 0 }
|
||||
model Player { Health, Score }
|
||||
|
||||
@Reads(Health) # declares the read dependency
|
||||
handler ReportLowHealth phase Update {
|
||||
for (Health) in query [Health, {Player}] {
|
||||
if Health.current < 25 { print(Health.current) }
|
||||
}
|
||||
}
|
||||
|
||||
handler SpawnPlayer phase Start {
|
||||
spawn Player { Health { current: 20 }, Score { total: 0 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
43
annot-server.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Server — Ludic</title>
|
||||
<meta name="description" content="Run a handler only on the authority; clients receive the result via @Sync.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Server</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Server</h1>
|
||||
</div>
|
||||
<code class="sig">@Server handler Name { … }</code>
|
||||
<div class="desc"><p><code>@Server</code> marks a handler as server-authoritative: it runs only on the peer acting as the authority, and clients receive its effects through ordinary <code>@Sync</code> replication rather than by running the handler themselves. This is the declarative alternative to sprinkling <code>is_server()</code> branches through gameplay code — the role is a property of the handler, so every line's placement stays legible. Unmarked handlers are the shared, deterministic simulation that runs everywhere; use <code>@Server</code> for decisions that must have a single source of truth, like awarding score, resolving damage, or spawning authoritative entities. Offline the role register defaults to server, so the guard collapses to "run here" and a single-player build is unchanged.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program AuthoritativeScore {
|
||||
property Score { total: int = 0 }
|
||||
model Scoreboard { Score }
|
||||
|
||||
handler Tick phase Update { # unmarked -> runs on every peer
|
||||
for (Score) in query [Score] { Score.total = Score.total + 1 }
|
||||
}
|
||||
|
||||
@Server handler AwardBonus phase Update { # authority only
|
||||
for (Score) in query [Score] { Score.total = Score.total + 100 }
|
||||
}
|
||||
|
||||
handler SpawnScoreboard phase Start {
|
||||
spawn Scoreboard { Score { total: 0 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
41
annot-sync.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Sync — Ludic</title>
|
||||
<meta name="description" content="Mark fields as replicated and models as owned so the compiler generates networking.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Sync</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Sync</h1>
|
||||
</div>
|
||||
<code class="sig">@Sync property … / @Owned</code>
|
||||
<div class="desc"><p>These are the two declarative switches at the top of Ludic's networking: <code>@Sync</code> says <b>what</b> replicates and <code>@Owned</code> says <b>who</b> owns an instance. Replication is opt-in at the field level and decided per model use-site — a field crosses the wire only when it is both replicable (marked <code>@Sync</code>, either on the field or via <code>@Sync property P</code> which marks every field of <code>P</code>) <b>and</b> participating (the model marks the component <code>@Sync</code>). So the same property can replicate in one model and not another, and there is no <code>@NoSync</code> because the surface is purely additive. From these marks the compiler generates the per-model <code>serialize</code>/<code>apply</code> codecs; <code>@Sync</code> on a non-POD-scalar field (like a <code>pointer</code>) is a compile error, since a machine-local pointer cannot cross the wire. <code>@Owned</code> adds the owner slot the ownership builtins and <code>@Predicted</code> read. See the dedicated <code>@Owned</code> page for ownership details.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program SyncedWorld {
|
||||
@Sync property Position { column: int = 0, row: int = 0 } # every field replicable
|
||||
property Health { @Sync current: int = 0, maximum: int = 0 } # only current replicable
|
||||
|
||||
@Owned model Player { @Sync Position, @Sync Health } # participates -> column,row,current
|
||||
model Scenery { Position } # not @Sync here -> never replicates
|
||||
|
||||
entry {
|
||||
spawn Player { Position { column: 3, row: 4 }, Health { current: 50, maximum: 100 } }
|
||||
for (Position, Health) in query [Position, Health, {Player}] {
|
||||
print(sync_size(self())) # 12 = Position(8) + Health.current(4)
|
||||
}
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
36
annot-system.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@System — Ludic</title>
|
||||
<meta name="description" content="Register a prebuilt binary module's system with the host at load, per phase.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@System</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@System</h1>
|
||||
</div>
|
||||
<code class="sig">"@System(Phase) function name() { … }"</code>
|
||||
<div class="desc"><p><code>@System(Phase)</code> marks a function in a prebuilt binary module (one compiled with <code>--emit-module</code>) as a runtime-registered system. The module carries a load-time constructor that hands each such function to the host through <code>ludic_register_system</code>, and the host's frame loop calls it every <code>Phase</code> after its own handlers — the runtime, escape-hatch counterpart of the compile-time <code>@EngineSystem</code>. The compiler supplies the function's address (Ludic source cannot take one). This is how a closed-source or other-language module contributes systems over the reflection C-ABI; source packages that compile into the consumer should prefer <code>@EngineSystem</code>. Phases are <code>Input</code>, <code>FixedUpdate</code>, <code>Update</code>, <code>LateUpdate</code> and <code>Render</code>.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program ManaModule {
|
||||
function module_init() -> void { world_register_prop("Mana", 2) } # register a component at load
|
||||
|
||||
@System(Update) function mana_regen() -> void { # runs every Update in the host
|
||||
let M = world_prop_id("Mana")
|
||||
let e = world_query_next(M, 0)
|
||||
if e >= 0 { world_set(e, M, 0, world_get(e, M, 0) + 1) }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
44
annot-toclients.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@ToClients — Ludic</title>
|
||||
<meta name="description" content="A remote event broadcast from the server to clients — a server→clients notification.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@ToClients</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@ToClients</h1>
|
||||
</div>
|
||||
<code class="sig">@ToClients event Name { … }</code>
|
||||
<div class="desc"><p><code>@ToClients</code> marks an <code>event</code> as a directional remote event that travels from the authority down to clients — a broadcast, again the ordinary event bus with a direction flag. When the server <code>emit</code>s it, the POD payload is serialized as an event id plus its packed fields and sent through the transport seam to the clients; each client's pump re-emits it into normal <code>@On</code> dispatch, so an <code>@On(Name)</code> handler on every client reacts. Use it for authoritative notifications a client should present but not decide — an explosion happened, a round ended, a pickup was granted. Its mirror is <code>@ToServer</code>, which carries client requests the other way.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program ExplosionBroadcast {
|
||||
property EffectLog { count: int = 0 }
|
||||
model Client { EffectLog }
|
||||
|
||||
@ToClients event Boom { column: int = 0, row: int = 0 } # server -> clients broadcast
|
||||
|
||||
@On(Boom) handler PlayExplosion { # every client reacts
|
||||
for (EffectLog) in query [EffectLog, {Client}] {
|
||||
EffectLog.count = EffectLog.count + 1
|
||||
}
|
||||
}
|
||||
|
||||
entry {
|
||||
spawn Client { EffectLog { count: 0 } }
|
||||
emit Boom(column: 8, row: 3) # serialized to clients, re-emitted on arrival
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
44
annot-toserver.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@ToServer — Ludic</title>
|
||||
<meta name="description" content="A remote event sent from a client to the server — a client→server request.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@ToServer</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@ToServer</h1>
|
||||
</div>
|
||||
<code class="sig">@ToServer event Name { … }</code>
|
||||
<div class="desc"><p><code>@ToServer</code> marks an <code>event</code> as a directional remote event that travels from a client up to the authority — an RPC, expressed as the ordinary event bus with a direction flag rather than a new concept. At an <code>emit</code> site the event's POD payload is serialized as an event id followed by its packed fields and sent through the transport seam toward the server; it does <b>not</b> run locally. On the far side, draining the transport (the runtime's pump) re-emits it into the normal <code>@On</code> dispatch, so a <code>@Server @On(Name)</code> handler picks it up. Use it for player intent — a move request, a fire command — that the authority must validate before it affects the world. Its mirror is <code>@ToClients</code>, which broadcasts the other way.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program FireRequest {
|
||||
property AmmoLog { shots_fired: int = 0 }
|
||||
model Turret { AmmoLog }
|
||||
|
||||
@ToServer event Fire { direction: int = 0 } # client -> server request
|
||||
|
||||
@Server @On(Fire) handler HandleFire { # authority validates and applies
|
||||
for (AmmoLog) in query [AmmoLog, {Turret}] {
|
||||
AmmoLog.shots_fired = AmmoLog.shots_fired + direction
|
||||
}
|
||||
}
|
||||
|
||||
entry {
|
||||
spawn Turret { AmmoLog { shots_fired: 0 } }
|
||||
emit Fire(direction: 1) # serialized onto the wire, not run locally
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
42
annot-writes.html
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@Writes — Ludic</title>
|
||||
<meta name="description" content="Declare that a handler writes a property — an analysis and scheduling hint.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="api.html#annotations">Annotations</a> <span>›</span> <span class="here">@Writes</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-annotation">annotation</span>
|
||||
<h1 id="top">@Writes</h1>
|
||||
</div>
|
||||
<code class="sig">@Writes(Property)</code>
|
||||
<div class="desc"><p><code>@Writes(Property)</code> declares that a handler writes the named property. Like <code>@Reads</code> it is an analysis hint that documents a handler's data footprint so tooling and scheduling can reason about conflicts — two handlers that write the same property, or one that writes what another reads, cannot be reordered freely. It does not bind fields or grant access on its own; the handler still queries and mutates the property in its body as usual. Declaring reads and writes together makes a handler's effect on the world legible at a glance, which matters most in a large, modular codebase. Keep the named property accurate so the declared footprint matches the code.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program WriteFootprint {
|
||||
property Health { current: int = 100 }
|
||||
model Enemy { Health }
|
||||
|
||||
@Reads(Health) @Writes(Health) # full read/write footprint
|
||||
handler ApplyPoison phase Update {
|
||||
for (Health) in query [Health, {Enemy}] {
|
||||
Health.current = Health.current - 1
|
||||
}
|
||||
}
|
||||
|
||||
handler SpawnEnemy phase Start {
|
||||
spawn Enemy { Health { current: 100 } }
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
146
api.html
Normal file
32
app-set_icon.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>App.set_icon — Ludic</title>
|
||||
<meta name="description" content="Set the Dock icon from an image the game ships.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-app.html">App</a> <span>›</span> <span class="here">App.set_icon</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">App.set_icon</h1>
|
||||
</div>
|
||||
<code class="sig">App.set_icon(path) -> void</code>
|
||||
<div class="desc"><p>Sets the application's icon — the Dock tile, the Cmd-Tab switcher, the About box — from an image the game ships.</p><p>A **bundled** app already has one: <code>ludic bundle</code> builds <code>AppIcon.icns</code> from the manifest's <code>app icon</code> and macOS reads it from the bundle, so calling this there is harmless and changes nothing you can see. A **plain binary** is the case this exists for. <code>ludic build</code> produces an executable with no bundle around it, so macOS has nowhere to find an icon and draws the generic one — which is what a developer looks at every time they run the game.</p><p>The path is resolved the same way every other asset is, through the pack first and the filesystem second, so the same call works in a packed build and in a project directory. An image that cannot be found or decoded leaves the current icon alone rather than clearing it.</p><p>Headless builds have no AppKit, so this compiles to nothing there.</p></div>
|
||||
<div class="params"><h2>Parameters</h2><div class="param" id="param-path"><code class="pname">path</code><span class="pdesc">an image the game ships (PNG, JPEG or TIFF); 1024x1024 is the size macOS wants</span></div></div>
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic">program Titled {
|
||||
entry {
|
||||
App.set_icon("assets/app/icon.png")
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
37
app-splash_hide.html
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>App.splash_hide — Ludic</title>
|
||||
<meta name="description" content="Dismiss the boot splash.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-app.html">App</a> <span>›</span> <span class="here">App.splash_hide</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">App.splash_hide</h1>
|
||||
</div>
|
||||
<code class="sig">App.splash_hide() -> void</code>
|
||||
<div class="desc"><p>Dismisses the boot splash. Safe to call when there is no splash, and safe to call twice.</p><p>A bundled game shows a splash while it starts: <code>ludic bundle</code> records <code>app splash</code> from the manifest into the bundle, and the runtime puts it on screen before <code>main</code> runs, out of the asset pack. Nothing takes it down on its own, because nothing else knows when the game has a first frame worth showing - a splash that disappears while the terrain is still loading leaves the same black gap it was there to cover.</p><p>So the game says when:</p><p>``<code>ludic skip
|
||||
program MyGame {
|
||||
entry {
|
||||
load_the_world() # the slow part the splash is covering
|
||||
open_the_window()
|
||||
draw_one_frame()
|
||||
App.splash_hide() # ...and only now
|
||||
}
|
||||
}
|
||||
</code>``</p><p>Headless there is no splash and no window, and the call lowers to nothing.</p><p>See also: <a href="../../SHIPPING">shipping a game</a>.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
43
app-window_hide.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>App.window_hide — Ludic</title>
|
||||
<meta name="description" content="Take the game's window off the screen.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-app.html">App</a> <span>›</span> <span class="here">App.window_hide</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">App.window_hide</h1>
|
||||
</div>
|
||||
<code class="sig">App.window_hide() -> void</code>
|
||||
<div class="desc"><p>Takes the game's window off the screen without closing it: its GL context or Vulkan surface, its size and its contents are kept, and the program goes on running — a hidden window does not end the run. <a href="app-window_show.html"><code>App.window_show</code></a> brings it back. It is how a launcher steps aside while the game it started with <a href="../process/process-spawn.html"><code>Process.spawn</code></a> runs.</p><p>Keep the frame loop going while hidden so the child can be polled, but there is nothing to see: skip the drawing, and throttle the loop yourself — a hidden window is not paced by the display, so an unthrottled loop spins. It does not touch the boot splash (<a href="app-splash_hide.html"><code>App.splash_hide</code></a> does). Safe to call when there is no window yet, and twice. Headless there is no window and the call lowers to nothing.</p><p>``<code>ludic skip
|
||||
program Launcher {
|
||||
var game: int = -1
|
||||
function play() -> void {
|
||||
game = Process.spawn(game_path(), new []string)
|
||||
if game >= 0 { App.window_hide() }
|
||||
}
|
||||
handler Watch phase Update {
|
||||
if game >= 0 and Process.poll(game) != -1 {
|
||||
Process.free(game)
|
||||
game = -1
|
||||
App.window_show()
|
||||
}
|
||||
}
|
||||
}
|
||||
</code>``</p><p><code>orderOut:</code> on macOS; <code>ShowWindow(SW_HIDE)</code> on Windows, which also forgets any key held as the window went.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
36
app-window_show.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>App.window_show — Ludic</title>
|
||||
<meta name="description" content="Bring the game's window back.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-app.html">App</a> <span>›</span> <span class="here">App.window_show</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">App.window_show</h1>
|
||||
</div>
|
||||
<code class="sig">App.window_show() -> void</code>
|
||||
<div class="desc"><p>Puts a window hidden by <a href="app-window_hide.html"><code>App.window_hide</code></a> back on screen, in front and focused, as it was. Safe to call on a window that is already showing, when there is no window, and headless, where it lowers to nothing.</p><p>``<code>ludic skip
|
||||
program Demo {
|
||||
entry {
|
||||
App.window_hide()
|
||||
wait_for_the_game()
|
||||
App.window_show()
|
||||
}
|
||||
}
|
||||
</code>``</p><p>On macOS <code>makeKeyAndOrderFront:</code> and the application is activated. On Windows <code>ShowWindow(SW_SHOW)</code> and <code>SetForegroundWindow</code>; Windows may refuse the focus to a program that is not in the foreground, in which case the window comes back and its taskbar button flashes instead.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-enqueue.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.enqueue — Ludic</title>
|
||||
<meta name="description" content="Queue a named image file to preload later.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.enqueue</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.enqueue</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.enqueue(name, path)</code>
|
||||
<div class="desc"><p>Adds a named image file to the preload queue without loading it now. Load the queue incrementally with <a href="assets-pump"><code>Assets.pump</code></a> from a loading scene, then reach each asset by name with <a href="assets-get"><code>Assets.get</code></a> once <a href="assets-ready"><code>Assets.ready</code></a>. Enqueueing is deterministic — the same order loads the same assets in the same order every run.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
31
assets-enqueue_dir.html
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.enqueue_dir — Ludic</title>
|
||||
<meta name="description" content="Enqueue every file of a directory, named by its file name.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.enqueue_dir</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.enqueue_dir</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.enqueue_dir(path: dir)</code>
|
||||
<div class="desc"><p>Each file is enqueued under its name without the extension (<code>assets/audio/hit.wav</code> → <code>hit</code>); the queue sorts it by extension as usual, so a sound directory becomes the sound bank in one line.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic"># doc-check: skip — illustrative
|
||||
Assets.enqueue_dir(path: "assets/audio")
|
||||
…
|
||||
Audio.play(name: "hit")</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
31
assets-font.html
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.font — Ludic</title>
|
||||
<meta name="description" content="A font loaded through the preload queue, by name.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.font</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.font</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.font(name: s) -> int</code>
|
||||
<div class="desc"><p>The font handle for a <code>.ttf</code> / <code>.ttc</code> that <code>Assets.enqueue</code> loaded under <code>name</code>; 0 while it is not loaded yet. The queue sorts by extension: images become sprites (<code>Assets.get</code>), sounds go to the audio bank (<code>Audio.play(name:)</code>), fonts here.</p></div>
|
||||
|
||||
<div class="examples"><h2>Example</h2><pre data-lang="ludic"># doc-check: skip — illustrative
|
||||
Assets.enqueue(name: "menu", path: "assets/fonts/PixelOperator.ttf")
|
||||
…
|
||||
menu_font = Assets.font(name: "menu")</pre></div>
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-get.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.get — Ludic</title>
|
||||
<meta name="description" content="Look up a named sprite's id (alias of Sprite.named).">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.get</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.get</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.get(name) -> int</code>
|
||||
<div class="desc"><p>Alias of <a href="sprite-named"><code>Sprite.named</code></a>: returns the sprite id registered under <code>name</code>, or <code>-1</code>.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-image.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.image — Ludic</title>
|
||||
<meta name="description" content="Load a whole image file as one sprite; returns its id.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.image</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.image</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.image(path) -> int</code>
|
||||
<div class="desc"><p>Loads the image at <code>path</code> as a single atlas sprite the size of the image, returning its id — the namespaced replacement for a bare <code>png_load</code>. Draw it with <a href="sprite-draw"><code>Sprite.draw</code></a>.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-load.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.load — Ludic</title>
|
||||
<meta name="description" content="Alias of Assets.image.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.load</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.load</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.load(path) -> int</code>
|
||||
<div class="desc"><p>Alias of <a href="assets-image"><code>Assets.image</code></a>: loads a whole image file as one sprite and returns its id.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-loaded.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.loaded — Ludic</title>
|
||||
<meta name="description" content="How many enqueued assets have loaded so far.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.loaded</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.loaded</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.loaded() -> int</code>
|
||||
<div class="desc"><p>Returns how many of the enqueued assets have loaded so far (advanced by <a href="assets-pump"><code>Assets.pump</code></a>) — the numerator for a progress bar.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-progress.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.progress — Ludic</title>
|
||||
<meta name="description" content="Loading progress as a 0..100 percent.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.progress</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.progress</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.progress() -> int</code>
|
||||
<div class="desc"><p>Returns the loading progress as a whole-number percent (<code>0..100</code>); an empty queue is <code>100</code>. Draw it as a progress bar on the loading screen.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-pump.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.pump — Ludic</title>
|
||||
<meta name="description" content="Load up to max queued assets this frame; returns how many it loaded.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.pump</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.pump</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.pump(max) -> int</code>
|
||||
<div class="desc"><p>Loads up to <code>max</code> queued assets this frame, registering each under its name, and returns how many it loaded. Call it each frame in a loading scene with a small <code>max</code> so the frame stays short and the loading screen animates; <a href="assets-ready"><code>Assets.ready</code></a> flips true when the queue is drained. This is the deterministic, no-threads form of async preloading — the work is spread across frames instead of stalling one.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-ready.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.ready — Ludic</title>
|
||||
<meta name="description" content="1 once every enqueued asset has loaded.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.ready</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.ready</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.ready() -> int</code>
|
||||
<div class="desc"><p>Returns <code>1</code> once every enqueued asset has been loaded (the queue is drained), else <code>0</code>. A loading scene pumps until this is true, then enters play (e.g. <code>become</code> the game scene).</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
28
assets-total.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Assets.total — Ludic</title>
|
||||
<meta name="description" content="How many assets are enqueued.">
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="nav"><div class="wrap nav-in"><a class="brand" href="index.html"><span class="logo">L</span> Ludic</a><button class="nav-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button><nav class="nav-links"><a href="index.html">Home</a><a href="api.html">API Reference</a><a class="nav-cta" href="https://git.workshopsoft.io/workshopsoft/ludic">Source ↗</a></nav></div></header>
|
||||
<main class="wrap item">
|
||||
<div class="crumbs"><a href="api.html">API Reference</a> <span>›</span> <a href="ns-assets.html">Assets</a> <span>›</span> <span class="here">Assets.total</span></div>
|
||||
<div class="item-head">
|
||||
<span class="kind-badge kind-method">method</span>
|
||||
<h1 id="top">Assets.total</h1>
|
||||
</div>
|
||||
<code class="sig">Assets.total() -> int</code>
|
||||
<div class="desc"><p>Returns the total number of assets enqueued with <a href="assets-enqueue"><code>Assets.enqueue</code></a> — the denominator for a progress bar.</p></div>
|
||||
|
||||
|
||||
|
||||
<a class="back" href="api.html">← All symbols</a>
|
||||
</main>
|
||||
<script src="ludic-highlight.js"></script>
|
||||
<script>Ludic.highlightAll(); Ludic.installCards(); Ludic.flashTarget();</script>
|
||||
</body></html>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
|
||||
|
||||
Tiny Dungeon (1.0)
|
||||
|
||||
Created/distributed by Kenney (www.kenney.nl)
|
||||
Creation date: 05-07-2022
|
||||
|
||||
------------------------------
|
||||
|
||||
License: (Creative Commons Zero, CC0)
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
This content is free to use in personal, educational and commercial projects.
|
||||
Support us by crediting Kenney or www.kenney.nl (this is not mandatory)
|
||||
|
||||
------------------------------
|
||||
|
||||
Donate: http://support.kenney.nl
|
||||
Patreon: http://patreon.com/kenney/
|
||||
|
||||
Follow on Twitter for updates:
|
||||
http://twitter.com/KenneyNL
|
||||
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
|
@ -1,76 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.8" tiledversion="1.8.2" orientation="orthogonal" renderorder="right-down" width="32" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="1">
|
||||
<tileset firstgid="1" source="sampleSheet.tsx"/>
|
||||
<layer id="1" name="Dungeon" width="32" height="20">
|
||||
<data encoding="csv">
|
||||
14,1610612787,16,1,1,3221225485,1,1,1,2,3,3,3,3,3,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
14,1610612787,17,3,3,4,1,1,1,14,41,41,41,41,41,16,1,1,1,1,1,25,1,1,1,1,1,1,1,1,1073741837,1,
|
||||
14,1610612787,58,41,41,16,25,1,1,14,1610612789,51,51,52,51,16,2,3,4,1,1,1,1,2,3,7,3,3,3,7,3,4,
|
||||
14,1610612787,51,52,51,16,1,1,1,14,1610612787,49,3221225522,49,49,16,14,41,16,1,1,536870925,1,14,41,19,41,30,41,19,41,16,
|
||||
14,1610612787,5,27,27,28,1,1073741837,1,14,1610612787,49,49,49,49,16,14,1610612789,16,1,1,1,1073741837,14,1610612789,31,51,51,52,31,51,16,
|
||||
14,1610612787,16,2,3,3,3,3,4,26,27,27,27,27,27,28,14,1610612787,17,7,3,3,4,14,1610612787,49,49,49,49,49,49,16,
|
||||
14,1610612787,16,14,41,41,41,41,17,3,3,3,3,4,1,1,14,1610612787,58,19,41,41,16,14,1610612787,49,49,49,49,49,49,16,
|
||||
18,1610612787,17,18,1610612789,51,52,51,58,41,41,22,41,16,2684354573,1,14,1610612787,51,31,51,51,16,14,1610612787,49,49,49,49,49,49,16,
|
||||
60,1610612787,58,60,1610612787,49,49,49,51,51,51,51,51,17,3,3,18,1610612787,49,43,49,3221225522,17,18,1610612787,49,49,49,49,49,2684354610,17,
|
||||
51,54,51,51,54,43,49,50,49,49,49,49,49,58,41,41,60,1610612787,49,49,43,49,58,60,37,38,38,38,38,38,39,58,
|
||||
49,43,49,49,49,49,49,49,49,49,49,49,49,51,52,51,51,54,49,49,49,49,51,52,54,49,49,49,49,49,49,51,
|
||||
49,49,49,5,27,6,1610612790,49,49,2147483698,49,49,49,49,49,49,49,49,49,49,49,49,49,5,27,27,27,27,27,27,27,27,
|
||||
49,49,49,16,1,14,1610612787,49,49,49,49,49,49,5,27,27,27,27,27,27,6,1610612790,49,16,1,1,1,1,1,1,1,1,
|
||||
27,27,27,28,3221225485,14,1610612787,49,49,5,27,27,27,28,2,3,3,3,3,3,18,1610612787,49,16,1,1,1,2,3,3,3,3,
|
||||
1,1,1,1,2,18,1610612787,49,49,17,3,4,1,1,14,41,20,41,41,21,60,1610612787,49,16,1,1,1073741837,14,41,11,12,41,
|
||||
1,2,3,3,18,60,1610612787,49,49,58,41,17,3,3,18,1610612789,32,51,51,33,51,54,2147483698,16,1,1,1,14,1610612789,51,51,51,
|
||||
1,14,41,41,60,1610612789,54,49,49,51,51,58,41,41,60,1610612787,49,43,49,49,49,49,49,17,3,3,3,18,1610612787,49,49,49,
|
||||
1,14,1610612789,52,51,54,49,49,2147483698,42,49,51,51,51,51,54,43,49,49,49,49,49,49,58,41,41,41,60,1610612787,49,49,49,
|
||||
1,14,1610612787,3758096434,49,49,49,49,49,3758096434,49,49,49,49,49,49,49,49,49,49,49,43,49,51,51,51,51,51,54,49,49,49,
|
||||
1,14,1610612787,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,3758096434,49
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="4" name="Objects" width="32" height="20">
|
||||
<data encoding="csv">
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,87,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,0,65,0,65,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,0,67,0,67,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,0,0,0,0,0,0,
|
||||
0,0,0,0,0,112,0,0,0,62,0,0,0,0,0,0,0,0,74,0,73,0,0,0,0,0,121,0,0,0,0,0,
|
||||
0,0,65,0,0,0,0,0,62,88,62,0,0,0,0,0,0,0,0,0,0,74,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,67,0,0,0,0,0,98,62,0,0,0,0,0,85,0,0,0,2147483758,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,65,0,0,0,0,0,0,0,0,99,61,0,0,0,0,0,0,0,0,0,123,0,0,0,0,0,0,0,0,0,0,
|
||||
0,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,78,78,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,82,0,0,0,0,0,0,0,0,0,0,0,0,0,124,0,0,0,0,0,0,0,0,0,83,
|
||||
0,0,0,0,0,0,0,82,0,0,0,0,0,0,0,0,0,0,0,124,0,0,0,0,0,0,0,0,0,90,0,0,
|
||||
0,0,0,0,0,0,0,82,0,0,0,0,0,0,0,0,0,0,0,0,0,2147483773,0,0,0,0,0,0,0,0,2147483749,0,
|
||||
0,0,0,0,0,0,0,94,71,71,71,71,71,72,0,0,0,0,0,0,0,0,0,0,0,0,83,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="5" name="Carts" width="32" height="20">
|
||||
<data encoding="csv">
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,55,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.8" tiledversion="1.8.2" name="tileset" tilewidth="16" tileheight="16" spacing="1" tilecount="132" columns="12">
|
||||
<image source="../Tilemap/tilemap.png" width="203" height="186"/>
|
||||
</tileset>
|
||||
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 99 B |
|
Before Width: | Height: | Size: 126 B |
|
Before Width: | Height: | Size: 150 B |
|
Before Width: | Height: | Size: 126 B |
|
Before Width: | Height: | Size: 178 B |
|
Before Width: | Height: | Size: 181 B |
|
Before Width: | Height: | Size: 164 B |