Compare commits
216 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3e83262db | ||
|
|
f31b4952e4 | ||
|
|
ad3f896f82 | ||
|
|
80ebf08dd3 | ||
|
|
f14b3d38a4 | ||
|
|
bf65c34b28 | ||
|
|
89b0790f19 | ||
|
|
9624bcf359 | ||
|
|
67a5c4a1ed | ||
|
|
8b5e66f514 | ||
|
|
5109457fcd | ||
|
|
5497e4b0b9 | ||
| d72b6eec06 | |||
| ae42f6ead0 | |||
| 471b118efd | |||
| 13a9c165f7 | |||
| da3c8e8284 | |||
| 9c0711a54c | |||
| 4598dc07e2 | |||
| 9c9dc5f423 | |||
| 12ee16faab | |||
| 398d041d96 | |||
| 7a02d6b0b5 | |||
| d47d70ff5b | |||
| 313ca4f3e6 | |||
| f940f01d9b | |||
| 90adaf9e84 | |||
| 2140fbec1b | |||
| 78300bdf9c | |||
| 514a447162 | |||
| 3d0bc66e40 | |||
| e0a15c1ca8 | |||
| fa12611c69 | |||
| 342f378860 | |||
| e8944598cc | |||
| 6af25af5f3 | |||
| f1935ad016 | |||
| 4d397bba5f | |||
| 80930ddd35 | |||
| 0133dd5329 | |||
| 635418a4da | |||
| 31193160a9 | |||
| 3086453c7b | |||
| a8b83b8e2b | |||
| 8ce2af89c8 | |||
| 5f0ee87dd9 | |||
| b88895e82f | |||
| 2f0c734d13 | |||
| e96b3159d7 | |||
| 8c95ecc547 | |||
| 02a98c9f32 | |||
| 7f95c0233e | |||
| a531228b95 | |||
| de86f383bf | |||
| bd811ee783 | |||
| 57d7f75940 | |||
| c5d6ea28e1 | |||
| 730daed20a | |||
| b9bae99a4c | |||
| 2c65048fb0 | |||
| 3388d77ec5 | |||
| 242da2e263 | |||
| 70fb2b9503 | |||
| 0aa056a0ae | |||
| b270318640 | |||
| bc759f1ed4 | |||
| 2f1ff85d8f | |||
| b7429cd9ec | |||
| 12a63374a8 | |||
| d80d7061e7 | |||
| abdefe0af0 | |||
| 4f76de7c9f | |||
| db8cd6220a | |||
| ced4e87d41 | |||
| 09e3d85821 | |||
| c1e421bbbb | |||
| 3a9381a56c | |||
| 90bdfbd2ae | |||
| a230d15ffc | |||
| 60bbd1f5d6 | |||
| 43ce8a4e01 | |||
| 1529a64588 | |||
| be5eec64c9 | |||
| 780a33f657 | |||
| c1c5dae6f2 | |||
| c489f32908 | |||
| b91f70cf2f | |||
| 24a207be01 | |||
| 44e31d9b21 | |||
|
|
b67234765a | ||
|
|
d07498c30e | ||
| 183a432116 | |||
| ead1466b2d | |||
| 8ef09a4e3e | |||
| 33672d8d5a | |||
| 1dc8aca373 | |||
| 02089a78da | |||
| 1f8e7c6d71 | |||
| 27079e127d | |||
| 5e9bb3535e | |||
| 250cf2fc89 | |||
| 57975495a9 | |||
| f3e7a780e2 | |||
| ee6cb0a670 | |||
| b3df34b405 | |||
| dbafa17670 | |||
| d9c8f97903 | |||
| ad2ec35bfb | |||
| 6331ba0b2f | |||
| 3d275b8e85 | |||
| bd61db9aae | |||
| ed8bd07518 | |||
| 27705f1ba2 | |||
| e964adc818 | |||
| c5213320ac | |||
| e0f8443e75 | |||
| 6702b3723a | |||
| f6e7228f75 | |||
| 14cebe4462 | |||
| c39fcaa7d7 | |||
| 1d9499c4f8 | |||
| 61050a5585 | |||
| 85420711df | |||
| 2efa7a4df5 | |||
| 1d018db5e9 | |||
| 023697dcd7 | |||
| 87ee12543e | |||
| b308bc0ef7 | |||
| 9d5ca54234 | |||
| 2ae73c3c58 | |||
| adfa2cc737 | |||
| 7c937df002 | |||
| 9fb9c959a3 | |||
| 61ebc8f317 | |||
| b7f668c58a | |||
| b1021c28b5 | |||
| 7d6f92283a | |||
| 2a295b1daf | |||
| 4398ec2936 | |||
| 324c358672 | |||
| cda8c40195 | |||
| 89b4ba125f | |||
| fcdbe62f99 | |||
| 57c7afcdb4 | |||
| 2e16c2d170 | |||
| f86c106593 | |||
| 04cf8f217f | |||
| 7e0ca4ff3d | |||
| fcc36c8a46 | |||
| 41affcd7ad | |||
| 4ecfded4ac | |||
| 25d5121a28 | |||
| 91095ed2cc | |||
| cbf52bb994 | |||
| d763b9646f | |||
| d7a9e0a304 | |||
| db720edeef | |||
| f241e85d8f | |||
| d18b414536 | |||
| c9bcf32381 | |||
| b45980c172 | |||
| b4e3f383ec | |||
| 532abd1e45 | |||
| 70528b0dcc | |||
| c5ca7302c2 | |||
| a27f85279e | |||
| bea915b5c7 | |||
| d743aee393 | |||
| 59aba9f691 | |||
| 199b4dc939 | |||
| 2edd23cfbb | |||
| 464d6f9ca6 | |||
| 413f9f156f | |||
| 4f87a116d5 | |||
| 86ffc931e8 | |||
| d72f47d66c | |||
| 7a6182cb85 | |||
| a1d37a1a0b | |||
| 9066b2cdbc | |||
| 238b5aac6a | |||
| 8e5ec9fef0 | |||
| 6ca2e01fba | |||
| 8cf30cd78d | |||
| 9b441fa35c | |||
| 61ca537909 | |||
| 0a82aea922 | |||
| f41c550bb8 | |||
| 829462d3b6 | |||
| 002da46045 | |||
| cfa73c58a8 | |||
| 5728effcc6 | |||
| fa1a0175b0 | |||
| 85edb18380 | |||
| 3a535ee04f | |||
| 9b31b392d2 | |||
| 999fa14059 | |||
| e925376b7a | |||
| 2596034365 | |||
| 163855b6e7 | |||
| 645d48aeae | |||
| ec800a88fc | |||
| abc37dee4e | |||
| 1ae7839275 | |||
| d976d1bc59 | |||
| 531a5b5d05 | |||
| 67713fab06 | |||
| b572729e9d | |||
| cdc6979458 | |||
| 564f88fee5 | |||
| 00c99dc05f | |||
| 1e12940445 | |||
| dc3c4a7580 | |||
| 434b62b036 | |||
| 2bd523e58a | |||
| 7cd6e8005e | |||
| a8a3745ca1 |
@@ -1,12 +1,17 @@
|
|||||||
[target.'cfg(target_os = "emscripten")']
|
[target.'cfg(target_os = "emscripten")']
|
||||||
# TODO: Document what the fuck this is.
|
|
||||||
rustflags = [
|
rustflags = [
|
||||||
# "-O", "-C", "link-args=-O2 --profiling",
|
# Stack size is required for this project, it will crash otherwise.
|
||||||
#"-C", "link-args=-O3 --closure 1",
|
"-C", "link-args=-sASYNCIFY=1 -sASYNCIFY_STACK_SIZE=8192 -sALLOW_MEMORY_GROWTH=1",
|
||||||
# "-C", "link-args=-g -gsource-map",
|
|
||||||
"-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1",
|
|
||||||
# "-C", "link-args=-sALLOW_MEMORY_GROWTH=1",
|
|
||||||
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sUSE_SDL_MIXER=2 -sUSE_OGG=1 -sUSE_SDL_GFX=2 -sUSE_SDL_TTF=2 -sSDL2_IMAGE_FORMATS=['png']",
|
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sUSE_SDL_MIXER=2 -sUSE_OGG=1 -sUSE_SDL_GFX=2 -sUSE_SDL_TTF=2 -sSDL2_IMAGE_FORMATS=['png']",
|
||||||
# USE_OGG, USE_VORBIS for OGG/VORBIS usage
|
"-C", "link-args=--preload-file assets/game/",
|
||||||
"-C", "link-args=--preload-file assets/",
|
]
|
||||||
]
|
runner = "node"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")']
|
||||||
|
rustflags = [
|
||||||
|
# Manually link zlib.
|
||||||
|
# The `sdl2` crate's build script uses `libpng`, which requires `zlib`.
|
||||||
|
# By adding `-lz` here, we ensure it's passed to the linker after `libpng`,
|
||||||
|
# which is required for the linker to correctly resolve symbols.
|
||||||
|
"-C", "link-arg=-lz",
|
||||||
|
]
|
||||||
|
|||||||
5
.config/nextest.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[profile.default]
|
||||||
|
fail-fast = false
|
||||||
|
|
||||||
|
[profile.coverage]
|
||||||
|
status-level = "none"
|
||||||
20
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
groups:
|
||||||
|
dependencies:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
groups:
|
||||||
|
dependencies:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
108
.github/workflows/build.yaml
vendored
@@ -1,104 +1,162 @@
|
|||||||
name: Build
|
name: Builds
|
||||||
|
on: ["push", "pull_request"]
|
||||||
on: [push]
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
env:
|
|
||||||
RUST_TOOLCHAIN: 1.86.0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: Build (${{ matrix.target }})
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
artifact_name: pacman
|
artifact_name: pacman
|
||||||
|
toolchain: 1.86.0
|
||||||
- os: macos-13
|
- os: macos-13
|
||||||
target: x86_64-apple-darwin
|
target: x86_64-apple-darwin
|
||||||
artifact_name: pacman
|
artifact_name: pacman
|
||||||
|
toolchain: 1.86.0
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
artifact_name: pacman
|
||||||
|
toolchain: 1.86.0
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
target: x86_64-pc-windows-gnu
|
target: x86_64-pc-windows-gnu
|
||||||
artifact_name: pacman.exe
|
artifact_name: pacman.exe
|
||||||
|
toolchain: 1.86.0
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Rust Toolchain
|
- name: Setup Rust Toolchain
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
toolchain: ${{ matrix.toolchain }}
|
||||||
|
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Cache vcpkg
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: target/vcpkg
|
||||||
|
key: A-vcpkg-${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('Cargo.toml', 'Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
A-vcpkg-${{ runner.os }}-${{ matrix.target }}-
|
||||||
|
|
||||||
|
- name: Vcpkg Linux Dependencies
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libltdl-dev
|
||||||
|
|
||||||
- name: Vcpkg
|
- name: Vcpkg
|
||||||
run: |
|
run: |
|
||||||
cargo install cargo-vcpkg
|
cargo install cargo-vcpkg
|
||||||
cargo vcpkg -v build
|
cargo vcpkg -v build
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --release
|
run: cargo build --release
|
||||||
|
|
||||||
- name: Install Cargo Binstall
|
|
||||||
uses: cargo-bins/cargo-binstall@main
|
|
||||||
|
|
||||||
- name: Acquire Package Version
|
- name: Acquire Package Version
|
||||||
shell: bash
|
id: get_version
|
||||||
|
shell: bash # required to prevent Windows runners from failing
|
||||||
run: |
|
run: |
|
||||||
cargo binstall toml-cli -y
|
set -euo pipefail # exit on error
|
||||||
PACKAGE_VERSION=$(toml get ./Cargo.toml package.version --raw)
|
echo "version=$(cargo metadata --format-version 1 --no-deps | jq '.packages[0].version' -r)" >> $GITHUB_OUTPUT
|
||||||
echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "pacman-${{ env.PACKAGE_VERSION }}-${{ matrix.target }}"
|
name: "pacman-${{ steps.get_version.outputs.version }}-${{ matrix.target }}"
|
||||||
path: ./target/release/${{ matrix.artifact_name }}
|
path: ./target/release/${{ matrix.artifact_name }}
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
wasm:
|
wasm:
|
||||||
|
name: Build (wasm32-unknown-emscripten)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
pages: write
|
pages: write
|
||||||
id-token: write
|
id-token: write
|
||||||
|
# concurrency group is used to prevent multiple page deployments from being attempted at the same time
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-wasm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Emscripten SDK
|
- name: Setup Emscripten SDK
|
||||||
uses: mymindstorm/setup-emsdk@v14
|
uses: pyodide/setup-emsdk@v15
|
||||||
with:
|
with:
|
||||||
version: 3.1.43
|
version: 3.1.43
|
||||||
|
actions-cache-folder: "emsdk-cache-b"
|
||||||
|
|
||||||
- name: Setup Rust (WASM32 Emscripten)
|
- name: Setup Rust (WASM32 Emscripten)
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
target: wasm32-unknown-emscripten
|
target: wasm32-unknown-emscripten
|
||||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
toolchain: 1.86.0
|
||||||
|
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Setup Bun
|
||||||
uses: pnpm/action-setup@v3
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
version: 8
|
bun-version: latest
|
||||||
run_install: true
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build with Emscripten
|
||||||
run: ./build.sh -er # release mode, skip emsdk
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Retry mechanism for Emscripten build - only retry on specific hash errors
|
||||||
|
MAX_RETRIES=3
|
||||||
|
RETRY_DELAY=30
|
||||||
|
|
||||||
|
for attempt in $(seq 1 $MAX_RETRIES); do
|
||||||
|
echo "Build attempt $attempt of $MAX_RETRIES"
|
||||||
|
|
||||||
|
# Capture output and check for specific error while preserving real-time output
|
||||||
|
if bun run -i web.build.ts 2>&1 | tee /tmp/build_output.log; then
|
||||||
|
echo "Build successful on attempt $attempt"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "Build failed on attempt $attempt"
|
||||||
|
|
||||||
|
# Check if the failure was due to the specific hash error
|
||||||
|
if grep -q "emcc: error: Unexpected hash:" /tmp/build_output.log; then
|
||||||
|
echo "::warning::Detected 'emcc: error: Unexpected hash:' error - will retry (attempt $attempt of $MAX_RETRIES)"
|
||||||
|
|
||||||
|
if [ $attempt -eq $MAX_RETRIES ]; then
|
||||||
|
echo "::error::All retry attempts failed. Exiting with error."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Waiting $RETRY_DELAY seconds before retry..."
|
||||||
|
sleep $RETRY_DELAY
|
||||||
|
|
||||||
|
# Exponential backoff: double the delay for next attempt
|
||||||
|
RETRY_DELAY=$((RETRY_DELAY * 2))
|
||||||
|
else
|
||||||
|
echo "Build failed but not due to hash error - not retrying"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||||
with:
|
with:
|
||||||
path: "./dist/"
|
path: "./dist/"
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
|
|||||||
80
.github/workflows/coverage.yaml
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
name: Code Coverage
|
||||||
|
|
||||||
|
on: ["push", "pull_request"]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_TOOLCHAIN: 1.86.0
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
coverage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||||
|
components: llvm-tools-preview
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Cache vcpkg
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: target/vcpkg
|
||||||
|
key: A-vcpkg-${{ runner.os }}-${{ hashFiles('Cargo.toml', 'Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
A-vcpkg-${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Vcpkg Linux Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libltdl-dev
|
||||||
|
|
||||||
|
- name: Vcpkg
|
||||||
|
run: |
|
||||||
|
cargo install cargo-vcpkg
|
||||||
|
cargo vcpkg -v build
|
||||||
|
|
||||||
|
- uses: taiki-e/install-action@cargo-llvm-cov
|
||||||
|
- uses: taiki-e/install-action@nextest
|
||||||
|
- uses: taiki-e/install-action@just
|
||||||
|
|
||||||
|
- name: Generate coverage report
|
||||||
|
run: |
|
||||||
|
just coverage
|
||||||
|
|
||||||
|
- name: Download Coveralls CLI
|
||||||
|
run: |
|
||||||
|
# use GitHub Releases URL instead of coveralls.io because they can't maintain their own files; it 404s
|
||||||
|
curl -L https://github.com/coverallsapp/coverage-reporter/releases/download/v0.6.15/coveralls-linux-x86_64.tar.gz | tar -xz -C /usr/local/bin
|
||||||
|
|
||||||
|
- name: Upload coverage to Coveralls
|
||||||
|
env:
|
||||||
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||||
|
run: |
|
||||||
|
if [ ! -f "lcov.info" ]; then
|
||||||
|
echo "Error: lcov.info file not found. Coverage generation may have failed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in {1..10}; do
|
||||||
|
echo "Attempt $i: Uploading coverage to Coveralls..."
|
||||||
|
if coveralls -n report lcov.info; then
|
||||||
|
echo "Successfully uploaded coverage report."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $i -lt 10 ]; then
|
||||||
|
delay=$((2**i))
|
||||||
|
echo "Attempt $i failed. Retrying in $delay seconds..."
|
||||||
|
sleep $delay
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Failed to upload coverage report after 10 attempts."
|
||||||
|
exit 1
|
||||||
58
.github/workflows/tests.yaml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
name: Tests & Checks
|
||||||
|
|
||||||
|
on: ["push", "pull_request"]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_TOOLCHAIN: 1.86.0
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||||
|
components: clippy, rustfmt
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Cache vcpkg
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: target/vcpkg
|
||||||
|
key: A-vcpkg-${{ runner.os }}-${{ hashFiles('Cargo.toml', 'Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
A-vcpkg-${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Vcpkg Linux Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libltdl-dev
|
||||||
|
|
||||||
|
- name: Vcpkg
|
||||||
|
run: |
|
||||||
|
cargo install cargo-vcpkg
|
||||||
|
cargo vcpkg -v build
|
||||||
|
|
||||||
|
- uses: taiki-e/install-action@nextest
|
||||||
|
|
||||||
|
- name: Run nextest
|
||||||
|
run: cargo nextest run --workspace
|
||||||
|
|
||||||
|
- name: Run clippy
|
||||||
|
run: cargo clippy -- -D warnings
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
|
- uses: taiki-e/install-action@cargo-audit
|
||||||
|
|
||||||
|
- name: Run security audit
|
||||||
|
run: cargo audit
|
||||||
20
.gitignore
vendored
@@ -1,7 +1,17 @@
|
|||||||
/target
|
# IDE, Other files
|
||||||
/dist
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
*.dll
|
|
||||||
rust-sdl2-emscripten/
|
rust-sdl2-emscripten/
|
||||||
assets/build.css
|
|
||||||
emsdk/
|
# Build files
|
||||||
|
target/
|
||||||
|
dist/
|
||||||
|
emsdk/
|
||||||
|
|
||||||
|
# Site build f iles
|
||||||
|
tailwindcss-*
|
||||||
|
assets/site/build.css
|
||||||
|
|
||||||
|
# Coverage reports
|
||||||
|
lcov.info
|
||||||
|
coverage.html
|
||||||
|
|||||||
@@ -20,3 +20,15 @@ repos:
|
|||||||
language: system
|
language: system
|
||||||
types: [rust]
|
types: [rust]
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
- id: cargo-check
|
||||||
|
name: cargo check
|
||||||
|
entry: cargo check --all-targets
|
||||||
|
language: system
|
||||||
|
types_or: [rust, cargo, cargo-lock]
|
||||||
|
pass_filenames: false
|
||||||
|
- id: cargo-check-wasm
|
||||||
|
name: cargo check for wasm32-unknown-emscripten
|
||||||
|
entry: cargo check --all-targets --target=wasm32-unknown-emscripten
|
||||||
|
language: system
|
||||||
|
types_or: [rust, cargo, cargo-lock]
|
||||||
|
pass_filenames: false
|
||||||
|
|||||||
7
BUILD.md
@@ -1,7 +0,0 @@
|
|||||||
# Building Pac-Man
|
|
||||||
|
|
||||||
## GitHub Actions Workflow
|
|
||||||
|
|
||||||
1. Build workflow produces executables & WASM files for all platforms
|
|
||||||
2. Uploaded as artifacts
|
|
||||||
3. Deployment workflow downloads artifacts and uploads to GitHub Pages
|
|
||||||
951
Cargo.lock
generated
40
Cargo.toml
@@ -1,23 +1,42 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pacman"
|
name = "pacman"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tracing = { version = "0.1.40", features = ["max_level_debug", "release_max_level_debug"]}
|
tracing = { version = "0.1.41", features = ["max_level_debug", "release_max_level_debug"]}
|
||||||
tracing-error = "0.2.0"
|
tracing-error = "0.2.0"
|
||||||
tracing-subscriber = {version = "0.3.17", features = ["env-filter"]}
|
tracing-subscriber = {version = "0.3.17", features = ["env-filter"]}
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
sdl2 = { version = "0.38.0", features = ["image", "ttf"] }
|
sdl2 = { version = "0.38.0", features = ["image", "ttf"] }
|
||||||
spin_sleep = "1.3.2"
|
spin_sleep = "1.3.2"
|
||||||
rand = "0.9.2"
|
rand = { version = "0.9.2", default-features = false, features = ["small_rng", "os_rng"] }
|
||||||
pathfinding = "4.14"
|
pathfinding = "4.14"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
thiserror = "1.0"
|
thiserror = "2.0.14"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
glam = "0.30.4"
|
glam = "0.30.5"
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
serde_json = "1.0.142"
|
||||||
|
smallvec = "1.15.1"
|
||||||
|
strum = "0.27.2"
|
||||||
|
strum_macros = "0.27.2"
|
||||||
|
phf = { version = "0.12.1", features = ["macros"] }
|
||||||
|
bevy_ecs = "0.16.1"
|
||||||
|
bitflags = "2.9.1"
|
||||||
|
parking_lot = "0.12.3"
|
||||||
|
micromap = "0.1.0"
|
||||||
|
thousands = "0.2.0"
|
||||||
|
pretty_assertions = "1.4.1"
|
||||||
|
num-width = "0.1.0"
|
||||||
|
circular-buffer = "1.1.0"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
panic = "abort"
|
||||||
|
opt-level = "z"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
@@ -41,7 +60,14 @@ rev = "2024.05.24" # release 2024.05.24 # to check for a new one, check https://
|
|||||||
|
|
||||||
[package.metadata.vcpkg.target]
|
[package.metadata.vcpkg.target]
|
||||||
x86_64-pc-windows-msvc = { triplet = "x64-windows-static-md" }
|
x86_64-pc-windows-msvc = { triplet = "x64-windows-static-md" }
|
||||||
stable-x86_64-unknown-linux-gnu = { triplet = "x86_64-unknown-linux-gnu" }
|
x86_64-unknown-linux-gnu = { triplet = "x64-linux" }
|
||||||
|
x86_64-apple-darwin = { triplet = "x64-osx" }
|
||||||
|
aarch64-apple-darwin = { triplet = "arm64-osx" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "emscripten")'.dependencies]
|
[target.'cfg(target_os = "emscripten")'.dependencies]
|
||||||
libc = "0.2.16"
|
libc = "0.2.175"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
phf = { version = "0.12.1", features = ["macros"] }
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
# Implementation
|
|
||||||
|
|
||||||
A document detailing the implementation the project from rendering, to game logic, to build systems.
|
|
||||||
|
|
||||||
## Rendering
|
|
||||||
|
|
||||||
1. Map
|
|
||||||
- May require procedural text generation later on (cacheable?)
|
|
||||||
2. Pacman
|
|
||||||
3. Ghosts
|
|
||||||
- Requires colors
|
|
||||||
4. Items
|
|
||||||
5. Interface
|
|
||||||
- Requires fonts
|
|
||||||
|
|
||||||
## Grid System
|
|
||||||
|
|
||||||
1. How does the grid system work?
|
|
||||||
|
|
||||||
The grid is 28 x 36 (although, the map texture is 28 x 37), and each cell is 24x24 (pixels).
|
|
||||||
Many of the walls in the map texture only occupy a portion of the cell, so some items are able to render across multiple cells.
|
|
||||||
24x24 assets include pellets, the energizer, and the map itself ()
|
|
||||||
|
|
||||||
2. What constraints must be enforced on Ghosts and PacMan?
|
|
||||||
|
|
||||||
3. How do movement transitions work?
|
|
||||||
|
|
||||||
All entities store a precise position, and a direction. This position is only used for animation, rendering, and collision purposes. Otherwise, a separate 'cell position' (which is 24 times less precise, owing to the fact that it is based on the entity's position within the grid).
|
|
||||||
|
|
||||||
When an entity is transitioning between cells, movement directions are acknowledged, but won't take effect until the next cell has been entered completely.
|
|
||||||
|
|
||||||
4. Between transitions, how does collision detection work?
|
|
||||||
|
|
||||||
It appears the original implementation used cell-level detection.
|
|
||||||
I worry this may be prone to division errors. Make sure to use rounding (50% >=).
|
|
||||||
33
Justfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
set shell := ["bash", "-c"]
|
||||||
|
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
|
||||||
|
|
||||||
|
# Regex to exclude files from coverage report, double escapes for Justfile + CLI
|
||||||
|
# You can use src\\\\..., but the filename alone is acceptable too
|
||||||
|
coverage_exclude_pattern := "src\\\\app.rs|audio.rs|src\\\\error.rs|platform\\\\emscripten.rs"
|
||||||
|
|
||||||
|
# !!! --ignore-filename-regex should be used on both reports & coverage testing
|
||||||
|
# !!! --remap-path-prefix prevents the absolute path from being used in the generated report
|
||||||
|
|
||||||
|
# Generate HTML report (for humans, source line inspection)
|
||||||
|
html: coverage
|
||||||
|
cargo llvm-cov report \
|
||||||
|
--remap-path-prefix \
|
||||||
|
--ignore-filename-regex "{{ coverage_exclude_pattern }}" \
|
||||||
|
--html \
|
||||||
|
--open
|
||||||
|
|
||||||
|
# Display report (for humans)
|
||||||
|
report-coverage: coverage
|
||||||
|
cargo llvm-cov report \
|
||||||
|
--remap-path-prefix \
|
||||||
|
--ignore-filename-regex "{{ coverage_exclude_pattern }}"
|
||||||
|
|
||||||
|
# Run & generate report (for CI)
|
||||||
|
coverage:
|
||||||
|
cargo llvm-cov \
|
||||||
|
--lcov \
|
||||||
|
--remap-path-prefix \
|
||||||
|
--ignore-filename-regex "{{ coverage_exclude_pattern }}" \
|
||||||
|
--output-path lcov.info \
|
||||||
|
--profile coverage \
|
||||||
|
--no-fail-fast nextest
|
||||||
136
README.md
@@ -1,79 +1,89 @@
|
|||||||
# Pac-Man
|
# Pac-Man
|
||||||
|
|
||||||
If the title doesn't clue you in, I'm remaking Pac-Man with SDL and Rust.
|
[![Tests Status][badge-test]][test] [![Build Status][badge-build]][build] [![If you're seeing this, Coveralls.io is broken again and it's not my fault.][badge-coverage]][coverage] [![Online Demo][badge-online-demo]][demo] [![Last Commit][badge-last-commit]][commits]
|
||||||
|
|
||||||
The project is _extremely_ early in development, but check back in a week, and maybe I'll have something cool to look
|
[badge-test]: https://github.com/Xevion/Pac-Man/actions/workflows/tests.yaml/badge.svg
|
||||||
at.
|
[badge-build]: https://github.com/Xevion/Pac-Man/actions/workflows/build.yaml/badge.svg
|
||||||
|
[badge-coverage]: https://coveralls.io/repos/github/Xevion/Pac-Man/badge.svg?branch=master
|
||||||
|
[badge-demo]: https://img.shields.io/github/deployments/Xevion/Pac-Man/github-pages?label=GitHub%20Pages
|
||||||
|
[badge-online-demo]: https://img.shields.io/badge/GitHub%20Pages-Demo-brightgreen
|
||||||
|
[badge-last-commit]: https://img.shields.io/github/last-commit/Xevion/Pac-Man
|
||||||
|
[build]: https://github.com/Xevion/Pac-Man/actions/workflows/build.yaml
|
||||||
|
[test]: https://github.com/Xevion/Pac-Man/actions/workflows/tests.yaml
|
||||||
|
[coverage]: https://coveralls.io/github/Xevion/Pac-Man?branch=master
|
||||||
|
[demo]: https://xevion.github.io/Pac-Man/
|
||||||
|
[commits]: https://github.com/Xevion/Pac-Man/commits/master
|
||||||
|
|
||||||
## Feature Targets
|
A faithful recreation of the classic Pac-Man arcade game written in Rust. This project aims to replicate the original game's mechanics, graphics, sound, and behavior as accurately as possible while providing modern development features like cross-platform compatibility and WebAssembly support.
|
||||||
|
|
||||||
- Near-perfect replication of logic, scoring, graphics, sound, and behaviors.
|
The game includes all the original features you'd expect from Pac-Man:
|
||||||
- Written in Rust, buildable on Windows, Linux, Mac and WebAssembly.
|
|
||||||
|
- [x] Classic maze navigation and dot collection
|
||||||
|
- [ ] Four ghosts with their unique AI behaviors (Blinky, Pinky, Inky, and Clyde)
|
||||||
|
- [ ] Power pellets that allow Pac-Man to eat ghosts
|
||||||
|
- [ ] Fruit bonuses that appear periodically
|
||||||
|
- [ ] Progressive difficulty with faster ghosts and shorter power pellet duration
|
||||||
|
- [x] Authentic sound effects and sprites
|
||||||
|
|
||||||
|
This cross-platform implementation is built with SDL2 for graphics, audio, and input handling. It can run on Windows, Linux, macOS, and in web browsers via WebAssembly.
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
Just because. And because I wanted to learn more about Rust, inter-operability with C, and compiling to WebAssembly.
|
||||||
|
|
||||||
|
I was inspired by a certain code review video on YouTube; [SOME UNIQUE C++ CODE // Pacman Clone Code Review](https://www.youtube.com/watch?v=OKs_JewEeOo) by The Cherno.
|
||||||
|
|
||||||
|
For some reason, I was inspired to try and replicate it in Rust, and it was uniquely challenging.
|
||||||
|
|
||||||
|
I wanted to hit a log of goals and features, making it a 'perfect' project that I could be proud of.
|
||||||
|
|
||||||
|
- Near-perfect replication of logic, scoring, graphics, sound, and behaviors. No hacks, workarounds, or poor designs.
|
||||||
|
- Written in Rust, buildable on Windows, Linux, Mac and WebAssembly. Statically linked, no runtime dependencies.
|
||||||
|
- Performant, low memory, CPU and GPU usage.
|
||||||
- Online demo, playable in a browser.
|
- Online demo, playable in a browser.
|
||||||
- Automatic build system, with releases for Windows, Linux, and Mac & Web-Assembly.
|
- Completely automatic build system with releases for all platforms.
|
||||||
- Debug tooling
|
- Well documented, well-tested, and maintainable.
|
||||||
- Game state visualization
|
|
||||||
- Game speed controls + pausing
|
|
||||||
- Log tracing
|
|
||||||
- Performance details
|
|
||||||
|
|
||||||
## Experimental Ideas
|
## Experimental Ideas
|
||||||
|
|
||||||
|
- Debug tooling
|
||||||
|
- Game state visualization
|
||||||
|
- Game speed controls + pausing
|
||||||
|
- Log tracing
|
||||||
|
- Performance details
|
||||||
|
- Customized Themes & Colors
|
||||||
|
- Color-blind friendly
|
||||||
- Perfected Ghost Algorithms
|
- Perfected Ghost Algorithms
|
||||||
- More than 4 ghosts
|
- More than 4 ghosts
|
||||||
- Custom Level Generation
|
- Custom Level Generation
|
||||||
- Multi-map tunnelling
|
- Multi-map tunnelling
|
||||||
- Online Scoreboard
|
- Online Scoreboard
|
||||||
- WebAssembly build contains a special API key for communicating with server.
|
- An online axum server with a simple database and OAuth2 authentication.
|
||||||
- To prevent abuse, the server will only accept scores from the WebAssembly build.
|
- Integrates with GitHub, Discord, and Google OAuth2 to acquire an email identifier & avatar.
|
||||||
|
- Avatars are optional for score submission and can be disabled, instead using a blank avatar.
|
||||||
|
- Avatars are downscaled to a low resolution pixellated image to maintain the 8-bit aesthetic.
|
||||||
|
- A custom name is used for the score submission, which is checked for potential abusive language.
|
||||||
|
- A max length of 14 characters, and a min length of 3 characters.
|
||||||
|
- Names are checked for potential abusive language via an external API.
|
||||||
|
- The client implementation should require zero configuration, environment variables, or special secrets.
|
||||||
|
- It simply defaults to the pacman server API, or can be overriden manually.
|
||||||
|
|
||||||
## Installation
|
## Build Notes
|
||||||
|
|
||||||
Besides SDL2, the following extensions are required: Image, Mixer, and TTF.
|
Since this project is still in progress, I'm only going to cover non-obvious build details. By reading the code, build scripts, and copying the online build workflows, you should be able to replicate the build process.
|
||||||
|
|
||||||
### Ubuntu
|
- We use rustc 1.86.0 for the build, due to bulk-memory-opt related issues on wasm32-unknown-emscripten.
|
||||||
|
- Technically, we could probably use stable or even nightly on desktop targets, but using different versions for different targets is a pain, mainly because of clippy warnings changing between versions.
|
||||||
On Ubuntu, you can install the required packages with the following command:
|
- Install `cargo-vcpkg` with `cargo install cargo-vcpkg`, then run `cargo vcpkg build` to build the requisite dependencies via vcpkg.
|
||||||
|
- For the WASM build, you need to have the Emscripten SDK cloned; you can do so with `git clone https://github.com/emscripten-core/emsdk.git`
|
||||||
```
|
- The first time you clone, you'll need to install the appropriate SDK version with `./emsdk install 3.1.43` and then activate it with `./emsdk activate 3.1.43`. On Windows, use `./emsdk/emsdk.ps1` instead.
|
||||||
sudo apt install libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
|
- I'm still not sure _why_ 3.1.43 is required, but it is. Perhaps in the future I will attempt to use a more modern version.
|
||||||
```
|
- Occasionally, the build will fail due to dependencies failing to download. I even have a retry mechanism in the build workflow due to this.
|
||||||
|
- You can then activate the Emscripten SDK with `source ./emsdk/emsdk_env.sh` or `./emsdk/emsdk_env.ps1` or `./emsdk/emsdk_env.bat` depending on your OS/terminal.
|
||||||
### Windows
|
- While using the `web.build.ts` is not technically required, it simplifies the build process and is very helpful.
|
||||||
|
- It is intended to be run with `bun`, which you can acquire at [bun.sh](https://bun.sh/)
|
||||||
On Windows, installation requires either building from source (not covered), or downloading the pre-built binaries.
|
- Tip: You can launch a fileserver with `python` or `caddy` to serve the files in the `dist` folder.
|
||||||
|
- `python3 -m http.server 8080 -d dist`
|
||||||
The latest releases can be found here:
|
- `caddy file-server --root dist` (install with `[sudo apt|brew|choco] install caddy` or [a dozen other ways](https://caddyserver.com/docs/install))
|
||||||
|
- `web.build.ts` auto installs dependencies, but you may need to pass `-i` or `--install=fallback|force` to install missing packages. My guess is that if you have some packages installed, it won't install any missing ones. If you have no packages installed, it will install all of them.
|
||||||
- [SDL2](https://github.com/libsdl-org/SDL/releases/latest/)
|
- If you want to have TypeScript resolution for development, you can manually install the dependencies with `bun install` in the `assets/site` folder.
|
||||||
- [SDL2_image](https://github.com/libsdl-org/SDL_image/releases/latest/)
|
|
||||||
- [SDL2_mixer](https://github.com/libsdl-org/SDL_mixer/releases/latest/)
|
|
||||||
- [SDL2_ttf](https://github.com/libsdl-org/SDL_ttf/releases/latest/)
|
|
||||||
|
|
||||||
Download each for your architecture, and locate the appropriately named DLL within. Move said DLL to root of this project.
|
|
||||||
|
|
||||||
In total, you should have the following DLLs in the root of the project:
|
|
||||||
|
|
||||||
- SDL2.dll
|
|
||||||
- SDL2_mixer.dll
|
|
||||||
- SDL2_ttf.dll
|
|
||||||
- SDL2_image.dll
|
|
||||||
- libpngX-X.dll
|
|
||||||
- Not sure on what specific version is to be used, or if naming matters. `libpng16-16.dll` is what I had used.
|
|
||||||
- zlib1.dll
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
To build the project, run the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo build
|
|
||||||
```
|
|
||||||
|
|
||||||
During development, you can easily run the project with:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo run
|
|
||||||
cargo run -q # Quiet mode, no logging
|
|
||||||
cargo run --release # Release mode, optimized
|
|
||||||
```
|
|
||||||
|
|||||||
95
STORY.md
@@ -32,6 +32,7 @@ The problem is that much of this work was done for pure-Rust applications - and
|
|||||||
This requires a C++ WebAssembly compiler such as Emscripten; and it's a pain to get working.
|
This requires a C++ WebAssembly compiler such as Emscripten; and it's a pain to get working.
|
||||||
|
|
||||||
Luckily though, someone else has done this before, and they fully documented it - [RuggRouge][ruggrouge].
|
Luckily though, someone else has done this before, and they fully documented it - [RuggRouge][ruggrouge].
|
||||||
|
|
||||||
- Built with Rust
|
- Built with Rust
|
||||||
- Uses SDL2
|
- Uses SDL2
|
||||||
- Compiling for WebAssembly with Emscripten
|
- Compiling for WebAssembly with Emscripten
|
||||||
@@ -46,7 +47,6 @@ The issue presented with some keys never being sent to the application.
|
|||||||
To confirm, enter safe mode or switch to a different browser without said extensions.
|
To confirm, enter safe mode or switch to a different browser without said extensions.
|
||||||
If the issue disappears, it's because of an extension in your browser stealing keys in a way that is incompatible with the batshit insanity of Emscripten.
|
If the issue disappears, it's because of an extension in your browser stealing keys in a way that is incompatible with the batshit insanity of Emscripten.
|
||||||
|
|
||||||
|
|
||||||
## A Long Break
|
## A Long Break
|
||||||
|
|
||||||
After hitting a wall with an issue with Emscripten where the tab would freeze after switching tabs (making it into a background tab), I decided to take a break from the project. A couple months went by without anything going on.
|
After hitting a wall with an issue with Emscripten where the tab would freeze after switching tabs (making it into a background tab), I decided to take a break from the project. A couple months went by without anything going on.
|
||||||
@@ -78,7 +78,7 @@ But this did help me narrow my search even more for a good example. I needed to
|
|||||||
|
|
||||||
I found [one such repository](https://github.com/KyleMiles/Rust-SDL-Emscripten-Template/), and interestingly, it used `latest` Emscripten (not a specific target like 1.39.20), and was new enough (2 years old, but still new enough) to be relevant.
|
I found [one such repository](https://github.com/KyleMiles/Rust-SDL-Emscripten-Template/), and interestingly, it used `latest` Emscripten (not a specific target like 1.39.20), and was new enough (2 years old, but still new enough) to be relevant.
|
||||||
|
|
||||||
Even more interesting, it didn't use the `main` loop closure, but instead used Emscripten's *Asyncify* feature to handle the main loop.
|
Even more interesting, it didn't use the `main` loop closure, but instead used Emscripten's _Asyncify_ feature to handle the main loop.
|
||||||
|
|
||||||
But, unlike my original project which called `std::thread::sleep` directly, it used bindings into Emscripten's functions like `emscripten_sleep`.
|
But, unlike my original project which called `std::thread::sleep` directly, it used bindings into Emscripten's functions like `emscripten_sleep`.
|
||||||
|
|
||||||
@@ -124,6 +124,7 @@ While working on the next extension of SDL2 for my test repository, SDL2-TTF had
|
|||||||
Luckily, I had a recently updated repository to copy off of, and the working fix was to lower the EMSDK version to `3.1.43`.
|
Luckily, I had a recently updated repository to copy off of, and the working fix was to lower the EMSDK version to `3.1.43`.
|
||||||
|
|
||||||
[Source](https://github.com/aelred/tetris/blob/0ad88153db1ca7962b42277504c0f7f9f3c675a9/tetris-sdl/src/main.rs#L34)
|
[Source](https://github.com/aelred/tetris/blob/0ad88153db1ca7962b42277504c0f7f9f3c675a9/tetris-sdl/src/main.rs#L34)
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
static FONT_DATA: &[u8] = include_bytes!("../assets/TerminalVector.ttf");
|
static FONT_DATA: &[u8] = include_bytes!("../assets/TerminalVector.ttf");
|
||||||
|
|
||||||
@@ -176,7 +177,7 @@ But I also didn't want to include some big framework on this, like Astro, so I l
|
|||||||
|
|
||||||
After fiddling and failing to find Hugo suitable, I stuck to plain HTML & the PostCSS method, which worked great. It's definitely not that fast for rapid development, but it works well enough.
|
After fiddling and failing to find Hugo suitable, I stuck to plain HTML & the PostCSS method, which worked great. It's definitely not that fast for rapid development, but it works well enough.
|
||||||
|
|
||||||
The only thing I'm unsatisfied with is why `postcss-cli` wasn't working when executed from `pnpm`. It works just fine from `pnpx`, but it has to download and setup the whole package on *every single invocation*, which is super slow. And probably expensive, in the long run.
|
The only thing I'm unsatisfied with is why `postcss-cli` wasn't working when executed from `pnpm`. It works just fine from `pnpx`, but it has to download and setup the whole package on _every single invocation_, which is super slow. And probably expensive, in the long run.
|
||||||
|
|
||||||
## Cross-platform Builds
|
## Cross-platform Builds
|
||||||
|
|
||||||
@@ -253,6 +254,7 @@ After a couple attempts with various test commits, I couldn't find it, and just
|
|||||||
> Note: VCPKG is annoying to install, the executable provided by Visual Studio Community does not permit classic-mode usage, so you'll still need to clone and bootstrap VCPKG (instructions in the repository README).
|
> Note: VCPKG is annoying to install, the executable provided by Visual Studio Community does not permit classic-mode usage, so you'll still need to clone and bootstrap VCPKG (instructions in the repository README).
|
||||||
|
|
||||||
As it happens, they were placed in
|
As it happens, they were placed in
|
||||||
|
|
||||||
- `$VCPKG_ROOT\packages\sdl2-gfx_x64-windows-release\bin\SDL2_gfx.dll` and
|
- `$VCPKG_ROOT\packages\sdl2-gfx_x64-windows-release\bin\SDL2_gfx.dll` and
|
||||||
- `$VCPKG_ROOT\packages\sdl2-gfx_x64-windows-release\lib\SDL2_gfx.lib` respectively.
|
- `$VCPKG_ROOT\packages\sdl2-gfx_x64-windows-release\lib\SDL2_gfx.lib` respectively.
|
||||||
|
|
||||||
@@ -324,10 +326,95 @@ I was thinking of a github-pages artifact name that aligns with the others, but
|
|||||||
|
|
||||||
Perhaps at the least I'll look into a 32-bit build for Windows, just for demonstration purposes.
|
Perhaps at the least I'll look into a 32-bit build for Windows, just for demonstration purposes.
|
||||||
|
|
||||||
|
## My Return to Pac-Man
|
||||||
|
|
||||||
|
It's been 15 months since I last touched the demo codebase, and much longer since I've touched the core Pac-Man project, and I got inspired to look back into it recently. I'm finally touching up on the story document, so if this reads a bit disjointed, that's why.
|
||||||
|
|
||||||
|
- I switched the dependency linking to use the internal statically-linked `vcpkg` feature, which is a lot easier to maintain. It's not perfect, but it's much better than the manual downloads and the dynamically linked `.dll` files I was doing before. With caching, it also tends to be far quicker.
|
||||||
|
- I switched all of the commits to use conventional commit messages, which is easier to read and understand.
|
||||||
|
- I integrated the demo project's emscripten workflow, updated sdl2 and started poking around in the project. I got into adding fonts, adding a reset button, a debug mode, score tracking, pellet consumption, etc.
|
||||||
|
- I spent a lot of time working on the audio timing, getting it to work flawlessly and compare really well with the original Pac-Man; the sound is incredibly important to the game, so I wanted to get it right.
|
||||||
|
|
||||||
|
## Pathfinding and Tunnelling
|
||||||
|
|
||||||
|
Pathfinding was very easy to get working, although tunnelling was a bit more difficult, and unfortunately I never got it working with the way I was doing things at the time. A lot of issues were happening with trying to get the transition between the tunnels to work, I could only get Pac-Man to teleport from one tunnel to the other, but moving smoothly between them was nigh impossible.
|
||||||
|
|
||||||
|
I did however get pathfinding to work between the tunnels, which was very satisfying to see using the debug visuals.
|
||||||
|
|
||||||
|
I ended up using the `pathfinding` crate and it was a breeze to use.
|
||||||
|
|
||||||
|
## Atlas Tiles
|
||||||
|
|
||||||
|
When I was looking around for Pac-Man sprites, I kept coming across atlas images, and I had been noticing for some time how my sprites were not correctly sized, and some of them just didn't match the original Pac-Man. I had been spending a lot of time making this Pac-Man project as close to the original as possible, and I felt like if I didn't use the original sprites, I wasn't doing it justice.
|
||||||
|
|
||||||
|
This had me thinking about how asset loading was a real pain in this project, and how I wanted to look into atlas tiles.
|
||||||
|
|
||||||
|
The arguments for copying between a texture and a canvas/surface/texture were very obviously rigged to allow for this, given that you had to specify the source `Rect`, meaning you could target a specific area of the texture. Such as tiles on an atlas image.
|
||||||
|
|
||||||
|
It didn't take long for me to get it working, I chose an existing crate called `clutterd` which provided a CLI for building atlas images with an metadata file describing the positions and sizes of the tiles.
|
||||||
|
|
||||||
|
Doing so required a full re-work of the animation and texture system, and I ended up making a breakthrough on how I managed lifetimes: lifetime annotations were plaguing the codebase, literally everywhere, and it was super annoying to keep writing and dealing with them.
|
||||||
|
|
||||||
|
So, I ended up using `unsafe` to forcibly cast the lifetimes to `'static`, which was a bit of a gamble, but given that they essentially behave as `'static` in practice, there wasn't much risk as I see it. I might re-look into my understanding of lifetimes and this in the future, but for the time being, it's a good solution that makes the codebase far easier to work with.
|
||||||
|
|
||||||
|
## Cross-platform Builds
|
||||||
|
|
||||||
|
Since the original `rust-sdl2-emscripten` demo project had cross-platform builds, I was ready to get it working for this project. For the most part, it wasn't hard, things tended to click into place, but unfortunately, the `emscripten` os target and somehow, the `linux` os target were both failing.
|
||||||
|
|
||||||
|
I'm still not sure what exactly causes it, but `emscripten` strongly prefers to be built on 1.86 (1.88 does not work, 1.87 might though).
|
||||||
|
Changing the toolchain to 1.86 fixed the issue when it was failing.
|
||||||
|
|
||||||
|
It did turn out though, that despite me getting the `emscripten` target building, it did not mean the application was functioning properly.
|
||||||
|
|
||||||
|
- Upon launch, it was immediately crashing due to issues with the audio subsystem; this was fixed with a simple increase to the audio buffer chunksize, apparently it has a minimum size of 256.
|
||||||
|
- Then, it was failing due to issues with the main loop, referencing the `ASYNCIFY_STACK_SIZE` variable in `.cargo/config.toml`, asking for it to be increased. I really didn't like the idea of increasing it for whatever reason, so I ended up looking into the `emscripten_main_loop` method of looping again, but nothing worked all that well, just like the last time I tried. So I increased the variable, doubling it from the default of 4096 to 8192. Things immediately worked, and the browser build was working.
|
||||||
|
|
||||||
|
Linux however was a far more annoying task, as it was failing to due the `cargo-vcpkg` build step (which built the SDL2 libraries necessary for static linking and building the project). It was hard to pin down at first, but packages seemed to be failing due to system dependencies not being available, so after adding a couple `apt` packages to the steps, things started to work.
|
||||||
|
|
||||||
|
Eventually though, it kept failing at the `sdl2` package, which was failing to build due to the `libpng` package not being able to find a bunch of symbols related to `zlib`. Almost nothing was written about this online, except for one issue on GitHub which hadn't been updated in 2 years.
|
||||||
|
|
||||||
|
I won't lie, Gemini helped me out here, suggesting adding `"-C", "link-arg=-lz",` to the `rustflags` section of `.cargo/config.toml`.
|
||||||
|
It seems like it moved the `zlib` library to the front of the link order, and things started magically working both locally and on the GitHub Actions runner.
|
||||||
|
|
||||||
|
I also added an ARM64 build for MacOS, which worked without any issues. Surprisingly, MacOS is the only platform that I've been able to get working without any issues. At least, I hope it's working; I don't really have a way to test it myself.
|
||||||
|
|
||||||
|
## Caching
|
||||||
|
|
||||||
|
I spent a bit of time after this improving the build process to take advantage of caching so that most builds would fly. The `cargo-vcpkg` was by far the most expensive step, and it unfortunately, despite being in the `target` directory (which is supposed to be cached by the `Swatinem/rust-cache@v2` action), was not being cached.
|
||||||
|
|
||||||
|
I played with the parameters for a bit before giving up and just manually adding a cache step to the workflow. It's expensive, uploading 300MB of artifact data to GitHub, but it works well, and I'm really doubtful it will change that much.
|
||||||
|
|
||||||
|
I also ended up improving the build process to use `cargo metadata` to get the package version, which means I could drop the `toml-cli` dependency and just use the `cargo` command + `jq` (which is already installed on the runner).
|
||||||
|
|
||||||
|
## Atlas Text
|
||||||
|
|
||||||
|
At some point, I wanted to use the original text from the game, so I created a text texture type for rendering text using the existing sprite atlas, which means I wasn't using the `ttf` feature at all. I'm stil unsure whether or not I'll use it, I might keep it because it seems like more hassle to remove it at this point. Perhaps I'll still use normal ttf fonts like Arial for debug-related displays, or maybe I'll create/use a custom font.
|
||||||
|
|
||||||
|
## Node Graph Positioning
|
||||||
|
|
||||||
|
After getting all this working, I was really excited to finally get closer to actually finishing the project. I felt like I had finally started checking a bunch of important boxes, so I started actually working on the 'ghost house' part of Pac-Man.
|
||||||
|
|
||||||
|
The ghost house is very different from the rest of the game as it doesn't render the tiles in the same way, on a static grid.
|
||||||
|
|
||||||
|
It's actually offset by 8 pixels, and the ghosts exit the house between two tiles, requiring a lot more customization and flexibility in my
|
||||||
|
rendering system.
|
||||||
|
|
||||||
|
I spent a fair bit of time trying to implement hacks into this to get it working, but I eventually gave up after realizing that there's no solution here using my existing system.
|
||||||
|
|
||||||
|
I remembered how I was having trouble with the transition states between the two tunnels (still not resolved), and it felt quite similar to my current situation; the inflexibility of my integer grid system was the main cause of the issue.
|
||||||
|
|
||||||
|
I started thinking of different ways to approach movement, and realized that the Pac-Man and Ghost's movement is quite limited and simple like railroad tracks, like nodes on a graph. Both problems could be solved by switching to a graph - most of the maze would look like a grid, each cell connected to eachother.
|
||||||
|
|
||||||
|
By representing one's position as a distance from the start node towards an end node, I could achieve smooth linear movement between nodes
|
||||||
|
that, for the most part, appears to use a cell-based grid, which also allowing more customized offsets.
|
||||||
|
|
||||||
|
The bigger downside was that I had to toss out almost all the existing code for the game, only keeping the audio and most of the texturing system, as well as the initialization code. It also meant I was using floating points for a lot of internal state, which is not ideal.
|
||||||
|
|
||||||
|
This ended up being okay though, as I was able to clean up a lot of gross code, and the system ended up being easier to work with by comparison.
|
||||||
|
|
||||||
[code-review-video]: https://www.youtube.com/watch?v=OKs_JewEeOo
|
[code-review-video]: https://www.youtube.com/watch?v=OKs_JewEeOo
|
||||||
[code-review-thumbnail]: https://img.youtube.com/vi/OKs_JewEeOo/hqdefault.jpg
|
[code-review-thumbnail]: https://img.youtube.com/vi/OKs_JewEeOo/hqdefault.jpg
|
||||||
[fighting-lifetimes-1]: https://devcry.heiho.net/html/2022/20220709-rust-and-sdl2-fighting-with-lifetimes.html
|
[fighting-lifetimes-1]: https://devcry.heiho.net/html/2022/20220709-rust-and-sdl2-fighting-with-lifetimes.html
|
||||||
[fighting-lifetimes-2]: https://devcry.heiho.net/html/2022/20220716-rust-and-sdl2-fighting-with-lifetimes-2.html
|
[fighting-lifetimes-2]: https://devcry.heiho.net/html/2022/20220716-rust-and-sdl2-fighting-with-lifetimes-2.html
|
||||||
[fighting-lifetimes-3]: https://devcry.heiho.net/html/2022/20220724-rust-and-sdl2-fighting-with-lifetimes-3.html
|
[fighting-lifetimes-3]: https://devcry.heiho.net/html/2022/20220724-rust-and-sdl2-fighting-with-lifetimes-3.html
|
||||||
[ruggrogue]: https://tung.github.io/ruggrogue/
|
[ruggrogue]: https://tung.github.io/ruggrogue/
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 174 B |
|
Before Width: | Height: | Size: 158 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 394 B |
|
Before Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 370 B |
BIN
assets/door.png
|
Before Width: | Height: | Size: 90 B |
1061
assets/game/atlas.json
Normal file
BIN
assets/game/atlas.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
@@ -1,65 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Pac-Man Arcade</title>
|
|
||||||
<link rel="stylesheet" href="build.css" />
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="bg-black text-yellow-400 text-center">
|
|
||||||
<a
|
|
||||||
href="https://github.com/Xevion/Pac-Man"
|
|
||||||
class="absolute top-0 right-0"
|
|
||||||
aria-label="View source on GitHub"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="80"
|
|
||||||
height="80"
|
|
||||||
viewBox="0 0 250 250"
|
|
||||||
class="fill-yellow-400 text-black"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
|
||||||
<path
|
|
||||||
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
|
||||||
class="octo-arm"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
|
||||||
class="octo-body"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<h1 class="text-4xl mt-10 scaled-text">Pac-Man Arcade</h1>
|
|
||||||
<p class="text-lg mt-5 scaled-text">
|
|
||||||
Welcome to the Pac-Man Arcade! Use the controls below to play.
|
|
||||||
</p>
|
|
||||||
<canvas
|
|
||||||
id="canvas"
|
|
||||||
class="block mx-auto mt-5"
|
|
||||||
width="800"
|
|
||||||
height="600"
|
|
||||||
></canvas>
|
|
||||||
<div class="mt-10">
|
|
||||||
<span
|
|
||||||
class="inline-block mx-2 px-4 py-2 bg-yellow-400 text-black rounded scaled-text"
|
|
||||||
>← ↑ → ↓ Move</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="inline-block mx-2 px-4 py-2 bg-yellow-400 text-black rounded scaled-text"
|
|
||||||
>Space Change Sprite</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="inline-block mx-2 px-4 py-2 bg-yellow-400 text-black rounded scaled-text"
|
|
||||||
>Shift + ↑↓ Change Volume</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
|
||||||
var Module = {
|
|
||||||
canvas: document.getElementById("canvas"),
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="pacman.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
BIN
assets/map.png
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
96
assets/site/index.html
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Pac-Man in Rust</title>
|
||||||
|
<link rel="stylesheet" href="build.css" />
|
||||||
|
<style>
|
||||||
|
/* Minimal fallback to prevent white flash and canvas pop-in before CSS loads */
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
background: #000;
|
||||||
|
color: #facc15;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#canvas {
|
||||||
|
display: block;
|
||||||
|
margin: 1.5rem auto;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-black text-yellow-400 text-center min-h-screen">
|
||||||
|
<a
|
||||||
|
href="https://github.com/Xevion/Pac-Man"
|
||||||
|
class="absolute top-0 right-0"
|
||||||
|
aria-label="View source on GitHub"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="80"
|
||||||
|
height="80"
|
||||||
|
viewBox="0 0 250 250"
|
||||||
|
class="fill-yellow-400 [&>.octo-arm,.octo-body]:fill-black"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||||
|
<path
|
||||||
|
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||||
|
class="octo-arm"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||||
|
class="octo-body"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="min-h-screen flex flex-col">
|
||||||
|
<main class="flex-1 flex items-center justify-center px-4">
|
||||||
|
<div class="w-full max-w-5xl">
|
||||||
|
<canvas
|
||||||
|
id="canvas"
|
||||||
|
oncontextmenu="event.preventDefault()"
|
||||||
|
class="block w-full h-full max-h-[90vh] aspect-square"
|
||||||
|
></canvas>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="mt-8 flex flex-wrap gap-3 justify-center items-center text-sm"
|
||||||
|
>
|
||||||
|
<span class="code">← ↑ → ↓</span>
|
||||||
|
<span class="opacity-70">Move</span>
|
||||||
|
|
||||||
|
<span class="mx-2 opacity-30">|</span>
|
||||||
|
|
||||||
|
<span class="code">Space</span>
|
||||||
|
<span class="opacity-70">Toggle Debug</span>
|
||||||
|
|
||||||
|
<span class="mx-2 opacity-30">|</span>
|
||||||
|
|
||||||
|
<span class="code">P</span>
|
||||||
|
<span class="opacity-70">Pause / Unpause</span>
|
||||||
|
|
||||||
|
<span class="mx-2 opacity-30">|</span>
|
||||||
|
|
||||||
|
<span class="code">M</span>
|
||||||
|
<span class="opacity-70">Mute / Unmute</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
const canvas = document.getElementById("canvas");
|
||||||
|
var Module = {
|
||||||
|
canvas: canvas,
|
||||||
|
preRun: [
|
||||||
|
() => {
|
||||||
|
[...canvas.classList]
|
||||||
|
.filter((className) => className.includes("shadow-"))
|
||||||
|
.forEach((className) => canvas.classList.remove(className));
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="pacman.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
28
assets/site/styles.css
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "TerminalVector";
|
||||||
|
src: url("TerminalVector.ttf");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Key badge styling */
|
||||||
|
.code {
|
||||||
|
@apply px-3 py-1 rounded-md font-mono text-[0.9em] lowercase inline-block align-middle;
|
||||||
|
background: rgba(250, 204, 21, 0.08); /* yellow-400 at low opacity */
|
||||||
|
border: 1px solid rgba(250, 204, 21, 0.25);
|
||||||
|
color: #fde68a; /* lighter yellow for readability */
|
||||||
|
font-family: "TerminalVector", ui-monospace, Consolas, "Courier New",
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Title styling */
|
||||||
|
.arcade-title {
|
||||||
|
font-family: "TerminalVector", ui-monospace, Consolas, "Courier New",
|
||||||
|
monospace;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-shadow: 0 0 18px rgba(250, 204, 21, 0.15),
|
||||||
|
0 0 2px rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Liberation Mono";
|
|
||||||
src:
|
|
||||||
url("LiberationMono.woff2") format("woff2"),
|
|
||||||
url("LiberationMono.woff") format("woff");
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
@apply w-full h-[65vh] min-h-[200px] block mx-auto bg-black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
@apply px-1 rounded font-mono bg-zinc-900 border border-zinc-700 lowercase;
|
|
||||||
}
|
|
||||||
BIN
assets/unpacked/cutscene/general_6_10.png
Normal file
|
After Width: | Height: | Size: 120 B |
BIN
assets/unpacked/cutscene/general_6_11.png
Normal file
|
After Width: | Height: | Size: 120 B |
BIN
assets/unpacked/cutscene/general_6_12.png
Normal file
|
After Width: | Height: | Size: 116 B |
BIN
assets/unpacked/cutscene/general_6_9.png
Normal file
|
After Width: | Height: | Size: 115 B |
BIN
assets/unpacked/cutscene/general_7_10.png
Normal file
|
After Width: | Height: | Size: 192 B |
BIN
assets/unpacked/cutscene/general_7_11.png
Normal file
|
After Width: | Height: | Size: 187 B |
BIN
assets/unpacked/cutscene/general_7_8.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
assets/unpacked/cutscene/general_7_9.png
Normal file
|
After Width: | Height: | Size: 215 B |
BIN
assets/unpacked/cutscene/general_8_10.png
Normal file
|
After Width: | Height: | Size: 107 B |
BIN
assets/unpacked/cutscene/general_8_11.png
Normal file
|
After Width: | Height: | Size: 189 B |
BIN
assets/unpacked/cutscene/general_8_8.png
Normal file
|
After Width: | Height: | Size: 115 B |
BIN
assets/unpacked/cutscene/general_8_9.png
Normal file
|
After Width: | Height: | Size: 195 B |
BIN
assets/unpacked/cutscene/general_9_10.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
assets/unpacked/cutscene/general_9_11.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
assets/unpacked/cutscene/general_9_8.png
Normal file
|
After Width: | Height: | Size: 125 B |
BIN
assets/unpacked/cutscene/general_9_9.png
Normal file
|
After Width: | Height: | Size: 122 B |
BIN
assets/unpacked/edible/apple.png
Normal file
|
After Width: | Height: | Size: 173 B |
BIN
assets/unpacked/edible/bell.png
Normal file
|
After Width: | Height: | Size: 175 B |
BIN
assets/unpacked/edible/cherry.png
Normal file
|
After Width: | Height: | Size: 213 B |
BIN
assets/unpacked/edible/galaxian.png
Normal file
|
After Width: | Height: | Size: 178 B |
BIN
assets/unpacked/edible/key.png
Normal file
|
After Width: | Height: | Size: 162 B |
BIN
assets/unpacked/edible/melon.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
assets/unpacked/edible/orange.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
assets/unpacked/edible/strawberry.png
Normal file
|
After Width: | Height: | Size: 195 B |
BIN
assets/unpacked/effects/100.png
Normal file
|
After Width: | Height: | Size: 136 B |
BIN
assets/unpacked/effects/1000.png
Normal file
|
After Width: | Height: | Size: 137 B |
BIN
assets/unpacked/effects/1600.png
Normal file
|
After Width: | Height: | Size: 147 B |
BIN
assets/unpacked/effects/200.png
Normal file
|
After Width: | Height: | Size: 139 B |
BIN
assets/unpacked/effects/2000.png
Normal file
|
After Width: | Height: | Size: 148 B |
BIN
assets/unpacked/effects/300.png
Normal file
|
After Width: | Height: | Size: 149 B |
BIN
assets/unpacked/effects/3000.png
Normal file
|
After Width: | Height: | Size: 152 B |
BIN
assets/unpacked/effects/400.png
Normal file
|
After Width: | Height: | Size: 135 B |
BIN
assets/unpacked/effects/500.png
Normal file
|
After Width: | Height: | Size: 150 B |
BIN
assets/unpacked/effects/5000.png
Normal file
|
After Width: | Height: | Size: 151 B |
BIN
assets/unpacked/effects/700.png
Normal file
|
After Width: | Height: | Size: 145 B |
BIN
assets/unpacked/effects/800.png
Normal file
|
After Width: | Height: | Size: 135 B |
BIN
assets/unpacked/ghost/blinky/down_a.png
Normal file
|
After Width: | Height: | Size: 181 B |
BIN
assets/unpacked/ghost/blinky/down_b.png
Normal file
|
After Width: | Height: | Size: 178 B |
BIN
assets/unpacked/ghost/blinky/left_a.png
Normal file
|
After Width: | Height: | Size: 184 B |
BIN
assets/unpacked/ghost/blinky/left_b.png
Normal file
|
After Width: | Height: | Size: 181 B |
BIN
assets/unpacked/ghost/blinky/right_a.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
assets/unpacked/ghost/blinky/right_b.png
Normal file
|
After Width: | Height: | Size: 181 B |
BIN
assets/unpacked/ghost/blinky/up_a.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
assets/unpacked/ghost/blinky/up_b.png
Normal file
|
After Width: | Height: | Size: 175 B |
BIN
assets/unpacked/ghost/clyde/down_a.png
Normal file
|
After Width: | Height: | Size: 189 B |
BIN
assets/unpacked/ghost/clyde/down_b.png
Normal file
|
After Width: | Height: | Size: 187 B |
BIN
assets/unpacked/ghost/clyde/left_a.png
Normal file
|
After Width: | Height: | Size: 194 B |
BIN
assets/unpacked/ghost/clyde/left_b.png
Normal file
|
After Width: | Height: | Size: 191 B |
BIN
assets/unpacked/ghost/clyde/right_a.png
Normal file
|
After Width: | Height: | Size: 194 B |
BIN
assets/unpacked/ghost/clyde/right_b.png
Normal file
|
After Width: | Height: | Size: 190 B |
BIN
assets/unpacked/ghost/clyde/up_a.png
Normal file
|
After Width: | Height: | Size: 184 B |
BIN
assets/unpacked/ghost/clyde/up_b.png
Normal file
|
After Width: | Height: | Size: 179 B |
BIN
assets/unpacked/ghost/eyes/down.png
Normal file
|
After Width: | Height: | Size: 127 B |
BIN
assets/unpacked/ghost/eyes/left.png
Normal file
|
After Width: | Height: | Size: 135 B |
BIN
assets/unpacked/ghost/eyes/right.png
Normal file
|
After Width: | Height: | Size: 134 B |
BIN
assets/unpacked/ghost/eyes/up.png
Normal file
|
After Width: | Height: | Size: 134 B |
BIN
assets/unpacked/ghost/frightened/blue_a.png
Normal file
|
After Width: | Height: | Size: 190 B |
BIN
assets/unpacked/ghost/frightened/blue_b.png
Normal file
|
After Width: | Height: | Size: 186 B |
BIN
assets/unpacked/ghost/frightened/white_a.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
assets/unpacked/ghost/frightened/white_b.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
assets/unpacked/ghost/inky/down_a.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
assets/unpacked/ghost/inky/down_b.png
Normal file
|
After Width: | Height: | Size: 175 B |