mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-15 07:41:21 +02:00
Merge pull request #1204 from castdrian/ios-improvements
feat(ios): improve branding and fix haptic feedback
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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 }}`
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|||||||
@@ -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
|
||||||
Wi‑Fi, or keep the phone plugged into the computer).
|
Wi‑Fi, or keep the phone plugged into the computer).
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
Reference in New Issue
Block a user