Compare commits

..
3 Commits
Author SHA1 Message Date
zwitschi 0f076205af Fix NSIS installer build: absolute paths via defines, space-free installed exe, BMP ICO
Full build / Import + headless tests (push) Successful in 31s
Full build / Full desktop build + optional release (push) Successful in 3m44s
- makensis resolves relative -DICON/-DARTIFACT against the script directory,
  so CI now passes $PWD-based absolute paths (reproduced locally in the same
  ubuntu:24.04 container; installer builds, 7z shows step-line.exe +
  uninstall.exe)
- Install as step-line.exe (no spaces: NSIS /oname splits on whitespace)
- OutFile overridable via -DOUT
- tools/generate_textures.py icon.ico now produced via ImageMagick with
  classic BMP entries (NSIS-compatible); PIL PNG-compressed ICOs rejected by
  NSIS
2026-10-31 14:22:55 +01:00
zwitschi 8bc84c1f36 Harden installer icon + NSIS rebuild path
Full build / Full desktop build + optional release (push) Failing after 3m24s
Full build / Import + headless tests (push) Successful in 27s
- Regenerate icon.ico with classic BMP entries (16/32/48) — PNG-compressed
  ICO entries can fail NSIS' icon loader on some builds
- CI copies assets/textures/icon.ico onto the packaging path right before
  makensis, removing any stale/missing-copy failures
- Recreate v0.2.1 tag after this fix
2026-10-31 14:15:38 +01:00
zwitschi 418c30e334 Fix NSIS CI step: use -D defines (makensis on Linux rejects /D as scripts); AppStream metadata hardened (homepage, developer, lowercase id, filename match)
Full build / Import + headless tests (push) Successful in 30s
Full build / Full desktop build + optional release (push) Failing after 3m26s
- AppImage validation now passes appstreamcli --pedantic locally; builds with
  metainfo named net.stepline.game.appdata.xml + desktop in usr/share/applications
2026-10-31 14:10:04 +01:00
28 changed files with 131 additions and 25 deletions

No files matched your search

+10 -4
View File
@@ -81,16 +81,22 @@ jobs:
run: | run: |
sudo apt-get update && sudo apt-get install -y nsis p7zip-full sudo apt-get update && sudo apt-get install -y nsis p7zip-full
VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)" VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)"
makensis /DVERSION="$VERSION" \ # NOTE: on Linux, makensis uses -D defines (the /D form is Windows-only
/DARTIFACT="build/step-line-windows-$VERSION.exe" \ # and is rejected as a script path), and it resolves relative paths
/DICON=packaging/windows/step-line.ico \ # against the script directory — pass absolute paths from the workspace.
cp assets/textures/icon.ico packaging/windows/step-line.ico
makensis \
-DVERSION="$VERSION" \
-DARTIFACT="$PWD/build/step-line-windows-$VERSION.exe" \
-DICON="$PWD/packaging/windows/step-line.ico" \
-DOUT="$PWD/build/step-line-setup-$VERSION.exe" \
packaging/windows/step-line.nsi packaging/windows/step-line.nsi
- name: Verify Windows installer contents - name: Verify Windows installer contents
run: | run: |
VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)" VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)"
ls -la build/step-line-setup-$VERSION.exe ls -la build/step-line-setup-$VERSION.exe
7z l build/step-line-setup-$VERSION.exe | grep -E "Step Line.exe|uninstall.exe" 7z l build/step-line-setup-$VERSION.exe | grep -E "step-line.exe|uninstall.exe"
- name: Package Linux AppImage - name: Package Linux AppImage
run: | run: |
+4
View File
@@ -68,3 +68,7 @@ performance hardening only.
- [ ] Profile the crowd: ensure textured materials and props hold target frame rate around a - [ ] Profile the crowd: ensure textured materials and props hold target frame rate around a
busy entrance. busy entrance.
- [ ] Add LOD or culling for distant NPCs/props if the crowd grows. - [ ] Add LOD or culling for distant NPCs/props if the crowd grows.
## Screenshot Refresh
- [ ] Regenerate and verify the six README screenshots from the current build.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 237 KiB

+5 -2
View File
@@ -27,8 +27,11 @@ cp "$HERE/step-line.desktop" "$APPDIR/step-line.desktop"
cp "$HERE/AppRun" "$APPDIR/AppRun" cp "$HERE/AppRun" "$APPDIR/AppRun"
chmod +x "$APPDIR/AppRun" chmod +x "$APPDIR/AppRun"
cp "$HERE/step-line.png" "$APPDIR/step-line.png" cp "$HERE/step-line.png" "$APPDIR/step-line.png"
mkdir -p "$APPDIR/usr/share/metainfo" # Desktop entry + metainfo in standard locations (AppStream validation requires
cp "$HERE/step-line.appdata.xml" "$APPDIR/usr/share/metainfo/step-line.appdata.xml" # the metainfo filename to match the component id, and the .desktop discoverable).
mkdir -p "$APPDIR/usr/share/applications" "$APPDIR/usr/share/metainfo"
cp "$HERE/step-line.desktop" "$APPDIR/usr/share/applications/step-line.desktop"
cp "$HERE/step-line.appdata.xml" "$APPDIR/usr/share/metainfo/net.stepline.game.appdata.xml"
TOOL="${APPIMAGE_TOOL:-/tmp/appimagetool-x86_64.AppImage}" TOOL="${APPIMAGE_TOOL:-/tmp/appimagetool-x86_64.AppImage}"
if [[ ! -f "$TOOL" ]]; then if [[ ! -f "$TOOL" ]]; then
+3 -1
View File
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application"> <component type="desktop-application">
<id>net.step-line.StepLine</id> <id>net.stepline.game</id>
<name>Step Line</name> <name>Step Line</name>
<summary>Workplace security simulation on Hollywood Boulevard</summary> <summary>Workplace security simulation on Hollywood Boulevard</summary>
<url type="homepage">https://git.allucanget.biz/zwitschi/security-game</url>
<developer id="net.stepline"><name>Step Line Team</name></developer>
<description> <description>
<p> <p>
Step Line is a workplace security simulation about observation, communication, Step Line is a workplace security simulation about observation, communication,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+7 -3
View File
@@ -14,8 +14,12 @@
!define ICON "packaging\windows\step-line.ico" !define ICON "packaging\windows\step-line.ico"
!endif !endif
; The installed executable always uses this clean name. ; The installed executable uses this clean name (no spaces — NSIS /oname tokens
!define INSTALLED_EXE "Step Line.exe" ; on whitespace, so a spaced name breaks the File directive).
!define INSTALLED_EXE "step-line.exe"
!ifndef OUT
!define OUT "build\step-line-setup-${VERSION}.exe"
!endif
!define COMPANY "Step Line Project" !define COMPANY "Step Line Project"
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Step Line" !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Step Line"
@@ -25,7 +29,7 @@ Icon "${ICON}"
UninstallIcon "${ICON}" UninstallIcon "${ICON}"
Name "Step Line ${VERSION}" Name "Step Line ${VERSION}"
OutFile "build\step-line-setup-${VERSION}.exe" OutFile "${OUT}"
InstallDir "$PROGRAMFILES64\Step Line" InstallDir "$PROGRAMFILES64\Step Line"
RequestExecutionLevel admin RequestExecutionLevel admin
+1
View File
@@ -0,0 +1 @@
appimagetool.png
+10
View File
@@ -0,0 +1,10 @@
#! /bin/sh
set -e
this_dir="$(dirname -- "$(readlink -f -- "$0")")"
# make appimagetool prefer the bundled mksquashfs
export PATH="$this_dir"/usr/bin:"$PATH"
exec "$this_dir"/usr/bin/appimagetool "$@"
+1
View File
@@ -0,0 +1 @@
usr/share/applications/appimagetool.desktop
+1
View File
@@ -0,0 +1 @@
usr/share/icons/hicolor/128x128/apps/appimagetool.png
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=appimagetool
Exec=appimagetool
Comment=Tool to generate AppImages from AppDirs
Icon=appimagetool
Categories=Development;
Terminal=true
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>appimagetool.desktop</id>
<metadata_license>MIT</metadata_license>
<project_license>MIT</project_license>
<name>appimagetool</name>
<summary>Create AppImage files</summary>
<description>
<p>
appimagetool is a tool that lets you generate AppImage files. An AppImage
is a self-running bundle that contains an application and everything
it needs to run that cannot reasonably expected to be part of each target system.
</p>
<p>
This can include libraries that are not commonly available on target systems,
resources such as translations, icons, fonts, and other auxiliary files.
appimagetool makes it easy to take an AppDir (e.g., generated by linuxdeployqt)
and turn it into an AppImage.
</p>
<p>
You can also embed update information so that your users can update the AppImage
using binary delta updates with the AppImageUpdate tool.
You can also sign the AppImage with GPG.
</p>
</description>
<url type="homepage">https://github.com/AppImage/AppImageKit</url>
<project_group>AppImage</project_group>
<provides>
<binary>appimagetool</binary>
</provides>
</component>
+15 -7
View File
@@ -140,7 +140,7 @@ func _init() -> void:
# --- body-camera overlay (REC + shift clock + corner brackets, top right) --- # --- body-camera overlay (REC + shift clock + corner brackets, top right) ---
var cam_box := VBoxContainer.new() var cam_box := VBoxContainer.new()
cam_box.set_anchors_preset(Control.PRESET_TOP_RIGHT) cam_box.set_anchors_preset(Control.PRESET_TOP_RIGHT)
cam_box.position = Vector2(-180, 12) cam_box.position = Vector2(-180, 50)
cam_box.alignment = BoxContainer.ALIGNMENT_END cam_box.alignment = BoxContainer.ALIGNMENT_END
add_child(cam_box) add_child(cam_box)
var rec_row := HBoxContainer.new() var rec_row := HBoxContainer.new()
@@ -180,9 +180,12 @@ func _init() -> void:
icon_row.add_child(wand_icon) icon_row.add_child(wand_icon)
# --- inspect panel (popup center) --- # --- inspect panel (popup center) ---
var inspect_center := CenterContainer.new()
inspect_center.set_anchors_preset(Control.PRESET_FULL_RECT)
inspect_center.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(inspect_center)
inspect_panel = PanelContainer.new() inspect_panel = PanelContainer.new()
inspect_panel.visible = false inspect_panel.visible = false
inspect_panel.set_anchors_preset(Control.PRESET_CENTER)
inspect_panel.modulate.a = 1.0 inspect_panel.modulate.a = 1.0
var ivbox := VBoxContainer.new() var ivbox := VBoxContainer.new()
inspect_panel.add_child(ivbox) inspect_panel.add_child(ivbox)
@@ -197,12 +200,15 @@ func _init() -> void:
insp_hint.text = "A = allow entry D = deny entry" insp_hint.text = "A = allow entry D = deny entry"
insp_hint.add_theme_font_size_override("font_size", 12) insp_hint.add_theme_font_size_override("font_size", 12)
ivbox.add_child(insp_hint) ivbox.add_child(insp_hint)
add_child(inspect_panel) inspect_center.add_child(inspect_panel)
# --- wand screening panel (popup center) --- # --- wand screening panel (popup center) ---
var wand_center := CenterContainer.new()
wand_center.set_anchors_preset(Control.PRESET_FULL_RECT)
wand_center.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(wand_center)
wand_panel = PanelContainer.new() wand_panel = PanelContainer.new()
wand_panel.visible = false wand_panel.visible = false
wand_panel.set_anchors_preset(Control.PRESET_CENTER)
var wvbox := VBoxContainer.new() var wvbox := VBoxContainer.new()
wand_panel.add_child(wvbox) wand_panel.add_child(wvbox)
wand_text = Label.new() wand_text = Label.new()
@@ -216,7 +222,7 @@ func _init() -> void:
wand_hint.text = "A = admit D = refuse entry" wand_hint.text = "A = admit D = refuse entry"
wand_hint.add_theme_font_size_override("font_size", 12) wand_hint.add_theme_font_size_override("font_size", 12)
wvbox.add_child(wand_hint) wvbox.add_child(wand_hint)
add_child(wand_panel) wand_center.add_child(wand_panel)
# --- debug overlay --- # --- debug overlay ---
debug_label = Label.new() debug_label = Label.new()
@@ -374,6 +380,7 @@ func show_inspection(controller: Object, artifacts: Array, result: Dictionary) -
continue continue
var card := TextureRect.new() var card := TextureRect.new()
card.texture = tex card.texture = tex
card.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
card.custom_minimum_size = Vector2(92, 58) card.custom_minimum_size = Vector2(92, 58)
card.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED card.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
card.tooltip_text = artifact.summary() card.tooltip_text = artifact.summary()
@@ -382,7 +389,8 @@ func show_inspection(controller: Object, artifacts: Array, result: Dictionary) -
card_row.add_child(card) card_row.add_child(card)
added_card = true added_card = true
if added_card: if added_card:
inspect_panel.add_child(card_row) var content := inspect_panel.get_child(0) as VBoxContainer
content.add_child(card_row)
var lines: Array[String] = [] var lines: Array[String] = []
for artifact in artifacts: for artifact in artifacts:
lines.append(" • " + artifact.summary()) lines.append(" • " + artifact.summary())
@@ -403,7 +411,7 @@ func _clear_card_row() -> void:
return return
var row := inspect_panel.find_child("INSPECT_CARD_ROW", true, false) as Control var row := inspect_panel.find_child("INSPECT_CARD_ROW", true, false) as Control
if row: if row:
inspect_panel.remove_child(row) row.get_parent().remove_child(row)
row.queue_free() row.queue_free()
func show_wand(controller: Object, result: Dictionary) -> void: func show_wand(controller: Object, result: Dictionary) -> void:
+5 -2
View File
@@ -34,9 +34,12 @@ func _init() -> void:
dim.set_anchors_preset(Control.PRESET_FULL_RECT) dim.set_anchors_preset(Control.PRESET_FULL_RECT)
add_child(dim) add_child(dim)
var center := CenterContainer.new()
center.set_anchors_preset(Control.PRESET_FULL_RECT)
center.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(center)
panel = PanelContainer.new() panel = PanelContainer.new()
panel.set_anchors_preset(Control.PRESET_CENTER) center.add_child(panel)
add_child(panel)
var vbox := VBoxContainer.new() var vbox := VBoxContainer.new()
panel.add_child(vbox) panel.add_child(vbox)
+6 -1
View File
@@ -12,9 +12,10 @@ var gm: Node = null
var state := 0 var state := 0
var state_frames := 0 var state_frames := 0
var targeted := false var targeted := false
var camera_staged := false
func _initialize() -> void: func _initialize() -> void:
root.size = Vector2i(1600, 900) root.size = Vector2i(1280, 720)
var packed: PackedScene = load("res://scenes/main.tscn") var packed: PackedScene = load("res://scenes/main.tscn")
var inst: Node = packed.instantiate() var inst: Node = packed.instantiate()
root.add_child(inst) root.add_child(inst)
@@ -29,6 +30,10 @@ func _process(_delta: float) -> bool:
push_error("GameManager not found") push_error("GameManager not found")
quit(1) quit(1)
return true return true
if gm != null and not camera_staged:
gm.guard.global_position = Vector3(0, 1.6, 5.0)
gm.guard.head.rotation.x = deg_to_rad(-8.0)
camera_staged = true
if frames > MAX_FRAMES: if frames > MAX_FRAMES:
print("timeout reached") print("timeout reached")
quit(0) quit(0)
+23 -5
View File
@@ -306,12 +306,30 @@ def app_icon():
icon("icon.png", badge, size=1024) icon("icon.png", badge, size=1024)
icon("icon.ico", badge, size=256) icon("icon.ico", badge, size=256)
# multi-size ICO for Windows (from the 256 render) # ICO for Windows. Must be BMP-entry based: PIL's ICO serializer emits
src = Image.open(os.path.join(ASSETS, "icon.png")) # PNG-compressed entries which NSIS' icon loader rejects. ImageMagick
# writes classic BMP entries; fall back to PIL only if ImageMagick is gone
# (flag it so a human notices).
import subprocess
import sys
ico_path = os.path.join(ASSETS, "icon.ico") ico_path = os.path.join(ASSETS, "icon.ico")
src.save(ico_path, format="ICO", sizes=[ src_png = os.path.join(ASSETS, "icon.png")
(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (16, 16)]) ran = False
print("wrote assets/textures/icon.ico (multi-size)") for cmd in (
["magick", src_png, "-background", "none", "-define", "icon:auto-resize=48,32,16", ico_path],
["convert", src_png, "-background", "none", "-define", "icon:auto-resize=48,32,16", ico_path],
):
try:
subprocess.run(cmd, check=True, capture_output=True)
ran = True
break
except (FileNotFoundError, subprocess.CalledProcessError):
continue
if ran:
print("wrote assets/textures/icon.ico (BMP 16/32/48 via ImageMagick)")
else:
print("WARNING: ImageMagick not found; writing PIL ICO (may be PNG-compressed)")
Image.open(src_png).convert("RGBA").save(ico_path, format="ICO", sizes=[(48, 48), (32, 32), (16, 16)])
def cards(): def cards():