diff --git a/.gitignore b/.gitignore index 5c25562..5cce437 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ __pycache__/ # built zip are artifacts -- dist/INSTALL.md is the tracked source. /dist/*.zip /dist/*.livecode +/dist/*.oxtstack diff --git a/dist/INSTALL.md b/dist/INSTALL.md index e1f75b7..c5009bf 100644 --- a/dist/INSTALL.md +++ b/dist/INSTALL.md @@ -1,35 +1,43 @@ # Box2Dxt Platformer — Install & Run -Everything needed to run the **Box2Dxt** physics platformer is in this folder. -No C compiler, no internet, no extra downloads — just these files and an -**OpenXTalk (OXT)** or **LiveCode 9.6.3+** IDE you already have installed. +Everything needed to run the **Box2Dxt** physics platformer is in this package — +no C compiler, no internet, no extra downloads. You just need **OpenXTalk (OXT)** +installed (the demo is an OXT `.oxtstack`; OXT is the OpenXTalk fork of LiveCode, +and the extension/engine are also compatible with **LiveCode 9.6.3+**). ## What's in this package ``` -box2dxt-platformer/ -├── INSTALL.md ← you are here -├── box2dxt.lcb ← the Box2Dxt extension (the b2… physics API) -├── platformer.livecode ← the platformer stack (you open this last) -└── lib/ - ├── box2dxt.dll ← native library — Windows (x64) - ├── box2dxt.dylib ← native library — macOS (Intel + Apple Silicon) - └── box2dxt.so ← native library — Linux (x86-64) +NewPlateformerDemo.oxtstack ← the platformer stack (you open this) +source/ +├── box2dxt.lcb ← the Box2Dxt extension (the b2… physics API) +├── box2d_lc.c ← the C shim it binds to (source, for rebuilding) +└── box2dxt-kit.livecodescript ← the Kit (the friendly b2k… layer; also embedded + in the stack, so the demo runs on its own) +libraries/ +├── box2dxt.dll ← native library — Windows (x64) +├── box2dxt.dylib ← native library — macOS (Intel + Apple Silicon) +└── box2dxt.so ← native library — Linux (x86-64) +spritesheets/ ← the demo's art (point the stack here on first run) ``` -You only need the **one** `lib/` file for your operating system. +To **run** the demo you only touch three things, in order: a native library +(Step 1), the extension (Step 2), and the stack (Step 3). The `box2d_lc.c` and +the standalone Kit in `source/` are there for reference / rebuilding — you don't +need them to play. --- ## Step 1 · Put the native library where the engine can find it -Box2Dxt's physics run in a small native library. Copy the file for your OS: +Box2Dxt's physics run in a small native library. Copy the **one file for your +OS** from `libraries/`: | Your OS | Copy this file | To here | |---------|----------------|---------| -| **Windows** | `lib/box2dxt.dll` | the **same folder** as `platformer.livecode` | -| **macOS** | `lib/box2dxt.dylib` | the **same folder** as `platformer.livecode` | -| **Linux** | `lib/box2dxt.so` | a library search path — see the note below | +| **Windows** | `libraries/box2dxt.dll` | the **same folder** as `NewPlateformerDemo.oxtstack` | +| **macOS** | `libraries/box2dxt.dylib` | the **same folder** as `NewPlateformerDemo.oxtstack` | +| **Linux** | `libraries/box2dxt.so` | a library search path — see the note below | **Do not rename these files.** They are already the bare name the loader looks for (`box2dxt`, with no `lib` prefix). Renaming is the #1 cause of "unable to @@ -38,7 +46,7 @@ load foreign library". > **Linux only.** The dynamic loader does *not* search the stack's folder. Put > `box2dxt.so` somewhere the loader looks: > ``` -> sudo cp lib/box2dxt.so /usr/lib/ && sudo ldconfig +> sudo cp libraries/box2dxt.so /usr/lib/ && sudo ldconfig > ``` > (or place it next to the OXT engine binary, or add its folder to > `LD_LIBRARY_PATH` before launching OXT). @@ -47,15 +55,15 @@ load foreign library". ## Step 2 · Install the extension -The extension `box2dxt.lcb` adds the `b2…` physics handlers to the IDE. +The extension `source/box2dxt.lcb` adds the `b2…` physics handlers to the IDE. -1. Launch **OXT / LiveCode**. +1. Launch **OXT**. 2. Open **Tools → Extension Manager**. -3. Click **+ (Add)**, choose **`box2dxt.lcb`** from this folder, then click - **Load**. +3. Click **+ (Add)**, choose **`source/box2dxt.lcb`** from this package, then + click **Load**. -> Alternatively: **Tools → Extension Builder**, open `box2dxt.lcb`, and click -> **Test** — that compiles and loads it in one step. +> Alternatively: **Tools → Extension Builder**, open `source/box2dxt.lcb`, and +> click **Test** — that compiles and loads it in one step. **Confirm it worked:** open the **Message Box** (the small toolbar icon, or Ctrl/Cmd-M) and type: @@ -70,15 +78,16 @@ Press Enter. You should see **`4`**. If you get an error, see *Troubleshooting*. ## Step 3 · Open the platformer -Open **`platformer.livecode`** (File → Open Stack…, or double-click it). The -game builds itself and starts immediately. +Open **`NewPlateformerDemo.oxtstack`** (File → Open Stack…, or double-click it). +The game builds itself and starts immediately. -- **First run** may ask you to locate a spritesheet folder. Click **Cancel** to - play with the built-in placeholder art, or point it at a Kenney spritesheet - folder if you have one. Once you **save** the stack, the artwork is remembered - and loads instantly every time after. -- **Controls:** **arrows** or **WASD** to move · **Space** to jump · **↓** to - duck. Grab the coins and touch the flag to advance — there are four levels. +- **First run** asks you to locate a spritesheet folder — choose the + **`spritesheets/`** folder in this package. After you **save** the stack the + art is remembered (and cached), so it loads instantly every time after. +- **Controls:** **arrows / WASD** move · **Space** jump (press again in mid-air + to **double-jump**, or off a wall to **wall-jump**) · **SHIFT / X** dash · + **↓** duck · **R** restart · **M** mute. Grab every coin (the flag turns gold) + and touch the flag to advance — there are four levels. That's it. Enjoy the physics. @@ -88,13 +97,15 @@ That's it. Enjoy the physics. | Symptom | Fix | |---------|-----| -| `b2Version()` errors, or "handler not found" | The extension isn't loaded. Redo **Step 2** (Extension Manager → Load `box2dxt.lcb`). It loads per IDE session. | -| First physics call says **"unable to load foreign library"** | The native library isn't found. Make sure the `lib/` file for your OS sits **next to `platformer.livecode`** (Windows/macOS) or on a loader path (Linux). Don't rename it. *Tip: launch OXT from a terminal — it prints the exact filename it's looking for.* | -| `b2Version()` returns a number **other than 4** | Your `box2dxt.lcb` and the native library are from different builds. Use the `.lcb` and the `lib/` file **from this same package** together. | +| `b2Version()` errors, or "handler not found" | The extension isn't loaded. Redo **Step 2** (Extension Manager → Load `source/box2dxt.lcb`). It loads per IDE session. | +| First physics call says **"unable to load foreign library"** | The native library isn't found. Make sure the `libraries/` file for your OS sits **next to `NewPlateformerDemo.oxtstack`** (Windows/macOS) or on a loader path (Linux). Don't rename it. *Tip: launch OXT from a terminal — it prints the exact filename it's looking for.* | +| `b2Version()` returns a number **other than 4** | Your `box2dxt.lcb` and the native library are from different builds. Use the `source/box2dxt.lcb` and the `libraries/` file **from this same package** together. | +| The level loads with plain placeholder shapes (no art) | On first run, point the spritesheet prompt at this package's **`spritesheets/`** folder, then **save** the stack. (Hold **Shift** while pressing **R** to re-pick the folder.) | | The window opens but nothing moves, or a `b2…` call errors | The extension loaded but the native library didn't (or is the wrong build). Re-check Steps 1–2 and confirm `put b2Version()` is `4` before opening the stack. | --- *Box2Dxt is the Box2D v3 physics engine packaged for OpenXTalk and the xTalk -language family. The platformer is one of several example games. For the full -toolkit, source, and documentation, see the project repository.* +language family. The platformer is one of several example games. The `source/` +folder holds the full extension, its C shim, and the Kit; for the complete +toolkit and documentation, see the project repository.* diff --git a/docs/building.md b/docs/building.md index ad15a5a..8b4b25f 100644 --- a/docs/building.md +++ b/docs/building.md @@ -78,32 +78,37 @@ with it). ## Packaging a distribution zip To hand someone a ready-to-run game (no repo, no toolchain, no internet), -bundle the extension, the per-platform native libraries, a built **and saved** -stack, and the end-user install guide into one zip. `tools/make-release.py` -does it: +bundle the source (extension + C shim + Kit), the per-platform native +libraries, the demo's spritesheets, a built **and saved** stack, and the +end-user install guide into one zip. `tools/make-release.py` does it: ```sh # Build & SAVE the stack in OXT first (e.g. the platformer), then: -python3 tools/make-release.py --stack /path/to/platformer.livecode -# -> dist/box2dxt-platformer.zip +python3 tools/make-release.py --stack /path/to/NewPlateformerDemo.oxtstack +# -> dist/NewPlateformerDemo.zip ``` -It copies `src/box2dxt.lcb` and `dist/INSTALL.md`, renames each `prebuilt/` -library to the bare name the loader wants under `lib/`, and adds your saved -stack — producing: +It copies `src/box2dxt.lcb` / `box2d_lc.c` / `box2dxt-kit.livecodescript` into +`source/`, renames each `prebuilt/` library to the bare name the loader wants +under `libraries/`, copies the platformer's `Spritesheets/` art into +`spritesheets/`, adds `dist/INSTALL.md`, and drops your saved stack at the +root — producing: ``` -box2dxt-platformer/ -├── INSTALL.md # the three-step end-user install guide -├── box2dxt.lcb -├── platformer.livecode # your --stack -└── lib/box2dxt.{dll,dylib,so} +NewPlateformerDemo/ +├── NewPlateformerDemo.oxtstack # your --stack +├── INSTALL.md # the three-step end-user install guide +├── source/ box2dxt.lcb, box2d_lc.c, box2dxt-kit.livecodescript +├── libraries/ box2dxt.{dll,dylib,so} +└── spritesheets/ the demo's PNG + XML sheets ``` Override a library with `--win` / `--mac` / `--linux` (e.g. an SSE2 or -older-glibc build); `--check` validates the inputs without writing the zip. -The recipient just follows `INSTALL.md`: drop their platform's `lib/` file -beside the stack, **Load** `box2dxt.lcb`, open the stack. +older-glibc build), the art folder with `--sheets`, or the stack's in-zip name +with `--stack-name`; `--check` validates the inputs without writing the zip. +The recipient follows `INSTALL.md`: drop their platform's `libraries/` file +beside the stack, **Load** `source/box2dxt.lcb`, open the stack, and point its +first-run prompt at `spritesheets/`. ## Platform & CPU notes diff --git a/tools/make-release.py b/tools/make-release.py index 73ddedf..72dc509 100755 --- a/tools/make-release.py +++ b/tools/make-release.py @@ -1,21 +1,22 @@ #!/usr/bin/env python3 -"""Assemble a ready-to-ship Box2Dxt platformer zip. +"""Assemble a ready-to-ship Box2Dxt platformer package. -The zip is self-contained: the extension, the per-platform native libraries -(already renamed to the bare name the loader wants), a saved stack, and the -end-user install guide (dist/INSTALL.md). See its layout in that guide. +The zip is self-contained and matches dist/INSTALL.md: -The only thing this script can't produce is the *saved* stack -- you build and -save platformer.livecode in OXT first, then point --stack at it: + NewPlateformerDemo.oxtstack (your saved stack -- --stack) + INSTALL.md + source/ box2dxt.lcb, box2d_lc.c, box2dxt-kit.livecodescript + libraries/ box2dxt.dll, box2dxt.dylib, box2dxt.so (renamed to the bare name) + spritesheets/ the platformer's PNG + XML sheets - python3 tools/make-release.py --stack ~/Desktop/platformer.livecode +The only thing this script can't produce is the *saved* stack -- build and save +it in OXT first, then point --stack at it: -By default the native libraries come from prebuilt/ (the committed ABI-4 -binaries). Override any of them with --win / --mac / --linux if you have a -fresher or differently-tuned build. + python3 tools/make-release.py --stack ~/Desktop/NewPlateformerDemo.oxtstack -Run with --check to validate the inputs (and the embedded-Kit sync) without -writing the zip. +By default the native libraries come from prebuilt/ (the committed ABI-4 +binaries) and the art from Spritesheets/. Override the libs with --win / --mac / +--linux, or the art folder with --sheets. --check validates the inputs only. """ import argparse @@ -25,13 +26,22 @@ REPO = Path(__file__).resolve().parent.parent -# arcname (inside lib/) -> default source under prebuilt/ +# the extension, the C shim, and the Kit -> source/ +SOURCE_FILES = ["box2dxt.lcb", "box2d_lc.c", "box2dxt-kit.livecodescript"] + +# bare deploy name -> default committed binary (goes to libraries/) DEFAULT_LIBS = { "box2dxt.dll": "prebuilt/box2dxt-windows-x64.dll", "box2dxt.dylib": "prebuilt/libbox2dxt-macos-universal.dylib", "box2dxt.so": "prebuilt/libbox2dxt-linux-x86_64.so", } +# the spritesheets the platformer loads (PNG + XML each) -> spritesheets/ +PLATFORMER_SHEETS = [ + "spritesheet-characters-default", "spritesheet-enemies-default", + "spritesheet-tiles-default", "spritesheet-backgrounds-default", "enemies", +] + def human(nbytes): size = float(nbytes) @@ -42,39 +52,63 @@ def human(nbytes): return f"{size:.1f} MB" +def rel(src): + return src.relative_to(REPO) if src.is_relative_to(REPO) else src + + def main(): ap = argparse.ArgumentParser(description="Build the Box2Dxt platformer distribution zip.") - ap.add_argument("--stack", help="path to the built & saved platformer stack (.livecode)") - ap.add_argument("--out", default="dist/box2dxt-platformer.zip", help="output zip path (default: dist/box2dxt-platformer.zip)") - ap.add_argument("--top", default="box2dxt-platformer", help="top-level folder name inside the zip") - ap.add_argument("--stack-name", default="platformer.livecode", help="filename the stack gets inside the zip (the guide refers to this name)") - ap.add_argument("--win", help="override the Windows library (-> lib/box2dxt.dll)") - ap.add_argument("--mac", help="override the macOS library (-> lib/box2dxt.dylib)") - ap.add_argument("--linux", help="override the Linux library (-> lib/box2dxt.so)") + ap.add_argument("--stack", help="path to the built & saved platformer stack (.oxtstack)") + ap.add_argument("--out", default="dist/NewPlateformerDemo.zip", help="output zip path") + ap.add_argument("--top", default="NewPlateformerDemo", help="top-level folder name inside the zip") + ap.add_argument("--stack-name", default="NewPlateformerDemo.oxtstack", help="filename the stack gets inside the zip (the guide refers to this name)") + ap.add_argument("--sheets", default="Spritesheets", help="folder holding the platformer's spritesheets") + ap.add_argument("--win", help="override the Windows library (-> libraries/box2dxt.dll)") + ap.add_argument("--mac", help="override the macOS library (-> libraries/box2dxt.dylib)") + ap.add_argument("--linux", help="override the Linux library (-> libraries/box2dxt.so)") ap.add_argument("--check", action="store_true", help="validate inputs only; do not write the zip") args = ap.parse_args() - # Resolve the four always-bundled pieces + the per-platform libraries. items = [] # (arcname-relative-to-top, source Path) problems = [] - lcb = REPO / "src" / "box2dxt.lcb" - install = REPO / "dist" / "INSTALL.md" - for arc, src in (("box2dxt.lcb", lcb), ("INSTALL.md", install)): + # source/ : the extension, the C shim, the Kit + for name in SOURCE_FILES: + src = REPO / "src" / name if src.is_file(): - items.append((arc, src)) + items.append((f"source/{name}", src)) else: - problems.append(f"missing required file: {src.relative_to(REPO) if src.is_relative_to(REPO) else src}") + problems.append(f"missing source file: src/{name}") + # the install guide, at the root + install = REPO / "dist" / "INSTALL.md" + if install.is_file(): + items.append(("INSTALL.md", install)) + else: + problems.append("missing dist/INSTALL.md") + + # libraries/ : the per-platform native libs, renamed to the bare name overrides = {"box2dxt.dll": args.win, "box2dxt.dylib": args.mac, "box2dxt.so": args.linux} for bare, default_rel in DEFAULT_LIBS.items(): src = Path(overrides[bare]).expanduser() if overrides[bare] else (REPO / default_rel) if src.is_file(): - items.append((f"lib/{bare}", src)) + items.append((f"libraries/{bare}", src)) else: - problems.append(f"missing library for lib/{bare}: {src}") - - # The saved stack is required to build the zip (but not just to --check). + problems.append(f"missing library for libraries/{bare}: {src}") + + # spritesheets/ : the demo's art (PNG + XML pairs) + sheets_dir = Path(args.sheets).expanduser() + if not sheets_dir.is_absolute(): + sheets_dir = REPO / args.sheets + for base in PLATFORMER_SHEETS: + for ext in ("png", "xml"): + src = sheets_dir / f"{base}.{ext}" + if src.is_file(): + items.append((f"spritesheets/{base}.{ext}", src)) + else: + problems.append(f"missing spritesheet: {src}") + + # the saved stack, at the root (required to build the zip, not just to --check) stack = None if args.stack: stack = Path(args.stack).expanduser() @@ -83,7 +117,7 @@ def main(): else: problems.append(f"--stack is not a file: {stack}") elif not args.check: - problems.append("--stack is required (build & save platformer.livecode in OXT first)") + problems.append("--stack is required (build & save the .oxtstack in OXT first)") if problems: print("Cannot build the release:", file=sys.stderr) @@ -93,19 +127,19 @@ def main(): print(f"Release contents (top folder: {args.top}/):") for arc, src in items: - print(f" {arc:<22} <- {src if not src.is_relative_to(REPO) else src.relative_to(REPO)} ({human(src.stat().st_size)})") + print(f" {arc:<34} <- {rel(src)} ({human(src.stat().st_size)})") if args.check: print("\n--check: inputs valid" + ("" if stack else " (no --stack given; a real build needs one)") + ".") return 0 - out = (REPO / args.out) if not Path(args.out).is_absolute() else Path(args.out) + out = Path(args.out) if Path(args.out).is_absolute() else (REPO / args.out) out.parent.mkdir(parents=True, exist_ok=True) with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z: for arc, src in items: z.write(src, f"{args.top}/{arc}") - print(f"\nWrote {out.relative_to(REPO) if out.is_relative_to(REPO) else out} ({human(out.stat().st_size)}).") + print(f"\nWrote {rel(out)} ({human(out.stat().st_size)}).") return 0