diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d5d4456..0db3b785 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,8 +94,8 @@ jobs: if: github.repository == 'bryanthaboi/gen1recomp' uses: actions/upload-artifact@v7 with: - name: gen1recomp-ios-ipa - path: dist/ios/gen1recomp.ipa + name: gen1recomp++-ios-ipa + path: dist/ios/gen1recomp++.ipa if-no-files-found: error retention-days: 7 - name: clean up signing keychain diff --git a/.github/workflows/ios-artifact-comment.yml b/.github/workflows/ios-artifact-comment.yml index b0bd58ce..3ba5386f 100644 --- a/.github/workflows/ios-artifact-comment.yml +++ b/.github/workflows/ios-artifact-comment.yml @@ -22,7 +22,7 @@ jobs: HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} HEAD_REPOSITORY: ${{ github.event.workflow_run.head_repository.full_name }} run: | - artifact_id="$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$RUN_ID/artifacts" --jq '.artifacts[] | select(.name == "gen1recomp-ios-ipa") | .id')" + artifact_id="$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$RUN_ID/artifacts" --jq '.artifacts[] | select(.name == "gen1recomp++-ios-ipa") | .id')" [ -n "$artifact_id" ] || exit 0 head_owner="${HEAD_REPOSITORY%%/*}" pr_number="$(gh api "repos/$GITHUB_REPOSITORY/pulls?state=open&head=$head_owner:$HEAD_BRANCH" --jq '.[0].number // empty')" @@ -44,7 +44,7 @@ jobs: uses: thollander/actions-comment-pull-request@v3 with: message: | - [gen1recomp.ipa](${{ steps.artifact.outputs.artifact_url }}) + [gen1recomp++.ipa](${{ steps.artifact.outputs.artifact_url }}) **Commit**: [#${{ steps.build-info.outputs.hash }}](https://github.com/${{ github.event.workflow_run.head_repository.full_name }}/commit/${{ github.event.workflow_run.head_sha }}) **Build Time**: `${{ steps.build-info.outputs.time }}` diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c111670d..58e35a5a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -429,9 +429,9 @@ jobs: [ -n "$apk" ] || { echo "::error::no Android APK found under dist/android/debug"; exit 1; } cp "$apk" "$outdir/gen1recomp-${v}-android.apk" - ipa="dist/ios/gen1recomp.ipa" + ipa="dist/ios/gen1recomp++.ipa" [ -f "$ipa" ] || { echo "::error::$ipa not found (expected from scripts/build_ios.sh --device)"; exit 1; } - cp "$ipa" "$outdir/gen1recomp-${v}-ios.ipa" + cp "$ipa" "$outdir/gen1recomp++-${v}-ios.ipa" swzip="dist/switch/gen1recomp-${v}-switch.zip" [ -f "$swzip" ] || { echo "::error::$swzip not found (expected from scripts/build_switch.sh --fused → pack_sd_zip.sh)"; exit 1; } @@ -560,7 +560,7 @@ jobs: "dist/release/gen1recomp-${v}-linux.zip" "dist/release/gen1recomp-${v}-linux-arm64.AppImage" "dist/release/gen1recomp-${v}-android.apk" - "dist/release/gen1recomp-${v}-ios.ipa" + "dist/release/gen1recomp++-${v}-ios.ipa" "dist/release/gen1recomp-${v}-switch.zip" "dist/release/gen1recomp-${v}-xbox-uwp.zip" "dist/release/gen1recomp-${v}-rg34xxsp-stockos64-mod.zip" @@ -581,14 +581,15 @@ jobs: run: | set -euo pipefail v="${{ needs.version.outputs.version }}" - ipa="dist/release/gen1recomp-${v}-ios.ipa" + ipa="dist/release/gen1recomp++-${v}-ios.ipa" app_repo="mobile/ios/app-repo.json" [ -f "$ipa" ] || { echo "::error::$ipa not found"; exit 1; } [ -f "$app_repo" ] || { echo "::error::$app_repo not found"; exit 1; } date="$(date -u +"%Y-%m-%d")" size="$(wc -c < "$ipa" | tr -d '[:space:]')" - download_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/v${v}/gen1recomp-${v}-ios.ipa" + download_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/v${v}/gen1recomp++-${v}-ios.ipa" + bundle_id="com.theboisclub.gen1recompplusplus" localized_description="Gen1Recomp - A native Lua / LÖVE2D recreation of Gen 1 Poke" release_notes="$(GH_TOKEN="${{ github.token }}" gh release view "v${v}" --json body --jq '.body // ""' 2>/dev/null || true)" if [ -n "$release_notes" ]; then @@ -602,15 +603,15 @@ jobs: --argjson size "$size" \ '{version: $version, date: $date, size: $size, downloadURL: $download_url, localizedDescription: $localized_description}')" - if jq -e --arg version "$v" \ - 'any(.apps[] | select(.bundleIdentifier == "com.theboisclub.gen1recomp").versions[]?; .version == $version)' \ + if jq -e --arg bundle_id "$bundle_id" --arg version "$v" \ + 'any(.apps[] | select(.bundleIdentifier == $bundle_id).versions[]?; .version == $version)' \ "$app_repo" >/dev/null; then - jq --arg version "$v" --argjson entry "$entry" \ - '(.apps[] | select(.bundleIdentifier == "com.theboisclub.gen1recomp").versions) |= map(if .version == $version then $entry else . end)' \ + jq --arg bundle_id "$bundle_id" --arg version "$v" --argjson entry "$entry" \ + '(.apps[] | select(.bundleIdentifier == $bundle_id).versions) |= map(if .version == $version then $entry else . end)' \ "$app_repo" > "$app_repo.tmp" else - jq --argjson entry "$entry" \ - '(.apps[] | select(.bundleIdentifier == "com.theboisclub.gen1recomp").versions) |= [$entry] + .' \ + jq --arg bundle_id "$bundle_id" --argjson entry "$entry" \ + '(.apps[] | select(.bundleIdentifier == $bundle_id).versions) |= [$entry] + .' \ "$app_repo" > "$app_repo.tmp" fi mv "$app_repo.tmp" "$app_repo" diff --git a/README.md b/README.md index c5bbecff..3710fd9e 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ in [docs/linux-arm64-build.md](docs/linux-arm64-build.md). ## iOS -Every release ships `gen1recomp-*-ios.ipa`. Sideload it with AltStore +Every release ships `gen1recomp++-*-ios.ipa`. Sideload it with AltStore (Windows or Mac) — see [docs/ios-sideload.md](docs/ios-sideload.md). To build and install from source on a Mac instead, see [docs/ios-install.md](docs/ios-install.md). diff --git a/docs/ios-sideload.md b/docs/ios-sideload.md index ba4907da..8c19cf01 100644 --- a/docs/ios-sideload.md +++ b/docs/ios-sideload.md @@ -1,6 +1,6 @@ # Sideload the iOS build with AltStore -Every GitHub Release ships an IPA (`gen1recomp-*-ios.ipa`). Install it on +Every GitHub Release ships an IPA (`gen1recomp++-*-ios.ipa`). Install it on your iPhone or iPad with [AltStore Classic](https://altstore.io/) — AltStore re-signs the app with **your** free Apple ID so you do not need a Mac or Xcode. @@ -23,7 +23,7 @@ Stuck? Start here: ## 2. Install the game -1. Download `gen1recomp-*-ios.ipa` from +1. Download `gen1recomp++-*-ios.ipa` from [Releases](https://github.com/bryanthaboi/gen1recomp/releases). 2. Open **AltStore** on the phone (AltServer must be running on the same Wi‑Fi, or keep the phone plugged into the computer). diff --git a/mobile/ios/README.md b/mobile/ios/README.md index 5ae5439c..37f48b4e 100644 --- a/mobile/ios/README.md +++ b/mobile/ios/README.md @@ -7,7 +7,7 @@ an iOS app with LÖVE 12.0. The app uses the public iOS Documents directory as its LÖVE save directory. There is no `pokemon-love2d` subdirectory and the app does not create a -README file there. When browsing `On My iPhone > gen1recomp` in Files, the +README file there. When browsing `On My iPhone > gen1recomp++` in Files, the directory contains the app's runtime data directly, including: - installed mods and downloaded ROMs @@ -46,7 +46,7 @@ scripts/build_ios.sh --device --release --install Device builds require a paired, unlocked device and a valid Apple signing identity. Set `DEVELOPMENT_TEAM` or `CODE_SIGN_IDENTITY` when automatic signing cannot select the intended account. Add `--ipa` to create -`dist/ios/gen1recomp.ipa`. +`dist/ios/gen1recomp++.ipa`. The script verifies the final app before packaging it: @@ -76,10 +76,10 @@ option. Simulator and device app bundles are copied to: ```text -dist/ios/Debug-iphonesimulator/gen1recomp.app -dist/ios/Release-iphonesimulator/gen1recomp.app -dist/ios/Debug-iphoneos/gen1recomp.app -dist/ios/Release-iphoneos/gen1recomp.app +dist/ios/Debug-iphonesimulator/gen1recomp++.app +dist/ios/Release-iphonesimulator/gen1recomp++.app +dist/ios/Debug-iphoneos/gen1recomp++.app +dist/ios/Release-iphoneos/gen1recomp++.app ``` The intermediate Xcode products are under `mobile/ios/build/`. Both locations @@ -94,9 +94,9 @@ saves; those are created at runtime in Documents. | Field | Default | | --- | --- | -| Display name | `gen1recomp` | -| Product name | `gen1recomp` | -| Bundle identifier | `com.theboisclub.gen1recomp` | +| Display name | `gen1recomp++` | +| Product name | `gen1recomp++` | +| Bundle identifier | `com.theboisclub.gen1recompplusplus` | | Save directory | Public `Documents` root | | Orientation | Portrait | diff --git a/mobile/ios/app-repo.json b/mobile/ios/app-repo.json index d5b1db26..e6c0cc84 100644 --- a/mobile/ios/app-repo.json +++ b/mobile/ios/app-repo.json @@ -1,11 +1,11 @@ { - "name": "gen1recomp App Repo", - "identifier": "com.theboisclub.gen1recomp.repo", + "name": "gen1recomp++ App Repo", + "identifier": "com.theboisclub.gen1recompplusplus.repo", "iconURL": "https://raw.githubusercontent.com/bryanthaboi/gen1recomp/main/assets/logo/gen1recomp_cover.png", "apps": [ { - "name": "gen1recomp", - "bundleIdentifier": "com.theboisclub.gen1recomp", + "name": "gen1recomp++", + "bundleIdentifier": "com.theboisclub.gen1recompplusplus", "developerName": "bryanthaboi", "iconURL": "https://raw.githubusercontent.com/bryanthaboi/gen1recomp/main/assets/logo/gen1recomp_cover.png", "localizedDescription": "Gen1Recomp - A native Lua / LÖVE2D recreation of Gen 1 Poke", diff --git a/mobile/ios/overlays/love-ios.plist b/mobile/ios/overlays/love-ios.plist index 6d7e2b30..d2f97a54 100644 --- a/mobile/ios/overlays/love-ios.plist +++ b/mobile/ios/overlays/love-ios.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - gen1recomp + gen1recomp++ CFBundleDocumentTypes @@ -32,7 +32,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - gen1recomp + gen1recomp++ CFBundlePackageType APPL CFBundleShortVersionString diff --git a/mobile/ios/patch_love_src.py b/mobile/ios/patch_love_src.py index b6d6eb40..e9a90a43 100644 --- a/mobile/ios/patch_love_src.py +++ b/mobile/ios/patch_love_src.py @@ -29,6 +29,9 @@ WRAP_SYSTEM = LOVE_SRC / "src" / "modules" / "system" / "wrap_System.cpp" PBXPROJ = LOVE_SRC / "platform" / "xcode" / "love.xcodeproj" / "project.pbxproj" APPLE_MM = LOVE_SRC / "src" / "common" / "apple.mm" FILESYSTEM_CPP = LOVE_SRC / "src" / "modules" / "filesystem" / "physfs" / "Filesystem.cpp" +IOS_MM = LOVE_SRC / "src" / "common" / "ios.mm" +IOS_H = LOVE_SRC / "src" / "common" / "ios.h" +SYSTEM_CPP = LOVE_SRC / "src" / "modules" / "system" / "System.cpp" ENTITLEMENTS_SRC = IOS_DIR / "overlays" / "love-ios.entitlements" NATIVE_FILES = ("GRPickerBridge.swift", "GRHealthBridge.swift", "GRBootstrap.m") @@ -120,7 +123,7 @@ int w_pickFileKinds(lua_State *L) typedef const char *(*GRUTF8)(id, SEL); const char *bytes = ((GRUTF8)objc_msgSend)(kinds, sel_registerName("UTF8String")); - if (bytes == nullptr || bytes[0] == '\0') + if (bytes == nullptr || bytes[0] == '\\0') { lua_pushnil(L); return 1; @@ -358,6 +361,50 @@ def patch_public_documents(): print("patch_love_src: iOS save directory routed to Documents root") +def patch_ios_haptics(): + text = pristine(IOS_MM, ("UIImpactFeedbackGenerator",)) + original = """void vibrate() +{ + @autoreleasepool + { + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); + } +} +""" + replacement = """void vibrate(double seconds) +{ + @autoreleasepool + { + UIImpactFeedbackStyle style = UIImpactFeedbackStyleLight; + if (seconds >= 0.035) + style = UIImpactFeedbackStyleHeavy; + else if (seconds >= 0.02) + style = UIImpactFeedbackStyleMedium; + UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] + initWithStyle:style]; + [generator prepare]; + [generator impactOccurred]; + } +} +""" + if original not in text: + fail(f"iOS haptic anchor not found in {IOS_MM}") + IOS_MM.write_text(text.replace(original, replacement, 1)) + + header = pristine(IOS_H, ("void vibrate(double seconds);",)) + header_original = "void vibrate();" + if header_original not in header: + fail(f"iOS haptic declaration not found in {IOS_H}") + IOS_H.write_text(header.replace(header_original, "void vibrate(double seconds);", 1)) + + system = pristine(SYSTEM_CPP, ("love::ios::vibrate(seconds)",)) + system_original = "love::ios::vibrate();" + if system_original not in system: + fail(f"iOS haptic call site not found in {SYSTEM_CPP}") + SYSTEM_CPP.write_text(system.replace(system_original, "love::ios::vibrate(seconds);", 1)) + print("patch_love_src: iOS haptics use Taptic Engine impact presets") + + def patch_pbxproj(): text = pristine(PBXPROJ) @@ -408,7 +455,9 @@ def patch_pbxproj(): fail(f"build configuration {config_id} not found") settings = ( "\t\t\t\tSWIFT_VERSION = 5.0;\n" - "\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 14.0;\n" + "\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n" + "\t\t\t\tPRODUCT_NAME = \"gen1recomp++\";\n" + "\t\t\t\tEXECUTABLE_NAME = \"gen1recomp++\";\n" '\t\t\t\tCODE_SIGN_ENTITLEMENTS = "ios/native/love-ios.entitlements";\n' ) text = text[: m.end()] + settings + text[m.end():] @@ -422,6 +471,7 @@ def main(): fail("love-src/ missing; run scripts/build_ios.sh --fetch first") copy_native_files() patch_public_documents() + patch_ios_haptics() patch_wrap_system() patch_pbxproj() diff --git a/scripts/build.sh b/scripts/build.sh index 2828ac83..5c6f8bc5 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -13,7 +13,7 @@ # dist/linux/gen1recomp-linux.zip (fused x86_64 AppImage) # dist/android/debug/*.apk (full gradle output stays under # mobile/android/app/build/outputs/apk/embedNoRecord/) -# dist/ios/-/gen1recomp.app (full xcodebuild output stays +# dist/ios/-/gen1recomp++.app (full xcodebuild output stays # under mobile/ios/build/Build/Products/) set -euo pipefail diff --git a/scripts/build_ios.sh b/scripts/build_ios.sh index 3a87dce9..9c7fac93 100755 --- a/scripts/build_ios.sh +++ b/scripts/build_ios.sh @@ -20,9 +20,9 @@ # - macOS + Xcode (xcodebuild) # - mobile/ios/love-src/ (see --fetch / mobile/ios/README.md) # -# Output: dist/ios/-/gen1recomp.app (convenience copy) -# dist/ios/gen1recomp.ipa (device builds only) -# mobile/ios/build/Build/Products/-/gen1recomp.app +# Output: dist/ios/-/gen1recomp++.app (convenience copy) +# dist/ios/gen1recomp++.ipa (device builds only) +# mobile/ios/build/Build/Products/-/gen1recomp++.app set -euo pipefail @@ -39,8 +39,9 @@ RESOURCES_DIR="$XCODE_DIR/ios/resources" LOVE_FILE="$RESOURCES_DIR/game.love" LIBS_DIR="$XCODE_DIR/ios/libraries" -APP_NAME="gen1recomp" -DISPLAY_NAME="gen1recomp" +APP_NAME="gen1recomp++" +DISPLAY_NAME="gen1recomp++" +PRODUCT_NAME="gen1recomp++" # Bundle ID resolution, most specific wins: # 1. GEN1_BUNDLE_ID env var # 2. mobile/ios/bundle_id.local (one line, gitignored — pins YOUR install @@ -50,7 +51,7 @@ DISPLAY_NAME="gen1recomp" # capabilities like HealthKit are involved), so a per-team default # lets anyone build without colliding with someone else's app # 4. simulator: the project default (no App ID registration involved) -BUNDLE_ID="${GEN1_BUNDLE_ID:-com.theboisclub.gen1recomp}" +BUNDLE_ID="${GEN1_BUNDLE_ID:-}" if [ -z "$BUNDLE_ID" ] && [ -f "$IOS_DIR/bundle_id.local" ]; then BUNDLE_ID="$(tr -d '[:space:]' < "$IOS_DIR/bundle_id.local")" fi @@ -138,11 +139,7 @@ if $DEVICE && [ -z "${DEVELOPMENT_TEAM:-}" ]; then fi fi if [ -z "$BUNDLE_ID" ]; then - if $DEVICE; then - BUNDLE_ID="com.gen1recomp.t$(printf '%s' "$DEVELOPMENT_TEAM" | tr '[:upper:]' '[:lower:]')" - else - BUNDLE_ID="com.theboisclub.pokemonred" - fi + BUNDLE_ID="com.theboisclub.gen1recompplusplus" fi # --------------------------------------------------------------- host checks @@ -226,13 +223,26 @@ verify_documents_overlay() { } apply_ios_icon() { - local source="$ROOT/assets/logo/gen1recomp_cover.png" + local source="$ROOT/assets/logo/logo.png" local target="$XCODE_DIR/Images.xcassets/iOS AppIcon.appiconset" [ -f "$source" ] || fail "missing iOS icon source: $source" [ -d "$target" ] || fail "missing iOS app icon set: $target" + local icon="$BUILD_DIR/gen1recomp-ios-icon.png" + mkdir -p "$BUILD_DIR" + if command -v magick >/dev/null 2>&1; then + magick -size 1024x1024 xc:black \ + \( "$source" -resize 900x900 \) -gravity center -composite \ + -alpha off "$icon" || fail "could not create iOS app icon: $source" + else + local scaled="$BUILD_DIR/gen1recomp-logo.png" + sips -Z 900 "$source" --out "$scaled" >/dev/null \ + || fail "could not resize iOS app icon source: $source" + sips -p 1024 1024 --padColor 000000 "$scaled" --out "$icon" >/dev/null \ + || fail "could not center iOS app icon source: $source" + fi local entry name size while IFS=: read -r name size; do - sips -z "$size" "$size" "$source" --out "$target/$name" >/dev/null + sips -z "$size" "$size" "$icon" --out "$target/$name" >/dev/null done <<'EOF' icon-1024pt@1x.png:1024 icon-29pt@1x.png:29 @@ -647,6 +657,7 @@ run_xcodebuild() { CURRENT_PROJECT_VERSION="$project_version" INFOPLIST_KEY_UIFileSharingEnabled=YES INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace=YES + IPHONEOS_DEPLOYMENT_TARGET=15.0 ONLY_ACTIVE_ARCH=NO DISABLE_MANUAL_TARGET_ORDER_BUILD_WARNING=YES ) @@ -666,6 +677,9 @@ run_xcodebuild() { if [ -n "${CODE_SIGN_IDENTITY:-}" ]; then args+=(CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY") fi + if [ "${GEN1_DISABLE_HEALTHKIT:-0}" = "1" ]; then + args+=(CODE_SIGN_ENTITLEMENTS=) + fi fi if ! xcodebuild -showsdks 2>/dev/null | grep -q "$sdk"; then @@ -708,18 +722,22 @@ run_xcodebuild() { fi local products="$BUILD_DIR/Build/Products/${config}-${sdk}" - local app="$products/$APP_NAME.app" - if [ ! -d "$app" ]; then - # PRODUCT_NAME override can still leave love.app on older projects - if [ -d "$products/love.app" ]; then - app="$products/$APP_NAME.app" - mv "$products/love.app" "$app" - warn "renamed love.app to $APP_NAME.app" - else - warn "xcodebuild finished but no .app under $products" - find "$BUILD_DIR/Build/Products" -name '*.app' 2>/dev/null | head -20 || true - return 0 + local app="" + local candidate + for candidate in "$products/$PRODUCT_NAME.app" "$products/$APP_NAME.app" "$products/love.app"; do + if [ -d "$candidate" ]; then + app="$candidate" + break fi + done + if [ -z "$app" ]; then + warn "xcodebuild finished but no .app under $products" + find "$BUILD_DIR/Build/Products" -name '*.app' 2>/dev/null | head -20 || true + return 0 + fi + if [ "$app" != "$products/$APP_NAME.app" ]; then + mv "$app" "$products/$APP_NAME.app" + app="$products/$APP_NAME.app" fi verify_documents_configuration "$app" @@ -760,7 +778,7 @@ run_xcodebuild() { fi } -# Pack Payload/.app into dist/ios/gen1recomp.ipa for release / sideload tools. +# Pack Payload/.app into dist/ios/gen1recomp++.ipa for release / sideload tools. package_ipa() { local app="$1" local ipa="$DIST/$APP_NAME.ipa" diff --git a/src/core/SaveData.lua b/src/core/SaveData.lua index 70dd44c7..9f1e58f2 100644 --- a/src/core/SaveData.lua +++ b/src/core/SaveData.lua @@ -335,11 +335,11 @@ function SaveData.defaultOptions() -- TouchControls.normalizeConfig folds it into both orientations on load. touchControls = { enabled = true }, -- Haptic feedback level for on-screen pad presses (#806): - -- off | light | medium | heavy, mapped to a love.system.vibrate - -- duration in src/core/TouchControls.lua. LIGHT by default, like the - -- overlay itself defaulting on, so an options.lua predating this key - -- gets the tick without going looking for the row. Inert wherever the - -- overlay never appears (desktop) or LOVE has no vibrator. + -- off | light | normal | strong, mapped to a love.system.vibrate duration + -- in src/core/TouchControls.lua. LIGHT by default, so an options.lua + -- predating this key gets the tick without going looking for the row. + -- Inert wherever the overlay never appears (desktop) or LOVE has no + -- vibrator. haptics = "light", -- Shared UI/mod timestamp presentation. DEVICE follows the process time -- locale where the platform exposes one; otherwise DateTime falls back to diff --git a/src/core/TouchControls.lua b/src/core/TouchControls.lua index d4d645bf..b3422edb 100644 --- a/src/core/TouchControls.lua +++ b/src/core/TouchControls.lua @@ -105,17 +105,21 @@ end -- would be dropped on every editor save. -- love.system.vibrate takes a duration and nothing else, so "intensity" is a -- duration preset: Android runs the platform vibrator for exactly that long, --- while iOS ignores the duration and fires the fixed system vibration, so --- there the three levels all read as simply on. -TouchControls.HAPTICS = { "off", "light", "medium", "heavy" } +-- while iOS maps each duration to a matching Taptic Engine impact. +TouchControls.HAPTICS = { "off", "light", "normal", "strong" } TouchControls.HAPTIC_DEFAULT = "light" -local HAPTIC_SECONDS = { off = 0, light = 0.012, medium = 0.025, heavy = 0.045 } +local HAPTIC_SECONDS = { + off = 0, light = 0.012, normal = 0.025, strong = 0.045, +} local HAPTIC_LABELS = { - off = "OFF", light = "LIGHT", medium = "MEDIUM", heavy = "HEAVY", + off = "OFF", light = "LIGHT", normal = "NORMAL", strong = "STRONG", } function TouchControls.normalizeHaptics(level) + if level == "physical" then return "light" end + if level == "medium" then return "normal" end + if level == "heavy" then return "strong" end if HAPTIC_SECONDS[level] then return level end return TouchControls.HAPTIC_DEFAULT end diff --git a/src/ui/OptionsMenu.lua b/src/ui/OptionsMenu.lua index ef21f209..c83c5450 100644 --- a/src/ui/OptionsMenu.lua +++ b/src/ui/OptionsMenu.lua @@ -501,7 +501,7 @@ local function buildRows(game) return true end }, -- Haptic feedback for on-screen pad presses (#806): OFF / LIGHT / - -- MEDIUM / HEAVY, where the intensity is a vibration duration -- + -- NORMAL / STRONG, where the intensity is a vibration duration -- -- love.system.vibrate takes nothing else. Hidden with TOUCH PAD below, -- since the only thing that buzzes is a virtual button press. { id = "haptics", label = Strings("VIBRATION"), @@ -515,7 +515,7 @@ local function buildRows(game) o.haptics = TC.cycleHaptics(o.haptics, dir) TC:applyOptions(o) -- sample the level being selected: stepping the row is the only way - -- to compare LIGHT against HEAVY without leaving the menu + -- to compare LIGHT against STRONG without leaving the menu TC.buzz(o.haptics) return true end },