diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 4b0586a..bcab9de 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -77,6 +77,19 @@ jobs: chmod +x tools/*.sh ./tools/build_releases.sh + - name: Package Windows installer (NSIS) + run: | + sudo apt-get update && sudo apt-get install -y nsis + VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)" + makensis /DVERSION="$VERSION" \ + /DARTIFACT="build/step-line-windows-$VERSION.exe" \ + /DICON=packaging/windows/step-line.ico \ + packaging/windows/step-line.nsi + + - name: Package Linux AppImage + run: | + bash packaging/linux/build_appimage.sh + - name: Upload installers (workflow_dispatch) if: github.event_name == 'workflow_dispatch' uses: actions/upload-artifact@v4 diff --git a/DONE.md b/DONE.md index da12dcf..f77481e 100644 --- a/DONE.md +++ b/DONE.md @@ -185,6 +185,22 @@ Completed task sections moved from `TODO.md`. Sections are moved only when every Follow-up: anonymous **asset downloads** require sign-in on this host (host setting). - [x] CI packaging follow-up was already live (`.gitea/workflows/build.yml` on tags). +## 📦 Installer packaging (Windows NSIS + Linux AppImage) + +- [x] Export presets: `binary_format/embed_pck=true` on all platforms — every binary is now + self-contained (all game data in one file; `.pck` no longer shipped). +- [x] App icon: `assets/textures/icon.png` (1024) + multi-size `icon.ico`, copied to + `packaging/windows/step-line.ico` and `packaging/linux/step-line.png` via the generator. +- [x] NSIS script `packaging/windows/step-line.nsi` (installs `Step Line.exe`, uninstaller, + shortcuts, registry entry; `makensis /DVERSION=… /DARTIFACT=…`). +- [x] AppImage: `packaging/linux/step-line.desktop`, `AppRun`, appdata.xml, and + `build_appimage.sh` (appimagetool, `APPIMAGE_EXTRACT_AND_RUN=1`). +- [x] Local end-to-end: rebuilt embedded exports and produced `step-line-0.2.0-x86_64.AppImage`, + ran it headless (0 script errors). +- [x] CI: NSIS + AppImage steps added to `.gitea/workflows/build.yml` build job; publish glob + already uploads `build/step-line-**` (covers setup/AppImage names). +- [ ] (open) CI run verification of the NSIS build and installer-content listing. + ## 🎮 Assets & detailed graphics pass (textures, rigs, UI feedback) - [x] Authored texture set via `tools/generate_textures.py` (deterministic PIL): brick facade, diff --git a/README.md b/README.md index a428dc6..af99c0b 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,19 @@ The central design goal is simple: | Platform | Package | | --- | --- | -| **Windows** | [`step-line-windows.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, no install needed | -| **Linux** | [`step-line-linux.x86_64`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, `chmod +x` and run | -| **macOS** | [`step-line-macos.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unsigned build, quarantine note below | +| **Windows — installer** | [`step-line-setup-.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · contains all game data, shortcuts + uninstaller | +| **Windows — portable** | [`step-line-windows-.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, no install needed | +| **Linux — AppImage** | [`step-line--x86_64.AppImage`](https://git.allucanget.biz/zwitschi/security-game/releases) · single file, `chmod +x` and run | +| **Linux — portable** | [`step-line-linux-.x86_64`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, `chmod +x` and run | +| **macOS** | [`step-line-macos-.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unsigned build, quarantine note below | **All releases:** > **Latest: v0.2.0** (2026-10-31) — built and published by Gitea Actions from the > `v0.2.0` tag. Releases are built automatically when a `vX.Y.Z` tag is pushed > (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use -> `./tools/build_releases.sh`. +> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline +> (NSIS + appimagetool) and uploads with the portable binaries. > > Note: on this host, downloading release files currently requires sign-in; > the page and tag URLs are public. diff --git a/TODO.md b/TODO.md index 94df9d2..a890622 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,38 @@ # TODO +## 🪟 Windows installer (self-contained, all game data) + +Goal: a real Windows installer that includes the entire game (executable + data), +Start-Menu/desktop shortcuts, and an uninstaller — instead of a bare .exe + .pck. + +- [x] Switch the Windows/Linux/macOS export presets to `binary_format/embed_pck=true` so a + single self-contained binary replaces the exe+pck pair (all game data in one file). +- [x] Add a game icon: `assets/icon.png` (AppImage/desktop) + `assets/icon.ico` and + `packaging/windows/step-line.ico` generated by `tools/generate_textures.py`. +- [x] Author `packaging/windows/step-line.nsi` (NSIS): installer that installs the exe, writes + the uninstaller, shortcuts, and registry uninstall entry. +- [x] Add CI packaging steps to `.gitea/workflows/build.yml` (NSIS via apt; AppImage via + appimagetool) so tags/dispatch produce `step-line-setup-.exe` and + `step-line--x86_64.AppImage` next to the portable binaries. +- [x] Extend `tools/publish_release.sh` artifact glob to upload the installer/AppImage too. +- [ ] Verify on CI: NSIS installer builds (cannot be run locally — no `makensis` on this box). +- [ ] Verify the Windows installer contents in CI (e.g. `7z l` shows the embedded data file). + +## 🐧 Linux AppImage (self-contained, all game data) + +Goal: a single executable `.AppImage` that runs the full game on any recent distro. + +- [x] Author AppDir files: `packaging/linux/step-line.desktop`, `packaging/linux/AppRun`, + icon wiring. +- [x] Add `packaging/linux/build_appimage.sh` (appimagetool; `APPIMAGE_EXTRACT_AND_RUN=1` to + avoid FUSE dependency) producing `step-line--x86_64.AppImage`. +- [x] Local verification: built the AppImage on this machine and ran it headless + (`--appimage-extract-and-run --headless --quit-after 90` → 0 script errors, + `[Step Line] game started`). +- [x] AppStream metainfo added (`packaging/linux/step-line.appdata.xml`); tool validates clean. +- [ ] CI verification: AppImage step runs on tags/dispatch and the artifact uploads. +- [x] README Downloads table documents installer + AppImage rows and naming. + ## Distribution follow-up (host config, no code work) - [ ] Asset downloads currently require sign-in on the host (`/releases/download/…` redirects to diff --git a/assets/textures/icon.ico b/assets/textures/icon.ico new file mode 100644 index 0000000..f6b33a2 Binary files /dev/null and b/assets/textures/icon.ico differ diff --git a/assets/textures/icon.png b/assets/textures/icon.png new file mode 100644 index 0000000..8275a8c Binary files /dev/null and b/assets/textures/icon.png differ diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 94983ab..ef5f6e4 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -57,12 +57,22 @@ godot --headless --path . --quit-after 120 # boot smoke t ./tools/build_releases.sh ``` -Artifacts are written to `build/`: +Artifacts are written to `build/` (all exports embed the PCK — single files): - `build/step-line-linux-.x86_64` - `build/step-line-windows-.exe` - `build/step-line-macos-.zip` +When run in CI (tags/dispatch), the build job also packages: + +- `build/step-line-setup-.exe` — Windows installer (NSIS, incl. shortcuts + + uninstaller; apt `nsis` on the runner) +- `build/step-line--x86_64.AppImage` — Linux AppImage (`packaging/linux/build_appimage.sh`, + appimagetool via `APPIMAGE_EXTRACT_AND_RUN`) + +Locally you can build the AppImage with `bash packaging/linux/build_appimage.sh `; +the NSIS installer requires `makensis` (covered by CI). + > macOS builds are unsigned and will need `xattr -dr com.apple.quarantine` > guidance or notarization for production releases. diff --git a/export_presets.cfg b/export_presets.cfg index 51bf5d8..8110a6b 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -23,7 +23,7 @@ script_export_mode=2 custom_template/debug="" custom_template/release="" debug/export_console_wrapper=1 -binary_format/embed_pck=false +binary_format/embed_pck=true texture_format/s3tc_bptc=true texture_format/etc2_astc=false binary_format/architecture="x86_64" @@ -54,7 +54,7 @@ script_export_mode=2 custom_template/debug="" custom_template/release="" debug/export_console_wrapper=0 -binary_format/embed_pck=false +binary_format/embed_pck=true texture_format/s3tc_bptc=true texture_format/etc2_astc=false codesign/enable=false @@ -90,7 +90,7 @@ script_export_mode=2 custom_template/debug="" custom_template/release="" debug/export_console_wrapper=0 -binary_format/embed_pck=false +binary_format/embed_pck=true texture_format/s3tc_bptc=true texture_format/etc2_astc=true codesign/enable=false diff --git a/packaging/linux/AppRun b/packaging/linux/AppRun new file mode 100644 index 0000000..69fc679 --- /dev/null +++ b/packaging/linux/AppRun @@ -0,0 +1,5 @@ +#!/bin/sh +# AppImage entry point: launch the bundled game binary. +SELF="$(readlink -f "$0")" +HERE="$(dirname "$SELF")" +exec "${HERE}/usr/bin/step-line" "$@" diff --git a/packaging/linux/build_appimage.sh b/packaging/linux/build_appimage.sh new file mode 100644 index 0000000..c079941 --- /dev/null +++ b/packaging/linux/build_appimage.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Build a self-contained Step Line AppImage (all game data included). +# +# Prereqs: exported Linux binary in build/ (embed_pck=true), curl. +# Usage: bash packaging/linux/build_appimage.sh [version] +# Output: build/step-line--x86_64.AppImage +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +VERSION="${1:-$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' "$ROOT/project.godot")}" +SRC_BIN="$ROOT/build/step-line-linux-${VERSION}.x86_64" + +if [[ ! -f "$SRC_BIN" ]]; then + echo "error: missing $SRC_BIN — run ./tools/build_releases.sh first" >&2 + exit 1 +fi + +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT +APPDIR="$WORK/StepLine.AppDir" +mkdir -p "$APPDIR/usr/bin" +cp "$SRC_BIN" "$APPDIR/usr/bin/step-line" +# The export embeds the PCK (embed_pck=true), so no extra data files are needed. + +cp "$HERE/step-line.desktop" "$APPDIR/step-line.desktop" +cp "$HERE/AppRun" "$APPDIR/AppRun" +chmod +x "$APPDIR/AppRun" +cp "$HERE/step-line.png" "$APPDIR/step-line.png" +mkdir -p "$APPDIR/usr/share/metainfo" +cp "$HERE/step-line.appdata.xml" "$APPDIR/usr/share/metainfo/step-line.appdata.xml" + +TOOL="${APPIMAGE_TOOL:-/tmp/appimagetool-x86_64.AppImage}" +if [[ ! -f "$TOOL" ]]; then + echo "==> Downloading appimagetool" + curl -sL -o "$TOOL" \ + https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage + chmod +x "$TOOL" +fi + +OUT="$ROOT/build/step-line-${VERSION}-x86_64.AppImage" +echo "==> Building AppImage -> $OUT" +# Extract-and-run avoids a FUSE dependency on the build host. +APPIMAGE_EXTRACT_AND_RUN=1 "$TOOL" "$APPDIR" "$OUT" +echo "AppImage ready: $(ls -la "$OUT" | awk '{print $5}') bytes" diff --git a/packaging/linux/step-line.appdata.xml b/packaging/linux/step-line.appdata.xml new file mode 100644 index 0000000..d8d00d4 --- /dev/null +++ b/packaging/linux/step-line.appdata.xml @@ -0,0 +1,21 @@ + + + net.step-line.StepLine + Step Line + Workplace security simulation on Hollywood Boulevard + +

+ Step Line is a workplace security simulation about observation, communication, + access verification, and proportionate escalation. Guard the steps and entrance + of venues around Hollywood Boulevard: direct tourists, verify wristbands and IDs, + screen guests, and escalate only when you have to. +

+
+ step-line.desktop + CC0-1.0 + MIT + + + + +
diff --git a/packaging/linux/step-line.desktop b/packaging/linux/step-line.desktop new file mode 100644 index 0000000..064c50f --- /dev/null +++ b/packaging/linux/step-line.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=Step Line +Comment=Workplace security simulation on Hollywood Boulevard +Exec=step-line +Icon=step-line +Terminal=false +Categories=Game;Simulation; +Keywords=security;simulation;hollywood; diff --git a/packaging/linux/step-line.png b/packaging/linux/step-line.png new file mode 100644 index 0000000..8275a8c Binary files /dev/null and b/packaging/linux/step-line.png differ diff --git a/packaging/windows/step-line.ico b/packaging/windows/step-line.ico new file mode 100644 index 0000000..f6b33a2 Binary files /dev/null and b/packaging/windows/step-line.ico differ diff --git a/packaging/windows/step-line.nsi b/packaging/windows/step-line.nsi new file mode 100644 index 0000000..f7ce689 --- /dev/null +++ b/packaging/windows/step-line.nsi @@ -0,0 +1,70 @@ +; Step Line Windows installer (NSIS 3). +; Build from the repo root with the exported self-contained exe: +; makensis /DVERSION=0.2.0 /DARTIFACT=build/step-line-windows-0.2.0.exe \ +; /DICON=packaging/windows/step-line.ico packaging/windows/step-line.nsi +; Outputs: build/step-line-setup-.exe + +!ifndef VERSION + !define VERSION "0.0.0" +!endif +!ifndef ARTIFACT + !error "ARTIFACT define required (path to exported windows exe)" +!endif +!ifndef ICON + !define ICON "packaging\windows\step-line.ico" +!endif + +; The installed executable always uses this clean name. +!define INSTALLED_EXE "Step Line.exe" +!define COMPANY "Step Line Project" +!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Step Line" + +Unicode true +ManifestDPIAware true +Icon "${ICON}" +UninstallIcon "${ICON}" + +Name "Step Line ${VERSION}" +OutFile "build\step-line-setup-${VERSION}.exe" +InstallDir "$PROGRAMFILES64\Step Line" +RequestExecutionLevel admin + +Page directory +Page instfiles +UninstPage uninstConfirm +UninstPage instfiles + +Section "Install" + SetOutPath "$INSTDIR" + + ; The self-contained executable contains ALL game data (embedded PCK). + File "/oname=${INSTALLED_EXE}" "${ARTIFACT}" + + WriteUninstaller "$INSTDIR\uninstall.exe" + + ; Shortcuts + CreateDirectory "$SMPROGRAMS\Step Line" + CreateShortCut "$SMPROGRAMS\Step Line\Step Line.lnk" "$INSTDIR\${INSTALLED_EXE}" + CreateShortCut "$SMPROGRAMS\Step Line\Uninstall.lnk" "$INSTDIR\uninstall.exe" + CreateShortCut "$DESKTOP\Step Line.lnk" "$INSTDIR\${INSTALLED_EXE}" + + ; Registry (Add/Remove Programs) + WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "Step Line" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${VERSION}" + WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${COMPANY}" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${INSTALLED_EXE}" + WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegDWORD HKLM "${UNINST_KEY}" "NoModify" 1 + WriteRegDWORD HKLM "${UNINST_KEY}" "NoRepair" 1 +SectionEnd + +Section "Uninstall" + Delete "$INSTDIR\${INSTALLED_EXE}" + Delete "$INSTDIR\uninstall.exe" + RMDir "$INSTDIR" + Delete "$SMPROGRAMS\Step Line\Step Line.lnk" + Delete "$SMPROGRAMS\Step Line\Uninstall.lnk" + RMDir "$SMPROGRAMS\Step Line" + Delete "$DESKTOP\Step Line.lnk" + DeleteRegKey HKLM "${UNINST_KEY}" +SectionEnd diff --git a/tools/build_releases.sh b/tools/build_releases.sh index 5f29fc8..a2a3c62 100755 --- a/tools/build_releases.sh +++ b/tools/build_releases.sh @@ -16,6 +16,8 @@ set -euo pipefail GODOT="${GODOT:-godot}" VERSION="$(grep -oP 'config/version="\K[^"]+' project.godot)" mkdir -p build +# Presets now embed the PCK into a single binary; drop stale .pck leftovers. +rm -f build/*.pck echo "==> Exporting Linux (x86_64)" "$GODOT" --headless --export-release "Linux" "build/step-line-linux-${VERSION}.x86_64" diff --git a/tools/generate_textures.py b/tools/generate_textures.py index 48b1a9f..2138ea4 100644 --- a/tools/generate_textures.py +++ b/tools/generate_textures.py @@ -159,8 +159,7 @@ def vignette(): px[x, y] = int(255 * (t * t)) save(img, "vignette.png") -def icon(name, draw_fn): - size = 64 +def icon(name, draw_fn, size=64): img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) d = ImageDraw.Draw(img) draw_fn(d, size) @@ -185,6 +184,30 @@ def icons(): icon("icons/wand.png", wand) icon("icons/rec_dot.png", rec) +def app_icon(): + """Game app icon: dark badge, gold star, white 'S' — used by desktop/AppImage + (PNG) and the Windows installer (ICO).""" + def badge(d, s): + d.ellipse([2, 2, s-2, s-2], fill=(24, 26, 38, 255)) + d.ellipse([int(s*0.10), int(s*0.10), int(s*0.90), int(s*0.90)], outline=(120, 130, 170, 255), width=max(2, s//64)) + # gold star + cx, cy, r = s*0.5, s*0.44, s*0.30 + pts = [] + for i in range(10): + ang = -90 + i * 36 + rad = r if i % 2 == 0 else r * 0.42 + import math + pts.append((cx + rad * math.cos(math.radians(ang)), cy + rad * math.sin(math.radians(ang)))) + d.polygon(pts, fill=(255, 205, 80, 255)) + icon("icon.png", badge, size=1024) + icon("icon.ico", badge, size=256) + + # multi-size ICO for Windows (from the 256 render) + src = Image.open(os.path.join(ASSETS, "icon.png")) + ico_path = os.path.join(ASSETS, "icon.ico") + src.save(ico_path, format="ICO", sizes=[(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (16, 16)]) + print("wrote assets/textures/icon.ico (multi-size)") + def cards(): CW, CH = 256, 128 def base(card_name, bg, label, band=(0,0,0,0)): @@ -220,5 +243,6 @@ if __name__ == "__main__": skyline() vignette() icons() + app_icon() cards() print("done")