177 lines
8.1 KiB
Markdown
177 lines
8.1 KiB
Markdown
# Arcade85
|
|
|
|
A 5-in-1 iOS collection of faithful **HD remakes** of five classic TI-85 calculator
|
|
games. Native **SwiftUI + SpriteKit**, one shared shell, each game a
|
|
`Scene` (SpriteKit) + `Sim` (pure Swift engine) pair.
|
|
|
|
This file is the operational runbook for an agent building/deploying Arcade85 on the
|
|
Mac Studio **without the human in the loop**. Read it top to bottom before touching
|
|
signing, git, or the build. It follows the same pattern as `~/dev/KyleCode/AGENTS.md`
|
|
(same Gitea, same `~/dev/.env` token, same signing identity).
|
|
|
|
---
|
|
|
|
## 1. Repo layout
|
|
|
|
```
|
|
Arcade85/
|
|
Arcade85.xcodeproj/ # hand-maintained (NOT xcodegen) — edit via Xcode/pbxproj
|
|
Arcade85/
|
|
Arcade85App.swift # @main
|
|
Shell/ # app chrome: RootView, GameHostView, GameCatalog,
|
|
# Theme, Haptics, HighScoreStore
|
|
Games/
|
|
SceneArt.swift # shared procedural texture/sprite helpers
|
|
DopeWars/ DopeWarsScene.swift + DopeWarsSim.swift
|
|
Lander/ LanderScene.swift + LanderSim.swift
|
|
Phoenix/ PhoenixScene.swift + PhoenixSim.swift + PhoenixData.swift
|
|
ZTetris/ TetrisScene.swift + TetrisSim.swift
|
|
ZMercury/ MercuryScene.swift + MercurySim.swift + MercuryData.swift
|
|
Galaxian/ (ported, dormant — see GameCatalog note)
|
|
ComingSoonScene.swift
|
|
README.md
|
|
```
|
|
|
|
**Convention:** every game is a `*Scene.swift` (SpriteKit, all UI/input) paired with a
|
|
`*Sim.swift` (pure Swift state machine — no UIKit/SpriteKit imports). The Sim is the
|
|
engine and is where mechanics live; the Scene only renders and routes touches. Keep
|
|
them separate. Dope Wars' `DopeWarsSim` is the reference for a well-factored Sim
|
|
(seeded RNG, a separate `extRng` stream for non-classic extensions, `#if DEBUG` debug
|
|
hooks, a `messages: [String]` queue the Scene drains one-per-tap).
|
|
|
|
The original TI-85 sources this was remade from live in sibling folders under
|
|
`~/dev/TI/` (`lander/`, `zetris/`, `phx4.0ti85/`, `zmercury-ti85/`, `galaxian-ti85/`).
|
|
They are **not** part of this repo.
|
|
|
|
## 2. Signing
|
|
|
|
- **Team:** `TG8X9PP8VT` (Kyle Albanese). **Identity:** "Apple Development: Kyle
|
|
Albanese (5SRT78F8BG)". **Bundle ID:** `com.albanese.arcade85`.
|
|
- **Automatic signing** (`CODE_SIGN_STYLE = Automatic`), `GENERATE_INFOPLIST_FILE = YES`
|
|
(no Info.plist file exists — don't add one).
|
|
- The current pbxproj does **not** pin `DEVELOPMENT_TEAM`; the identity in the login
|
|
keychain is used. If a device build ever fails to sign, set
|
|
`DEVELOPMENT_TEAM = TG8X9PP8VT` on the target (don't switch to manual profiles).
|
|
|
|
### Environment prerequisites (why this works)
|
|
|
|
Commands run as the user's **own launchd job in `gui/501` (Aqua session)**, so the
|
|
login keychain is readable by `xcodebuild`/`codesign`. Verify before a device build
|
|
(do NOT change anything if it fails — stop and tell the user):
|
|
|
|
```bash
|
|
launchctl managername # must print: Aqua
|
|
security find-identity -v -p codesigning /Users/fung/Library/Keychains/login.keychain-db
|
|
# must list: "Apple Development: Kyle Albanese (5SRT78F8BG)"
|
|
```
|
|
|
|
**Hard rules:** never revoke/recreate the Apple Development cert, don't touch keychain
|
|
settings, keep automatic signing, never commit `*.p8` or `*.env` secrets.
|
|
|
|
## 3. Build
|
|
|
|
Run from `/Users/fung/dev/TI/Arcade85`.
|
|
|
|
### Simulator (no signing — default for quick checks)
|
|
```bash
|
|
xcodebuild -project Arcade85.xcodeproj -scheme Arcade85 \
|
|
-destination 'generic/platform=iOS Simulator' -configuration Debug \
|
|
-derivedDataPath build/dd build CODE_SIGNING_ALLOWED=NO
|
|
```
|
|
Success: `** BUILD SUCCEEDED **`. (Verified 2026-09-16.)
|
|
|
|
### Device (real iPhone, auto-signed)
|
|
```bash
|
|
xcodebuild -project Arcade85.xcodeproj -scheme Arcade85 \
|
|
-destination 'generic/platform=iOS' -derivedDataPath build/dd \
|
|
-allowProvisioningUpdates build
|
|
```
|
|
Install/launch a paired device (list with `xcrun devicectl list devices`):
|
|
```bash
|
|
APP=build/dd/Build/Products/Debug-iphoneos/Arcade85.app
|
|
xcrun devicectl device install app --device <UDID> "$APP"
|
|
xcrun devicectl device process launch --device <UDID> com.albanese.arcade85
|
|
```
|
|
|
|
**There are no automated tests in this project.** A passing build is the check. For
|
|
gameplay, run in the simulator and play, or use the Dope Wars debug env vars below.
|
|
|
|
### Dope Wars debug env vars (DEBUG builds only)
|
|
`DopeWarsScene.didMove(to:)` reads `ProcessInfo` env vars for screenshot/demo poses:
|
|
`ARCADE_AUTOPLAY=1` (auto-start a run), `ARCADE_DEMO=1` (deterministic day-12 demo),
|
|
`ARCADE_DEMO_VIEW=buy|chase|tier3|over|ride|keypad|sharkchase|endless`. The
|
|
`DopeWarsSim` also exposes `debug*` mutators (wallet, debt, price, tier, held, forced
|
|
RNG rolls) gated behind `#if DEBUG`.
|
|
|
|
### Dope Wars — wave-1 mechanics (the "make it dynamic" rework)
|
|
The classic uniform per-day price roll was replaced with a **momentum +
|
|
mean-reversion** model: each day a product carries `momentum` (0.50) of yesterday's
|
|
deviation from its **per-borough anchor** (`boroughAnchor`/`methAnchor`/
|
|
`chinaWhiteAnchor`) plus a ±`noisePct` (0.15) random walk, with event **shocks**
|
|
(decaying at `shockDecay` 0.70) injected on top. Tuning constants live in the
|
|
`// MARK: Wave-1 tuning` block — change them there, not inline.
|
|
Other wave-1 systems, all in `DopeWarsSim`:
|
|
- **Price history / sparkline** — `spark: [[Int]]` (last 7 days per product) feeds the
|
|
per-row `SKShapeNode` sparkline on the board.
|
|
- **Market impact** — `marketImpact()` nudges today's local price by the share of the
|
|
day's volume you traded (buy pushes up, sell pushes down); `buy` charges the
|
|
post-impact price.
|
|
- **Paid information** — `buyForecast(for:)` (Bronx contact, 70% accurate, 1-day) +
|
|
the existing free `Rumor`.
|
|
- **Reputation** — hidden `rep` (0…100, starts 50) moved by `changeRep`; drives
|
|
`fenceBonus`, `heatMult`, and shark terms.
|
|
- **Investments** — `Investment` enum (coat +30, fence auto-sell, shark protection);
|
|
`buyInvestment` (Bronx only).
|
|
- **Chase bribe** — `chaseBribe()` + `bribeCost` (RUN/FIGHT/BRIBE in the chase sheet).
|
|
- **Shark favors** — `Favor` (risky no-profit delivery), `acceptFavor`/`refuseFavor`/
|
|
`resolveFavorDelivery`; good-standing cap (`sharkCap`, doubled by protection) sets
|
|
`sharkRate` (7% vs 10%).
|
|
The Scene mirrors each with a sheet/mode (`.contact`, `.investments`, `.favor`,
|
|
`.bronxMenu`, chase BRIBE). Keep the Scene/Sim split — mechanics in the Sim, pixels
|
|
in the Scene.
|
|
|
|
## 4. Git & Gitea
|
|
|
|
Remote: Gitea `https://git.kwalb.com/kyle/Arcade85` (branch `main`). No git
|
|
credentials in the keychain — use the API token from `~/dev/.env` inline, and let the
|
|
credential prompt fail to store (harmless). **The repo is created on first push:**
|
|
|
|
```bash
|
|
TOKEN=$(sed -n 's/^GITEA_API=//p' ~/dev/.env)
|
|
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
-d '{"name":"Arcade85","private":false,"default_branch":"main"}' \
|
|
https://git.kwalb.com/api/v1/user/repos
|
|
# then:
|
|
git push "https://kyle:$TOKEN@git.kwalb.com/kyle/Arcade85.git" main
|
|
```
|
|
|
|
API: `curl -H "Authorization: token $TOKEN" https://git.kwalb.com/api/v1/repos/kyle/Arcade85/…`
|
|
|
|
**Only commit/push when asked.** Stage only intended files. Never stage `*.p8` or
|
|
`*.env`.
|
|
|
|
### `.env` — do not `source` it
|
|
`~/dev/.env` holds `GITEA_URL`, `GITEA_API` (token), `GITEA_USERNAME`, `GITEA_EMAIL`
|
|
plus other service keys. **Do not `source` it** — a later line is a bare
|
|
`https://claude.ai/magic-link…` URL (not `key=value`) which breaks `source` in zsh.
|
|
Read values with sed:
|
|
```bash
|
|
TOKEN=$(sed -n 's/^GITEA_API=//p' ~/dev/.env)
|
|
```
|
|
|
|
## 5. `.gitignore` (already in place — don't remove)
|
|
`build/`, `DerivedData/`, `*.xcuserstate`, `xcuserdata/`, `*.moved-aside`, `*.hmap`,
|
|
`*.ipa`, `*.dSYM.zip`, `*.dSYM`, `.DS_Store`, `.build/`, `.swiftpm/`.
|
|
|
|
Add `*.p8` and `*.env` to this ignore list if/when they could appear here (none do
|
|
today, but the rule is: never commit signing keys or secret files).
|
|
|
|
## 6. Adding a game
|
|
|
|
1. Create `Games/<Name>/<Name>Scene.swift` + `<Name>Sim.swift` (mirror Dope Wars).
|
|
2. Add both files to the Xcode target in `Arcade85.xcodeproj/project.pbxproj`
|
|
(hand-maintained — there is **no** `project.yml`/xcodegen step; edit the pbxproj or
|
|
use Xcode's "Add Files to Arcade85").
|
|
3. Register the slot in `Shell/GameCatalog.swift`.
|
|
4. Build (simulator) to verify.
|