diff --git a/AGENTS.md b/AGENTS.md index 2a87f42..d9c7cc3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,6 +15,9 @@ - Controller navigation must not depend on touch, mouse, keyboard, or hidden developer-only shortcuts. - Focus state must always be visible and predictable when controller navigation is active. - Avoid interaction patterns that trap focus, lose focus, or require precise pointer input. +- Controller navigation fixes must not break live combat bindings. When changing shared input routing, verify that combat-only actions still win during active combat: movement, ability casts, party targeting, pause, and overlay navigation must all work on AYN Thor controls. +- `data-game-nav-active` and other custom navigation markers may coexist with live combat HUDs. Do not treat those markers as menu mode during active combat unless a pause/dialog/result overlay is visible. +- Before considering any controller input change complete, test both states: no-overlay combat and menu/dialog navigation. No-overlay combat must include at least one successful ability cast from a controller binding and continuous analog movement on the AYN Thor control scheme. ## Architecture Requirements @@ -34,6 +37,7 @@ - IWT2 rendering can use a 2D canvas/game runtime, but renderer objects must not become the source of truth for saveable gameplay state. - IWT2 needs continuous movement input: left analog stick on AYN Thor and WASD on keyboard. Do not rely only on existing menu-style navigation actions for arena movement. - IWT2 menus, dialogs, inventory, collection log, pause overlays, and game-select screen must still follow controller navigation requirements. +- IWT2 combat must preserve AYN Thor face/shoulder button ability bindings even when party frames, HUD controls, or target rails use custom controller navigation state. Menu navigation priority must never swallow ability inputs while `data-combat-active="true"` and no overlay is open. - IWT2 boss mechanics and indicators must be modular. Add reusable hit-shape, damage/status, telegraph/indicator, hazard, and marker primitives under `src/modes/iwt2/sim/` or `src/modes/iwt2/render/` instead of hardcoding boss-specific rules in Phaser scenes. Boss AI may compose these primitives, but renderer code should draw sim-owned indicators generically. - Every IWT2 boss must have a boss pet collection-log reward. Add the pet to IWT2 boss reward metadata when adding the boss, and keep boss kill pet drops at a rare 1 in 500 chance. - IWT2 boss and party movement must be built with crowding and hazard escape in mind from the first implementation. New bosses must not rely on straight-line chase or single-vector hazard avoidance if the boss can create ground effects, wall pressure, summons, or multi-boss overlap. diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 872425c..173e43a 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -54,12 +54,18 @@ code updates to be a Git pull plus app restart. Portainer is not required. Use TrueNAS **Apps > Discover > Install via YAML**. -Repository: +Repository URL: ```text https://git.whoagland.com/phenom/i-want-to-heal.git ``` +Local Gitea repository path on the same TrueNAS host: + +```text +/mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/i-want-to-heal.git +``` + TrueNAS paths: ```text @@ -67,12 +73,15 @@ TrueNAS paths: /mnt/usbssds/apps/iwanttoheal/data ``` -Create the app directory and clone the repo: +Create the app directory and clone the repo. When Gitea is hosted on the same +TrueNAS box, prefer the local repository path instead of HTTPS. This avoids +Git-over-HTTPS TLS failures while downloading large pack files. ```sh sudo mkdir -p /mnt/usbssds/apps/iwanttoheal cd /mnt/usbssds/apps/iwanttoheal -sudo git clone https://git.whoagland.com/phenom/i-want-to-heal.git app +sudo git config --global --add safe.directory /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/i-want-to-heal.git +sudo git clone /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/i-want-to-heal.git app ``` Because the clone was run with `sudo`, give the normal TrueNAS user ownership: @@ -101,6 +110,25 @@ cd /mnt/usbssds/apps/iwanttoheal/app git pull ``` +If setting up a different app hosted by the same Gitea instance, first find its +bare repository path: + +```sh +sudo find /mnt -type d -name "REPO-NAME.git" -prune -print 2>/dev/null +``` + +Then substitute that path in the `safe.directory` and `git clone` commands: + +```sh +sudo git config --global --add safe.directory /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/OWNER/REPO-NAME.git +sudo git clone /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/OWNER/REPO-NAME.git app +sudo chown -R truenas_admin:truenas_admin app +``` + +`safe.directory` is a Git trust exception for the Gitea-owned bare repository. +It is needed because Gitea's repository files are owned by the app/container +user, not by `truenas_admin`. + If Git fails with `chmod ... Operation not permitted`, do not use a media or SMB dataset for the repo. Git needs normal file locking and chmod behavior. Create or use a dedicated apps dataset and clone under `/mnt/usbssds/apps/...`. diff --git a/IWantToHeal-Thor-v1.1.32.apk b/IWantToHeal-Thor-v1.1.32.apk new file mode 100644 index 0000000..2c55281 Binary files /dev/null and b/IWantToHeal-Thor-v1.1.32.apk differ diff --git a/android/app/build.gradle b/android/app/build.gradle index faf8b7a..fe906c1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.warren.iwanttoheal" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 112 - versionName "1.1.31" + versionCode 113 + versionName "1.1.32" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/docs/push-updates.md b/docs/push-updates.md index 9018eb4..e8c438e 100644 --- a/docs/push-updates.md +++ b/docs/push-updates.md @@ -104,6 +104,10 @@ npm run release:cli -- --release-only 1.1.26 ## Step 5: Update TrueNAS +The TrueNAS app checkout should be cloned from the local Gitea bare repository +path when Gitea runs on the same TrueNAS host. After that setup, normal updates +still use `git pull`; the remote is local filesystem storage instead of HTTPS. + ```sh cd /mnt/usbssds/apps/iwanttoheal/app git pull diff --git a/src/input.tsx b/src/input.tsx index f605ba3..d233e5a 100644 --- a/src/input.tsx +++ b/src/input.tsx @@ -748,7 +748,7 @@ export function InputProvider({ children }: { children: ReactNode }) { 'navigateLeft', 'navigateRight', ] satisfies InputAction[] - const dedicatedNavigationActive = hasDedicatedGameNavigation() + const dedicatedNavigationActive = hasDedicatedGameNavigation() && (!combatActive || uiOverlay) const action = dedicatedNavigationActive ? uiPriority.find((candidate) => bindingsRef.current.controller[candidate] === token) ?? (DPAD_NAV_ACTIONS[token] && isDedicatedNavigationAction(DPAD_NAV_ACTIONS[token]) diff --git a/src/modes/iwt2/IWantToHeal2App.tsx b/src/modes/iwt2/IWantToHeal2App.tsx index 70205c0..a305e1c 100644 --- a/src/modes/iwt2/IWantToHeal2App.tsx +++ b/src/modes/iwt2/IWantToHeal2App.tsx @@ -47,9 +47,11 @@ import { type Iwt2RoguelikeVariant, } from './content/roguelike' import { - createIwt2PveRoguelikeBossPair, + createIwt2WeightedRoguelikeBossPair, createUniformIwt2RoguelikeBossPair, IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED, + IWT2_PVP_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED, + IWT2_PVP_STADIUM_WEIGHTED_BOSS_PROGRESSION_ENABLED, } from './content/roguelikeBossProgression' type Iwt2Screen = @@ -527,7 +529,7 @@ function createRoguelikeRun( contentType: Iwt2RoguelikeContentType, ): Iwt2RoguelikeRunState { return { - bossIds: createRoguelikeBossPair(variant, 1), + bossIds: createRoguelikeBossPair(variant, contentType, 1), buffs: [], contentType, debuffs: [], @@ -595,15 +597,25 @@ function applyRoguelikeChoice( return { ...run, ...nextBase, - bossIds: createRoguelikeBossPair(run.variant, run.stage + 1), + bossIds: createRoguelikeBossPair(run.variant, run.contentType, run.stage + 1), ...buildRoguelikeChoices(save, run.variant), stage: run.stage + 1, } } -function createRoguelikeBossPair(variant: Iwt2RoguelikeVariant, stage: number): Iwt2BossId[] { - if (variant === 'pve' && IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED) { - return createIwt2PveRoguelikeBossPair(stage) +function createRoguelikeBossPair( + variant: Iwt2RoguelikeVariant, + contentType: Iwt2RoguelikeContentType, + stage: number, +): Iwt2BossId[] { + const weightedProgressionEnabled = variant === 'pve' + ? IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED + : contentType === 'stadium' + ? IWT2_PVP_STADIUM_WEIGHTED_BOSS_PROGRESSION_ENABLED + : IWT2_PVP_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED + + if (weightedProgressionEnabled) { + return createIwt2WeightedRoguelikeBossPair(stage) } return createUniformIwt2RoguelikeBossPair() } diff --git a/src/modes/iwt2/content/roguelikeBossProgression.ts b/src/modes/iwt2/content/roguelikeBossProgression.ts index c4cd1b5..2fb799c 100644 --- a/src/modes/iwt2/content/roguelikeBossProgression.ts +++ b/src/modes/iwt2/content/roguelikeBossProgression.ts @@ -1,6 +1,8 @@ import { IWT2_BOSS_METADATA, type Iwt2BossId } from './bosses' export const IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED = true +export const IWT2_PVP_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED = true +export const IWT2_PVP_STADIUM_WEIGHTED_BOSS_PROGRESSION_ENABLED = true type Iwt2RoguelikeBossTier = 'early' | 'mid' | 'late' @@ -43,6 +45,13 @@ const IWT2_ROGUELIKE_BOSS_TIER_BY_ID: Record export function createIwt2PveRoguelikeBossPair( stage: number, random: () => number = Math.random, +): Iwt2BossId[] { + return createIwt2WeightedRoguelikeBossPair(stage, random) +} + +export function createIwt2WeightedRoguelikeBossPair( + stage: number, + random: () => number = Math.random, ): Iwt2BossId[] { const choices: Iwt2BossId[] = [] const maxThreat = maxThreatForStage(stage)