Compare commits
No commits in common. "main" and "v0.2.0" have entirely different histories.
2504 changed files with 75673 additions and 822036 deletions
|
|
@ -6,12 +6,6 @@
|
||||||
"runtimeExecutable": "python3",
|
"runtimeExecutable": "python3",
|
||||||
"runtimeArgs": ["-m", "http.server", "8123", "-d", "build/web"],
|
"runtimeArgs": ["-m", "http.server", "8123", "-d", "build/web"],
|
||||||
"port": 8123
|
"port": 8123
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ludic-docs",
|
|
||||||
"runtimeExecutable": "python3",
|
|
||||||
"runtimeArgs": ["-m", "http.server", "8124", "-d", "build/pages"],
|
|
||||||
"port": 8124
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ labels:
|
||||||
|
|
||||||
## Environment
|
## Environment
|
||||||
|
|
||||||
- Command used (e.g. `bin/ludic build foo.ludic --headless`):
|
- Command used (e.g. `bin/x app foo.ludic --headless`):
|
||||||
- Target (native macOS / headless / web-wasm):
|
- Target (native macOS / headless / web-wasm):
|
||||||
- Commit (`git rev-parse --short HEAD`):
|
- Commit (`git rev-parse --short HEAD`):
|
||||||
- OS / arch:
|
- OS / arch:
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ Closes #
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
- [ ] `bin/ludic-dev test` passes.
|
- [ ] `bin/x test` passes.
|
||||||
- [ ] For compiler/runtime changes: `bin/ludic-dev reseed && bin/ludic-dev bootstrap-cfree`
|
- [ ] For compiler/runtime changes: `bin/x reseed && bin/x bootstrap-cfree`
|
||||||
still reaches the self-hosting fixpoint with no C compiler in the loop.
|
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).
|
- [ ] `ludic-fmt` leaves the touched files unchanged (2-space, LF, UTF-8).
|
||||||
- [ ] New/changed stdlib symbols are documented under `docs/language/**` and
|
- [ ] New/changed stdlib symbols are documented under `docs/language/**` and
|
||||||
registered in `tools/docgen/inventory.json`
|
registered in `tools/docgen/inventory.json`
|
||||||
(`bin/ludic-dev docs-gen && bin/ludic-dev docs-check build/pages` passes).
|
(`bin/x docs-gen && bin/x docs-check build/pages` passes).
|
||||||
- [ ] Commits follow [Conventional Commits](https://www.conventionalcommits.org).
|
- [ ] Commits follow [Conventional Commits](https://www.conventionalcommits.org).
|
||||||
- [ ] No new C / Python / JS in tooling (Ludic only), and no generated
|
- [ ] No new C / Python / JS in tooling (Ludic only), and no generated
|
||||||
artifacts committed outside `build/` / `bin/`.
|
artifacts committed outside `build/` / `bin/`.
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,14 @@ jobs:
|
||||||
clang-16 --version | head -1
|
clang-16 --version | head -1
|
||||||
|
|
||||||
- name: Check out the triggering commit
|
- 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: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
git config --global --add safe.directory '*'
|
git config --global --add safe.directory '*'
|
||||||
git clone "$REPO_URL" .
|
git clone https://git.workshopsoft.io/workshopsoft/ludic.git .
|
||||||
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
||||||
git log --oneline -1
|
git log --oneline -1
|
||||||
# See ci.yml for why the Linux build injects the stdio shim via LUDIC_CC.
|
# 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_CC=clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll" >> "$GITHUB_ENV"
|
||||||
echo "LUDIC_HOME=$(pwd)" >> "$GITHUB_ENV"
|
echo "LUDIC_HOME=$(pwd)" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Bootstrap x from the seed
|
- name: Bootstrap x from the seed
|
||||||
|
|
@ -43,11 +40,11 @@ jobs:
|
||||||
set -eu
|
set -eu
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
clang-16 tools/ci/linux_stdio_shim.ll selfhost/ludicc.seed.ll -o bin/ludicc
|
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/ludicc tools/x/main.ludic -o bin/x
|
||||||
|
|
||||||
- name: Rebuild the compiler from the seed and assert byte-identity
|
- name: Rebuild the compiler from the seed and assert byte-identity
|
||||||
# `ludic-dev bootstrap-cfree` assembles the seed with clang, has that seed
|
# `x bootstrap-cfree` assembles the seed with clang, has that seed
|
||||||
# compiler recompile selfhost.ludic to out.ll, and `cmp`s out.ll against
|
# 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
|
# the checked-in seed. It returns non-zero if they differ — i.e. if the
|
||||||
# seed is stale relative to the compiler source.
|
# seed is stale relative to the compiler source.
|
||||||
run: bin/ludic-dev bootstrap-cfree
|
run: bin/x bootstrap-cfree
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ name: ci
|
||||||
|
|
||||||
# Build the language toolchain from its IR seed and run the regression suites on
|
# 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
|
# 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
|
# was docs.yml, so nothing gated a change on `x test` / `x test-tools` or on the
|
||||||
# compiler even building from the seed. See also bootstrap.yml, which proves the
|
# compiler even building from the seed. See also bootstrap.yml, which proves the
|
||||||
# C-free self-rebuild reproduces the seed byte-for-byte.
|
# C-free self-rebuild reproduces the seed byte-for-byte.
|
||||||
on:
|
on:
|
||||||
|
|
@ -33,22 +33,19 @@ jobs:
|
||||||
clang-16 --version | head -1
|
clang-16 --version | head -1
|
||||||
|
|
||||||
- name: Check out the triggering commit
|
- 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: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
git config --global --add safe.directory '*'
|
git config --global --add safe.directory '*'
|
||||||
git clone "$REPO_URL" .
|
git clone https://git.workshopsoft.io/workshopsoft/ludic.git .
|
||||||
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
||||||
git log --oneline -1
|
git log --oneline -1
|
||||||
# The toolchain is macOS-first; on this Linux runner it links against a
|
# 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
|
# tiny C-free IR shim that supplies the Darwin standard-stream globals
|
||||||
# (__stdoutp/__stderrp) over glibc's stdout/stderr. Injected through
|
# (__stdoutp/__stderrp) over glibc's stdout/stderr. Injected through
|
||||||
# LUDIC_CC so every clang invocation — the seed bootstrap, `ludic-dev build`,
|
# LUDIC_CC so every clang invocation — the seed bootstrap, `x build`,
|
||||||
# and each compiled test program — picks it up. Absolute path so it
|
# and each compiled test program — picks it up. Absolute path so it
|
||||||
# still resolves if a step changes directory.
|
# still resolves if a step changes directory.
|
||||||
echo "LUDIC_CC=clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll -lm" >> "$GITHUB_ENV"
|
echo "LUDIC_CC=clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll" >> "$GITHUB_ENV"
|
||||||
echo "LUDIC_HOME=$(pwd)" >> "$GITHUB_ENV"
|
echo "LUDIC_HOME=$(pwd)" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Bootstrap the toolchain from the IR seed (clang only)
|
- name: Bootstrap the toolchain from the IR seed (clang only)
|
||||||
|
|
@ -60,20 +57,20 @@ jobs:
|
||||||
# pre-built binaries: the language builds itself from source + seed.
|
# pre-built binaries: the language builds itself from source + seed.
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
clang-16 tools/ci/linux_stdio_shim.ll selfhost/ludicc.seed.ll -o bin/ludicc
|
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/ludicc tools/x/main.ludic -o bin/x
|
||||||
bin/ludic-dev build
|
bin/x build
|
||||||
|
|
||||||
- name: Regression suite (ludic-dev test)
|
- name: Regression suite (x test)
|
||||||
run: bin/ludic-dev test
|
run: bin/x test
|
||||||
|
|
||||||
- name: Editor-toolchain suite (ludic-dev test-tools)
|
- name: Editor-toolchain suite (x test-tools)
|
||||||
# Grammar/lexer/vocabulary sync, ludic-fmt idempotence (the project's
|
# Grammar/lexer/vocabulary sync, ludic-fmt idempotence (the project's
|
||||||
# formatting contract — hand alignment is deliberately preserved, so the
|
# 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
|
# 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
|
# assets. Cross-file LSP behaviour and the golden renders are macOS-ABI
|
||||||
# bound and skip here — visibly — until the runtime's directory walk and
|
# bound and skip here — visibly — until the runtime's directory walk and
|
||||||
# windowing are portable.
|
# windowing are portable.
|
||||||
run: bin/ludic-dev test-tools
|
run: bin/x test-tools
|
||||||
|
|
||||||
- name: Docs cover the implementation
|
- name: Docs cover the implementation
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -81,9 +78,9 @@ jobs:
|
||||||
# The whole docs toolchain is written in Ludic and runs through x —
|
# The whole docs toolchain is written in Ludic and runs through x —
|
||||||
# no Python anywhere. check-impl / check-vocabulary / check-docs guard
|
# no Python anywhere. check-impl / check-vocabulary / check-docs guard
|
||||||
# the sources; docs-gen builds the site and docs-check is its coverage
|
# the sources; docs-gen builds the site and docs-check is its coverage
|
||||||
# + integrity guard. (check-vocabulary also runs in `ludic-dev test-tools`.)
|
# + integrity guard. (check-vocabulary also runs in `x test-tools`.)
|
||||||
bin/ludic-dev check-impl
|
bin/x check-impl
|
||||||
bin/ludic-dev check-vocabulary
|
bin/x check-vocabulary
|
||||||
bin/ludic-dev check-docs
|
bin/x check-docs
|
||||||
bin/ludic-dev docs-gen --out build/pages
|
bin/x docs-gen --out build/pages
|
||||||
bin/ludic-dev docs-check build/pages
|
bin/x docs-check build/pages
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,13 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Check out with history
|
- name: Check out with history
|
||||||
env:
|
env:
|
||||||
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
BEFORE: ${{ github.event.before }}
|
||||||
|
BASE: ${{ github.base_ref }}
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
git config --global --add safe.directory '*'
|
git config --global --add safe.directory '*'
|
||||||
# Full clone so both endpoints of the range are present.
|
# Full clone so both endpoints of the range are present.
|
||||||
git clone "$REPO_URL" .
|
git clone https://git.workshopsoft.io/workshopsoft/ludic.git .
|
||||||
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
git checkout "${GITHUB_SHA}" 2>/dev/null || git checkout "${GITHUB_REF_NAME:-main}"
|
||||||
|
|
||||||
- name: Lint the new commits
|
- name: Lint the new commits
|
||||||
|
|
@ -35,15 +36,10 @@ jobs:
|
||||||
# - pull_request: base branch .. this commit
|
# - pull_request: base branch .. this commit
|
||||||
# - push: the pushed range (event.before .. this commit)
|
# - push: the pushed range (event.before .. this commit)
|
||||||
# - new branch / unknown: just the tip 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
|
if [ -n "${BASE:-}" ]; then
|
||||||
git fetch --quiet origin "${BASE}" 2>/dev/null || true
|
git fetch --quiet origin "${BASE}" 2>/dev/null || true
|
||||||
RANGE="origin/${BASE}..${GITHUB_SHA}"
|
RANGE="origin/${BASE}..${GITHUB_SHA}"
|
||||||
elif [ -n "${BEFORE:-}" ] && ! printf '%s' "$BEFORE" | grep -qE '^0+$' \
|
elif [ -n "${BEFORE:-}" ] && ! printf '%s' "$BEFORE" | grep -qE '^0+$'; then
|
||||||
&& git cat-file -e "${BEFORE}^{commit}" 2>/dev/null; then
|
|
||||||
RANGE="${BEFORE}..${GITHUB_SHA}"
|
RANGE="${BEFORE}..${GITHUB_SHA}"
|
||||||
else
|
else
|
||||||
RANGE="${GITHUB_SHA}~1..${GITHUB_SHA}"
|
RANGE="${GITHUB_SHA}~1..${GITHUB_SHA}"
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,10 @@ on:
|
||||||
paths:
|
paths:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
- 'tools/docgen/**'
|
- 'tools/docgen/**'
|
||||||
- 'tools/ludic-cli/**'
|
- 'tools/x/**'
|
||||||
# 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'
|
- '.forgejo/workflows/docs.yml'
|
||||||
workflow_dispatch: {}
|
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:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
|
|
@ -37,8 +25,8 @@ jobs:
|
||||||
# "Waiting" forever with "no online runner found matching this label".
|
# "Waiting" forever with "no online runner found matching this label".
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
# The generator is now Ludic, so this builds the toolchain from its IR seed
|
# 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
|
# (clang assembles the seed into bin/ludicc, which compiles bin/x) exactly
|
||||||
# like the ci workflow, then runs `ludic-dev docs-gen`. node:20-bookworm carries git
|
# like the ci workflow, then runs `x docs-gen`. node:20-bookworm carries git
|
||||||
# for the clone + publish; clang-16 is the only extra the bootstrap needs.
|
# for the clone + publish; clang-16 is the only extra the bootstrap needs.
|
||||||
container: node:20-bookworm
|
container: node:20-bookworm
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -53,23 +41,23 @@ jobs:
|
||||||
- name: Generate the documentation site
|
- name: Generate the documentation site
|
||||||
env:
|
env:
|
||||||
SOURCE_REF: ${{ github.ref_name }}
|
SOURCE_REF: ${{ github.ref_name }}
|
||||||
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
git config --global --add safe.directory '*'
|
git config --global --add safe.directory '*'
|
||||||
git clone --depth 1 --branch "${SOURCE_REF:-main}" "$REPO_URL" src
|
git clone --depth 1 --branch "${SOURCE_REF:-main}" \
|
||||||
|
https://git.workshopsoft.io/workshopsoft/ludic.git src
|
||||||
cd src
|
cd src
|
||||||
# The toolchain is macOS-first; on this Linux runner it links against a
|
# 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
|
# 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
|
# glibc's, injected through LUDIC_CC. docs-gen is a pure CLI (no
|
||||||
# windowing), so the C-free bootstrap is all it needs.
|
# 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_CC="clang-16 $(pwd)/tools/ci/linux_stdio_shim.ll"
|
||||||
export LUDIC_HOME="$(pwd)"
|
export LUDIC_HOME="$(pwd)"
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
clang-16 tools/ci/linux_stdio_shim.ll selfhost/ludicc.seed.ll -o bin/ludicc
|
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/ludicc tools/x/main.ludic -o bin/x
|
||||||
bin/ludic-dev docs-gen --out ../public
|
bin/x docs-gen --out ../public
|
||||||
bin/ludic-dev docs-check ../public
|
bin/x docs-check ../public
|
||||||
cd ..
|
cd ..
|
||||||
echo "--- generated files ---"
|
echo "--- generated files ---"
|
||||||
ls -la public
|
ls -la public
|
||||||
|
|
@ -79,8 +67,6 @@ jobs:
|
||||||
PAGES_TOKEN: ${{ secrets.PAGES_TOKEN }}
|
PAGES_TOKEN: ${{ secrets.PAGES_TOKEN }}
|
||||||
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SOURCE_SHA: ${{ github.sha }}
|
SOURCE_SHA: ${{ github.sha }}
|
||||||
SERVER_URL: ${{ github.server_url }}
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
TOKEN="${PAGES_TOKEN:-${AUTO_TOKEN:-}}"
|
TOKEN="${PAGES_TOKEN:-${AUTO_TOKEN:-}}"
|
||||||
|
|
@ -94,6 +80,5 @@ jobs:
|
||||||
git config user.email "docs@workshopsoft.io"
|
git config user.email "docs@workshopsoft.io"
|
||||||
git add -A
|
git add -A
|
||||||
git commit -q -m "docs: regenerate site from ${SOURCE_SHA}"
|
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 "https://ludic-docs-bot:${TOKEN}@git.workshopsoft.io/workshopsoft/ludic.git" pages
|
||||||
git push -f "${SERVER_URL%%://*}://ludic-docs-bot:${TOKEN}@${SERVER_URL#*://}/${REPO}.git" pages
|
|
||||||
echo "published $(git rev-parse --short HEAD) to 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
.gitattributes
vendored
|
|
@ -1 +0,0 @@
|
||||||
packages/*/lib/** filter=lfs diff=lfs merge=lfs -text
|
|
||||||
36
.gitignore
vendored
36
.gitignore
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
# Generated build tree: LLVM IR, objects, compiled apps, the headless render
|
# 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` /
|
# (build/out.ppm) and the docs site all land under build/ (see `bin/x build` /
|
||||||
# `bin/ludic clean`). Root-anchored so a source dir named "build" elsewhere is never
|
# `bin/x clean`). Root-anchored so a source dir named "build" elsewhere is never
|
||||||
# accidentally ignored. Nothing is written to the repo root any more.
|
# accidentally ignored. Nothing is written to the repo root any more.
|
||||||
/build/
|
/build/
|
||||||
|
|
||||||
|
|
@ -9,24 +9,16 @@
|
||||||
# packaged plugin .zip are local-only build inputs/outputs.
|
# packaged plugin .zip are local-only build inputs/outputs.
|
||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
# the toolchain binaries (ludicc, ludic, ludic-dev, ludic-fmt, ludic-lsp) — all built
|
# the toolchain binaries (ludicc, ludic, x, ludic-fmt, ludic-lsp) — all built
|
||||||
# into bin/ by the one-line bootstrap + `bin/ludic-dev build`; never checked in. The
|
# into bin/ by the one-line bootstrap + `bin/x build`; never checked in. The
|
||||||
# only thing published is the source and the LLVM-IR seed (selfhost/ludicc.seed.ll).
|
# only thing published is the source and the LLVM-IR seed (selfhost/ludicc.seed.ll).
|
||||||
/bin/
|
/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
|
# editor toolchain build artifacts
|
||||||
tools/editors/vscode/node_modules/
|
tools/editors/vscode/node_modules/
|
||||||
tools/editors/vscode/*.vsix
|
tools/editors/vscode/*.vsix
|
||||||
tools/editors/jetbrains/.gradle/
|
tools/editors/jetbrains/.gradle/
|
||||||
tools/editors/jetbrains/build/
|
tools/editors/jetbrains/build/
|
||||||
tools/editors/jetbrains/.kotlin/
|
|
||||||
|
|
||||||
# IntelliJ plugin SDK sandbox (tools/editors/jetbrains)
|
# IntelliJ plugin SDK sandbox (tools/editors/jetbrains)
|
||||||
.intellijPlatform/
|
.intellijPlatform/
|
||||||
|
|
@ -40,23 +32,5 @@ tools/editors/jetbrains/.kotlin/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
# Release artifacts produced by `ludic-dev release`
|
# Release artifacts produced by `x release`
|
||||||
/dist/
|
/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/
|
|
||||||
|
|
|
||||||
1211
CHANGELOG.md
1211
CHANGELOG.md
File diff suppressed because it is too large
Load diff
119
COMPILING.md
119
COMPILING.md
|
|
@ -1,46 +1,37 @@
|
||||||
# Compiling Ludic
|
# Compiling Ludic
|
||||||
|
|
||||||
> **Note:** `ludicc` is **written in Ludic** (`selfhost/*.ludic`) and built from a
|
> **Note (2026-08-27):** `ludicc` is now **written in Ludic** (`selfhost/*.ludic`)
|
||||||
> checked-in IR seed — the C compiler this document once described has been
|
> and built from a checked-in IR seed — the C compiler this document describes has
|
||||||
> deleted. The native pipeline below (Ludic → LLVM IR → object → binary) is
|
> been deleted. The native pipeline below (Ludic → LLVM IR → object → binary) is
|
||||||
> unchanged. `ludicc` drives clang itself (via an `os_system` intrinsic), so
|
> unchanged. `ludicc` now drives clang itself (via an `os_system` intrinsic), so
|
||||||
> `ludicc app.ludic -o bin/app` and `--emit-llvm` work directly. `--fmt` is
|
> `ludicc app.ludic -o bin/app` and `--emit-llvm` work directly, and a sibling
|
||||||
> reimplemented as a lex+parse gate (the doc-check hook). The `--target`/
|
> command `ludic app.ludic` compiles to a temporary binary and runs it in one
|
||||||
> cross-compile and `--shared` paths are still features of the old C driver not
|
> step. The whole toolchain is built by `bin/x build`; `bin/x app` remains as a
|
||||||
> yet re-implemented on the self-hosted toolchain. See the
|
> convenience wrapper over the compiler. `--fmt` is reimplemented as a lex+parse
|
||||||
> [Bootstrap deep-dive](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Bootstrap) §5.7 on the wiki.
|
> 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.
|
> From a clean checkout, build the compiler and the task-runner in one line, then
|
||||||
|
> let `bin/x` do the rest (run it from the repository root):
|
||||||
>
|
>
|
||||||
> ```bash
|
> ```bash
|
||||||
> curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh # the toolchain, into ~/.ludic
|
> # one-time bootstrap: clang assembles the seed, then ludicc compiles bin/x
|
||||||
> ludic new mygame && cd mygame
|
> clang selfhost/ludicc.seed.ll -o bin/ludicc && bin/ludicc tools/x/main.ludic -o bin/x
|
||||||
> ludic run # compile + run
|
> bin/x build # rebuild the whole toolchain into bin/
|
||||||
> ludic build --headless # compile, deterministic render
|
> # (ludicc, ludic, x, ludic-fmt, ludic-lsp)
|
||||||
|
> bin/ludicc examples/games/snake.ludic -o bin/snake # compile
|
||||||
|
> bin/ludic examples/games/snake.ludic # compile + run
|
||||||
|
> bin/x help # list every command
|
||||||
> ```
|
> ```
|
||||||
>
|
>
|
||||||
> From a clean checkout, the compiler and the CLI come up in two lines and the
|
> The binaries are multi-call (one native binary under two names): invoked as
|
||||||
> CLI does the rest (run it from the repository root):
|
> `ludicc` it compiles, as `ludic` it compiles-and-runs. A `.ludic` file with
|
||||||
>
|
> systems is a game and links windowed by default; `--headless` and `--windowed`
|
||||||
> ```bash
|
> force the mode. The runtime (`runtime/native/cocoa.ll`) is found via
|
||||||
> # one-time bootstrap: clang assembles the seed, then ludicc compiles bin/ludic
|
> `$LUDIC_HOME`, defaulting to the directory the binary sits in — keep them in
|
||||||
> mkdir -p bin && clang selfhost/ludicc.seed.ll -o bin/ludicc
|
> `bin/`, or set `LUDIC_HOME` and put them on `PATH`. `$LUDIC_CC` overrides the
|
||||||
> bin/ludicc --unsafe --globals tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
> assembler/linker (default `clang`).
|
||||||
> 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
|
`ludicc` is a compiler, not a translator. It lexes, parses, checks and lowers
|
||||||
|
|
@ -51,10 +42,10 @@ and find your program rewritten in another language.
|
||||||
|
|
||||||
```
|
```
|
||||||
app.ludic
|
app.ludic
|
||||||
│ ludicc — lex, parse, lower (selfhost/frontend/*.ludic,
|
│ ludicc — lex, parse, check, lower (compiler/ludicc.c,
|
||||||
▼ selfhost/backend/*.ludic)
|
▼ compiler/native.c)
|
||||||
app.ll LLVM IR: your handlers, your properties, your runtime
|
app.ll LLVM IR: your systems, your properties, your runtime
|
||||||
│ IR assembler (selfhost/main.ludic drives $LUDIC_CC)
|
│ IR assembler (compiler/driver.c)
|
||||||
▼
|
▼
|
||||||
app.o Mach-O / ELF / COFF object code
|
app.o Mach-O / ELF / COFF object code
|
||||||
│ system linker
|
│ system linker
|
||||||
|
|
@ -73,9 +64,6 @@ point at a different LLVM toolchain if you have one.
|
||||||
| a windowed native executable | `ludicc game.ludic -o build/game` |
|
| a windowed native executable | `ludicc game.ludic -o build/game` |
|
||||||
| a headless executable | `ludicc game.ludic --headless -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 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 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` |
|
| 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` |
|
| an object file † | `ludicc src.ludic -c -o src.o` |
|
||||||
|
|
@ -85,33 +73,32 @@ 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
|
(see the note at the top). The rows above the line work today via the
|
||||||
self-hosted `ludicc`.
|
self-hosted `ludicc`.
|
||||||
|
|
||||||
`bin/ludic build` wraps the common cases:
|
`bin/x app` wraps the common cases:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bin/ludic build examples/games/snake.ludic # -> build/snake (native)
|
bin/x app examples/games/snake.ludic # -> build/snake (native)
|
||||||
bin/ludic build examples/library/combat.ludic --lib # -> build/libcombat.* (library)
|
bin/x app examples/library/combat.ludic --lib # -> build/libcombat.* (library)
|
||||||
bin/ludic build examples/games/snake.ludic --headless # -> build/snake_headless (out.ppm)
|
bin/x app examples/games/snake.ludic --headless # -> build/snake_headless (out.ppm)
|
||||||
bin/ludic build examples/games/snake.ludic --web # -> build/web/ (browser)
|
bin/x app examples/games/snake.ludic --web # -> build/web/ (browser)
|
||||||
```
|
```
|
||||||
|
|
||||||
The `--lib` and `--web` targets were part of the old C driver and are **not yet
|
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
|
re-implemented** on the self-hosted toolchain — `bin/x app` supports the native
|
||||||
windowed and `--headless` builds today.
|
windowed and `--headless` builds today.
|
||||||
|
|
||||||
## Programs and libraries
|
## Programs and libraries
|
||||||
|
|
||||||
> **Not yet on the self-hosted toolchain.** `--shared` and the `nm`/library
|
> **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`
|
> workflow below describe the old C driver's behavior; the self-hosted `ludicc`
|
||||||
> builds executables only for now. The `@export function` semantics are
|
> builds executables only for now. The `module`/`@export fn` semantics are
|
||||||
> unchanged — only the packaging step is pending.
|
> unchanged — only the packaging step is pending.
|
||||||
|
|
||||||
A source file opens with `program Name { … }`.
|
A source file opens with `game Name { … }` or `module Name { … }`.
|
||||||
|
|
||||||
* A program with **handlers** is a game: it gets the phase-ordered frame loop
|
* A **game** gets an entry point and the phase-ordered frame loop
|
||||||
(`Start`, then `Input → FixedUpdate → Update → LateUpdate → Render` each tick).
|
(`Start`, then `Input → FixedUpdate → Update → LateUpdate → Render` each tick).
|
||||||
* A program with only an **`entry`** block is a tool: it runs `entry` and exits.
|
* A **module** gets neither. It is a library, and only its `@export fn`s become
|
||||||
* Either kind can be a library: only its `@export function`s become public
|
public symbols; everything else stays private to the library.
|
||||||
symbols; everything else stays private.
|
|
||||||
|
|
||||||
```ludic
|
```ludic
|
||||||
# doc-check: skip — illustrative: elided body
|
# doc-check: skip — illustrative: elided body
|
||||||
|
|
@ -209,11 +196,8 @@ intrinsics compile to nothing there, so a headless binary never references a
|
||||||
symbol the window would have provided.
|
symbol the window would have provided.
|
||||||
|
|
||||||
Other platforms build headless today. A Win32 or X11 port is another `.ll` file
|
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`,
|
with the same five entry points — `win_open`, `win_poll`, `win_present`,
|
||||||
`win_running`, `win_close`), keys (`win_held`, `win_held_bit`), the mouse and
|
`win_running`, `win_close` — and no compiler change.
|
||||||
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
|
## The web
|
||||||
|
|
||||||
|
|
@ -235,7 +219,7 @@ only the triple changes.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bin/ludic build examples/games/chronorift.ludic --web
|
bin/x app examples/games/chronorift.ludic --web
|
||||||
python3 -m http.server -d build/web 8000 # then open http://localhost:8000/
|
python3 -m http.server -d build/web 8000 # then open http://localhost:8000/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -323,7 +307,7 @@ 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
|
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`
|
and the wasm one must render byte-identical frames from the same input. `bin/x test`
|
||||||
asserts exactly that, which is a much stronger check on the backend than
|
asserts exactly that, which is a much stronger check on the backend than
|
||||||
"it started".
|
"it started".
|
||||||
|
|
||||||
|
|
@ -337,13 +321,13 @@ entity allocator, save/load snapshots, the frame loop, the window, and the whole
|
||||||
graphics stack — framebuffer, PNG decoding, sprites, 9-slice, TrueType text and
|
graphics stack — framebuffer, PNG decoding, sprites, 9-slice, TrueType text and
|
||||||
the retained UI.
|
the retained UI.
|
||||||
|
|
||||||
None of it goes through C. `bin/ludic-dev test` asserts that directly: no C source
|
None of it goes through C. `bin/x test` asserts that directly: no C source
|
||||||
survives in `runtime/`, no C emitter survives in `ludicc`, and the examples all
|
survives in `runtime/`, no C emitter survives in `ludicc`, and the examples all
|
||||||
build, run and render from IR alone.
|
build, run and render from IR alone.
|
||||||
|
|
||||||
## Every flag
|
## Every flag
|
||||||
|
|
||||||
The self-hosted `ludicc`/`ludic` (built with `bin/ludic-dev build-cli`) accept:
|
The self-hosted `ludicc`/`ludic` (built with `bin/x build-cli`) accept:
|
||||||
|
|
||||||
```
|
```
|
||||||
<file.ludic> the program to compile (first non-flag argument)
|
<file.ludic> the program to compile (first non-flag argument)
|
||||||
|
|
@ -353,18 +337,15 @@ The self-hosted `ludicc`/`ludic` (built with `bin/ludic-dev build-cli`) accept:
|
||||||
--windowed force a windowed (Cocoa) build
|
--windowed force a windowed (Cocoa) build
|
||||||
--headless force a headless build (stdin input, out.ppm output)
|
--headless force a headless build (stdin input, out.ppm output)
|
||||||
--emit-llvm stop at LLVM IR — write it and exit, no clang
|
--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
|
--fmt lex + parse only; exit 0 if it parses, 1 on a parse error
|
||||||
(the check-docs gate; canonical formatting not yet restored)
|
(the check-docs gate; canonical formatting not yet restored)
|
||||||
--save-temps keep the intermediate .ll
|
--save-temps keep the intermediate .ll
|
||||||
--run compile then run (what `ludic run` uses)
|
--run compile then run (implicit when invoked as `ludic`)
|
||||||
(unknown -flags are ignored with a warning, never taken as the input file)
|
(unknown -flags are ignored with a warning, never taken as the input file)
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
LUDIC_CC the LLVM that assembles IR and drives the linker (clang)
|
LUDIC_CC the LLVM that assembles IR and drives the linker (clang)
|
||||||
LUDIC_HOME the install root — runtime/, packages/, VERSION
|
LUDIC_HOME where runtime/native/ lives (default: the binary's dir)
|
||||||
(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
|
Mode is automatic when neither `--windowed` nor `--headless` is given: a program
|
||||||
|
|
|
||||||
175
CONTRIBUTING.md
175
CONTRIBUTING.md
|
|
@ -18,25 +18,18 @@ runtime, and the tooling are all written in Ludic and built by Ludic.
|
||||||
From a clean checkout, one line lifts the toolchain off the seed:
|
From a clean checkout, one line lifts the toolchain off the seed:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p bin && clang selfhost/ludicc.seed.ll -o bin/ludicc
|
clang selfhost/ludicc.seed.ll -o bin/ludicc && bin/ludicc tools/x/main.ludic -o bin/x
|
||||||
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
|
That gives you `bin/x`, the Ludic task runner that replaces every build/test
|
||||||
build/test shell script in the repo and builds everything, including itself and
|
shell script in the repo. From then on it builds everything — including itself:
|
||||||
`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
|
```bash
|
||||||
bin/ludic-dev build # the whole toolchain into bin/ (ludicc, ludic, ludic-dev, ludic-fmt, ludic-lsp)
|
bin/x build # rebuild the whole toolchain into bin/ (ludicc, ludic, x, ludic-fmt, ludic-lsp)
|
||||||
bin/ludic-dev help # every contributor task
|
bin/x help # list every command
|
||||||
bin/ludic help # what a user of the language sees
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Always run `ludic-dev` from the repository root, so `assets/` and `selfhost/`
|
Always run `x` from the repository root, so `assets/` and `selfhost/` resolve.
|
||||||
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
|
## The development loop
|
||||||
|
|
||||||
|
|
@ -44,18 +37,18 @@ When you change the compiler or runtime, prove the self-hosting fixpoint still
|
||||||
holds before you push:
|
holds before you push:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bin/ludic-dev reseed # regenerate selfhost/ludicc.seed.ll after a compiler change
|
bin/x 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/x bootstrap-cfree # rebuild the compiler from the seed with NO C compiler in the loop
|
||||||
bin/ludic-dev test # the full regression suite
|
bin/x test # the full regression suite
|
||||||
```
|
```
|
||||||
|
|
||||||
Other useful targets:
|
Other useful targets:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bin/ludic build <file.ludic> [--headless] # compile a program to a native app in build/
|
bin/x app <file.ludic> [--headless] # compile a program to a native app in build/
|
||||||
bin/ludic-dev selfhost-test # correctness + bootstrap fixpoints
|
bin/x selfhost-test # correctness + bootstrap fixpoints
|
||||||
bin/ludic-dev test-tools # the editor-toolchain suite (ludic-fmt, ludic-lsp)
|
bin/x test-tools # the editor-toolchain suite (ludic-fmt, ludic-lsp)
|
||||||
bin/ludic clean # remove build/, out.ppm and stray artifacts
|
bin/x clean # remove build/, out.ppm and stray artifacts
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding to the standard library
|
## Adding to the standard library
|
||||||
|
|
@ -68,7 +61,7 @@ The stdlib lives in the runtime (`runtime/`) and is surfaced as namespaces
|
||||||
and register its id in `tools/docgen/inventory.json`. Each documented
|
and register its id in `tools/docgen/inventory.json`. Each documented
|
||||||
namespace gets exactly **one** directory (the docs check enforces this).
|
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.
|
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
|
4. Run `python3 tools/docgen/gen.py && python3 tools/docgen/check.py` — the
|
||||||
check fails if any inventory symbol lacks a page or is still seed text.
|
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
|
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.
|
[`changes/`](changes/README.md) with a `bump:` level and a one-line summary.
|
||||||
|
|
@ -77,105 +70,20 @@ The stdlib lives in the runtime (`runtime/`) and is surfaced as namespaces
|
||||||
## Versioning & releases
|
## Versioning & releases
|
||||||
|
|
||||||
The toolchain is versioned with [SemVer](https://semver.org); `VERSION` is the
|
The toolchain is versioned with [SemVer](https://semver.org); `VERSION` is the
|
||||||
single source of truth and `ludicc --version` (or `ludic version`) reports it.
|
single source of truth and `ludicc --version` (or `x version`) reports it.
|
||||||
|
|
||||||
Releases are changeset-driven. Every user-facing change ships with a changeset
|
Releases are changeset-driven. Every user-facing change ships with a changeset
|
||||||
(step 5 above). Read the next release before cutting it:
|
(step 5 above). To cut a release:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ludic-dev release --dry-run # render the CHANGELOG section, write nothing
|
x release [major|minor|patch] # omit the level to derive it from the changesets
|
||||||
```
|
```
|
||||||
|
|
||||||
Then cut it:
|
That aggregates the pending changesets into a new `CHANGELOG.md` section, bumps
|
||||||
|
`VERSION`, commits `chore(release): vX.Y.Z`, and tags it. Add `--publish` (with
|
||||||
```bash
|
`FORGEJO_TOKEN` set) to also push and create the Forgejo release with source and
|
||||||
ludic-dev release [major|minor|patch] # omit the level to derive it from the changesets
|
toolchain tarballs. The tag doubles as the reproducible bootstrap point: the
|
||||||
git push origin main --follow-tags
|
source archive plus its checked-in seed rebuild that exact toolchain.
|
||||||
```
|
|
||||||
|
|
||||||
`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
|
## Conventions
|
||||||
|
|
||||||
|
|
@ -191,7 +99,7 @@ half-finished rename fails there rather than in someone's terminal.
|
||||||
| `perf` | a performance improvement |
|
| `perf` | a performance improvement |
|
||||||
| `docs` | documentation only (`docs/`, README, comments) |
|
| `docs` | documentation only (`docs/`, README, comments) |
|
||||||
| `test` | tests only |
|
| `test` | tests only |
|
||||||
| `build` | the build/bootstrap machinery (seed, `bin/ludic`, linking) |
|
| `build` | the build/bootstrap machinery (seed, `bin/x`, linking) |
|
||||||
| `ci` | CI workflows under `.forgejo/` |
|
| `ci` | CI workflows under `.forgejo/` |
|
||||||
| `style` | formatting/whitespace, no behaviour change |
|
| `style` | formatting/whitespace, no behaviour change |
|
||||||
| `chore` | routine housekeeping with no other bucket |
|
| `chore` | routine housekeeping with no other bucket |
|
||||||
|
|
@ -213,7 +121,7 @@ half-finished rename fails there rather than in someone's terminal.
|
||||||
so a green local commit is a green CI run.
|
so a green local commit is a green CI run.
|
||||||
|
|
||||||
- **Formatting:** `ludic-fmt` is the source of truth (2-space indent, LF, UTF-8);
|
- **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 repo `.editorconfig` mirrors it. Run `bin/ludic-fmt -w` on files you touch.
|
||||||
The contract CI enforces is *idempotence* — `ludic-fmt` re-run on its own output
|
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
|
is a no-op — which leaves deliberate hand alignment in place; it is not a
|
||||||
blanket `fmt(x) == x`.
|
blanket `fmt(x) == x`.
|
||||||
|
|
@ -241,44 +149,11 @@ non-destructive version of "tidy the history" without touching a single commit.
|
||||||
## Pull requests
|
## Pull requests
|
||||||
|
|
||||||
- Base your branch on `main`.
|
- Base your branch on `main`.
|
||||||
- Ensure `bin/ludic-dev test` (and `bin/ludic-dev bootstrap-cfree` for compiler/runtime changes)
|
- Ensure `bin/x test` (and `bin/x bootstrap-cfree` for compiler/runtime changes)
|
||||||
pass, and that `ludic-fmt` leaves your files unchanged.
|
pass, and that `ludic-fmt` leaves your files unchanged.
|
||||||
- Fill in the PR template checklist. Reference the issue you close with
|
- Fill in the PR template checklist. Reference the issue you close with
|
||||||
`Closes #NN` in the description or a commit message.
|
`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
|
## Reporting issues
|
||||||
|
|
||||||
Use the templates under [`.forgejo/issue_template/`](.forgejo/issue_template):
|
Use the templates under [`.forgejo/issue_template/`](.forgejo/issue_template):
|
||||||
|
|
|
||||||
1940
LANGUAGE.md
1940
LANGUAGE.md
File diff suppressed because it is too large
Load diff
293
README.md
293
README.md
|
|
@ -1,200 +1,177 @@
|
||||||
# Ludic
|
# Ludic
|
||||||
|
|
||||||
A compiled language for 2D games. The entity-component system is part of the
|
Ludic is an **ahead-of-time compiled** language for 2D games with an
|
||||||
syntax, the runtime is deterministic fixed-point, and `ludicc` lowers Ludic
|
entity-component core, a deterministic fixed-point runtime, and its graphics
|
||||||
straight to LLVM IR — **no C is generated, compiled or linked in a build.**
|
stack built into the language. `ludicc` lowers Ludic straight to LLVM IR and
|
||||||
|
emits a native binary — and **`ludicc` is itself written in Ludic**, compiles
|
||||||
|
its own source to a byte-exact fixpoint, and rebuilds from a checked-in IR seed
|
||||||
|
with **no C compiler in the loop**.
|
||||||
|
|
||||||
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
|
.ludic ──► ludicc ──► LLVM IR ──► object ──► native binary
|
||||||
that on every push.
|
(in Ludic)
|
||||||
|
|
||||||
- **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
|
**No C is generated, compiled or linked in a build.** No interpreter, no
|
||||||
|
transpiler, no C runtime: the framebuffer, sprites, PNG/DEFLATE decoding,
|
||||||
|
TrueType text, the retained UI, the registers and the RNG are all written in
|
||||||
|
Ludic (`runtime/native/*.ludic`); only the window seam — five `win_*` functions
|
||||||
|
— is hand-written LLVM IR against the platform ABI (`runtime/native/cocoa.ll`),
|
||||||
|
the same floor Rust and Swift stand on.
|
||||||
|
|
||||||
Install the toolchain — the compiler, the `ludic` CLI, the engine runtime, the
|
## Backends
|
||||||
formatter and the language server — with one command:
|
|
||||||
|
| Backend | Status |
|
||||||
|
|---|---|
|
||||||
|
| **Native 2D** (macOS/Cocoa window; headless render for CI) | **Shipping** — the default `bin/x app` target. |
|
||||||
|
| **Web / wasm32** | **In progress.** The browser platform layer is in-tree and documented — `runtime/web/` (the `<canvas>` window `platform.js`, the libc-free `wasm.ll` floor) and a Node harness that diffs native vs. wasm frame-for-frame (`tools/ludic-web/run.mjs`). Emitting wasm was a capability of the retired C compiler and is **not yet re-wired on the self-hosted toolchain**; see [COMPILING.md](COMPILING.md). |
|
||||||
|
|
||||||
|
The same is true of `--target` cross-compilation and `--shared` libraries: both
|
||||||
|
are designed and documented, both lived in the old C compiler, and both are
|
||||||
|
pending re-implementation on the self-hosted native toolchain.
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
`bin/x` is the project's task runner — one native binary, written in Ludic and
|
||||||
|
compiled by Ludic, that replaces every build/test/bootstrap shell script.
|
||||||
|
Bootstrap it once from a clean checkout (the only step Ludic can't do for
|
||||||
|
itself, since compiling Ludic needs a compiler) with clang alone:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh
|
clang selfhost/ludicc.seed.ll -o bin/ludicc && bin/ludicc tools/x/main.ludic -o bin/x
|
||||||
```
|
```
|
||||||
|
|
||||||
It installs into `~/.ludic` and puts `~/.ludic/bin` on your `PATH` in every
|
Then build the whole toolchain and run a game:
|
||||||
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
|
```bash
|
||||||
ludic new mygame
|
bin/x build # -> bin/{ludicc,ludic,x,ludic-fmt,ludic-lsp}
|
||||||
cd mygame
|
bin/x app examples/games/snake.ludic # compile + open a native window
|
||||||
ludic run # compiles src/main.ludic and opens a native window
|
./build/snake
|
||||||
```
|
```
|
||||||
|
|
||||||
`ludic new` writes a manifest, a program that already moves something on screen,
|
Render a frame headlessly (what CI checks) — output lands in `build/`, never the
|
||||||
and a test. `ludic build` stops at the binary; `ludic bundle` goes on to the
|
repo root:
|
||||||
thing you can actually give someone. Rendering is deterministic, so a frame can
|
|
||||||
be produced without a window, which is what CI diffs:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ludic test
|
bin/x app examples/games/chronorift.ludic --headless
|
||||||
ludic build --headless
|
mkdir -p build && printf 'ddddwww' | ./build/chronorift_headless # writes build/out.ppm
|
||||||
printf 'ddddwww' | ./build/mygame_headless # writes build/out.ppm
|
sips -s format png build/out.ppm --out frame.png
|
||||||
```
|
```
|
||||||
|
|
||||||
`ludic help` lists every command, and `ludic doctor` checks the install.
|
Run the suites:
|
||||||
[`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
|
```bash
|
||||||
mkdir -p bin && clang selfhost/ludicc.seed.ll -o bin/ludicc
|
bin/x test # full regression: compiler builds from seed, every example, golden renders
|
||||||
bin/ludicc tools/ludic-cli/dev.ludic -o bin/ludic-dev
|
bin/x selfhost-test # correctness + the self-hosting / C-free bootstrap fixpoints
|
||||||
bin/ludic-dev build # -> bin/{ludicc,ludic,ludic-dev,ludic-fmt,ludic-lsp}
|
bin/x help # every command
|
||||||
bin/ludic-dev test # the regression suite
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## The language
|
## Layout
|
||||||
|
|
||||||
- **ECS in the syntax.** `property`, `model` and `handler` are keywords. Query
|
| Path | What it is |
|
||||||
with `for (a, b) in query [A, B, {Tag}] where <expr> { … }`; `spawn` and
|
|------|-----------|
|
||||||
`despawn` recycle entity slots; `@`-annotations drive lifecycle hooks.
|
| [`selfhost/*.ludic`](selfhost/) | **the compiler, written in Ludic** — lexer, parser, and the LLVM-IR backend (ECS storage, queries, spawn, `match`/`machine`, UI, scenes, save/load, fixed-point). Built from `selfhost/ludicc.seed.ll` with clang alone. |
|
||||||
- **Deterministic by construction.** Q16.16 `fixed` arithmetic and a seeded RNG
|
| [`selfhost/golden/renders.sha256`](selfhost/golden/renders.sha256) | text baseline of render-output hashes (replaces binary `.ppm` fixtures); regenerate with `bin/x golden`. |
|
||||||
give the same frame byte-for-byte on every run — the basis for replays,
|
| [`tools/x/*.ludic`](tools/x/) | **the task runner, written in Ludic** — one binary (`bin/x`) that builds, tests, bootstraps and reseeds the project, replacing every shell script. |
|
||||||
lockstep netcode and golden-image tests.
|
| [`runtime/native/`](runtime/native/) | the runtime **in Ludic** for the native path: `core` (framebuffer, input, RNG), `image`/`inflate` (PNG + DEFLATE, no zlib), `truetype` (glyph rasterizer), `ui` (retained widget tree); plus `cocoa.ll`, the macOS window seam in LLVM IR. |
|
||||||
- **Scenes and state machines.** `scene` / `layer` / `become` model
|
| [`runtime/web/`](runtime/web/) | the browser platform layer: `platform.js` (the `<canvas>` window), `wasm.ll` (the libc-free floor), `index.html`. |
|
||||||
mutually-exclusive game states with enter and exit hooks; `match` / `machine`
|
| [`examples/`](examples/README.md) | the example tour, grouped by intent — `games/`, `rendering/`, `ecs/`, `events/`, `networking/`, `lang/`, `library/`. See [examples/README.md](examples/README.md). |
|
||||||
/ `state` handle dispatch and per-entity FSMs.
|
| [`tools/ludic-tools/`](tools/ludic-tools/) | the editor toolchain **in Ludic**: `ludic-fmt` (formatter) and `ludic-lsp` (language server) — one lexer, one vocabulary shared by both. |
|
||||||
- **Events and networking.** A cancellable event bus (`event` / `emit` / `@On`)
|
| [`tools/editors/`](tools/editors/README.md) | plugins for VS Code and JetBrains, plus config for Neovim, Helix, Emacs, Sublime and Zed. |
|
||||||
and networking primitives (`@Sync`, ownership, RPCs) over a built-in transport.
|
| [`docs/`](docs/) | the per-symbol API reference, regenerated into the docs site. |
|
||||||
- **Batteries in the language.** Framebuffer primitives, PNG sprites, TrueType
|
| [`COMPILING.md`](COMPILING.md) | the native pipeline: `ludicc → LLVM IR → exe`, the `rt_*` runtime protocol, and the (pending) wasm/cross-compile/shared-library paths. |
|
||||||
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
|
The design and roadmap material lives on the **[wiki](https://git.workshopsoft.io/workshopsoft/ludic/wiki)**:
|
||||||
[API reference](https://workshopsoft.pages.workshopsoft.io/ludic/api.html)
|
the [Events](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Design/Events),
|
||||||
documents every symbol on its own page.
|
[Networking](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Design/Networking),
|
||||||
|
[Scenes](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Design/Scenes),
|
||||||
|
[Lifecycle](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Design/Lifecycle),
|
||||||
|
[Mobile](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Design/Mobile) and
|
||||||
|
[Syntax-redesign](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Design/Syntax-Redesign)
|
||||||
|
design records, the [Bootstrap deep-dive](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Bootstrap),
|
||||||
|
and the [Luanti roadmap](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Roadmap/Luanti).
|
||||||
|
The root keeps only this README plus the two user-facing references,
|
||||||
|
[`LANGUAGE.md`](LANGUAGE.md) and [`COMPILING.md`](COMPILING.md).
|
||||||
|
|
||||||
## Shipping
|
## Language at a glance
|
||||||
|
|
||||||
A built binary is a program, not an application: it opens its assets by a path
|
- `program` / `property` (typed fields + defaults) / `model` (named entity kinds)
|
||||||
relative to the working directory, so it runs from the project root and nowhere
|
/ `system` (`phase`, `@annotations`, `reads`/`writes`).
|
||||||
else, and it wears the generic executable icon.
|
- ECS queries `for (a, b) in query [A, B, {Tag}] where <expr> { … }`,
|
||||||
|
`spawn`/`despawn` with slot reuse, `@`-driven lifecycle hooks.
|
||||||
|
- An **event bus** (`event` / `emit` / `@On`, cancellable, `@Public` promotion)
|
||||||
|
and **networking** primitives (`@Sync`, ownership, RPCs) over a built-in
|
||||||
|
loopback transport — all deterministic, all pure Ludic.
|
||||||
|
- `scene` / `layer` / `become`, `match` / `machine` + `state`.
|
||||||
|
- Types `int`, `fixed` (Q16.16), `bool`, `entity`, `str`, `byte`, typed buffers;
|
||||||
|
a growing namespaced **standard library** (`Math`, `Vector`, `Time`/`Date`/
|
||||||
|
`Duration`/`Clock`, `Random`, `Hash`, `Crypto`, sorting, …).
|
||||||
|
- Deterministic seeded RNG and `save()`/`load()` snapshot of the whole World.
|
||||||
|
- Built-in 2D: framebuffer primitives, PNG sprites, TrueType text, 9-slice, and
|
||||||
|
a retained `ui` widget tree declared as data.
|
||||||
|
|
||||||
```bash
|
See [LANGUAGE.md](LANGUAGE.md) for the full reference, and
|
||||||
ludic pack # every asset the game opens, into one .lpak
|
[examples/README.md](examples/README.md) for runnable demos of each feature.
|
||||||
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
|
## Editor support
|
||||||
|
|
||||||
Editors spawn `ludic lsp`; the server ships with the toolchain, so there is
|
```bash
|
||||||
nothing extra to install. It speaks LSP 3.17 over stdio, so one binary serves
|
bin/x tools # -> bin/ludic-fmt, bin/ludic-lsp
|
||||||
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
|
`ludic-lsp` speaks LSP 3.17 over stdio, so one binary serves every editor:
|
||||||
|
context-aware completion, diagnostics from the compiler itself,
|
||||||
|
go-to-definition and rename across `import`ed files, and comment-preserving
|
||||||
|
formatting. `ludic-fmt` is the same formatter as a CLI, for pre-commit hooks and
|
||||||
|
CI. Both also understand ```` ```ludic ```` fences in Markdown. Plugins and
|
||||||
|
drop-in config are in [`tools/editors/`](tools/editors/README.md).
|
||||||
|
|
||||||
The native 2D backend ships: a Cocoa window on macOS, a headless renderer for
|
## Chrono Rift — the flagship game
|
||||||
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
|
[`examples/games/chronorift.ludic`](examples/games/chronorift.ludic) is a
|
||||||
layer is in-tree under [`runtime/web/`](runtime/web/), but emitting wasm was a
|
playable co-op JRPG — overworld, dungeon, random encounters, a turn-based co-op
|
||||||
capability of the retired C compiler and has not been re-wired on the
|
battle, a boss, an item shop and snapshot save/load — split across modules under
|
||||||
self-hosted toolchain. `--target` cross-compilation and `--shared` libraries are
|
[`games/chronorift/`](examples/games/chronorift/). Its art is CC0
|
||||||
in the same position. See [COMPILING.md](COMPILING.md).
|
[Kenney](https://kenney.nl) sprites, decoded from PNG at runtime by the
|
||||||
|
Ludic-written PNG/DEFLATE decoder — no zlib, no external dependency.
|
||||||
|
|
||||||
Releases follow SemVer and are cut from changesets by `ludic-dev release`, then built
|
- **Overworld:** `WASD` move, `K` save, `L` load.
|
||||||
and published by CI from the tag; see [CHANGELOG.md](CHANGELOG.md).
|
- **Battle (local co-op):** P1/Knight `W`/`S` select, `Space` confirm;
|
||||||
|
P2/Mage `I`/`K` select, `J` confirm.
|
||||||
|
|
||||||
|
## Status & roadmap
|
||||||
|
|
||||||
|
The compiler self-hosts to a byte-exact fixpoint and rebuilds from its IR seed
|
||||||
|
with no C compiler; the ECS runtime, windowed + headless 2D rendering, the event
|
||||||
|
bus, the deterministic networking stack, scenes, and save/load are all in place
|
||||||
|
and covered by `bin/x test`. CI gates every push and PR on the build, the test
|
||||||
|
suites, and that C-free fixpoint. The toolchain is versioned with SemVer
|
||||||
|
(`ludicc --version`); releases and the `CHANGELOG.md` are cut from changesets by
|
||||||
|
`x release`.
|
||||||
|
|
||||||
|
Active work and proposals — the standard library, a fuller type system,
|
||||||
|
rendering/animation/lighting extras, input, filesystem/IO, testing, and
|
||||||
|
re-wiring the web/wasm and cross-compile backends — are tracked as issues, not
|
||||||
|
inlined here:
|
||||||
|
|
||||||
|
- **Issues & proposals:** <https://git.workshopsoft.io/workshopsoft/ludic/issues>
|
||||||
|
- **Docs site (API reference):** <https://workshopsoft.pages.workshopsoft.io/ludic/>
|
||||||
|
- **Wiki (design & roadmap):** <https://git.workshopsoft.io/workshopsoft/ludic/wiki>
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
[CONTRIBUTING.md](CONTRIBUTING.md) covers the development loop, the commit and
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development loop
|
||||||
code conventions, how the bootstrap fixpoint works, and what a self-hosted CI
|
(`bin/x reseed` → `bin/x bootstrap-cfree` → `bin/x test`), the code and commit
|
||||||
runner needs. Issue and pull-request templates are under
|
conventions, and how the bootstrap fixpoint works. Issue and pull-request
|
||||||
[`.forgejo/`](.forgejo/).
|
templates live under [`.forgejo/`](.forgejo/).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The compiler and runtime are licensed under the
|
The Ludic compiler and runtime source are licensed under the
|
||||||
[Apache License 2.0](LICENSE) (`SPDX-License-Identifier: Apache-2.0`).
|
[Apache License 2.0](LICENSE) (`SPDX-License-Identifier: Apache-2.0`) — a
|
||||||
|
permissive license with an explicit patent grant.
|
||||||
|
|
||||||
The bundled [Kenney](https://kenney.nl) art under `assets/kenney/` is
|
The bundled [Kenney](https://kenney.nl) art under `assets/kenney/` is
|
||||||
third-party and released under **CC0 1.0**; each pack keeps its own
|
third-party and released under **CC0 1.0** (public domain); each pack keeps its
|
||||||
`License.txt`. Code and assets are licensed separately — Apache-2.0 covers the
|
own `License.txt`. Code and assets are licensed separately: Apache-2.0 covers
|
||||||
source, not the art.
|
the source, not the art.
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.22.0
|
0.2.0
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +0,0 @@
|
||||||
Everything under assets/polyhaven/ is fetched from https://polyhaven.com and is
|
|
||||||
released by Poly Haven under CC0 1.0 (public domain). Files are not committed;
|
|
||||||
see manifest.txt for the exact sources. Re-fetch with tools/glgen/fetch_assets.sh.
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
# Tiled golden fixtures
|
|
||||||
|
|
||||||
The curated, version-pinned corpus for the Ludic Tiled reader (design record:
|
|
||||||
[Design: Tiled maps](https://git.workshopsoft.io/workshopsoft/ludic/wiki/Design%2FTiled),
|
|
||||||
issues #67–#74). No single Tiled file covers the format surface, so this is a
|
|
||||||
subset of the official [`mapeditor/tiled`](https://github.com/mapeditor/tiled)
|
|
||||||
`examples/` tree plus a few hand-authored files for the gaps the official
|
|
||||||
examples miss.
|
|
||||||
|
|
||||||
## Vendored from mapeditor/tiled (`examples/`)
|
|
||||||
|
|
||||||
Fetched from `https://raw.githubusercontent.com/mapeditor/tiled/master/examples/`.
|
|
||||||
Tiled's example assets carry their own licenses (see the upstream repo's
|
|
||||||
per-folder `*.license`/README); vendored here with attribution for testing only.
|
|
||||||
|
|
||||||
| File | Exercises |
|
|
||||||
|---|---|
|
|
||||||
| `desert.tmx` + `desert.tsx` | orthogonal, external `.tsx`, base64+zlib (P0/P1) |
|
|
||||||
| `sewers.tmx` | orthogonal, base64+zlib, embedded tileset, opacity (P0) |
|
|
||||||
| `orthogonal-outside.tmx` | object layers, shapes, custom properties (P4) |
|
|
||||||
| `perspective_walls.tsx` | per-tile bool properties, `<tileoffset>` (P4) |
|
|
||||||
| `isometric_grass_and_water.tmx` | isometric orientation, Wang set (P5) |
|
|
||||||
| `hexagonal-mini.tmx` | hexagonal orientation (P5) |
|
|
||||||
|
|
||||||
## Hand-authored (CC0 / public domain)
|
|
||||||
|
|
||||||
| File | Exercises |
|
|
||||||
|---|---|
|
|
||||||
| `handmade.tmx` + `handmade.tsx` | a 4×4 orthogonal map whose four layers carry the **same** GID array in CSV, base64-uncompressed, base64+gzip and base64+zlib — so every encoding path must decode identically. The last tile is GID 1 with the horizontal-flip flag (`0x80000001`), forcing real GID decode. `handmade.tsx` also carries a `solid` bool property and a per-tile `<objectgroup>` collision shape for P2. |
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored P3 fixture: animated tile + tile object (issue #71). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="4" height="4" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="2">
|
|
||||||
<tileset firstgid="1" source="anim_tiles.tsx"/>
|
|
||||||
<layer id="1" name="bg" width="4" height="4">
|
|
||||||
<data encoding="csv">
|
|
||||||
1,0,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<objectgroup id="2" name="objects">
|
|
||||||
<object id="1" gid="53" x="16" y="32" width="16" height="16"/>
|
|
||||||
</objectgroup>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored animated tileset over the Kenney atlas (issue #71). CC0. -->
|
|
||||||
<tileset version="1.10" tiledversion="1.10.2" name="anim" tilewidth="16" tileheight="16" spacing="1" tilecount="132" columns="12">
|
|
||||||
<image source="../kenney/tiny-dungeon/Tilemap/tilemap.png" width="203" height="186"/>
|
|
||||||
<tile id="0">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="0" duration="100"/>
|
|
||||||
<frame tileid="40" duration="100"/>
|
|
||||||
<frame tileid="80" duration="100"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
</tileset>
|
|
||||||
|
|
@ -1,267 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<tileset version="1.8" tiledversion="1.8.2" name="beach_tileset" tilewidth="16" tileheight="16" tilecount="936" columns="36">
|
|
||||||
<image source="beach_tileset.png" width="576" height="416"/>
|
|
||||||
<tile id="37">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="37" duration="250"/>
|
|
||||||
<frame tileid="46" duration="250"/>
|
|
||||||
<frame tileid="55" duration="250"/>
|
|
||||||
<frame tileid="64" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="38">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="38" duration="250"/>
|
|
||||||
<frame tileid="47" duration="250"/>
|
|
||||||
<frame tileid="56" duration="250"/>
|
|
||||||
<frame tileid="65" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="39">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="39" duration="250"/>
|
|
||||||
<frame tileid="48" duration="250"/>
|
|
||||||
<frame tileid="57" duration="250"/>
|
|
||||||
<frame tileid="66" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="41">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="41" duration="250"/>
|
|
||||||
<frame tileid="50" duration="250"/>
|
|
||||||
<frame tileid="59" duration="250"/>
|
|
||||||
<frame tileid="68" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="42">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="42" duration="250"/>
|
|
||||||
<frame tileid="51" duration="250"/>
|
|
||||||
<frame tileid="60" duration="250"/>
|
|
||||||
<frame tileid="69" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="43">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="43" duration="250"/>
|
|
||||||
<frame tileid="52" duration="250"/>
|
|
||||||
<frame tileid="61" duration="250"/>
|
|
||||||
<frame tileid="70" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="73">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="73" duration="250"/>
|
|
||||||
<frame tileid="82" duration="250"/>
|
|
||||||
<frame tileid="91" duration="250"/>
|
|
||||||
<frame tileid="100" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="75">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="75" duration="250"/>
|
|
||||||
<frame tileid="84" duration="250"/>
|
|
||||||
<frame tileid="93" duration="250"/>
|
|
||||||
<frame tileid="102" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="76">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="76" duration="250"/>
|
|
||||||
<frame tileid="85" duration="250"/>
|
|
||||||
<frame tileid="94" duration="250"/>
|
|
||||||
<frame tileid="103" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="77">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="77" duration="250"/>
|
|
||||||
<frame tileid="86" duration="250"/>
|
|
||||||
<frame tileid="95" duration="250"/>
|
|
||||||
<frame tileid="104" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="79">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="79" duration="250"/>
|
|
||||||
<frame tileid="88" duration="250"/>
|
|
||||||
<frame tileid="97" duration="250"/>
|
|
||||||
<frame tileid="106" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="109">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="109" duration="250"/>
|
|
||||||
<frame tileid="118" duration="250"/>
|
|
||||||
<frame tileid="127" duration="250"/>
|
|
||||||
<frame tileid="136" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="110">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="110" duration="250"/>
|
|
||||||
<frame tileid="119" duration="250"/>
|
|
||||||
<frame tileid="128" duration="250"/>
|
|
||||||
<frame tileid="137" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="114">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="114" duration="250"/>
|
|
||||||
<frame tileid="123" duration="250"/>
|
|
||||||
<frame tileid="132" duration="250"/>
|
|
||||||
<frame tileid="141" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="115">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="115" duration="250"/>
|
|
||||||
<frame tileid="124" duration="250"/>
|
|
||||||
<frame tileid="133" duration="250"/>
|
|
||||||
<frame tileid="142" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="146">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="146" duration="250"/>
|
|
||||||
<frame tileid="155" duration="250"/>
|
|
||||||
<frame tileid="164" duration="250"/>
|
|
||||||
<frame tileid="173" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="148">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="148" duration="250"/>
|
|
||||||
<frame tileid="157" duration="250"/>
|
|
||||||
<frame tileid="166" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="150">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="150" duration="250"/>
|
|
||||||
<frame tileid="159" duration="250"/>
|
|
||||||
<frame tileid="168" duration="250"/>
|
|
||||||
<frame tileid="177" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="181">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="181" duration="250"/>
|
|
||||||
<frame tileid="190" duration="250"/>
|
|
||||||
<frame tileid="199" duration="250"/>
|
|
||||||
<frame tileid="208" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="182">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="182" duration="250"/>
|
|
||||||
<frame tileid="191" duration="250"/>
|
|
||||||
<frame tileid="200" duration="250"/>
|
|
||||||
<frame tileid="209" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="186">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="186" duration="250"/>
|
|
||||||
<frame tileid="195" duration="250"/>
|
|
||||||
<frame tileid="204" duration="250"/>
|
|
||||||
<frame tileid="213" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="187">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="187" duration="250"/>
|
|
||||||
<frame tileid="196" duration="250"/>
|
|
||||||
<frame tileid="205" duration="250"/>
|
|
||||||
<frame tileid="214" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="217">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="217" duration="250"/>
|
|
||||||
<frame tileid="226" duration="250"/>
|
|
||||||
<frame tileid="235" duration="250"/>
|
|
||||||
<frame tileid="244" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="219">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="219" duration="250"/>
|
|
||||||
<frame tileid="228" duration="250"/>
|
|
||||||
<frame tileid="237" duration="250"/>
|
|
||||||
<frame tileid="246" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="220">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="220" duration="250"/>
|
|
||||||
<frame tileid="229" duration="250"/>
|
|
||||||
<frame tileid="238" duration="250"/>
|
|
||||||
<frame tileid="247" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="221">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="221" duration="250"/>
|
|
||||||
<frame tileid="230" duration="250"/>
|
|
||||||
<frame tileid="239" duration="250"/>
|
|
||||||
<frame tileid="248" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="223">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="223" duration="250"/>
|
|
||||||
<frame tileid="232" duration="250"/>
|
|
||||||
<frame tileid="241" duration="250"/>
|
|
||||||
<frame tileid="250" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="253">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="253" duration="250"/>
|
|
||||||
<frame tileid="262" duration="250"/>
|
|
||||||
<frame tileid="271" duration="250"/>
|
|
||||||
<frame tileid="280" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="254">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="254" duration="250"/>
|
|
||||||
<frame tileid="263" duration="250"/>
|
|
||||||
<frame tileid="272" duration="250"/>
|
|
||||||
<frame tileid="281" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="255">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="255" duration="250"/>
|
|
||||||
<frame tileid="264" duration="250"/>
|
|
||||||
<frame tileid="273" duration="250"/>
|
|
||||||
<frame tileid="282" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="257">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="257" duration="250"/>
|
|
||||||
<frame tileid="266" duration="250"/>
|
|
||||||
<frame tileid="275" duration="250"/>
|
|
||||||
<frame tileid="284" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="258">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="258" duration="250"/>
|
|
||||||
<frame tileid="267" duration="250"/>
|
|
||||||
<frame tileid="276" duration="250"/>
|
|
||||||
<frame tileid="285" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
<tile id="259">
|
|
||||||
<animation>
|
|
||||||
<frame tileid="259" duration="250"/>
|
|
||||||
<frame tileid="268" duration="250"/>
|
|
||||||
<frame tileid="277" duration="250"/>
|
|
||||||
<frame tileid="286" duration="250"/>
|
|
||||||
</animation>
|
|
||||||
</tile>
|
|
||||||
</tileset>
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored collision tileset for the Ludic Tiled demos (issue #69). CC0. -->
|
|
||||||
<tileset version="1.10" tiledversion="1.10.2" name="collision" tilewidth="16" tileheight="16" tilecount="2" columns="1">
|
|
||||||
<tile id="1">
|
|
||||||
<properties>
|
|
||||||
<property name="oneway" type="bool" value="true"/>
|
|
||||||
</properties>
|
|
||||||
</tile>
|
|
||||||
</tileset>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
{"maps": [{"fileName": "zstd_map.tmx", "x": 0, "y": 0, "width": 384, "height": 256}, {"fileName": "grid_maze.tmx", "x": 384, "y": 0, "width": 128, "height": 80}], "onlyShowAdjacentMaps": false, "type": "world"}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<map version="1.0" tiledversion="1.1.5" orientation="orthogonal" renderorder="right-down" width="40" height="40" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="desert.tsx"/>
|
|
||||||
<layer id="1" name="Ground" width="40" height="40">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJztmNkKwjAQRaN9cAPrAq5Yq3Xf6v9/nSM2VIbQJjEZR+nDwQZScrwztoORECLySBcIgZ7nc2y4KfyWDLx+Jb9nViNgDEwY+KioAXUgQN4+zpoCMwPmQAtoAx2CLFbA2oDEo9+hwG8DnIDtF/2K8ks086Tw2zH0uyMv7HcRr/6/EvvhnsPrsrxwX7rwU/0ODig/eV3mh3N1ld8eraWPaX6+64s9McesfrqcHfg1MpoifxcVEWjukyw+9AtFPl/I71pER3Of6j4bv7HI54s+MChhqLlPdZ/P3qMmFuo5h5NnTOhjM5tReN2yT51n5/v7J3F0vi46fk+ne7aX0i9l6If7mpufTX3f5wsqv9TAD2fJLT9VrTn7UeZnM5tR+v0LMQOHXwFnxe2/warGFRWf8QDjOLfP
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<tileset version="1.4" tiledversion="1.4.3" name="Desert" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
|
||||||
<image source="tmw_desert_spacing.png" width="265" height="199"/>
|
|
||||||
<tile id="30" probability="0.01"/>
|
|
||||||
<tile id="31" probability="0.01"/>
|
|
||||||
<tile id="37" probability="0.01"/>
|
|
||||||
<tile id="38" probability="0.01"/>
|
|
||||||
<tile id="39" probability="0.01"/>
|
|
||||||
<tile id="45" probability="0"/>
|
|
||||||
<tile id="46" probability="0.01"/>
|
|
||||||
<tile id="47" probability="0.01"/>
|
|
||||||
<wangsets>
|
|
||||||
<wangset name="Desert" type="corner" tile="5">
|
|
||||||
<wangcolor name="Desert" color="#ff0000" tile="29" probability="1"/>
|
|
||||||
<wangcolor name="Brick" color="#00ff00" tile="9" probability="1"/>
|
|
||||||
<wangcolor name="Cobblestone" color="#0000ff" tile="33" probability="1"/>
|
|
||||||
<wangcolor name="Dirt" color="#ff7700" tile="14" probability="1"/>
|
|
||||||
<wangtile tileid="0" wangid="0,1,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="1" wangid="0,1,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="2" wangid="0,1,0,1,0,2,0,1"/>
|
|
||||||
<wangtile tileid="3" wangid="0,4,0,1,0,4,0,4"/>
|
|
||||||
<wangtile tileid="4" wangid="0,4,0,4,0,1,0,4"/>
|
|
||||||
<wangtile tileid="5" wangid="0,1,0,4,0,1,0,1"/>
|
|
||||||
<wangtile tileid="6" wangid="0,1,0,4,0,4,0,1"/>
|
|
||||||
<wangtile tileid="7" wangid="0,1,0,1,0,4,0,1"/>
|
|
||||||
<wangtile tileid="8" wangid="0,2,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="9" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="10" wangid="0,1,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="11" wangid="0,1,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="12" wangid="0,4,0,4,0,4,0,1"/>
|
|
||||||
<wangtile tileid="13" wangid="0,4,0,4,0,1,0,1"/>
|
|
||||||
<wangtile tileid="14" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="15" wangid="0,1,0,1,0,4,0,4"/>
|
|
||||||
<wangtile tileid="16" wangid="0,2,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="17" wangid="0,2,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="18" wangid="0,1,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="19" wangid="0,2,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="20" wangid="0,2,0,2,0,1,0,2"/>
|
|
||||||
<wangtile tileid="21" wangid="0,4,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="22" wangid="0,4,0,1,0,1,0,4"/>
|
|
||||||
<wangtile tileid="23" wangid="0,1,0,1,0,1,0,4"/>
|
|
||||||
<wangtile tileid="24" wangid="0,1,0,3,0,1,0,1"/>
|
|
||||||
<wangtile tileid="25" wangid="0,1,0,3,0,3,0,1"/>
|
|
||||||
<wangtile tileid="26" wangid="0,1,0,1,0,3,0,1"/>
|
|
||||||
<wangtile tileid="27" wangid="0,1,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="28" wangid="0,2,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="29" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="30" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="31" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="32" wangid="0,3,0,3,0,1,0,1"/>
|
|
||||||
<wangtile tileid="33" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="34" wangid="0,1,0,1,0,3,0,3"/>
|
|
||||||
<wangtile tileid="35" wangid="0,3,0,1,0,3,0,3"/>
|
|
||||||
<wangtile tileid="36" wangid="0,3,0,3,0,1,0,3"/>
|
|
||||||
<wangtile tileid="37" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="38" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="39" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="40" wangid="0,3,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="41" wangid="0,3,0,1,0,1,0,3"/>
|
|
||||||
<wangtile tileid="42" wangid="0,1,0,1,0,1,0,3"/>
|
|
||||||
<wangtile tileid="43" wangid="0,1,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="44" wangid="0,3,0,3,0,3,0,1"/>
|
|
||||||
<wangtile tileid="45" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="46" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="47" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
</wangset>
|
|
||||||
</wangsets>
|
|
||||||
</tileset>
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored map for the Ludic Tiled demos (issue #69). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="8" height="5" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="collision.tsx"/>
|
|
||||||
<layer id="1" name="collision" width="8" height="5">
|
|
||||||
<data encoding="csv">
|
|
||||||
1,1,1,1,1,1,1,1,
|
|
||||||
1,0,0,0,0,0,0,1,
|
|
||||||
1,0,1,1,1,1,0,1,
|
|
||||||
1,0,0,0,0,1,0,1,
|
|
||||||
1,1,1,1,1,1,1,1
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
{
|
|
||||||
"type": "map",
|
|
||||||
"version": "1.10",
|
|
||||||
"tiledversion": "1.10.2",
|
|
||||||
"orientation": "orthogonal",
|
|
||||||
"renderorder": "right-down",
|
|
||||||
"width": 4,
|
|
||||||
"height": 4,
|
|
||||||
"tilewidth": 16,
|
|
||||||
"tileheight": 16,
|
|
||||||
"infinite": false,
|
|
||||||
"nextlayerid": 3,
|
|
||||||
"nextobjectid": 1,
|
|
||||||
"tilesets": [
|
|
||||||
{
|
|
||||||
"firstgid": 1,
|
|
||||||
"source": "handmade.tsx"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"layers": [
|
|
||||||
{
|
|
||||||
"type": "tilelayer",
|
|
||||||
"id": 1,
|
|
||||||
"name": "csv",
|
|
||||||
"width": 4,
|
|
||||||
"height": 4,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"opacity": 1,
|
|
||||||
"visible": true,
|
|
||||||
"data": [
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
10,
|
|
||||||
11,
|
|
||||||
12,
|
|
||||||
13,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
2147483649
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tilelayer",
|
|
||||||
"id": 2,
|
|
||||||
"name": "zlib",
|
|
||||||
"width": 4,
|
|
||||||
"height": 4,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"opacity": 1,
|
|
||||||
"visible": true,
|
|
||||||
"encoding": "base64",
|
|
||||||
"compression": "zlib",
|
|
||||||
"data": "eJwNw4cNACAMBLEPvYaVGT1nySYpMbOwsrFzcHJx8/DS+WjSDw1EAPo="
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored fixture for the Ludic Tiled reader (issue #67). Public domain (CC0). -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="4" height="4" tilewidth="16" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="handmade.tsx"/>
|
|
||||||
<layer id="1" name="csv" width="4" height="4">
|
|
||||||
<data encoding="csv">
|
|
||||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,2147483649
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<layer id="2" name="base64" width="4" height="4">
|
|
||||||
<data encoding="base64">
|
|
||||||
AQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAAQAAgA==
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<layer id="3" name="gzip" width="4" height="4">
|
|
||||||
<data encoding="base64" compression="gzip">
|
|
||||||
H4sIAAAAAAAC/w3Dhw0AIAwEsQ+9hpUZPWfJJikxs7CysXNwcnHz8NL5aNIPlvf4ekAAAAA=
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<layer id="4" name="zlib" width="4" height="4">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJwNw4cNACAMBLEPvYaVGT1nySYpMbOwsrFzcHJx8/DS+WjSDw1EAPo=
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored fixture for the Ludic Tiled reader (issue #67). Public domain (CC0). -->
|
|
||||||
<tileset version="1.10" tiledversion="1.10.2" name="handmade" tilewidth="16" tileheight="16" spacing="0" margin="0" tilecount="16" columns="4">
|
|
||||||
<image source="handmade.png" width="64" height="64"/>
|
|
||||||
<tile id="4">
|
|
||||||
<properties>
|
|
||||||
<property name="solid" type="bool" value="true"/>
|
|
||||||
</properties>
|
|
||||||
</tile>
|
|
||||||
<tile id="6">
|
|
||||||
<objectgroup draworder="index">
|
|
||||||
<object id="1" x="0" y="8" width="16" height="8"/>
|
|
||||||
</objectgroup>
|
|
||||||
</tile>
|
|
||||||
</tileset>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<map version="1.0" orientation="hexagonal" renderorder="right-down" width="20" height="20" tilewidth="14" tileheight="12" hexsidelength="6" staggeraxis="y" staggerindex="odd" nextobjectid="2">
|
|
||||||
<tileset firstgid="1" name="hex mini" tilewidth="18" tileheight="18">
|
|
||||||
<tileoffset x="0" y="1"/>
|
|
||||||
<image source="hexmini.png" width="106" height="72"/>
|
|
||||||
</tileset>
|
|
||||||
<layer name="Ground" width="20" height="20">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJyl1FEKhDAMBNBSt6jVaL3/Za2QwDAkVdiPQda2zyTonimlU1N6Ws+lkZ6l56AUXcPY2qlniv5uL5Z5BdyDvFXXMoX3Rp44axl6nqFejj3LLK6xgmf3Zg06Qs+O+qiaDOZOVgXPs7jfCme8Hkce1+fNlGdlM3myDTzc580fz1htW2Baj15/R/J72wLvcVZN5HnzGnmVPJ5hNH+0dt33j4ex91TARUs+WjNZz/fewKvJfy+/1naR+dX7OfdEnUYefyOeZZ7Vht/b5HjefxJbO1iTE7YWuEpg5hfPzi8D782x3Mg7DV4=
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored P5 fixture: image + group layers (issue #73). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="4" height="4" tilewidth="16" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="anim_tiles.tsx"/>
|
|
||||||
<imagelayer id="1" name="bg" offsetx="8" offsety="4" repeatx="1">
|
|
||||||
<image source="../kenney/tiny-dungeon/Tilemap/tilemap.png" width="203" height="186"/>
|
|
||||||
</imagelayer>
|
|
||||||
<group id="2" name="grp" offsetx="16" opacity="0.5" tintcolor="#ff0000">
|
|
||||||
<layer id="3" name="inner" width="4" height="4">
|
|
||||||
<data encoding="csv">
|
|
||||||
2,0,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</group>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
{"type": "map", "version": "1.10", "orientation": "orthogonal", "renderorder": "right-down", "width": 0, "height": 0, "tilewidth": 16, "tileheight": 16, "infinite": true, "tilesets": [{"firstgid": 1, "source": "collision.tsx"}], "layers": [{"type": "tilelayer", "id": 1, "name": "ground", "width": 0, "height": 0, "startx": 0, "starty": 0, "chunks": [{"x": 0, "y": 0, "width": 16, "height": 16, "data": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]}, {"x": 16, "y": 0, "width": 16, "height": 16, "data": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]}]}]}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored P6 infinite/chunked fixture (issue #74). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="0" height="0" tilewidth="16" tileheight="16" infinite="1" nextlayerid="2" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="collision.tsx"/>
|
|
||||||
<layer id="1" name="ground" width="0" height="0">
|
|
||||||
<data encoding="csv">
|
|
||||||
<chunk x="0" y="0" width="16" height="16">
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
|
|
||||||
</chunk>
|
|
||||||
<chunk x="16" y="0" width="16" height="16">
|
|
||||||
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
|
|
||||||
</chunk>
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<map version="1.4" tiledversion="1.4.3" orientation="isometric" renderorder="right-down" width="25" height="25" tilewidth="64" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" name="isometric_grass_and_water" tilewidth="64" tileheight="64" tilecount="24" columns="4">
|
|
||||||
<tileoffset x="0" y="16"/>
|
|
||||||
<grid orientation="isometric" width="64" height="32"/>
|
|
||||||
<image source="isometric_grass_and_water.png" width="256" height="384"/>
|
|
||||||
<wangsets>
|
|
||||||
<wangset name="Grass and Water" type="corner" tile="15">
|
|
||||||
<wangcolor name="Grass" color="#8ab022" tile="0" probability="1"/>
|
|
||||||
<wangcolor name="Water" color="#378dc2" tile="23" probability="1"/>
|
|
||||||
<wangtile tileid="0" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="1" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="2" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="3" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="4" wangid="0,1,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="5" wangid="0,1,0,1,0,2,0,1"/>
|
|
||||||
<wangtile tileid="6" wangid="0,1,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="7" wangid="0,2,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="8" wangid="0,2,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="9" wangid="0,1,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="10" wangid="0,2,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="11" wangid="0,2,0,2,0,1,0,2"/>
|
|
||||||
<wangtile tileid="12" wangid="0,1,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="13" wangid="0,1,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="14" wangid="0,2,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="15" wangid="0,2,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="16" wangid="0,1,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="17" wangid="0,1,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="18" wangid="0,2,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="19" wangid="0,2,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="20" wangid="0,2,0,1,0,2,0,1"/>
|
|
||||||
<wangtile tileid="21" wangid="0,1,0,2,0,1,0,2"/>
|
|
||||||
<wangtile tileid="22" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="23" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
</wangset>
|
|
||||||
</wangsets>
|
|
||||||
</tileset>
|
|
||||||
<layer id="1" name="Tile Layer 1" width="25" height="25">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJx1lttywjAMROVgyqVtAoFC/v9L68xoh5PFPGhIYktrrVYyS0QszZ7Nvpvd0n7y24L1Q7MhrTSreN/le821HZ7lv9qYa6sdE0cYs/kX7PXYwtfaevYp7WDrd+SnHByjYr/npP1zZ4/elcuM71rjeckdc5KNHX75fMwc9s2uzb6AsYstJzwrv5/Tz89SLIZy8v203llV8xl7yMU+462/v81OqA114/UhrzUxRqwprnh6ZGzp2PNQfPqRu/X9hnMV8F/xLg1L42erDf2oaa2RI2qPtbgbhmw2H69nMUxx/gVccXdC3AW/o/HV60vW59Lhu8arDxmfGIPFUV1qbLVQEIs4PlOeHQxqVjmzr5mLYsmf+5Qj5yM1r3Ne4p1D5VcMh3qWZibLx2fYkBhPYOv81I9wbrGd45zFU7zrndpwDjkHXXfej9zHc3EG+D3AWcCZMJif7hTnVxr6i9edtoBDz8N7kxqbY6sN9gJnsnqIOqCme7Un76579sIV8dccHvHqZefH76BP9wjzkVapM2rL+5/8cR6QS9eh8p2AT12y5oO9+7yh5hzLZypnHX29/pzB9PE7bOg8Mza5KvGu4R7mp/89zqvr7x+TnxEn
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,285 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<map version="1.8" tiledversion="1.8.5" orientation="orthogonal" renderorder="right-down" width="45" height="31" tilewidth="16" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="38">
|
|
||||||
<properties>
|
|
||||||
<property name="enemyTint" type="color" value="#ffa33636"/>
|
|
||||||
</properties>
|
|
||||||
<tileset firstgid="1" name="outdoor" tilewidth="16" tileheight="16" tilecount="288" columns="24">
|
|
||||||
<image source="buch-outdoor.png" width="384" height="192"/>
|
|
||||||
<tile id="6" probability="0.1"/>
|
|
||||||
<tile id="27" probability="0.05"/>
|
|
||||||
<tile id="28" probability="0.05"/>
|
|
||||||
<tile id="30" probability="0.1"/>
|
|
||||||
<tile id="51" probability="0.05"/>
|
|
||||||
<tile id="52" probability="0.05"/>
|
|
||||||
<tile id="54" probability="0.1"/>
|
|
||||||
<tile id="75" probability="0.05"/>
|
|
||||||
<tile id="76" probability="0.05"/>
|
|
||||||
<tile id="78" probability="0.1"/>
|
|
||||||
<tile id="82" probability="0.1"/>
|
|
||||||
<tile id="83" probability="0.1"/>
|
|
||||||
<tile id="99" probability="0.05"/>
|
|
||||||
<tile id="102" probability="0.1"/>
|
|
||||||
<tile id="106" probability="0.1"/>
|
|
||||||
<tile id="107" probability="0.1"/>
|
|
||||||
<tile id="126" probability="0.1"/>
|
|
||||||
<wangsets>
|
|
||||||
<wangset name="Terrains" type="corner" tile="25">
|
|
||||||
<wangcolor name="Grass" color="#fce94f" tile="150" probability="1"/>
|
|
||||||
<wangcolor name="Dirt" color="#ef2929" tile="100" probability="1"/>
|
|
||||||
<wangcolor name="Dark Dirt" color="#f57900" tile="34" probability="1"/>
|
|
||||||
<wangcolor name="Water" color="#729fcf" tile="171" probability="1"/>
|
|
||||||
<wangtile tileid="0" wangid="0,1,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="1" wangid="0,1,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="2" wangid="0,1,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="3" wangid="0,1,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="4" wangid="0,1,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="5" wangid="0,1,0,1,0,2,0,1"/>
|
|
||||||
<wangtile tileid="6" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="7" wangid="0,2,0,3,0,2,0,2"/>
|
|
||||||
<wangtile tileid="8" wangid="0,2,0,3,0,3,0,2"/>
|
|
||||||
<wangtile tileid="9" wangid="0,2,0,3,0,3,0,2"/>
|
|
||||||
<wangtile tileid="10" wangid="0,2,0,3,0,3,0,2"/>
|
|
||||||
<wangtile tileid="11" wangid="0,2,0,3,0,3,0,2"/>
|
|
||||||
<wangtile tileid="12" wangid="0,2,0,2,0,3,0,2"/>
|
|
||||||
<wangtile tileid="13" wangid="0,1,0,3,0,1,0,1"/>
|
|
||||||
<wangtile tileid="14" wangid="0,1,0,3,0,3,0,1"/>
|
|
||||||
<wangtile tileid="15" wangid="0,1,0,3,0,3,0,1"/>
|
|
||||||
<wangtile tileid="16" wangid="0,1,0,3,0,3,0,1"/>
|
|
||||||
<wangtile tileid="17" wangid="0,1,0,3,0,3,0,1"/>
|
|
||||||
<wangtile tileid="18" wangid="0,1,0,1,0,3,0,1"/>
|
|
||||||
<wangtile tileid="24" wangid="0,2,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="25" wangid="0,2,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="26" wangid="0,2,0,2,0,1,0,2"/>
|
|
||||||
<wangtile tileid="27" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="28" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="29" wangid="0,1,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="30" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="31" wangid="0,3,0,3,0,2,0,2"/>
|
|
||||||
<wangtile tileid="32" wangid="0,3,0,2,0,3,0,3"/>
|
|
||||||
<wangtile tileid="33" wangid="0,3,0,3,0,2,0,3"/>
|
|
||||||
<wangtile tileid="34" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="35" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="36" wangid="0,2,0,2,0,3,0,3"/>
|
|
||||||
<wangtile tileid="37" wangid="0,3,0,3,0,1,0,1"/>
|
|
||||||
<wangtile tileid="38" wangid="0,3,0,1,0,3,0,3"/>
|
|
||||||
<wangtile tileid="39" wangid="0,3,0,3,0,1,0,3"/>
|
|
||||||
<wangtile tileid="40" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="42" wangid="0,1,0,1,0,3,0,3"/>
|
|
||||||
<wangtile tileid="48" wangid="0,2,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="49" wangid="0,1,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="50" wangid="0,2,0,2,0,2,0,1"/>
|
|
||||||
<wangtile tileid="51" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="52" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="53" wangid="0,1,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="54" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="55" wangid="0,3,0,3,0,2,0,2"/>
|
|
||||||
<wangtile tileid="56" wangid="0,2,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="57" wangid="0,3,0,3,0,3,0,2"/>
|
|
||||||
<wangtile tileid="58" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="59" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="60" wangid="0,2,0,2,0,3,0,3"/>
|
|
||||||
<wangtile tileid="61" wangid="0,3,0,3,0,1,0,1"/>
|
|
||||||
<wangtile tileid="62" wangid="0,1,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="63" wangid="0,3,0,3,0,3,0,1"/>
|
|
||||||
<wangtile tileid="66" wangid="0,1,0,1,0,3,0,3"/>
|
|
||||||
<wangtile tileid="72" wangid="0,2,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="73" wangid="0,2,0,1,0,2,0,1"/>
|
|
||||||
<wangtile tileid="74" wangid="0,1,0,2,0,1,0,2"/>
|
|
||||||
<wangtile tileid="75" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="76" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="77" wangid="0,1,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="78" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="79" wangid="0,3,0,3,0,2,0,2"/>
|
|
||||||
<wangtile tileid="80" wangid="0,3,0,2,0,3,0,2"/>
|
|
||||||
<wangtile tileid="81" wangid="0,2,0,3,0,2,0,3"/>
|
|
||||||
<wangtile tileid="82" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="83" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="84" wangid="0,2,0,2,0,3,0,3"/>
|
|
||||||
<wangtile tileid="85" wangid="0,3,0,3,0,1,0,1"/>
|
|
||||||
<wangtile tileid="86" wangid="0,3,0,1,0,3,0,1"/>
|
|
||||||
<wangtile tileid="87" wangid="0,1,0,3,0,1,0,3"/>
|
|
||||||
<wangtile tileid="90" wangid="0,1,0,1,0,3,0,3"/>
|
|
||||||
<wangtile tileid="96" wangid="0,2,0,2,0,1,0,1"/>
|
|
||||||
<wangtile tileid="97" wangid="0,1,0,2,0,1,0,2"/>
|
|
||||||
<wangtile tileid="98" wangid="0,2,0,1,0,2,0,1"/>
|
|
||||||
<wangtile tileid="99" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="100" wangid="0,2,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="101" wangid="0,1,0,1,0,2,0,2"/>
|
|
||||||
<wangtile tileid="102" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="103" wangid="0,3,0,3,0,2,0,2"/>
|
|
||||||
<wangtile tileid="104" wangid="0,2,0,3,0,2,0,3"/>
|
|
||||||
<wangtile tileid="105" wangid="0,3,0,2,0,3,0,2"/>
|
|
||||||
<wangtile tileid="106" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="107" wangid="0,3,0,3,0,3,0,3"/>
|
|
||||||
<wangtile tileid="108" wangid="0,2,0,2,0,3,0,3"/>
|
|
||||||
<wangtile tileid="109" wangid="0,3,0,3,0,1,0,1"/>
|
|
||||||
<wangtile tileid="110" wangid="0,1,0,3,0,1,0,3"/>
|
|
||||||
<wangtile tileid="111" wangid="0,3,0,1,0,3,0,1"/>
|
|
||||||
<wangtile tileid="114" wangid="0,1,0,1,0,3,0,3"/>
|
|
||||||
<wangtile tileid="120" wangid="0,2,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="121" wangid="0,2,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="122" wangid="0,2,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="123" wangid="0,2,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="124" wangid="0,2,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="125" wangid="0,1,0,1,0,1,0,2"/>
|
|
||||||
<wangtile tileid="126" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="127" wangid="0,3,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="128" wangid="0,3,0,2,0,2,0,3"/>
|
|
||||||
<wangtile tileid="129" wangid="0,3,0,2,0,2,0,3"/>
|
|
||||||
<wangtile tileid="130" wangid="0,3,0,2,0,2,0,3"/>
|
|
||||||
<wangtile tileid="131" wangid="0,3,0,2,0,2,0,3"/>
|
|
||||||
<wangtile tileid="132" wangid="0,2,0,2,0,2,0,3"/>
|
|
||||||
<wangtile tileid="133" wangid="0,3,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="134" wangid="0,3,0,1,0,1,0,3"/>
|
|
||||||
<wangtile tileid="135" wangid="0,3,0,1,0,1,0,3"/>
|
|
||||||
<wangtile tileid="136" wangid="0,3,0,1,0,1,0,3"/>
|
|
||||||
<wangtile tileid="137" wangid="0,3,0,1,0,1,0,3"/>
|
|
||||||
<wangtile tileid="138" wangid="0,1,0,1,0,1,0,3"/>
|
|
||||||
<wangtile tileid="144" wangid="0,1,0,4,0,1,0,1"/>
|
|
||||||
<wangtile tileid="145" wangid="0,1,0,4,0,4,0,1"/>
|
|
||||||
<wangtile tileid="146" wangid="0,1,0,4,0,4,0,1"/>
|
|
||||||
<wangtile tileid="147" wangid="0,1,0,4,0,4,0,1"/>
|
|
||||||
<wangtile tileid="148" wangid="0,1,0,4,0,4,0,1"/>
|
|
||||||
<wangtile tileid="149" wangid="0,1,0,1,0,4,0,1"/>
|
|
||||||
<wangtile tileid="150" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="151" wangid="0,2,0,4,0,2,0,2"/>
|
|
||||||
<wangtile tileid="152" wangid="0,2,0,4,0,4,0,2"/>
|
|
||||||
<wangtile tileid="153" wangid="0,2,0,4,0,4,0,2"/>
|
|
||||||
<wangtile tileid="154" wangid="0,2,0,4,0,4,0,2"/>
|
|
||||||
<wangtile tileid="155" wangid="0,2,0,4,0,4,0,2"/>
|
|
||||||
<wangtile tileid="156" wangid="0,2,0,2,0,4,0,2"/>
|
|
||||||
<wangtile tileid="168" wangid="0,4,0,4,0,1,0,1"/>
|
|
||||||
<wangtile tileid="169" wangid="0,4,0,1,0,4,0,4"/>
|
|
||||||
<wangtile tileid="170" wangid="0,4,0,4,0,1,0,4"/>
|
|
||||||
<wangtile tileid="171" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="172" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="173" wangid="0,1,0,1,0,4,0,4"/>
|
|
||||||
<wangtile tileid="174" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="175" wangid="0,4,0,4,0,2,0,2"/>
|
|
||||||
<wangtile tileid="176" wangid="0,4,0,2,0,4,0,4"/>
|
|
||||||
<wangtile tileid="177" wangid="0,4,0,4,0,2,0,4"/>
|
|
||||||
<wangtile tileid="178" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="179" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="180" wangid="0,2,0,2,0,4,0,4"/>
|
|
||||||
<wangtile tileid="192" wangid="0,4,0,4,0,1,0,1"/>
|
|
||||||
<wangtile tileid="193" wangid="0,1,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="194" wangid="0,4,0,4,0,4,0,1"/>
|
|
||||||
<wangtile tileid="195" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="196" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="197" wangid="0,1,0,1,0,4,0,4"/>
|
|
||||||
<wangtile tileid="198" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="199" wangid="0,4,0,4,0,2,0,2"/>
|
|
||||||
<wangtile tileid="200" wangid="0,2,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="201" wangid="0,4,0,4,0,4,0,2"/>
|
|
||||||
<wangtile tileid="202" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="203" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="204" wangid="0,2,0,2,0,4,0,4"/>
|
|
||||||
<wangtile tileid="216" wangid="0,4,0,4,0,1,0,1"/>
|
|
||||||
<wangtile tileid="217" wangid="0,4,0,1,0,4,0,1"/>
|
|
||||||
<wangtile tileid="218" wangid="0,1,0,4,0,1,0,4"/>
|
|
||||||
<wangtile tileid="219" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="220" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="221" wangid="0,1,0,1,0,4,0,4"/>
|
|
||||||
<wangtile tileid="222" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="223" wangid="0,4,0,4,0,2,0,2"/>
|
|
||||||
<wangtile tileid="224" wangid="0,4,0,2,0,4,0,2"/>
|
|
||||||
<wangtile tileid="225" wangid="0,2,0,4,0,2,0,4"/>
|
|
||||||
<wangtile tileid="226" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="227" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="228" wangid="0,2,0,2,0,4,0,4"/>
|
|
||||||
<wangtile tileid="240" wangid="0,4,0,4,0,1,0,1"/>
|
|
||||||
<wangtile tileid="241" wangid="0,1,0,4,0,1,0,4"/>
|
|
||||||
<wangtile tileid="242" wangid="0,4,0,1,0,4,0,1"/>
|
|
||||||
<wangtile tileid="243" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="244" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="245" wangid="0,1,0,1,0,4,0,4"/>
|
|
||||||
<wangtile tileid="246" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="247" wangid="0,4,0,4,0,2,0,2"/>
|
|
||||||
<wangtile tileid="248" wangid="0,2,0,4,0,2,0,4"/>
|
|
||||||
<wangtile tileid="249" wangid="0,4,0,2,0,4,0,2"/>
|
|
||||||
<wangtile tileid="250" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="251" wangid="0,4,0,4,0,4,0,4"/>
|
|
||||||
<wangtile tileid="252" wangid="0,2,0,2,0,4,0,4"/>
|
|
||||||
<wangtile tileid="264" wangid="0,4,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="265" wangid="0,4,0,1,0,1,0,4"/>
|
|
||||||
<wangtile tileid="266" wangid="0,4,0,1,0,1,0,4"/>
|
|
||||||
<wangtile tileid="267" wangid="0,4,0,1,0,1,0,4"/>
|
|
||||||
<wangtile tileid="268" wangid="0,4,0,1,0,1,0,4"/>
|
|
||||||
<wangtile tileid="269" wangid="0,1,0,1,0,1,0,4"/>
|
|
||||||
<wangtile tileid="270" wangid="0,1,0,1,0,1,0,1"/>
|
|
||||||
<wangtile tileid="271" wangid="0,4,0,2,0,2,0,2"/>
|
|
||||||
<wangtile tileid="272" wangid="0,4,0,2,0,2,0,4"/>
|
|
||||||
<wangtile tileid="273" wangid="0,4,0,2,0,2,0,4"/>
|
|
||||||
<wangtile tileid="274" wangid="0,4,0,2,0,2,0,4"/>
|
|
||||||
<wangtile tileid="275" wangid="0,4,0,2,0,2,0,4"/>
|
|
||||||
<wangtile tileid="276" wangid="0,2,0,2,0,2,0,4"/>
|
|
||||||
</wangset>
|
|
||||||
</wangsets>
|
|
||||||
</tileset>
|
|
||||||
<layer id="1" name="Ground" width="45" height="31">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJyNWE1vVVUU3Y0KQeXL4kBL7QAiEkcopQOIkjgysXagYeLIpE0HGucEtY5U6giw1Bj8AdiWxhZ+ANLS4nv+AJKW1+TVH9DklWfSR+LeeWt51j3eWxmsnPvuPR9rr7P3Pvu8hpkd7DFb8dYb24Pf89bFtOOio8/xquNreZ/jdccxx0nH53j+SN7F81nHOfz+BGs1vP0C706i/1kZ+6asMWRdrvMY+4q3bzmedTwj/FfAO/i2HVcc10s4f+t4QzidcEw6vnd8lz0HfrAu3wOO8xj3seO44AT6cY0GONPWTx1jjp8cvzjO4Fsbfan/luP3XXgrJ+4XtWnjeQX9VzBmErgsdud8lXdwCb94G5w3HE+g+R5ZcwDPDx114U3dY569wkW5UZ8J8I6+AfalPbT7MvhS03nMMV3CNzT+A5zHMt70obvezjrWwfue47HwynUhH3JivwG01E1Be0cs8eW3mOd9x2lwXHVsOx7AP8bgI/PCZ5+PmwHvHceSaK76UZO2cGyg1X2mT/eKrtOwUX2Le/RcicbB4zfHyz6239sPs3VijtB6E33XRPPr0CbXmpwaAvrEPPyBvsDvOn4IbQ+4jkHnDaxfE52POl6zFIe0n1oH12Vw37Hq2HzH8W5o6DgE9FrRn+l7uk8rVvSZi6LvDdF4GaBPU2edS32kDjub1vWrMt6Mq8gJkc/OwQbypq9T54jrKcfVkrl+Fq5ctwn9/hTOZefIgPC+7VhEW9tFb+bwyL2Rw3le5DHGeVuY6xrma0DPJtpYL+Ip/JT+TM7uT9/k649jji3MEWMO+5gF2Py4hLOel8y/A1bMKdRbeW8K99BkzlIOmMVe37GuL2u+y/PYCNbbAkfGwCo0vyX9cq0nwbcsXnP/e97xAjADzutoQ6def3+kJ2lMnCmZL/wl/C7y87bsTXDuOB5Bt//jpbgiflBlQwe6am4bE40Dw6J1PscW9inmiJhlvvzVcaAn7fnT8JyC/ZG77u/SX3Nx8CPnQNRxl6xY05XZPYN5HoBv+F+VL49kccF9on9GG757q2J84KXMD0bBrw/+EHt7yro5umqOh7IW1330FHtNvkvQdg72co8GKtajnn2ib+QJ1p7T2XMgzlDPI//mv+C8Db5NWVO/MeangB/Bt4Y9CnuXs7FVIEfWofy9d5cxPF8mRKtNaDQLvxiy5O/xvgMteQ5si7Y1PLdkf8owjvW+hIan7L85gnXJRLd/wQ+ZD+jPjOU1K9ao1JLfeWatgfdtvGcNsBtnzjcu+/+VdWMubNA6i3lNx9MWahl7qvUHa1RqS79pWjord9C/hedFjLsKaByQ72KJLax1437GmqZX+OV2Mz+FP9y0dI52wKuFd3y/CH3Zh1qz1f7x7n62X/USHm3ggqU7pdYyeZ57EedTxI3W1XXw25T1a+DFs4vn/Bz4d7BHm5bOOe4XvzdLeBBRO0YdwzszeYevjFvymYg1xiv1qFvKtx1o17QUi1EXMHYOgfsR8G9Z8hXWiZyrjvFVnFl/kfOxjDdrmeAZeeuapTOce8x6gHquAVEv6jnL51VozH6shdbAn3VnrBvxdtCS7/LdeUv/NUQNxntlG7ypK/dxHWuw/tbzlTVIrMlaKu4Zeu4egm30I+a/WbTL+KaaHoeWh8GX+sZ/I1HnDlixFr9nqY5dtFQztjK+vMurbsH7hqU7J/X+GxyXMGcNWJI9oy9csOJ/PKEr/0cJe96Dvjdl/3g/2ZZ1uH9/Wbe2GnR8AC7Rb8dSPOV2jaIfc7ae6ew7akX/nQRP+kC8j/OO98vgvA95Yj/q2v2SK7Sm5fx5rC2gT/R/YsXacRT7EeMXLMVdcNa4rYrByMd6B676j44+3QFn1U7jTLkzz6nGw/I93m9Yyot5HcdzhPkrELkrYjD8mnGZc2a9pnUO+Q5mPEO/fivG21Grtktty+8eqjM58b873tk+s25c6t0+cNdS/cAYv1Oxfhn6pR20oh+dzvoOZ/b+A6OnAUo=
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<layer id="2" name="Fringe" width="45" height="31">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJzVl9lNw0AURZ8ltgr4YquAjliaYPl8DdAChCVABRB2KkBhpwK2sKQC4FgiShQ8tseMx/aVjmRLY+fO3JnnFxGRGZiVamkBFos2EaOBQGQw6N6PFmcltcbwOx50vU+neKYztsb1OmzAZq4uo9XxbjO2wfUBHMJRjt5c6RyacAlXcF2oGzu9wCu0YCohpwcPftJqFdZ+r9uGMf1nvWjtwX7P/UiEN5vzUiZRW5Qao9QarUHRftKI2qLUGKXWaCOjZ9/zprYoNUapNdrM+Hsu5m0jaotSY5Rao62M73Ax70d4gme4yOgjSb01tH/eu/947xd8izvfS7Ds6F2+VZP8e5oVx+8rY0/D90dN385QPnuapN7DZnxvT1O2HiCNqtYD2GaXt3aot9tQh62K9CJn+DyFEziuiOd7fN7BLdyUxDN5C7kL+Yf74I8+8fkB7/AGQ+zdYU/7t21YI/IWchfyD/dBoibwO1nwmSNvIXch/3AfxCopE18ibyF3If9wH8TKNhOfMq2nTSZ5yXSeTOvZn0nU83Mw78Bb1P/tUKbzlHY9k87jD20Li3Y=
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<objectgroup id="3" name="Objects">
|
|
||||||
<object id="1" name="maggots" type="Location" x="435" y="74" width="155" height="99">
|
|
||||||
<properties>
|
|
||||||
<property name="spawncount" type="int" value="5"/>
|
|
||||||
<property name="spawntype" value="maggot"/>
|
|
||||||
</properties>
|
|
||||||
</object>
|
|
||||||
<object id="2" name="discover chest" type="Trigger" x="201" y="200" width="127" height="127">
|
|
||||||
<properties>
|
|
||||||
<property name="script" type="file" value="chest-discovered.lua"/>
|
|
||||||
</properties>
|
|
||||||
<ellipse/>
|
|
||||||
</object>
|
|
||||||
<object id="3" name="unreachable" type="Fixture" x="2" y="158">
|
|
||||||
<properties>
|
|
||||||
<property name="static" type="bool" value="true"/>
|
|
||||||
</properties>
|
|
||||||
<polygon points="0,0 55,-23 96,-117 110,-61 104,-42 119,-33 116,6 104,9 100,36 60,43 53,58 43,58 34,74 21,69 18,90 0,89"/>
|
|
||||||
</object>
|
|
||||||
<object id="5" name="guard" type="NPC" x="22" y="361">
|
|
||||||
<polyline points="-3,120 87,91 154,96 181,16 273,-1"/>
|
|
||||||
</object>
|
|
||||||
<object id="6" name="guard" type="NPC" x="277" y="18">
|
|
||||||
<polyline points="0,0 75,78 133,82 176,179 274,183"/>
|
|
||||||
</object>
|
|
||||||
<object id="10" gid="282" x="413.333" y="225.333" width="16" height="16"/>
|
|
||||||
<object id="11" gid="282" x="421.667" y="218" width="16" height="16"/>
|
|
||||||
<object id="12" gid="2147483930" x="423" y="235.333" width="16" height="16"/>
|
|
||||||
<object id="13" gid="282" x="5" y="70" width="16" height="16"/>
|
|
||||||
<object id="14" gid="282" x="-3.66667" y="80.3333" width="16" height="16"/>
|
|
||||||
<object id="16" gid="283" x="538" y="418.333" width="16" height="16"/>
|
|
||||||
<object id="17" gid="283" x="407.667" y="462" width="16" height="16"/>
|
|
||||||
<object id="18" gid="283" x="417" y="473.667" width="16" height="16"/>
|
|
||||||
<object id="19" gid="283" x="402.667" y="469" width="16" height="16"/>
|
|
||||||
<object id="21" gid="2147483930" x="683.333" y="260.5" width="16" height="16"/>
|
|
||||||
<object id="22" gid="282" x="692.167" y="269.167" width="16" height="16"/>
|
|
||||||
<object id="23" gid="282" x="701.667" y="247.833" width="16" height="16"/>
|
|
||||||
<object id="24" gid="282" x="688.5" y="242" width="16" height="16"/>
|
|
||||||
<object id="25" gid="282" x="670.5" y="263.5" width="16" height="16"/>
|
|
||||||
<object id="26" gid="282" x="680" y="284" width="16" height="16"/>
|
|
||||||
<object id="27" gid="282" x="643.833" y="283.667" width="16" height="16"/>
|
|
||||||
<object id="28" gid="282" x="63.4165" y="386" width="16" height="16"/>
|
|
||||||
<object id="29" gid="282" x="9.0835" y="356.167" width="16" height="16"/>
|
|
||||||
<object id="30" gid="282" x="11.9165" y="385" width="16" height="16"/>
|
|
||||||
<object id="31" gid="282" x="54.2495" y="378.5" width="16" height="16"/>
|
|
||||||
<object id="32" gid="2147483930" x="2.4165" y="364.5" width="16" height="16"/>
|
|
||||||
<object id="33" gid="2147483930" x="41.5835" y="382.833" width="16" height="16"/>
|
|
||||||
<object id="34" type="Sign" gid="257" x="670.667" y="87" width="16" height="16">
|
|
||||||
<properties>
|
|
||||||
<property name="text" value="East West"/>
|
|
||||||
</properties>
|
|
||||||
</object>
|
|
||||||
<object id="37" name="player-start" type="Location" x="192" y="160">
|
|
||||||
<point/>
|
|
||||||
</object>
|
|
||||||
</objectgroup>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored P2 collision fixture (issue #70). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="16" height="10" tilewidth="16" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="p2_tiles.tsx"/>
|
|
||||||
<layer id="1" name="collision" width="16" height="10">
|
|
||||||
<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,1,1,1,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,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>
|
|
||||||
<layer id="2" name="props" width="16" height="10">
|
|
||||||
<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,4,4,4,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,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>
|
|
||||||
<layer id="3" name="floor" width="16" height="10">
|
|
||||||
<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,
|
|
||||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored collision-metadata tileset (issue #70). CC0. -->
|
|
||||||
<tileset version="1.10" tiledversion="1.10.2" name="p2tiles" tilewidth="16" tileheight="16" tilecount="5" columns="5">
|
|
||||||
<tile id="1">
|
|
||||||
<objectgroup draworder="index">
|
|
||||||
<object id="1" x="0" y="0" width="16" height="16"/>
|
|
||||||
</objectgroup>
|
|
||||||
</tile>
|
|
||||||
<tile id="2"><properties><property name="oneway" type="bool" value="true"/></properties></tile>
|
|
||||||
<tile id="3"><properties><property name="solid" type="bool" value="true"/></properties></tile>
|
|
||||||
<tile id="4"><properties><property name="trigger" type="bool" value="true"/></properties></tile>
|
|
||||||
</tileset>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<template>
|
|
||||||
<object type="Enemy" width="16" height="16">
|
|
||||||
<properties>
|
|
||||||
<property name="hp" type="int" value="25"/>
|
|
||||||
</properties>
|
|
||||||
</object>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored P4 fixture: object shapes, custom types, templates (issue #72). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="16" height="16" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="13">
|
|
||||||
<objectgroup id="1" name="shapes">
|
|
||||||
<object id="1" x="10" y="10" width="20" height="30"/>
|
|
||||||
<object id="2" x="40" y="10" width="20" height="20"><ellipse/></object>
|
|
||||||
<object id="3" x="70" y="10"><point/></object>
|
|
||||||
<object id="4" x="90" y="10"><polygon points="0,0 16,0 16,16 0,16"/></object>
|
|
||||||
<object id="5" x="120" y="10"><polyline points="0,0 10,10 20,0"/></object>
|
|
||||||
<object id="6" x="10" y="60" width="80" height="20"><text pixelsize="12" halign="center">Hello</text></object>
|
|
||||||
</objectgroup>
|
|
||||||
<objectgroup id="2" name="spawns">
|
|
||||||
<object id="10" type="Enemy" x="32" y="48" width="16" height="16">
|
|
||||||
<properties><property name="hp" type="int" value="99"/></properties>
|
|
||||||
</object>
|
|
||||||
<object id="11" type="Enemy" x="64" y="48" width="16" height="16"/>
|
|
||||||
<object id="12" template="p4_enemy.tx" x="80" y="48"/>
|
|
||||||
</objectgroup>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<objecttypes>
|
|
||||||
<objecttype name="Enemy" color="#ff0000">
|
|
||||||
<property name="hp" type="int" default="10"/>
|
|
||||||
<property name="speed" type="int" default="3"/>
|
|
||||||
<property name="boss" type="bool" default="false"/>
|
|
||||||
</objecttype>
|
|
||||||
</objecttypes>
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<tileset name="perspective_walls" tilewidth="64" tileheight="64">
|
|
||||||
<tileoffset x="-32" y="0"/>
|
|
||||||
<image source="perspective_walls.png"/>
|
|
||||||
<tile id="13">
|
|
||||||
<properties>
|
|
||||||
<property name="door" value="true"/>
|
|
||||||
</properties>
|
|
||||||
</tile>
|
|
||||||
<tile id="14">
|
|
||||||
<properties>
|
|
||||||
<property name="door" value="true"/>
|
|
||||||
</properties>
|
|
||||||
</tile>
|
|
||||||
<tile id="15">
|
|
||||||
<properties>
|
|
||||||
<property name="pickup" value="true"/>
|
|
||||||
</properties>
|
|
||||||
</tile>
|
|
||||||
</tileset>
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored map for the Ludic Tiled demos (issue #69). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="16" height="10" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="collision.tsx"/>
|
|
||||||
<layer id="1" name="collision" width="16" height="10">
|
|
||||||
<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,2,2,2,0,0,0,
|
|
||||||
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<map version="1.0" orientation="orthogonal" width="50" height="50" tilewidth="24" tileheight="24">
|
|
||||||
<tileset firstgid="1" name="sewer_tileset" tilewidth="24" tileheight="24">
|
|
||||||
<image source="sewer_tileset.png" trans="ff00ff" width="192" height="217"/>
|
|
||||||
</tileset>
|
|
||||||
<layer name="Bottom" width="50" height="50">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJzt19kKwjAQBdDim0sFqwguL3Vf/sP//ySnkIFhSGrSdEnxPhyQxqJ32kySPMuyJTkZC5KLa2dyNEo1Lsds3wkl/4f+7V/0/f+Y40Je5GpUn2+J5NiQCdmO/HlMyYzMI3JwLUJx7drIsSIFWUfk4FrY3GvGuHb8vr4jcmhNcnAtpIflmmar3VA5nqaWMUKeQ1d9dyht59iRPTmMPEdpua8Put/Frh88P5q84zFkv/NZP2TOlOaH7Hc+64fMmdL8cPVbV9+tcn5MzpTmR0gv12uDbQ4MNT8AIA73Uq3v3hqLe2ldTx4D21k1tf2ubw59Vk1tX+KbQ59VXfuSlMn9SJHV70tS5jqrYu8BAAAAAAAAAABd+wIHfQq1
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<layer name="Top" width="50" height="50" opacity="0.49">
|
|
||||||
<data encoding="base64" compression="zlib">
|
|
||||||
eJzt1jsKgDAQQEELtVKvYuGvEDvvfya3MBeQQAzMwCPdsum2af5lL71AJv7xL7X+Y46WtzXayq7z2RGd0f2+V6a5bdRFfaZ5pQzRGE2lFwEAoArpDk7Veg+nOzjlHgYAAAAAIIcHvboDlQ==
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Hand-authored P6 zstd fixture (issue #74). CC0. -->
|
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="24" height="16" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="1">
|
|
||||||
<tileset firstgid="1" source="collision.tsx"/>
|
|
||||||
<layer id="1" name="csv" width="24" height="16">
|
|
||||||
<data encoding="csv">
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<layer id="2" name="zstd" width="24" height="16">
|
|
||||||
<data encoding="base64" compression="zstd">
|
|
||||||
KLUv/WAABe0AADgBAAAAAwIBBgC4mgT54KqgO9JxR0xFgOm26RIB
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Changesets
|
# Changesets
|
||||||
|
|
||||||
A **changeset** is one small Markdown file describing a single user-facing change,
|
A **changeset** is one small Markdown file describing a single user-facing change,
|
||||||
dropped in this directory. `ludic-dev release` consumes every changeset here into a new
|
dropped in this directory. `x release` consumes every changeset here into a new
|
||||||
`CHANGELOG.md` section, bumps `VERSION`, and deletes the consumed files.
|
`CHANGELOG.md` section, bumps `VERSION`, and deletes the consumed files.
|
||||||
|
|
||||||
## Format
|
## Format
|
||||||
|
|
@ -14,38 +14,12 @@ the changelog. Markdown is fine.
|
||||||
```
|
```
|
||||||
|
|
||||||
- `bump:` — `major`, `minor`, or `patch` (SemVer). The release version is bumped
|
- `bump:` — `major`, `minor`, or `patch` (SemVer). The release version is bumped
|
||||||
by the **highest** level among the pending changesets (unless `ludic-dev release <level>`
|
by the **highest** level among the pending changesets (unless `x release <level>`
|
||||||
overrides it).
|
overrides it).
|
||||||
- `type:` — the Conventional Commit type (`feat`, `fix`, `perf`, `docs`, …). It
|
- `type:` — the Conventional Commit type (`feat`, `fix`, `perf`, `docs`, …); it
|
||||||
decides which group the change lands in: `feat` → **Features**, `fix` →
|
becomes the bold prefix of the changelog bullet.
|
||||||
**Fixes**, `perf` → **Performance**, and so on, in that order. A type with no
|
|
||||||
known heading gets one named after itself.
|
|
||||||
|
|
||||||
## Writing the body
|
|
||||||
|
|
||||||
The body is markdown and reaches the changelog as markdown: it becomes one list
|
|
||||||
item, with continuation lines indented to stay inside it. Nested bullets, blank
|
|
||||||
lines between paragraphs and inline code all survive.
|
|
||||||
|
|
||||||
```
|
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**Tiled map support** — load and draw Tiled maps.
|
|
||||||
|
|
||||||
- **TMX/TSX** — the XML formats, decoded to the same intermediate as JSON.
|
|
||||||
- **Collision** — the `collision` layer projects onto the engine tilemap.
|
|
||||||
```
|
|
||||||
|
|
||||||
Lead with the thing that changed, not with the mechanism. A reader scanning the
|
|
||||||
release should be able to stop after your first clause.
|
|
||||||
|
|
||||||
## Adding one
|
## Adding one
|
||||||
|
|
||||||
Create a file with a short, unique name, e.g. `changes/regex-namespace.md`. Any
|
Create a file with a short, unique name, e.g. `changes/regex-namespace.md`. Any
|
||||||
filename works except this `README.md`, which the release step always skips.
|
filename works except this `README.md`, which the release step always skips.
|
||||||
|
|
||||||
Preview how the next release will read before cutting it — this writes nothing:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ludic-dev release --dry-run
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
`Actor.cast_hidden` separates being DRAWN from CASTING. `ac_visible` rejected any hidden
|
|
||||||
actor from the shadow pass as well as the scene pass, so a game that hides the player's
|
|
||||||
own body - first person, or a viewfinder held to the eye - lost that player's shadow
|
|
||||||
entirely. An actor hidden because the camera is inside its head is still standing in the
|
|
||||||
sun; set this on it and it keeps its shadow. Everything else still stops casting when it
|
|
||||||
is hidden.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
A leaf is not matte.
|
|
||||||
|
|
||||||
Foliage roughness was pinned to 1.0 and grazing Fresnel switched off, so nothing green in
|
|
||||||
the game had a highlight anywhere: the glint off waxy leaves and wet needles, which is
|
|
||||||
most of what makes a real stand look alive rather than painted, was simply absent.
|
|
||||||
|
|
||||||
The reason it was switched off is real. A crown is card quads, and at a grazing angle the
|
|
||||||
card's normal is a lie, so a plain specular lobe frosted whole crowns white against the
|
|
||||||
sky. So the sheen comes back as its own term gated on exactly that: it fades out as the
|
|
||||||
card turns edge-on, which is where its normal stops meaning anything. A tight lobe for the
|
|
||||||
glint, a weak wide one for the waxy rim, and nothing at all at the angles that frosted.
|
|
||||||
|
|
||||||
Thin-leaf translucency reaches further with it - a backlit stand glows for as far as you
|
|
||||||
can see it, not 140 m - and a dense crown passes 0.45 of it rather than 0.3. The distance
|
|
||||||
cap that stops a two-pixel clump card becoming a lime disc stays.
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
Anti-aliasing that exists, and a lens for the viewfinder.
|
|
||||||
|
|
||||||
THE SHIPPING DEFAULT HAD NO ANTI-ALIASING AT ALL. The temporal resolve was removed (for
|
|
||||||
good reasons - it reprojected water through the surface plane and dragged the mirror image
|
|
||||||
behind the camera), the setting's first option went on saying "Temporal", and MSAA defaults
|
|
||||||
to one sample. Every machine without DLSS - which is every Mac - drew a frame full of grass
|
|
||||||
blades and needle cards with nothing smoothing a single edge.
|
|
||||||
|
|
||||||
FXAA now, in the sharpen pass, because that pass already reads this pixel's neighbourhood
|
|
||||||
and runs last on the LDR image. It has no history, so it cannot drag or smear a reflection.
|
|
||||||
Measured on edge pixels: 25.5% less single-pixel staircase.
|
|
||||||
|
|
||||||
One trap worth recording. The unsharp mask's delta is computed from the RAW image and only
|
|
||||||
then applied to the anti-aliased colour. Taking the centre from the FXAA result and the
|
|
||||||
neighbours from the raw texture measures a difference that is half smoothing and half
|
|
||||||
signal, so the mask sharpens exactly the edges FXAA just softened - measured, that first
|
|
||||||
version was 29% WORSE than no anti-aliasing at all.
|
|
||||||
|
|
||||||
And depth of field, for the photo mode: a disc of taps whose radius is the pixel's circle
|
|
||||||
of confusion, signed so the two sides of the focal plane differ and normalised by the focus
|
|
||||||
distance, because a lens focused at two metres throws a background out far harder than one
|
|
||||||
focused at two hundred. A tap only counts if it is at least as out of focus as the pixel
|
|
||||||
it is blurring into, which is what keeps a sharp foreground from haloing into a blurred
|
|
||||||
background. It runs between the scene and the bloom so a blurred highlight still blooms,
|
|
||||||
and the whole pass is skipped when the aperture is shut - in ordinary play there is no lens
|
|
||||||
and this never draws.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**Actions and reducers.** `action PickUp { item: int }` is a typed record of something that
|
|
||||||
happened; `reducer Bag on PickUp(b: mut Bag, a: PickUp) { ... }`, in the module that owns the state,
|
|
||||||
says what it means for that one state - a reducer takes exactly its state and the action, and a
|
|
||||||
second state is refused; `dispatch PickUp { item: 7 }` queues one from anywhere. The queue is drained
|
|
||||||
at the end of every phase of the frame loop, after every phase of ludic.base's `core_tick_all`, and
|
|
||||||
where a program calls `drain_actions()`: in dispatch order, each action's reducers in the order of
|
|
||||||
their states' names, an action a reducer dispatches queued behind (a queue still growing after 64
|
|
||||||
rounds stops the program, naming the action). `ludic deps` reports `widest_function` - the most
|
|
||||||
states any function or entry point of the program takes - and `--check` ratchets it.
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
An aspen leaf hangs on a flattened stalk and turns in air a spruce never feels, and the
|
|
||||||
kit had no way to say so. `layer_flutter(l, v)` gives a scatter layer a per-leaf tremble:
|
|
||||||
the vertex stage offsets each leaf by a phase taken from its own place on the card, so
|
|
||||||
neighbouring leaves are never in step, and writes the result out as a varying the
|
|
||||||
fragment stage uses to flash the leaf's pale underside as it turns. The flash is the part
|
|
||||||
that reads - a still frame of a tremble is a still frame of nothing. One uniform, one
|
|
||||||
varying, no extra pass, and every other layer leaves it at zero.
|
|
||||||
|
|
||||||
Also fixes the sway itself, which was measured in METRES: `hgt * hgt * 0.35` is right for
|
|
||||||
a 40 cm flower and puts ten metres of sideways into a 14 m trunk, so every tall tree in
|
|
||||||
the valley stood bent over like a fishing rod. It is a fraction of the model's own height
|
|
||||||
now, so the tip moves a few per cent of the tree whatever the tree is and the base does
|
|
||||||
not move at all.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`ludic.anim` carries ozz-animation (0.17.0, MIT) as a native library**, the second package to do
|
|
||||||
so after `ludic.physics`. Its skeleton and each clip are built at LOAD from the numbers the package
|
|
||||||
already reads - a skin's parents and rest pose, a clip's flattened channels - so there is no bake
|
|
||||||
step and no new file. `anim_oz_skel(sk)`, `anim_oz_clip(c)`, `anim_oz_ctx(s)` and
|
|
||||||
`anim_oz_sample(ctx, clip, t, rot, pos, n)` are the first step (Maroon Lake's phase 19.1): a sampled
|
|
||||||
rotation agrees with `anim_mix` to within 1e-4 a component. `anim_play` is unchanged. The library is
|
|
||||||
built by `native/build.sh` from the pinned release, and on Windows it imports KERNEL32 alone.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`@Asset(kind, map)`: a path under each map's directory.** A field whose file lives in the map's own folder
|
|
||||||
(a grass kind's density picture, `ground/blades.png`) says so, and `ludicc --check` looks for it in every
|
|
||||||
map - a @PerMap row's in its map, a game-wide row's in all of them - refusing a map that lacks it unless the
|
|
||||||
field is `@Asset(kind, map, optional)`. The schema marks the attribute `"scope": "map"`.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**An attribute before `export` is kept.** `@ToClients export event E`, `@Sync export property P`,
|
|
||||||
`@Owned export model M` and the rest lost the attributes written in front of `export`: the parser read
|
|
||||||
them, then parsed the declaration afresh and forgot them - so an exported remote event was silently
|
|
||||||
local; and `export @ToClients event` was refused outright. Attributes and `export` now read in
|
|
||||||
either order into the same declaration.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**A bake's inputs can follow the data.** `bake_expand(inputs, map)` gives a Bakes row's inputs as they are
|
|
||||||
hashed: `{map}` put as the map's key, and each input with a `*` put as the paths it matches, sorted as whole
|
|
||||||
paths byte by byte, dot-names left out, a glob matching nothing gone. `bake_maps(first_input)` is the maps a
|
|
||||||
`{map}` row covers: each directory under assets/maps holding its first input (`bake_maps_in` under another
|
|
||||||
root). A runner hashes `bake_inputs_hash(bake_expand(row.inputs, map))`, the same path the check takes, so
|
|
||||||
the two cannot disagree on stale; a game's Python tools are its checked twin.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**Bakes you can look at, and three more of the renderer's textures read from one.** `ludic.lab` writes
|
|
||||||
raw 8-bit pixels (1 to 4 channels) as a PNG (`lab_png_write`, `png_write.ludic`, importable alone with its
|
|
||||||
own `LabPngState`) and turns float textures into honest previews (`png_convert.ludic`: R32F min..max as
|
|
||||||
grey, RG16F as red and green x255, HDR RGBA16F as x/(1+x) then sRGB). `ludic.render3d` takes three bakes
|
|
||||||
the game names (`bake_load.ludic`) and makes each as before when one is missing or stale: an impostor's
|
|
||||||
atlases as BC7 with their baked mips, through the compressed upload (`impostor_from_baked`,
|
|
||||||
`impostor_fill_bc7`; a fog opening past its cards reads the bake again instead of painting), the sky's image-based light at the start yaw per prefilter width (`sky_baked_in`; any other
|
|
||||||
turn of the sky is convolved), and the grass carpet (`carpet_from_baked`, `carpet_bytes`).
|
|
||||||
`impostor_from_bytes` returns null, keeping nothing, when the bytes are not the impostor's shape.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**Textures a game baked at build time are read before the PNG.** `png_decode` first takes
|
|
||||||
`assets/baked/png/<path>.tex` (a game's `ludic bake` output: the samples, ready to upload), and a cut-out
|
|
||||||
load (`tex_load_ex` with dilate) first takes `assets/baked/cutouts/<path>.bc7` (padded as `tex_dilate`
|
|
||||||
pads, then BC7 with its mips) - so a boot decodes, pads and converts nothing it can take ready-made. Both
|
|
||||||
are ludic.base's baked form, read by hand (baked_tex.ludic: the "LBAK" header, the key and version); a
|
|
||||||
missing or stale one falls back to the PNG as before. `tex_load_dds_at` reads a .dds at an offset.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`bind Purse { money: g_money }` - a port member bound to a variable.** A member that takes nothing
|
|
||||||
may name a global instead of a function; the compiler writes the getter in the bind's file, so the
|
|
||||||
one-line wrapper is gone. A member that takes something is refused a variable.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**`ludic build` keeps its LLVM IR out of the project.** The intermediate `.ll` was written beside
|
|
||||||
the binary (`build/<name>.ll`) and deleted after linking, so a project's tree held one for the
|
|
||||||
length of every build, and two builds at once deleted each other's - which surfaced as a
|
|
||||||
`clang: no such file` that read exactly like a compile error. It now goes to the run's own
|
|
||||||
temporary directory and goes with it. `--save-temps` still keeps it at `build/<name>.ll`.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**A function named like a built-in a call always takes is refused.** `function words(st, k)` compiled,
|
|
||||||
and every call to it became the built-in `words(n)` - n zeroed ints, with a pointer for n - and LLVM
|
|
||||||
refused the IR far from the cause. A top-level function whose name a call always takes as the
|
|
||||||
compiler's own (`words`, `keep`, `print`, `save`, `load`, `key`, ...; the table is
|
|
||||||
`selfhost/check/check_builtins.ludic`, held to `emit_call` by `ludic-dev syntax --check`) is now an
|
|
||||||
error at its declaration, as `run` already was. Every other built-in (`buffer`, `floats`, `double`,
|
|
||||||
...) yields to a function the program declares, in the checker as it already did in codegen.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`ludic build --check` / `ludicc --check`: check without building.** The parse, the type checker
|
|
||||||
and the module rules (`export`, `uses`, layers, ports, registries) run, and nothing is emitted or
|
|
||||||
linked - about three seconds on Maroon Lake where a build takes about a minute. In this mode the
|
|
||||||
checker asks the module rules at each reference it resolves, since the emitter that usually asks
|
|
||||||
them does not run.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**Check an unsaved buffer.** `ludic build --check --diagnostics=json --stdin-file <path>` (and
|
|
||||||
`ludicc --check --stdin-file <path>`) checks the program as usual, but wherever the compiler would open
|
|
||||||
`<path>` - the entry, an import reached through a barrel, a component's `.xml` / `.lss`, an `.lres` -
|
|
||||||
it reads the text on stdin instead, so an editor's diagnostics follow typing without a save. Paths are
|
|
||||||
matched after normalising both (separators, relative to the working directory, `.` / `..` folded).
|
|
||||||
Diagnostics carry the file's usual name with lines and columns in the buffer; a `<path>` the program
|
|
||||||
never opens is reported as one warning.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**A chunked table's keys are unique across its map, and not interned.** A row's id is `(map, key)`, so a
|
|
||||||
tree moved into another chunk keeps it, and `ludicc --check` refuses a key written in two of a map's
|
|
||||||
chunk files, naming both. The keys are no longer interned: interning every key a player walked past would
|
|
||||||
have filled the bounded intern table and kept them all for good. A chunk slot keeps its keys in its own
|
|
||||||
buffers, rewritten in place when the slot is refilled; `intern(row.key)` keeps one past `_out`.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: feat
|
|
||||||
Things sit ON the ground rather than hovering over it. The screen-space GI pass takes
|
|
||||||
a second, much tighter set of taps (a 0.40 m radius that grows with distance, with a
|
|
||||||
range check so a far surface behind a near one cannot darken it) and folds the result
|
|
||||||
into the ambient occlusion it already had. The wide radius answers "how enclosed is
|
|
||||||
this", which a trunk meeting grass barely registers; the tight one answers "is
|
|
||||||
something touching here", which is the shadow the eye looks for to place an object.
|
|
||||||
It is eight taps on a buffer the pass had already bound.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**Namespaces are declared in Ludic.** `alias meth(labels) = target` in a `namespace` block makes
|
|
||||||
`Ns.meth(...)` a call to `target`, taking named arguments by those labels; with no label list the
|
|
||||||
target's own parameter names are the labels. The engine's 41 table-driven namespaces - `Http`,
|
|
||||||
`Udp`, `Process`, `Json`, `Value`, `Screen`, `Input`, `Audio`, `World`, `Tiled` and the rest, 438
|
|
||||||
methods - moved out of the compiler into `runtime/native/namespaces.ludic`, and a package owns an
|
|
||||||
API the same way. The code a program compiles to is unchanged byte for byte, and the checker now
|
|
||||||
checks an alias call's arguments against its target.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`def Recipes from "recipes.lres"` - a game fills a package's open registry from its own resource
|
|
||||||
file.** The entries are checked against the registry's record as the file is read, with errors at
|
|
||||||
the resource file's line, and they are defs of the module that wrote the line: the registry must be
|
|
||||||
open to it, and they sit in the stable order (the declaring module's entries, then other modules'
|
|
||||||
by name, and file order within a file).
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**Default parameters, components, views and templates.** A parameter can have a default (`pad: float = 8.0`).
|
|
||||||
A call leaves out what it does not change, and may pass its first arguments by position and the
|
|
||||||
rest by name.
|
|
||||||
|
|
||||||
A `view` declaration is the one bridge between a program and its UI. It names the fields a
|
|
||||||
template may read, the functions it may ask and the `on` events it may send, and it writes
|
|
||||||
`view_<name>() -> UiView`.
|
|
||||||
|
|
||||||
A `component Name { prop, state, fields, functions, on events }` declaration beside `Name.xml` and
|
|
||||||
`Name.lss` is a UI component. Its template and styles are compiled in (with `@import` inlined), each
|
|
||||||
mounted instance keeps its own props and state, styles are scoped to it, and a parent's `class`,
|
|
||||||
`style` and `id` land on its root.
|
|
||||||
|
|
||||||
`ludic.ui` is now a template runtime. Screens and components are XML files loaded at run time,
|
|
||||||
with:
|
|
||||||
- `{expression}` bindings;
|
|
||||||
- `<if>`, `<else>` and `<each>`;
|
|
||||||
- props, `<slot/>` and per-instance `<state>`;
|
|
||||||
- `on-press` actions that send events, `set` state or `emit` to the component's user;
|
|
||||||
- component libraries (`export="true"`, `<import src as>`);
|
|
||||||
- HTML's elements (`div`, `p`, `h1`-`h6`, `ul`/`li`, `img`, `hr`, ...), with a default stylesheet;
|
|
||||||
- HTML's attributes: `id`, `class`, `style`, `hidden`, `disabled` and `onclick`, with any other
|
|
||||||
attribute kept for selectors;
|
|
||||||
- the CSS box model (padding and margin in 1-4 values, borders, `px` and `%`) and flex layout
|
|
||||||
(`flex-grow`, `justify-content`, `align-items`/`align-self`, `flex-wrap`, min and max sizes)
|
|
||||||
under CSS's property names;
|
|
||||||
- stylesheets, in a `<style>` or an `.lss` file (a Ludic StyleSheet) that others import and that
|
|
||||||
can `@import` more;
|
|
||||||
- CSS's selectors: `#id`, compound classes, `[attr=value]`, descendant and `>` combinators,
|
|
||||||
`:hover`, `:disabled`, `:first-child`, `:last-child`, `:nth-child`, `:not` and more, weighed by
|
|
||||||
specificity.
|
|
||||||
|
|
||||||
- more CSS: custom properties and `var()`, `position` with insets and `z-index`, `em`/`rem`/`vw`/`vh`,
|
|
||||||
`@media`, wrapping text and ellipsis, `overflow`, `+`/`~`, `:nth-child(an+b)`, `:checked`, `:active`;
|
|
||||||
- more React: keyed lists, `<let>`, `<provide>` context, `<fragment>`, named slots, `on-mount` and
|
|
||||||
`on-unmount`;
|
|
||||||
- native elements a program draws itself (`ui_native`, `ui_fire`), and form controls;
|
|
||||||
- errors with file and line, hot reload (`ui_reload`), and an inspector-style dump.
|
|
||||||
|
|
||||||
The runtime is a UI framework, not only a template engine:
|
|
||||||
- it takes input itself: focus and keyboard navigation, the pointer, scroll boxes, `autofocus`;
|
|
||||||
- it has built-in controls (button, checkbox, radio, range, select, text, key), styled as CSS
|
|
||||||
parts;
|
|
||||||
- `ludic.ui/render3d.ludic` is a render3d backend, with textures, atlases, nine-slices, clipping
|
|
||||||
and scale;
|
|
||||||
- more CSS: `rgba()`/`#rrggbbaa`, `border-radius`, `outline`, `box-shadow`, `background-image`,
|
|
||||||
`border-image`, group `opacity`, `@keyframes` / `animation` / `transition`;
|
|
||||||
- HTML mixed content, and boolean attributes;
|
|
||||||
- `popover` (a top layer that keeps the pointer and keys, with light dismissal), `title` tooltips,
|
|
||||||
and `<progress>` / `<meter>`;
|
|
||||||
- importing `ludic.ui/render3d.ludic` installs the backend, and atlases take rows;
|
|
||||||
- hooks for the program's language, sounds and clock.
|
|
||||||
|
|
||||||
What a game's screens found missing, now in `ludic.ui`:
|
|
||||||
- `<input type="number" min max step>`: typed digits, Enter or leaving it commits them clamped, the
|
|
||||||
arrows step it;
|
|
||||||
- `<input type="key">` listens for any key (Tab and the arrows included) once Enter or a click starts
|
|
||||||
it; Esc stops it, Backspace clears it, `shown` names the value, and `ui_capturing()` tells the host;
|
|
||||||
- `note="..."` under any control's label (`.ui-note`); a range's `decimals`, `format="percent"` and
|
|
||||||
`unit`; a track laid out as a row, with the range's fill as tall as it;
|
|
||||||
- popovers anchored beside an element (`anchor="id"`, or a bare `anchor` for the element before it,
|
|
||||||
`placement`), flipped to the other side and kept on the screen;
|
|
||||||
- `flex-shrink` (a scroll box in a column takes the room its siblings leave), `flex: grow shrink`,
|
|
||||||
`order`, and text in a row wrapping in the room its siblings leave;
|
|
||||||
- `calc()` over px, %, em, rem, vw, vh and `var()`; `width: 0` and `height: 0` mean 0;
|
|
||||||
- `text-shadow`; tooltips of several lines; `ui_opacity()` for a native's draw;
|
|
||||||
- `border-image` drawn as painted with no background colour, tinted by one, and not at all under
|
|
||||||
`transparent`; a picture file drawn untinted (an atlas cell still takes `color`);
|
|
||||||
- the render3d backend loads a picture again when its file changes (`ui_image_reload`), draws a path
|
|
||||||
with a drive letter as a path, and slices a nine-slice by its texture's own width and height;
|
|
||||||
- a component root that is itself a component takes every user's class, style and id, and the
|
|
||||||
sheets that style it are weighed together by specificity;
|
|
||||||
- a component's event may be called `set`; a `string` prop given a number reads it as text; two
|
|
||||||
components of one name are an error naming both files;
|
|
||||||
- the scrollbar is `.ui-scrollbar` and `.ui-thumb`: a press on the thumb holds it where it was taken,
|
|
||||||
a press on the track jumps the thumb's middle there, and neither presses what is under the bar;
|
|
||||||
- a popover's own controls take its presses whatever lies under it, a press outside only closes it,
|
|
||||||
and while one is up the scroll boxes outside it do not take the pointer;
|
|
||||||
- the first gamepad moves the focus (d-pad, left stick), steps ranges and selects, and presses (A)
|
|
||||||
and goes back (B); a held direction, on the pad or the arrow keys, repeats after 0.42 s and then
|
|
||||||
every 0.11 s on the ui clock (`UiInput.held_*`, `pad_a`, `pad_b` for a host);
|
|
||||||
- pointer events: `on-pointerdown` / `pointermove` / `pointerup` / `drag` / `wheel` with `event.x`,
|
|
||||||
`y`, `dx`, `dy`, `button` and `wheel`, and `ui_native_input(tag, fn)` for a native; a press captures
|
|
||||||
the pointer until release; the pointer hits the topmost element in painting order, and
|
|
||||||
`pointer-events: none` lets it through;
|
|
||||||
- `on-down` and `on-up` on a button (the pointer, Enter or A), with `:active` true while it is held
|
|
||||||
there rather than whenever the pointer is down over it;
|
|
||||||
- an anchored popover's `align="start|center|end"`, and `within="id"` (by default the nearest
|
|
||||||
scroll box around it) for the bounds it is flipped against and kept inside;
|
|
||||||
- `text-fit: shrink MIN` shrinks a line to its box, then cuts it with an ellipsis; `line-height`;
|
|
||||||
an `em` reads the font size the element ends with (a `font-size` later in the rule, or in a later
|
|
||||||
rule), not the one it had so far;
|
|
||||||
- `min()`, `max()` and `clamp()`, in `calc()` or on their own; `top` / `right` / `bottom` / `left`
|
|
||||||
as a percentage or a `calc()` of one, of the containing block;
|
|
||||||
- a nine-slice's corners are clamped to half the box in each direction on its own and cut on whole
|
|
||||||
pixels (`ui_nine_cuts`), so a small key cap has no seam;
|
|
||||||
- `scroll-top="{px}"` holds a scroll box at an offset, with `on-scroll` when the player moves it;
|
|
||||||
`ui_scroll_set(id, px)` moves one once;
|
|
||||||
- `linear-gradient(...)` backgrounds; `aspect-ratio`; `object-fit` for pictures (the renderer's
|
|
||||||
`image_w` / `image_h`) and `ui_object_fit` for natives;
|
|
||||||
- `translate="no"` keeps an element's text as written; a title of several lines is translated whole,
|
|
||||||
else line by line;
|
|
||||||
- `<input type="key">` takes a mouse button (`UI_MOUSE_LEFT` / `RIGHT` / `MIDDLE`, 256-258) and is
|
|
||||||
`:capturing` while it listens;
|
|
||||||
- a `title` shows for the keyboard's focus too, after the same half second; a focus ring drawn
|
|
||||||
through a renderer with no `rect` no longer crashes;
|
|
||||||
- a component with no stylesheet of its own reads a theme's `:root` variables from around it (it
|
|
||||||
did; now a test says so);
|
|
||||||
- `ui_scale()` and `ui_box("id")`, the scale and an element's laid-out box, for a host;
|
|
||||||
- `on-submit` on a text field (Enter or A; the focus and text stay unless `clear-on-submit`);
|
|
||||||
- `zoom` on any element, and a length over a length in `calc()` is a plain number;
|
|
||||||
- `on-hold` every frame a button is held, with `event.dt` and `event.t`;
|
|
||||||
- a transition lands exactly on its end value (it had stopped a rounding error short of it, at every
|
|
||||||
frame rate).
|
|
||||||
|
|
||||||
render3d gains `tex_width` / `tex_height`, and the XML reader keeps text runs among elements in
|
|
||||||
order (`mixed`).
|
|
||||||
|
|
||||||
A `view` field set to a literal or a named function's result needs no type.
|
|
||||||
|
|
||||||
Screens are drawn through a registered renderer. `Value` gains a float kind.
|
|
||||||
|
|
||||||
Also:
|
|
||||||
- A program's function named like one of the runtime's is refused; it had been silently taking the
|
|
||||||
runtime's own calls. So is one named like a compiler built-in (`run`, `exit`, `free`, `fill`,
|
|
||||||
...): every call to a program's own `run` compiled into C's `system()`, and clang failed on the IR.
|
|
||||||
- An index is evaluated before the slice's elements are read. A `xs[f()]` whose `f` grew `xs`
|
|
||||||
read stale memory.
|
|
||||||
- A runtime error names the file its expression is in, not the program's.
|
|
||||||
|
|
||||||
Two declarations with one name (a package's private global and a program's, say) are reported as
|
|
||||||
such before type checking. They used to surface as a page of type errors about the wrong type.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: feature
|
|
||||||
**`ludic deps --writes` warns about a write through a local alias.** `let t = thing_cur` and then
|
|
||||||
`t.used = 1` writes another module's record just as `thing_cur.used = 1` does; a local bound straight
|
|
||||||
from another module's global (or from such a local) is now followed within its function and each
|
|
||||||
write through it listed as a warning. A reference that arrives from a function's result is not.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**`ludic deps`: a reach counts every state apart, however the states are numbered.** The reach and
|
|
||||||
write-reach bitsets packed 60 states to a word, but an `int` is 32 bits, so `1 << 45` came back as bit
|
|
||||||
13 and states 32 apart shared a bit: a function taking both counted one, fewer than it takes, and the
|
|
||||||
counts (`widest_reach`, `widest_write_reach`, `--reach`, `--wreach`) rose and fell with how a program's
|
|
||||||
states happened to be numbered. The sets now hold 30 to a word. On Maroon Lake `widest_reach` goes
|
|
||||||
58 -> 76 and `widest_write_reach` 54 -> 64 - the real numbers, which the old count hid.
|
|
||||||
`examples/state/reach_wide.ludic` (40 states, S00 and S32 taken together) holds it.
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**`ludic deps` sees through fn values, and lists the widest functions.** A step list or a registry of
|
|
||||||
fn values takes no state and still reaches every state its steps take; `widest_reach` is the most
|
|
||||||
states any function can come to - by a call, a `fn f` it writes, or a global holding fn values it
|
|
||||||
reads - reported beside `widest_function` with how many of them it does not take itself
|
|
||||||
(`the widest reach: app_boot (src/app/boot.ludic:30), 72 states (72 through calls and fn values it
|
|
||||||
does not take)`). `--widest N` lists the N functions that take the most states with what each
|
|
||||||
reaches; `--reach N` orders them by reach. A baseline written before this has no `widest_reach` and
|
|
||||||
does not hold it until it is rewritten.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**`ludic deps` says what a function can come to CHANGE** (`widest_write_reach`, and `--wreach N`
|
|
||||||
lists the functions by it): the states it reaches as `mut`, through calls, `fn` values and step
|
|
||||||
lists. Reach itself is sharper: `Port.member()` reaches that member's binding only, and
|
|
||||||
`Registry[i].field` (or a local holding `Registry[i]`) reaches that field only - a question asked of
|
|
||||||
a port or a table that also holds verbs no longer reaches the verbs.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`ludic.devlink`: the interface's verbs.** A `DevlinkUi` port, every member defaulting to "not offered":
|
|
||||||
`ui_screen` (the screen's root class and its components), `ui_model "<Class>" "<out>"` and `ui_tree "<out>"`
|
|
||||||
(the game writes a component's model or the whole tree to a file, no `..`, and the answer names it, so a
|
|
||||||
datagram stays small) and `ui_override "<path>" "<file>"` (a template or stylesheet read from another file
|
|
||||||
and reloaded keeping state; `""` clears one, `"" ""` all). Answered at once; nothing made per frame.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`ludic.devlink`: an editor's live link into a running dev build** (protocol v1, frozen with Ludic
|
|
||||||
Studio). Loopback UDP through the `DevlinkNet` port, one request a frame parsed in place from one fixed
|
|
||||||
8 KB buffer and answered into another; every verb a `DevlinkWorld` member defaulting to "not offered":
|
|
||||||
`ping`, `hello` (the build's schema hash as 16 hex digits), `cam_get` / `cam_set` / `cam_release`, `goto`,
|
|
||||||
`map_load`, `time`, `weather`, `shot`, `pause` / `resume` / `step`, the slow three answered later by id.
|
|
||||||
A socket is opened only when `enabled()` says so (a game binds `dev_tools`), a sender off this machine is
|
|
||||||
dropped, and a connected co-op session refuses everything but `ping` and `hello`.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: performance
|
|
||||||
**Cut-out edge padding runs on every core.** `tex_dilate`'s passes hand their rows, sixteen at a time, to
|
|
||||||
`Job.parallel_for`: within a pass a row writes only its own still-masked texels and reads only
|
|
||||||
neighbours the mask already let go, so the bytes are the ones the single-threaded loop made. The worker
|
|
||||||
is handed plain buffers in a `DilateJob` and makes nothing. `tex_dilate_bytes` is the slice-taking
|
|
||||||
form (safe_api.ludic), and `examples/rendering/dilate.ludic` holds the result against the old loop
|
|
||||||
(prints DILATE OK). It was 206 ms of the main thread in a Maroon Lake boot.
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**A dispatched action no longer allocates a record each time.** `dispatch A { ... }` made a fresh
|
|
||||||
record for the queue, and Ludic frees nothing, so a system dispatching every frame (an input's
|
|
||||||
`Move`, a frame's time) grew the program by a record a frame. The queue now keeps a list per
|
|
||||||
action: a dispatch takes the next one (making one only when all are queued), fills every field
|
|
||||||
as `new` would - given, or its default - and `drain_actions()` hands them all back once the queue
|
|
||||||
is empty. A reducer reads its action only during the drain, so nothing sees a record after it is
|
|
||||||
reused; keep what must last in the state, not the action. `ludic.base`'s `actions_test` holds a
|
|
||||||
reused record getting its defaults back.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**A name is defined once, for every kind of declaration.** Two functions with one name were
|
|
||||||
already an error; two `var`s or `const`s (or an enum and a const), or two `property` / `event`
|
|
||||||
records, kept the first definition silently. A game lost months to it: two files both said
|
|
||||||
`KEY_LEFT`, one meaning an arrow key's code and one a binding slot, and the menus read the slot.
|
|
||||||
They are now an error that names both files and lines. `examples/rejected/` holds the two cases,
|
|
||||||
checked by a new `reject_case` in the test runner (an example the compiler must refuse).
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**The built-in ECS grows.** Every component was a fixed array of 1024 slots, so a game past 1024
|
|
||||||
entities could not have them (and until the last release silently corrupted memory trying). The
|
|
||||||
per-entity stores are heap blocks now, doubled by `L_grow` as entities outgrow them, the new slots
|
|
||||||
zero: 100 000 entities spawn and query. `Prop.has` bounds against the live capacity and
|
|
||||||
`Pool.capacity` answers it. A snapshot (`save`/`load`, `world_save`/`world_load`) records its slot
|
|
||||||
count first and a load grows to it before reading the stores back, so a snapshot's size follows the
|
|
||||||
world's instead of a fixed 1024. A mod's registered components grow with the rest.
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**A schema for editors, and every error as JSON.** `ludicc --emit-schema out.json` (and `ludic
|
|
||||||
schema [file] [-o FILE]`) writes what the compiler resolved once the program type-checks: every
|
|
||||||
record with its fields' types, defaults, doc comments and places; every registry with its record,
|
|
||||||
prefix, resource file, openness and its entries in their final order after the open-registry merge
|
|
||||||
(key, constant, index, file:line:col of the entry and of each field value, and which file brought
|
|
||||||
which entries in); every const; and every function a `fn` value can name, with the `fn_type` a field sees (its states stripped).
|
|
||||||
Deterministic, `"schema_version": 1`. Fields and registries carry editor attributes on the existing
|
|
||||||
`@` syntax - `@Ref(Registry)`, `@OneOf(PREFIX_)`, `@Range(lo, hi)`, `@Unit("m/s")`, `@Asset("gltf")`,
|
|
||||||
`@Color`, `@Node(field)`, `@Clip(field)`, `@Material(field)`, `@Tint(SLOT)`, `@Derived`, `@Text`, `@Multiline`, `@Key`, and `@AppendOnly` / `@ByKey` on
|
|
||||||
a registry - which change nothing but go into the schema; `@Ref` naming no registry is an error, and
|
|
||||||
so is `@Node` / `@Clip` naming a field that is not a glTF (`@Asset("gltf")`, or an `@Ref` to one), and
|
|
||||||
a listing `@OneOf` (`@OneOf(A, B)`, not a prefix `@OneOf(P_)`) naming a constant that does not exist, and `@Tint` naming no constant. A field may now carry several attributes. `ludicc --check
|
|
||||||
--diagnostics=json` (`ludic build --check --diagnostics=json`) prints every error as one JSON array
|
|
||||||
of `{file, line, col, severity, message}` on stdout; tokens and nodes now know their column.
|
|
||||||
|
|
||||||
`Build.schema_hash()` answers FNV-1a 64 of the program's own schema (the bytes `ludic schema` prints),
|
|
||||||
computed only when a program names it, and 0 under `ludicc --release`, which `ludic bundle` now passes.
|
|
||||||
|
|
||||||
A target no part of the program declares (an `@Ref` registry, an `@Tint` or listed `@OneOf` constant) is
|
|
||||||
a warning and `"unresolved": true` in the schema, so a package can name the game's registry; a name of
|
|
||||||
another kind is an error. `@OneOf` on a string field takes words, and every registry row's value is
|
|
||||||
checked against them.
|
|
||||||
|
|
||||||
The schema has a `components` list: each UI component's module, place, doc, template and stylesheet
|
|
||||||
paths, its `props` and `state` (type, default as written, place, doc), `states_read` (the states its
|
|
||||||
header names, apart from its model), `derived` fields with their types, and the `functions` and
|
|
||||||
`events` its template calls with their parameters (states and instance stripped), and the
|
|
||||||
registered native tags its template uses. A `natives` list gives every `ui_native` /
|
|
||||||
`ui_native_input` call with a literal tag: the tag, the function called, its handler and its place.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**A function named like an engine namespace method's target is refused where that method is
|
|
||||||
called.** `Random.range` is `rng_range`, so a package's own `rng_range(a, b, c)` silently took
|
|
||||||
every `Random.range(1, 6)` (and the checker then asked for its third argument). It is now an error
|
|
||||||
naming the function, the namespace method and the call.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**`expect_eq` on strings compares their text.** It lowered to an integer compare of two pointers,
|
|
||||||
which the IR refused; now two strings with the same text are equal (a null only to a null), and a
|
|
||||||
failure prints both: `expect_eq failed (got "camp", want "lake")`.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**`expect_eq` and `expect_near` take floats.** On a float or a double they compared with an integer
|
|
||||||
instruction, and the build failed in clang ("defined with type 'float' but expected 'i32'"); they
|
|
||||||
compare as floats now (the wider kind of the two) and a failure prints the numbers.
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**`ludic fmt` for editors.** `ludic fmt --lint --json` prints the violations `--lint` reports as one JSON
|
|
||||||
array on stdout, `[{"file", "line", "col", "rule", "message"}]` ordered by file, line and column (the
|
|
||||||
summary on stderr, `--lint`'s exit status, and the baseline never rewritten). `ludic fmt -` formats
|
|
||||||
stdin to stdout under the project found from the working directory (the nearest `package.ludic`
|
|
||||||
upwards), and refuses a buffer that does not read as Ludic - an open string, a bracket never closed or
|
|
||||||
closed by the wrong one - with exit 2 and `<name>:<line>:<col>: error: ...` on stderr.
|
|
||||||
`--stdin-name <path>` makes the buffer that file: the project is found from its directory, and
|
|
||||||
`ludic fmt - --lint --json --stdin-name <path>` judges it against that file's baseline and `lint
|
|
||||||
paths`, reporting it under the name given. Hooks around `fmt` and `get` read nothing from stdin and
|
|
||||||
write to stderr when the command's stdout is a program's (`--json`, `-`).
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`friend module lab of fishing, data` - a friend of some modules, not all.** A scoped friend sees
|
|
||||||
the private names of the modules it names and only the exports of every other; `friend module lab`
|
|
||||||
alone still sees everything.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**Functions are values (L2).** `fn(int, float) -> bool` is a type, `fn name` is any top-level
|
|
||||||
function's value (it used to be only a thread worker's address), and a call through a local, a
|
|
||||||
global, a record field, a slice element, a parameter or a result of a function type is an
|
|
||||||
indirect call. Two different function types do not mix, a call through one checks its argument
|
|
||||||
count, and a value may be `null`. A registry can hold behaviour and a package can take
|
|
||||||
callbacks. `Job.parallel_for` still checks that its worker takes (int, pointer) and returns
|
|
||||||
nothing. `ludic-dev selfhost-build` now says why it failed instead of exiting 1 silently.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: feature
|
|
||||||
**The blades' density window can be filled without a GPU.** grass_density.ludic's gb_frame is split: gb_window_fill
|
|
||||||
fills the GB_TILES x GB_TILES window of density tiles round the camera's (zeros off the map) and sets its corner,
|
|
||||||
and gb_frame sends it. The same bytes as before; a test reads gb_win after gb_window_fill.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**Generic records and functions.** `property Pool<T> { items: []T }`, `function first<T>(xs: []T)
|
|
||||||
-> T` and `function map<T, U>(xs: []T, f: fn(T) -> U) -> []U`; a type writes an instance as
|
|
||||||
`Pool<Thing>`, nested as deep as needed. A call's type arguments come from its arguments, or from
|
|
||||||
the declared type its result is written into, and are refused with the parameter named when
|
|
||||||
neither says. Each instance is compiled once as an ordinary record or function. `ludic-fmt` keeps
|
|
||||||
`Pool<Thing>` together while still spacing `a < b`.
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
**`ludic.render3d`: painted ground layers grow solid things, with ids, and the trample is data.**
|
|
||||||
`ground_fill`'s candidate is its own function, `ground_candidate` (pure `gf_*` steps with the density read
|
|
||||||
between them, into a caller-held `GroundCand`), and `ground_fill` draws exactly its answers - the same
|
|
||||||
operations in the same order as before, so every cover layer grows bit for bit what it did. A layer with
|
|
||||||
`solid: true` is filled at `step0` with band 0's hashes whatever the camera, a far band drawing a stable
|
|
||||||
subset; each thing has an int id from (layer, chunk, cell) (`ground_solid_id`), and `ground_solid_list` /
|
|
||||||
`ground_solid_at` answer a chunk's things or one by id for physics, the nav bake and saves.
|
|
||||||
`r3d_ground_clearing(x, z, r_in, r_out, floor)` hands the trample over as discs the editor can see, beside
|
|
||||||
the `r3d_on_ground_trample` callback, which still works. `tests/ground_fill_test.ludic` holds all of it to
|
|
||||||
`tests/ground_fill_golden.json` (written by `tests/gen/ground_fill_golden.ludic`), the file the studio's
|
|
||||||
TypeScript generator is tested against.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**`Http.text` and `Http.header` return copies.** They handed back the handle's own buffer (and on macOS the
|
|
||||||
response object's string), which `Http.free` then released: a text read before the free and used after it
|
|
||||||
was garbage or empty - maroon-lake's map list wrote a 0-byte maps.json. Each call now returns a string that
|
|
||||||
is the caller's to keep. Read a body once per response.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**English left is an error (phase 26.9).** Under a `lang` line, a template's own words, a text
|
|
||||||
attribute's, a quoted choice that reads as words and a `@Text` row still holding English now refuse
|
|
||||||
the build, where they were warnings; `ludic deps` still counts them as `english_left`. Hole counts and
|
|
||||||
undescribed splits stay warnings. ludic.ui's own words - the key field's "Right click", "Middle
|
|
||||||
click", "Left click" and "press a key..." - are keys (`ui_tk(ui_st, k"ui.right_click", plain)`,
|
|
||||||
`ui.*` in the program's `.po`), with their plain English for a program that binds no translator.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**Text keys below a row, padding, and `tr`'s cast.** A `@Text Key` in a record nested in a registry
|
|
||||||
row (and in each item of a list of them) is filled with its derived key, `<registry>.<row>.<field>.<i>.<field>`,
|
|
||||||
as a top-level one is, and a `@Text []Key` a row leaves out takes `<...>.0`, `.1`, ... for as many as
|
|
||||||
the source `.po` has - so no `.lres` spells a key. `field: null` is no text. `trf` / `trn`'s trailing
|
|
||||||
`""` arguments are padding and not counted against the English's holes. And `string(x)` of a string or
|
|
||||||
a `Key` is no allocation to the escape analysis: it is `x` itself, so a `tr(key)` that returns it
|
|
||||||
passes `arena strict` (a template's lone hole still copies).
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: change
|
|
||||||
**ludic.i18n draws plain text as it is: the English path is gone (phase 26.9).** `L` makes a key, a
|
|
||||||
key glued into text, or a line bracketed inside another; anything else - a player's name, a chat
|
|
||||||
line, a number - is drawn as it is, in every language, so a player named "Settings" stays
|
|
||||||
"Settings". Removed with it: the lookup of English words (exact lines, patterns with holes, a
|
|
||||||
paragraph a sentence at a time, padding), `Ln` (use `trn(kn"...")`), `i18n_pattern_count`, and an
|
|
||||||
English argument's own lookup inside a key's hole. A language `.po` is read for its keys and
|
|
||||||
plurals only. A game still on English msgids draws them untranslated until they are keys.
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`ludic.i18n`: keys (phase 26).** A key names what a text is for, and `en.po` says it in English like any
|
|
||||||
other language. A key is a string with a marker byte (`I18N_KEY`, `I18N_PLURAL`), its arguments after
|
|
||||||
byte 31, so the code that makes text never takes `I18nState`: `tr(k)`, `trf(k, a, b, c, d)` and
|
|
||||||
`trn(k, n, a, b, c)` build it, and `L` makes it into text where it is drawn - the language in use, else
|
|
||||||
en.po (read the first time a key is asked for), else the key itself, `[[key]]` in a developer's build
|
|
||||||
(`i18n_loud`). Holes take their arguments in the language's order, a key argument made first; plural
|
|
||||||
keys go by each language's rule. A string with no marker takes the old English path, so a game can
|
|
||||||
move over a file at a time.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**A key used only in a listener counts as used.** The key check walked the program's declarations but
|
|
||||||
not the bodies kept beside them - `@On` listeners, the lifecycle hooks, tests, computed fields and
|
|
||||||
scenes - so a key used only there was never checked against en.po and `ludic schema` listed it
|
|
||||||
unused. They are walked now, as every other pass walks them.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**Inflate in a caller-owned context.** `ZInflate` holds everything one inflate works in - the bit reader,
|
|
||||||
the RFC's length and distance tables, and every Huffman table and scratch list a block builds - made once
|
|
||||||
by `z_inflate_new()` and rebuilt in place, so an inflate allocates nothing and each thread that inflates
|
|
||||||
holds a context of its own. `z_inflate_in(z, src, len, out, cap)`, `z_uncompress_in` (zlib) and
|
|
||||||
`z_gunzip_in` (gzip) take the context; `z_inflate` / `z_uncompress` / `z_gunzip` keep their signatures,
|
|
||||||
working in the one context `RtInflateState` holds, so no caller changes.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**The most negative int is emitted as itself.** The compiler's `itoa` negated a negative value before
|
|
||||||
taking its digits, and `-(-2147483648)` overflows back to itself, so a literal such as `0x80000000` in an
|
|
||||||
int (`b & 0x80000000`) was written into the IR as a bare `-` and the build failed in LLVM. The digits are
|
|
||||||
now taken off the value as it is. Needs a reseed to reach `bin/ludicc`.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**`Json.parse` decodes an escaped string in one pass.** A string with escapes in it was joined a character
|
|
||||||
at a time, and every shorter copy was kept: a long escaped text took gigabytes (ui-preview's story model
|
|
||||||
reached 6.5 GB in three seconds). It is now decoded into one buffer sized from the text. ui-preview's
|
|
||||||
protocol-v1.md states how an `@import` path is resolved and which key a `file` override answers to.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: fix
|
|
||||||
**`Json.parse` decodes `\uXXXX`.** An escaped code point is UTF-8 now - a surrogate pair joined into one, a
|
|
||||||
lone surrogate or bad hex as U+FFFD - where the backslash was dropped and the hex kept as text ("iu015f"),
|
|
||||||
and `\t`, `\r`, `\b` and `\f` are the characters they name. Python's `json.dump` writes non-ASCII this way.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feature
|
|
||||||
**`Json.write_file(value, path)` saves without keeping anything.** It writes the text
|
|
||||||
`Json.encode` would give straight to the file (through `path.tmp` and a rename), built in a buffer
|
|
||||||
the runtime keeps, so a game that saves often holds nothing more for it. `Json.encode` itself is
|
|
||||||
built the same way and makes only the string it returns: it used to join its text with `+`, keeping
|
|
||||||
every piece and every number's text. `examples/lang/json_saves.ludic`: 1000 saves, 0 bytes.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
bump: patch
|
|
||||||
type: feature
|
|
||||||
**ludic.lab: 16-bit PNGs** — `lab_png_write16_from(st, path, w, h, channels, px, at)` writes 16-bit samples (two bytes each, most significant first) the way `lab_png_write_from` writes 8-bit ones, so a test can write a height map render3d's decoder reads back as R16; `lp_png_bytes(stride, h)` is the size of one.
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
bump: minor
|
|
||||||
type: feat
|
|
||||||
Three things a crown and a hand light were getting wrong.
|
|
||||||
|
|
||||||
**An aspen's trunk quaked with its leaves.** `layer_flutter`'s mask was the vertex's
|
|
||||||
HEIGHT alone — anything more than 1.2 m up trembled — so on a ten metre aspen the whole
|
|
||||||
bole moved, and a tree that is supposed to shiver read as a column of cloth. What
|
|
||||||
separates a leaf from a bole is not height, it is distance from the model's own centre
|
|
||||||
line: the leaves are out at the edge of the crown and the trunk is on the axis. The mask
|
|
||||||
is radial now, and small plants keep the old one, because a flower is all leaf.
|
|
||||||
|
|
||||||
**Foliage in the camera's face.** A third-person camera walks into a crown and the branch
|
|
||||||
between it and the body is an opaque wall a metre from the lens; in first person it is the
|
|
||||||
whole screen. `#define NEAR_FADE` dithers tree foliage out inside arm's length of the
|
|
||||||
camera — in the depth PREPASS, so the lit pass never sees those pixels either and the
|
|
||||||
equal-depth optimisation is untouched. `gl_Position.w` is the view depth for a perspective
|
|
||||||
projection, so it costs one varying and no uniform. Blades, cards and flowers are left
|
|
||||||
alone on purpose: they live at the player's feet, they are always that close, and fading
|
|
||||||
them opens a hole in the meadow. Nor is the BOLE ever faded: a trunk and its needle cards
|
|
||||||
are one mesh drawn by one program, so the first cut dissolved the trunk into a dither
|
|
||||||
pattern as you walked up to it - a solid tree you can see through reads as a fault, where
|
|
||||||
a branch getting out of your way reads as the camera being polite. The mask is the same
|
|
||||||
signal the quake uses. The shadow pass keeps every leaf, or a tree would stop shading the
|
|
||||||
ground it stands on as you walked up to it.
|
|
||||||
|
|
||||||
**A hand light had the reach of a candle, whatever it was meant to be.** `handLight`'s
|
|
||||||
falloff was an inverse square windowed off between 26 and 6 metres, with both numbers
|
|
||||||
hard-coded: two per cent of its own near field by ten metres out, so carrying a torch at
|
|
||||||
night lit your boots and nothing else. `daylight_hand` takes a `reach` in metres now and
|
|
||||||
the falloff is a gentle power out to it — about nine tenths at a metre, half at half the
|
|
||||||
reach, a tenth at nine tenths of it, nothing past it. A pool of light with a gradient in
|
|
||||||
it rather than a hotspot with a cliff.
|
|
||||||
|
|
||||||
**And the wheel on macOS did nothing, or everything.** `scrollingDeltaY` is a double, and
|
|
||||||
the Cocoa event pump truncated it to an int PER EVENT before accumulating: a trackpad or a
|
|
||||||
Magic Mouse sends a stream of fractions of a line, every one of which truncated to zero, so
|
|
||||||
the wheel was dead; a notched mouse sends three to ten lines at once, so it jumped. The
|
|
||||||
fraction is accumulated now, a precise delta is scaled from points to notches, and the
|
|
||||||
remainder carries to the next frame - one gesture, one step, on both kinds of mouse.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue