Merge pull request #1204 from castdrian/ios-improvements

feat(ios): improve branding and fix haptic feedback
This commit is contained in:
bryanthaboi
2026-08-13 06:06:31 -04:00
committed by GitHub
14 changed files with 146 additions and 73 deletions
+2 -2
View File
@@ -94,8 +94,8 @@ jobs:
if: github.repository == 'bryanthaboi/gen1recomp' if: github.repository == 'bryanthaboi/gen1recomp'
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: gen1recomp-ios-ipa name: gen1recomp++-ios-ipa
path: dist/ios/gen1recomp.ipa path: dist/ios/gen1recomp++.ipa
if-no-files-found: error if-no-files-found: error
retention-days: 7 retention-days: 7
- name: clean up signing keychain - name: clean up signing keychain
+2 -2
View File
@@ -22,7 +22,7 @@ jobs:
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
HEAD_REPOSITORY: ${{ github.event.workflow_run.head_repository.full_name }} HEAD_REPOSITORY: ${{ github.event.workflow_run.head_repository.full_name }}
run: | 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 [ -n "$artifact_id" ] || exit 0
head_owner="${HEAD_REPOSITORY%%/*}" head_owner="${HEAD_REPOSITORY%%/*}"
pr_number="$(gh api "repos/$GITHUB_REPOSITORY/pulls?state=open&head=$head_owner:$HEAD_BRANCH" --jq '.[0].number // empty')" 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 uses: thollander/actions-comment-pull-request@v3
with: with:
message: | 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 }}) **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 }}` **Build Time**: `${{ steps.build-info.outputs.time }}`
+12 -11
View File
@@ -444,9 +444,9 @@ jobs:
[ -n "$apk" ] || { echo "::error::no Android APK found under dist/android/debug"; exit 1; } [ -n "$apk" ] || { echo "::error::no Android APK found under dist/android/debug"; exit 1; }
cp "$apk" "$outdir/gen1recomp-${v}-android.apk" 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; } [ -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" 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; } [ -f "$swzip" ] || { echo "::error::$swzip not found (expected from scripts/build_switch.sh --fused → pack_sd_zip.sh)"; exit 1; }
@@ -580,7 +580,7 @@ jobs:
"dist/release/gen1recomp-${v}-linux.zip" "dist/release/gen1recomp-${v}-linux.zip"
"dist/release/gen1recomp-${v}-linux-arm64.AppImage" "dist/release/gen1recomp-${v}-linux-arm64.AppImage"
"dist/release/gen1recomp-${v}-android.apk" "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}-switch.zip"
"dist/release/gen1recomp-${v}-xbox-uwp.zip" "dist/release/gen1recomp-${v}-xbox-uwp.zip"
"dist/release/gen1recomp-${v}-rg34xxsp-stockos64-mod.zip" "dist/release/gen1recomp-${v}-rg34xxsp-stockos64-mod.zip"
@@ -602,14 +602,15 @@ jobs:
run: | run: |
set -euo pipefail set -euo pipefail
v="${{ needs.version.outputs.version }}" 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" app_repo="mobile/ios/app-repo.json"
[ -f "$ipa" ] || { echo "::error::$ipa not found"; exit 1; } [ -f "$ipa" ] || { echo "::error::$ipa not found"; exit 1; }
[ -f "$app_repo" ] || { echo "::error::$app_repo not found"; exit 1; } [ -f "$app_repo" ] || { echo "::error::$app_repo not found"; exit 1; }
date="$(date -u +"%Y-%m-%d")" date="$(date -u +"%Y-%m-%d")"
size="$(wc -c < "$ipa" | tr -d '[:space:]')" 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" 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)" release_notes="$(GH_TOKEN="${{ github.token }}" gh release view "v${v}" --json body --jq '.body // ""' 2>/dev/null || true)"
if [ -n "$release_notes" ]; then if [ -n "$release_notes" ]; then
@@ -623,15 +624,15 @@ jobs:
--argjson size "$size" \ --argjson size "$size" \
'{version: $version, date: $date, size: $size, downloadURL: $download_url, localizedDescription: $localized_description}')" '{version: $version, date: $date, size: $size, downloadURL: $download_url, localizedDescription: $localized_description}')"
if jq -e --arg version "$v" \ if jq -e --arg bundle_id "$bundle_id" --arg version "$v" \
'any(.apps[] | select(.bundleIdentifier == "com.theboisclub.gen1recomp").versions[]?; .version == $version)' \ 'any(.apps[] | select(.bundleIdentifier == $bundle_id).versions[]?; .version == $version)' \
"$app_repo" >/dev/null; then "$app_repo" >/dev/null; then
jq --arg version "$v" --argjson entry "$entry" \ jq --arg bundle_id "$bundle_id" --arg version "$v" --argjson entry "$entry" \
'(.apps[] | select(.bundleIdentifier == "com.theboisclub.gen1recomp").versions) |= map(if .version == $version then $entry else . end)' \ '(.apps[] | select(.bundleIdentifier == $bundle_id).versions) |= map(if .version == $version then $entry else . end)' \
"$app_repo" > "$app_repo.tmp" "$app_repo" > "$app_repo.tmp"
else else
jq --argjson entry "$entry" \ jq --arg bundle_id "$bundle_id" --argjson entry "$entry" \
'(.apps[] | select(.bundleIdentifier == "com.theboisclub.gen1recomp").versions) |= [$entry] + .' \ '(.apps[] | select(.bundleIdentifier == $bundle_id).versions) |= [$entry] + .' \
"$app_repo" > "$app_repo.tmp" "$app_repo" > "$app_repo.tmp"
fi fi
mv "$app_repo.tmp" "$app_repo" mv "$app_repo.tmp" "$app_repo"
+1 -1
View File
@@ -242,7 +242,7 @@ in [docs/linux-arm64-build.md](docs/linux-arm64-build.md).
## iOS ## 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 (Windows or Mac) — see [docs/ios-sideload.md](docs/ios-sideload.md). To
build and install from source on a Mac instead, see build and install from source on a Mac instead, see
[docs/ios-install.md](docs/ios-install.md). [docs/ios-install.md](docs/ios-install.md).
+2 -2
View File
@@ -1,6 +1,6 @@
# Sideload the iOS build with AltStore # 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 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 re-signs the app with **your** free Apple ID so you do not need a Mac or
Xcode. Xcode.
@@ -23,7 +23,7 @@ Stuck? Start here:
## 2. Install the game ## 2. Install the game
1. Download `gen1recomp-*-ios.ipa` from 1. Download `gen1recomp++-*-ios.ipa` from
[Releases](https://github.com/bryanthaboi/gen1recomp/releases). [Releases](https://github.com/bryanthaboi/gen1recomp/releases).
2. Open **AltStore** on the phone (AltServer must be running on the same 2. Open **AltStore** on the phone (AltServer must be running on the same
WiFi, or keep the phone plugged into the computer). WiFi, or keep the phone plugged into the computer).
+9 -9
View File
@@ -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. 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 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: directory contains the app's runtime data directly, including:
- installed mods and downloaded ROMs - 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 Device builds require a paired, unlocked device and a valid Apple signing
identity. Set `DEVELOPMENT_TEAM` or `CODE_SIGN_IDENTITY` when automatic identity. Set `DEVELOPMENT_TEAM` or `CODE_SIGN_IDENTITY` when automatic
signing cannot select the intended account. Add `--ipa` to create 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: The script verifies the final app before packaging it:
@@ -76,10 +76,10 @@ option.
Simulator and device app bundles are copied to: Simulator and device app bundles are copied to:
```text ```text
dist/ios/Debug-iphonesimulator/gen1recomp.app dist/ios/Debug-iphonesimulator/gen1recomp++.app
dist/ios/Release-iphonesimulator/gen1recomp.app dist/ios/Release-iphonesimulator/gen1recomp++.app
dist/ios/Debug-iphoneos/gen1recomp.app dist/ios/Debug-iphoneos/gen1recomp++.app
dist/ios/Release-iphoneos/gen1recomp.app dist/ios/Release-iphoneos/gen1recomp++.app
``` ```
The intermediate Xcode products are under `mobile/ios/build/`. Both locations 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 | | Field | Default |
| --- | --- | | --- | --- |
| Display name | `gen1recomp` | | Display name | `gen1recomp++` |
| Product name | `gen1recomp` | | Product name | `gen1recomp++` |
| Bundle identifier | `com.theboisclub.gen1recomp` | | Bundle identifier | `com.theboisclub.gen1recompplusplus` |
| Save directory | Public `Documents` root | | Save directory | Public `Documents` root |
| Orientation | Portrait | | Orientation | Portrait |
+4 -4
View File
@@ -1,11 +1,11 @@
{ {
"name": "gen1recomp App Repo", "name": "gen1recomp++ App Repo",
"identifier": "com.theboisclub.gen1recomp.repo", "identifier": "com.theboisclub.gen1recompplusplus.repo",
"iconURL": "https://raw.githubusercontent.com/bryanthaboi/gen1recomp/main/assets/logo/gen1recomp_cover.png", "iconURL": "https://raw.githubusercontent.com/bryanthaboi/gen1recomp/main/assets/logo/gen1recomp_cover.png",
"apps": [ "apps": [
{ {
"name": "gen1recomp", "name": "gen1recomp++",
"bundleIdentifier": "com.theboisclub.gen1recomp", "bundleIdentifier": "com.theboisclub.gen1recompplusplus",
"developerName": "bryanthaboi", "developerName": "bryanthaboi",
"iconURL": "https://raw.githubusercontent.com/bryanthaboi/gen1recomp/main/assets/logo/gen1recomp_cover.png", "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", "localizedDescription": "Gen1Recomp - A native Lua / LÖVE2D recreation of Gen 1 Poke",
+2 -2
View File
@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>gen1recomp</string> <string>gen1recomp++</string>
<key>CFBundleDocumentTypes</key> <key>CFBundleDocumentTypes</key>
<array> <array>
<dict> <dict>
@@ -32,7 +32,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>gen1recomp</string> <string>gen1recomp++</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
+52 -2
View File
@@ -29,6 +29,9 @@ WRAP_SYSTEM = LOVE_SRC / "src" / "modules" / "system" / "wrap_System.cpp"
PBXPROJ = LOVE_SRC / "platform" / "xcode" / "love.xcodeproj" / "project.pbxproj" PBXPROJ = LOVE_SRC / "platform" / "xcode" / "love.xcodeproj" / "project.pbxproj"
APPLE_MM = LOVE_SRC / "src" / "common" / "apple.mm" APPLE_MM = LOVE_SRC / "src" / "common" / "apple.mm"
FILESYSTEM_CPP = LOVE_SRC / "src" / "modules" / "filesystem" / "physfs" / "Filesystem.cpp" 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" ENTITLEMENTS_SRC = IOS_DIR / "overlays" / "love-ios.entitlements"
NATIVE_FILES = ("GRPickerBridge.swift", "GRHealthBridge.swift", "GRBootstrap.m") NATIVE_FILES = ("GRPickerBridge.swift", "GRHealthBridge.swift", "GRBootstrap.m")
@@ -120,7 +123,7 @@ int w_pickFileKinds(lua_State *L)
typedef const char *(*GRUTF8)(id, SEL); typedef const char *(*GRUTF8)(id, SEL);
const char *bytes = ((GRUTF8)objc_msgSend)(kinds, const char *bytes = ((GRUTF8)objc_msgSend)(kinds,
sel_registerName("UTF8String")); sel_registerName("UTF8String"));
if (bytes == nullptr || bytes[0] == '\0') if (bytes == nullptr || bytes[0] == '\\0')
{ {
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
@@ -358,6 +361,50 @@ def patch_public_documents():
print("patch_love_src: iOS save directory routed to Documents root") 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(): def patch_pbxproj():
text = pristine(PBXPROJ) text = pristine(PBXPROJ)
@@ -408,7 +455,9 @@ def patch_pbxproj():
fail(f"build configuration {config_id} not found") fail(f"build configuration {config_id} not found")
settings = ( settings = (
"\t\t\t\tSWIFT_VERSION = 5.0;\n" "\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' '\t\t\t\tCODE_SIGN_ENTITLEMENTS = "ios/native/love-ios.entitlements";\n'
) )
text = text[: m.end()] + settings + text[m.end():] 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") fail("love-src/ missing; run scripts/build_ios.sh --fetch first")
copy_native_files() copy_native_files()
patch_public_documents() patch_public_documents()
patch_ios_haptics()
patch_wrap_system() patch_wrap_system()
patch_pbxproj() patch_pbxproj()
+1 -1
View File
@@ -13,7 +13,7 @@
# dist/linux/gen1recomp-linux.zip (fused x86_64 AppImage) # dist/linux/gen1recomp-linux.zip (fused x86_64 AppImage)
# dist/android/debug/*.apk (full gradle output stays under # dist/android/debug/*.apk (full gradle output stays under
# mobile/android/app/build/outputs/apk/embedNoRecord/) # mobile/android/app/build/outputs/apk/embedNoRecord/)
# dist/ios/<Config>-<sdk>/gen1recomp.app (full xcodebuild output stays # dist/ios/<Config>-<sdk>/gen1recomp++.app (full xcodebuild output stays
# under mobile/ios/build/Build/Products/) # under mobile/ios/build/Build/Products/)
set -euo pipefail set -euo pipefail
+43 -25
View File
@@ -20,9 +20,9 @@
# - macOS + Xcode (xcodebuild) # - macOS + Xcode (xcodebuild)
# - mobile/ios/love-src/ (see --fetch / mobile/ios/README.md) # - mobile/ios/love-src/ (see --fetch / mobile/ios/README.md)
# #
# Output: dist/ios/<Config>-<sdk>/gen1recomp.app (convenience copy) # Output: dist/ios/<Config>-<sdk>/gen1recomp++.app (convenience copy)
# dist/ios/gen1recomp.ipa (device builds only) # dist/ios/gen1recomp++.ipa (device builds only)
# mobile/ios/build/Build/Products/<Config>-<sdk>/gen1recomp.app # mobile/ios/build/Build/Products/<Config>-<sdk>/gen1recomp++.app
set -euo pipefail set -euo pipefail
@@ -39,8 +39,9 @@ RESOURCES_DIR="$XCODE_DIR/ios/resources"
LOVE_FILE="$RESOURCES_DIR/game.love" LOVE_FILE="$RESOURCES_DIR/game.love"
LIBS_DIR="$XCODE_DIR/ios/libraries" LIBS_DIR="$XCODE_DIR/ios/libraries"
APP_NAME="gen1recomp" APP_NAME="gen1recomp++"
DISPLAY_NAME="gen1recomp" DISPLAY_NAME="gen1recomp++"
PRODUCT_NAME="gen1recomp++"
# Bundle ID resolution, most specific wins: # Bundle ID resolution, most specific wins:
# 1. GEN1_BUNDLE_ID env var # 1. GEN1_BUNDLE_ID env var
# 2. mobile/ios/bundle_id.local (one line, gitignored — pins YOUR install # 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 # capabilities like HealthKit are involved), so a per-team default
# lets anyone build without colliding with someone else's app # lets anyone build without colliding with someone else's app
# 4. simulator: the project default (no App ID registration involved) # 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 if [ -z "$BUNDLE_ID" ] && [ -f "$IOS_DIR/bundle_id.local" ]; then
BUNDLE_ID="$(tr -d '[:space:]' < "$IOS_DIR/bundle_id.local")" BUNDLE_ID="$(tr -d '[:space:]' < "$IOS_DIR/bundle_id.local")"
fi fi
@@ -138,11 +139,7 @@ if $DEVICE && [ -z "${DEVELOPMENT_TEAM:-}" ]; then
fi fi
fi fi
if [ -z "$BUNDLE_ID" ]; then if [ -z "$BUNDLE_ID" ]; then
if $DEVICE; then BUNDLE_ID="com.theboisclub.gen1recompplusplus"
BUNDLE_ID="com.gen1recomp.t$(printf '%s' "$DEVELOPMENT_TEAM" | tr '[:upper:]' '[:lower:]')"
else
BUNDLE_ID="com.theboisclub.pokemonred"
fi
fi fi
# --------------------------------------------------------------- host checks # --------------------------------------------------------------- host checks
@@ -226,13 +223,26 @@ verify_documents_overlay() {
} }
apply_ios_icon() { 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" local target="$XCODE_DIR/Images.xcassets/iOS AppIcon.appiconset"
[ -f "$source" ] || fail "missing iOS icon source: $source" [ -f "$source" ] || fail "missing iOS icon source: $source"
[ -d "$target" ] || fail "missing iOS app icon set: $target" [ -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 local entry name size
while IFS=: read -r name size; do 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' done <<'EOF'
icon-1024pt@1x.png:1024 icon-1024pt@1x.png:1024
icon-29pt@1x.png:29 icon-29pt@1x.png:29
@@ -647,6 +657,7 @@ run_xcodebuild() {
CURRENT_PROJECT_VERSION="$project_version" CURRENT_PROJECT_VERSION="$project_version"
INFOPLIST_KEY_UIFileSharingEnabled=YES INFOPLIST_KEY_UIFileSharingEnabled=YES
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace=YES INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace=YES
IPHONEOS_DEPLOYMENT_TARGET=15.0
ONLY_ACTIVE_ARCH=NO ONLY_ACTIVE_ARCH=NO
DISABLE_MANUAL_TARGET_ORDER_BUILD_WARNING=YES DISABLE_MANUAL_TARGET_ORDER_BUILD_WARNING=YES
) )
@@ -666,6 +677,9 @@ run_xcodebuild() {
if [ -n "${CODE_SIGN_IDENTITY:-}" ]; then if [ -n "${CODE_SIGN_IDENTITY:-}" ]; then
args+=(CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY") args+=(CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY")
fi fi
if [ "${GEN1_DISABLE_HEALTHKIT:-0}" = "1" ]; then
args+=(CODE_SIGN_ENTITLEMENTS=)
fi
fi fi
if ! xcodebuild -showsdks 2>/dev/null | grep -q "$sdk"; then if ! xcodebuild -showsdks 2>/dev/null | grep -q "$sdk"; then
@@ -708,18 +722,22 @@ run_xcodebuild() {
fi fi
local products="$BUILD_DIR/Build/Products/${config}-${sdk}" local products="$BUILD_DIR/Build/Products/${config}-${sdk}"
local app="$products/$APP_NAME.app" local app=""
if [ ! -d "$app" ]; then local candidate
# PRODUCT_NAME override can still leave love.app on older projects for candidate in "$products/$PRODUCT_NAME.app" "$products/$APP_NAME.app" "$products/love.app"; do
if [ -d "$products/love.app" ]; then if [ -d "$candidate" ]; then
app="$products/$APP_NAME.app" app="$candidate"
mv "$products/love.app" "$app" break
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
fi 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 fi
verify_documents_configuration "$app" verify_documents_configuration "$app"
@@ -760,7 +778,7 @@ run_xcodebuild() {
fi fi
} }
# Pack Payload/<app>.app into dist/ios/gen1recomp.ipa for release / sideload tools. # Pack Payload/<app>.app into dist/ios/gen1recomp++.ipa for release / sideload tools.
package_ipa() { package_ipa() {
local app="$1" local app="$1"
local ipa="$DIST/$APP_NAME.ipa" local ipa="$DIST/$APP_NAME.ipa"
+5 -5
View File
@@ -335,11 +335,11 @@ function SaveData.defaultOptions()
-- TouchControls.normalizeConfig folds it into both orientations on load. -- TouchControls.normalizeConfig folds it into both orientations on load.
touchControls = { enabled = true }, touchControls = { enabled = true },
-- Haptic feedback level for on-screen pad presses (#806): -- Haptic feedback level for on-screen pad presses (#806):
-- off | light | medium | heavy, mapped to a love.system.vibrate -- off | light | normal | strong, mapped to a love.system.vibrate duration
-- duration in src/core/TouchControls.lua. LIGHT by default, like the -- in src/core/TouchControls.lua. LIGHT by default, so an options.lua
-- overlay itself defaulting on, so an options.lua predating this key -- predating this key gets the tick without going looking for the row.
-- gets the tick without going looking for the row. Inert wherever the -- Inert wherever the overlay never appears (desktop) or LOVE has no
-- overlay never appears (desktop) or LOVE has no vibrator. -- vibrator.
haptics = "light", haptics = "light",
-- Shared UI/mod timestamp presentation. DEVICE follows the process time -- Shared UI/mod timestamp presentation. DEVICE follows the process time
-- locale where the platform exposes one; otherwise DateTime falls back to -- locale where the platform exposes one; otherwise DateTime falls back to
+9 -5
View File
@@ -105,17 +105,21 @@ end
-- would be dropped on every editor save. -- would be dropped on every editor save.
-- love.system.vibrate takes a duration and nothing else, so "intensity" is a -- love.system.vibrate takes a duration and nothing else, so "intensity" is a
-- duration preset: Android runs the platform vibrator for exactly that long, -- duration preset: Android runs the platform vibrator for exactly that long,
-- while iOS ignores the duration and fires the fixed system vibration, so -- while iOS maps each duration to a matching Taptic Engine impact.
-- there the three levels all read as simply on. TouchControls.HAPTICS = { "off", "light", "normal", "strong" }
TouchControls.HAPTICS = { "off", "light", "medium", "heavy" }
TouchControls.HAPTIC_DEFAULT = "light" 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 = { local HAPTIC_LABELS = {
off = "OFF", light = "LIGHT", medium = "MEDIUM", heavy = "HEAVY", off = "OFF", light = "LIGHT", normal = "NORMAL", strong = "STRONG",
} }
function TouchControls.normalizeHaptics(level) 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 if HAPTIC_SECONDS[level] then return level end
return TouchControls.HAPTIC_DEFAULT return TouchControls.HAPTIC_DEFAULT
end end
+2 -2
View File
@@ -501,7 +501,7 @@ local function buildRows(game)
return true return true
end }, end },
-- Haptic feedback for on-screen pad presses (#806): OFF / LIGHT / -- 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, -- love.system.vibrate takes nothing else. Hidden with TOUCH PAD below,
-- since the only thing that buzzes is a virtual button press. -- since the only thing that buzzes is a virtual button press.
{ id = "haptics", label = Strings("VIBRATION"), { id = "haptics", label = Strings("VIBRATION"),
@@ -515,7 +515,7 @@ local function buildRows(game)
o.haptics = TC.cycleHaptics(o.haptics, dir) o.haptics = TC.cycleHaptics(o.haptics, dir)
TC:applyOptions(o) TC:applyOptions(o)
-- sample the level being selected: stepping the row is the only way -- 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) TC.buzz(o.haptics)
return true return true
end }, end },