init
76
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- clean-master
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.toml') }}
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install alsa and udev
|
||||
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev
|
||||
if: runner.os == 'linux'
|
||||
- name: Build & run tests
|
||||
run: cargo test
|
||||
all-doc-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ubuntu-latest-cargo-all-doc-tests-${{ hashFiles('**/Cargo.toml') }}
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install alsa and udev
|
||||
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev
|
||||
- name: Run doc tests with all features (this also compiles README examples)
|
||||
run: cargo test --doc --all-features
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ubuntu-latest-cargo-lint-${{ hashFiles('**/Cargo.toml') }}
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
- name: Install alsa and udev
|
||||
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev
|
||||
- name: Run clippy
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -Dwarnings
|
||||
- name: Check format
|
||||
run: cargo fmt --all -- --check
|
40
.github/workflows/deploy-page.yaml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: deploy-github-page
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-web:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install rust toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev
|
||||
- name: Install trunk
|
||||
uses: jetli/trunk-action@v0.4.0
|
||||
with:
|
||||
version: 'latest'
|
||||
- name: Add wasm target
|
||||
run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- name: Build Release
|
||||
run: |
|
||||
trunk build --release
|
||||
- name: optimize Wasm
|
||||
uses: NiklasEi/wasm-opt-action@v2
|
||||
with:
|
||||
file: dist/*.wasm
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: JamesIves/github-pages-deploy-action@v4.2.5
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: dist
|
75
.github/workflows/release-android-google-play.yaml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# For setup instructions regarding this workflow, see https://www.nikl.me/blog/2023/github_workflow_to_publish_android_app/
|
||||
|
||||
name: release-android-google-play
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'GitHub Release'
|
||||
required: true
|
||||
type: string
|
||||
play_release:
|
||||
description: 'Release name from google play console'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
# used for uploading the app to a GitHub release
|
||||
GAME_EXECUTABLE_NAME: yachtpit
|
||||
BUNDLE_PATH: "target/x/release/android/mobile.aab"
|
||||
PACKAGE_NAME: "io.gs.yachtpit"
|
||||
# release track; you can promote a build to "higher" tracks in the play console or publish to a different track directly
|
||||
# see track at https://github.com/r0adkll/upload-google-play#inputs for more options
|
||||
TRACK: internal
|
||||
MOBILE_DIRECTORY: mobile
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
bundle-sign-release:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev lld llvm
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Add Android targets
|
||||
run: rustup target add aarch64-linux-android armv7-linux-androideabi
|
||||
- name: Install cargo-binstall
|
||||
run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||
- name: Install xbuild
|
||||
run: cargo binstall --git https://github.com/NiklasEi/xbuild --bin-dir x xbuild -y
|
||||
- name: Build app bundle
|
||||
run: |
|
||||
cd ${{ env.MOBILE_DIRECTORY }}
|
||||
x doctor
|
||||
x build --release --platform android --store play
|
||||
- name: sign app bundle
|
||||
run: |
|
||||
KEYSTORE_PATH=${{ runner.temp }}/upload-keystore.jks
|
||||
echo -n "${{ secrets.PLAYSTORE_KEYSTORE }}" | base64 --decode > $KEYSTORE_PATH
|
||||
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore $KEYSTORE_PATH -storepass "${{ secrets.PLAYSTORE_KEYSTORE_PASSWORD }}" ${{ env.BUNDLE_PATH }} upload
|
||||
- name: Upload self-signed bundle to GitHub
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ env.BUNDLE_PATH }}
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ inputs.version }}_android.aab
|
||||
release_name: ${{ inputs.version }}
|
||||
tag: ${{ inputs.version }}
|
||||
overwrite: true
|
||||
- name: prepare Google play store secrets
|
||||
run: |
|
||||
SERVICE_ACCOUNT=${{ runner.temp }}/service-account.json
|
||||
echo -n "${{ secrets.PLAYSTORE_SERVICE_ACCOUNT }}" | base64 --decode > $SERVICE_ACCOUNT
|
||||
- name: upload bundle to Google play store
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJson: ${{ runner.temp }}/service-account.json
|
||||
packageName: ${{ env.PACKAGE_NAME }}
|
||||
releaseName: ${{ inputs.play_release }}
|
||||
releaseFiles: ${{ env.BUNDLE_PATH }}
|
||||
track: ${{ env.TRACK }}
|
97
.github/workflows/release-ios-testflight.yaml
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
name: release-ios-testflight
|
||||
|
||||
# This workflow builds, archives, exports, validates and uploads your ios app.
|
||||
# The version from input is only used for artifact names and as the release to upload the final ipa to.
|
||||
# Bump the versions in `mobile/ios-src/Info.plist` to change the version of your app bundle.
|
||||
|
||||
# Special setup and Apple Developer Program membership (99$/year) is required for this workflow!
|
||||
|
||||
# For setup instructions, see https://www.nikl.me/blog/2023/github_workflow_to_publish_ios_app/
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version - in the form of v1.2.3'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
# used for uploading the app to a GitHub release
|
||||
GAME_EXECUTABLE_NAME: yachtpit
|
||||
XCODE_PROJECT: mobile
|
||||
MOBILE_DIRECTORY: mobile
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-for-iOS:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 40
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Add iOS targets
|
||||
run: rustup target add aarch64-apple-ios
|
||||
- name: Install the Apple certificate and provisioning profile
|
||||
id: profile
|
||||
env:
|
||||
IOS_CERTIFICATE: ${{ secrets.IOS_CERTIFICATE }}
|
||||
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
|
||||
IOS_PROVISION_PROFILE: ${{ secrets.IOS_PROVISION_PROFILE }}
|
||||
IOS_KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
||||
run: |
|
||||
# create variables
|
||||
CERTIFICATE_PATH=${{ runner.temp }}/build_certificate.p12
|
||||
PP_PATH=${{ runner.temp }}/profile.mobileprovision
|
||||
KEYCHAIN_PATH=${{ runner.temp }}/app-signing.keychain-db
|
||||
|
||||
# import certificate and provisioning profile from secrets
|
||||
echo -n "$IOS_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH
|
||||
echo -n "$IOS_PROVISION_PROFILE" | base64 --decode -o $PP_PATH
|
||||
uuid=`grep UUID -A1 -a $PP_PATH | grep -io "[-A-F0-9]\{36\}"`
|
||||
echo "uuid=$uuid" >> $GITHUB_OUTPUT
|
||||
|
||||
# create temporary keychain
|
||||
security create-keychain -p "$IOS_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||
security unlock-keychain -p "$IOS_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
|
||||
# import certificate to keychain
|
||||
security import $CERTIFICATE_PATH -P "$IOS_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
# apply provisioning profile
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/$uuid.mobileprovision
|
||||
- name: Build app for iOS
|
||||
run: |
|
||||
cd ${{ env.MOBILE_DIRECTORY }}
|
||||
xcodebuild PROVISIONING_PROFILE=${{ steps.profile.outputs.uuid }} -scheme ${{ env.XCODE_PROJECT }} clean archive -archivePath "Actions" -configuration Release -arch arm64
|
||||
- name: export ipa
|
||||
env:
|
||||
EXPORT_PLIST: ${{ secrets.IOS_EXPORT_PRODUCTION }}
|
||||
run: |
|
||||
EXPORT_PLIST_PATH=${{ runner.temp }}/ExportOptions.plist
|
||||
echo -n "$EXPORT_PLIST" | base64 --decode --output $EXPORT_PLIST_PATH
|
||||
xcodebuild PROVISIONING_PROFILE=${{ steps.profile.outputs.uuid }} -exportArchive -archivePath ${{ env.MOBILE_DIRECTORY }}/Actions.xcarchive -exportOptionsPlist $EXPORT_PLIST_PATH -exportPath ${{ runner.temp }}/export
|
||||
- name: decode API key
|
||||
env:
|
||||
API_KEY_BASE64: ${{ secrets.IOS_APPSTORE_API_PRIVATE_KEY }}
|
||||
run: |
|
||||
mkdir -p ~/private_keys
|
||||
echo -n "$API_KEY_BASE64" | base64 --decode --output ~/private_keys/AuthKey_${{ secrets.IOS_APPSTORE_API_KEY_ID }}.p8
|
||||
- name: Upload to testflight
|
||||
run: |
|
||||
xcrun altool --validate-app -f ${{ runner.temp }}/export/${{ env.XCODE_PROJECT }}.ipa -t ios --apiKey ${{ secrets.IOS_APPSTORE_API_KEY_ID }} --apiIssuer ${{ secrets.IOS_APPSTORE_ISSUER_ID }}
|
||||
xcrun altool --upload-app -f ${{ runner.temp }}/export/${{ env.XCODE_PROJECT }}.ipa -t ios --apiKey ${{ secrets.IOS_APPSTORE_API_KEY_ID }} --apiIssuer ${{ secrets.IOS_APPSTORE_ISSUER_ID }}
|
||||
- name: Upload release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ runner.temp }}/export/${{ env.XCODE_PROJECT }}.ipa
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ inputs.version }}_ios.ipa
|
||||
release_name: ${{ inputs.version }}
|
||||
tag: ${{ inputs.version }}
|
||||
overwrite: true
|
271
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
name: release-flow
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v[0-9]+.[0-9]+.[0-9]+*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version - in the form of v1.2.3'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
# This variable is used to name release output files.
|
||||
GAME_EXECUTABLE_NAME: yachtpit
|
||||
GAME_OSX_APP_NAME: yachtpit
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get tag
|
||||
id: tag
|
||||
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
outputs:
|
||||
version: ${{ inputs.version || steps.tag.outputs.tag }}
|
||||
|
||||
build-macOS:
|
||||
runs-on: macos-latest
|
||||
needs: get-version
|
||||
env:
|
||||
# macOS 11.0 Big Sur is the first version to support universal binaries
|
||||
MACOSX_DEPLOYMENT_TARGET: 11.0
|
||||
VERSION: ${{needs.get-version.outputs.version}}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Remove build script
|
||||
run: |
|
||||
rm build.rs
|
||||
- name: Install rust toolchain for Apple Silicon
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
targets: aarch64-apple-darwin
|
||||
- name: Build release for Apple Silicon
|
||||
run: |
|
||||
SDKROOT=$(xcrun -sdk macosx --show-sdk-path) cargo build --profile dist --target=aarch64-apple-darwin
|
||||
- name: Install rust toolchain for Apple x86
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
targets: x86_64-apple-darwin
|
||||
- name: Build release for x86 Apple
|
||||
run: |
|
||||
SDKROOT=$(xcrun -sdk macosx --show-sdk-path) cargo build --profile dist --target=x86_64-apple-darwin
|
||||
- name: Create Universal Binary
|
||||
run: |
|
||||
lipo -create -output target/dist/${{ env.GAME_EXECUTABLE_NAME }} target/aarch64-apple-darwin/dist/${{ env.GAME_EXECUTABLE_NAME }} target/x86_64-apple-darwin/dist/${{ env.GAME_EXECUTABLE_NAME }}
|
||||
- name: Create release
|
||||
run: |
|
||||
mkdir -p build/macos/src/Game.app/Contents/MacOS/assets
|
||||
cp -r assets/ build/macos/src/Game.app/Contents/MacOS/assets
|
||||
cp -r credits/ build/macos/src/Game.app/Contents/MacOS/credits
|
||||
cp target/dist/${{ env.GAME_EXECUTABLE_NAME }} build/macos/src/Game.app/Contents/MacOS/
|
||||
mv build/macos/src/Game.app build/macos/src/${{ env.GAME_OSX_APP_NAME }}.app
|
||||
ln -s /Applications build/macos/src/
|
||||
hdiutil create -fs HFS+ -volname "${{ env.GAME_OSX_APP_NAME }}" -srcfolder build/macos/src ${{ env.GAME_EXECUTABLE_NAME }}.dmg
|
||||
- name: Upload release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ env.GAME_EXECUTABLE_NAME }}.dmg
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ env.VERSION }}_macOS.dmg
|
||||
release_name: ${{ env.VERSION }}
|
||||
tag: ${{ env.VERSION }}
|
||||
overwrite: true
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
needs: get-version
|
||||
env:
|
||||
VERSION: ${{needs.get-version.outputs.version}}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install rust toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev
|
||||
- name: Build release
|
||||
run: |
|
||||
cargo build --profile dist
|
||||
- name: Prepare release
|
||||
run: |
|
||||
chmod +x target/dist/${{ env.GAME_EXECUTABLE_NAME }}
|
||||
mv target/dist/${{ env.GAME_EXECUTABLE_NAME }} .
|
||||
- name: Bundle release
|
||||
run: |
|
||||
tar -czf ${{ env.GAME_EXECUTABLE_NAME }}_linux.tar.gz ${{ env.GAME_EXECUTABLE_NAME }} assets credits
|
||||
- name: Upload release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ env.GAME_EXECUTABLE_NAME }}_linux.tar.gz
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ env.VERSION }}_linux.tar.gz
|
||||
release_name: ${{ env.VERSION }}
|
||||
tag: ${{ env.VERSION }}
|
||||
overwrite: true
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
needs: get-version
|
||||
env:
|
||||
VERSION: ${{needs.get-version.outputs.version}}
|
||||
BUILD_INSTALLER: ${{ false }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install rust toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install dotnet
|
||||
if: ${{ env.BUILD_INSTALLER }}
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: build/windows/installer/global.json
|
||||
- name: Build release
|
||||
run: |
|
||||
cargo build --profile dist
|
||||
- name: Prepare release
|
||||
run: |
|
||||
mkdir target/dist/assets && cp -r assets target/dist/assets
|
||||
mkdir target/dist/credits && cp -r credits target/dist/credits
|
||||
- name: Zip release
|
||||
uses: vimtor/action-zip@v1.1
|
||||
with:
|
||||
files: target/dist/assets/ target/dist/credits/ target/dist/${{ env.GAME_EXECUTABLE_NAME }}.exe
|
||||
dest: ${{ env.GAME_EXECUTABLE_NAME }}_windows.zip
|
||||
- name: Create Installer
|
||||
if: ${{ env.BUILD_INSTALLER }}
|
||||
shell: bash
|
||||
run: |
|
||||
tag=${{ env.VERSION }}
|
||||
version="${tag#v}"
|
||||
dotnet build -p:Version=$version -c Release build/windows/installer/Installer.wixproj --output installer
|
||||
- name: Upload release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ env.GAME_EXECUTABLE_NAME }}_windows.zip
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ env.VERSION }}_windows.zip
|
||||
tag: ${{ env.VERSION }}
|
||||
overwrite: true
|
||||
- name: Upload installer
|
||||
if: ${{ env.BUILD_INSTALLER }}
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: installer/en-US/installer.msi
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ env.VERSION }}_windows.msi
|
||||
release_name: ${{ env.VERSION }}
|
||||
tag: ${{ env.VERSION }}
|
||||
overwrite: true
|
||||
|
||||
build-web:
|
||||
runs-on: ubuntu-latest
|
||||
needs: get-version
|
||||
env:
|
||||
VERSION: ${{needs.get-version.outputs.version}}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install rust toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev
|
||||
- name: Install trunk
|
||||
uses: jetli/trunk-action@v0.4.0
|
||||
with:
|
||||
version: latest
|
||||
- name: Add wasm target
|
||||
run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- name: Build Release
|
||||
run: |
|
||||
trunk build --release
|
||||
- name: Optimize Wasm
|
||||
uses: NiklasEi/wasm-opt-action@v2
|
||||
with:
|
||||
file: dist/*.wasm
|
||||
- name: Zip release
|
||||
uses: vimtor/action-zip@v1.1
|
||||
with:
|
||||
files: dist/
|
||||
dest: ${{ env.GAME_EXECUTABLE_NAME }}_web.zip
|
||||
- name: Upload release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ env.GAME_EXECUTABLE_NAME }}_web.zip
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ env.VERSION }}_web.zip
|
||||
release_name: ${{ env.VERSION }}
|
||||
tag: ${{ env.VERSION }}
|
||||
overwrite: true
|
||||
|
||||
build-for-iOS:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
needs: get-version
|
||||
env:
|
||||
VERSION: ${{needs.get-version.outputs.version}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Add iOS targets
|
||||
run: rustup target add aarch64-apple-ios x86_64-apple-ios
|
||||
- name: Build app for iOS
|
||||
run: |
|
||||
cd mobile
|
||||
make xcodebuild-iphone-release
|
||||
mkdir Payload
|
||||
mv build/Build/Products/Release-iphoneos/*.app Payload
|
||||
zip -r ${{ env.GAME_EXECUTABLE_NAME }}.zip Payload
|
||||
mv ${{ env.GAME_EXECUTABLE_NAME }}.zip ${{ env.GAME_EXECUTABLE_NAME }}.ipa
|
||||
- name: Upload release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: mobile/${{ env.GAME_EXECUTABLE_NAME }}.ipa
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ env.VERSION }}_unsigned_ios.ipa
|
||||
release_name: ${{ env.VERSION }}
|
||||
tag: ${{ env.VERSION }}
|
||||
overwrite: true
|
||||
|
||||
build-for-Android:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
needs: get-version
|
||||
env:
|
||||
VERSION: ${{needs.get-version.outputs.version}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Add Android targets
|
||||
# could add more targets like armv7-linux-androideabi here (then also add to cargo-apk config)
|
||||
run: rustup target add aarch64-linux-android
|
||||
- name: Install Cargo APK
|
||||
run: cargo install --force cargo-apk
|
||||
- name: Build app for Android
|
||||
# This uses a debug build, since release builds require keystore configuration
|
||||
# For AAB builds that can be pushed to the Play store, see the release-android-google-play workflow.
|
||||
run: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME cargo apk build --package mobile
|
||||
- name: Upload release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: target/debug/apk/${{ env.GAME_OSX_APP_NAME }}.apk
|
||||
asset_name: ${{ env.GAME_EXECUTABLE_NAME }}_${{ env.VERSION }}_android.apk
|
||||
release_name: ${{ env.VERSION }}
|
||||
tag: ${{ env.VERSION }}
|
||||
overwrite: true
|
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
target/
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
dist/
|
275
BACKLOG.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# Backlog - yachtpit
|
||||
|
||||
## Goal
|
||||
Complete the yachtpit instrument cluster with comprehensive icon system and enhance user experience for marine navigation and monitoring.
|
||||
|
||||
## Overview
|
||||
**Duration:** 2 weeks
|
||||
**Capacity:** 40 story points
|
||||
**Focus:** Icons, UI/UX, Integration, Testing
|
||||
|
||||
---
|
||||
|
||||
## High Priority ~ 24 points
|
||||
|
||||
|
||||
### Track: Core Navigation & Instrumentation
|
||||
|
||||
#### US-001: Navigation Display Icons (8 points)
|
||||
**As a** yacht captain
|
||||
**I want** to see intuitive navigation icons (compass, GPS, waypoints)
|
||||
**So that** I can quickly understand my vessel's position and heading
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Compass rose icon displays current heading
|
||||
- [ ] GPS satellite icon shows connection status
|
||||
- [ ] Waypoint icons mark navigation points
|
||||
- [ ] North arrow provides directional reference
|
||||
- [ ] Icons are visible in marine lighting conditions
|
||||
|
||||
**Technical Tasks:**
|
||||
- Create compass rose SVG icon (2h)
|
||||
- Implement GPS status indicator component (3h)
|
||||
- Add waypoint marker system (4h)
|
||||
- Update TextureAssets in loading.rs (1h)
|
||||
- Add navigation icons to assets/textures/icons/ (1h)
|
||||
|
||||
#### US-002: Instrument Gauge Icons (8 points)
|
||||
**As a** yacht operator
|
||||
**I want** clear visual indicators for speed, depth, and engine status
|
||||
**So that** I can monitor critical vessel parameters at a glance
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Speed gauge displays with knots unit indicator
|
||||
- [ ] Depth sounder shows water depth with meter units
|
||||
- [ ] Engine temperature gauge with warning states
|
||||
- [ ] Fuel level indicator with consumption tracking
|
||||
- [ ] Battery level with charging status
|
||||
|
||||
**Technical Tasks:**
|
||||
- Design speedometer and depth gauge icons (3h)
|
||||
- Create engine status indicator components (4h)
|
||||
- Implement fuel and battery level displays (3h)
|
||||
- Add gauge needle animations (2h)
|
||||
- Update YachtData struct with new parameters (1h)
|
||||
|
||||
#### US-003: System Status Indicators (8 points)
|
||||
**As a** yacht crew member
|
||||
**I want** color-coded status indicators for all systems
|
||||
**So that** I can immediately identify operational, warning, and fault conditions
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Green dots for operational systems
|
||||
- [ ] Yellow dots for warning states
|
||||
- [ ] Red dots for fault/offline systems
|
||||
- [ ] Blue dots for standby mode
|
||||
- [ ] Status changes reflect real system states
|
||||
|
||||
**Technical Tasks:**
|
||||
- Create status dot icon set (2h)
|
||||
- Implement SystemStatus component (3h)
|
||||
- Add status update logic to system displays (4h)
|
||||
- Create alert notification system (2h)
|
||||
- Add system health monitoring (2h)
|
||||
|
||||
---
|
||||
|
||||
## Medium Priority ~ 12 points
|
||||
|
||||
|
||||
### Track: Advanced Marine Systems
|
||||
|
||||
#### US-004: Radar & AIS Integration
|
||||
**As a** yacht navigator
|
||||
**I want** radar and AIS system displays with appropriate icons
|
||||
**So that** I can track other vessels and obstacles around my yacht
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Radar dish icon shows system status
|
||||
- [ ] AIS ship icons display other vessels
|
||||
- [ ] Target blip indicators for radar contacts
|
||||
- [ ] Radio wave icons for communication status
|
||||
- [ ] Integration with existing system selection
|
||||
|
||||
**Technical Tasks:**
|
||||
- Design radar and AIS icon set (2h)
|
||||
- Implement radar sweep animation (3h)
|
||||
- Add AIS target tracking display (4h)
|
||||
- Update system interaction handlers (1h)
|
||||
|
||||
#### US-005: Weather & Environmental Icons
|
||||
**As a** yacht captain
|
||||
**I want** weather condition indicators with wind and atmospheric data
|
||||
**So that** I can make informed navigation decisions based on conditions
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Wind vane shows direction and speed
|
||||
- [ ] Barometer icon displays atmospheric pressure
|
||||
- [ ] Temperature and humidity indicators
|
||||
- [ ] Beaufort scale wind force display
|
||||
- [ ] Weather data updates in real-time simulation
|
||||
|
||||
**Technical Tasks:**
|
||||
- Create weather icon collection (2h)
|
||||
- Implement wind direction component (2h)
|
||||
- Add atmospheric data display (3h)
|
||||
- Integrate weather simulation system (2h)
|
||||
|
||||
---
|
||||
|
||||
## Low Priority ~ 4 points
|
||||
|
||||
### Track: Safety & Emergency Systems
|
||||
|
||||
#### US-006: Safety Equipment Icons
|
||||
**As a** yacht safety officer
|
||||
**I want** visual indicators for safety equipment status
|
||||
**So that** I can ensure all emergency equipment is ready and accessible
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Life ring icon for safety equipment
|
||||
- [ ] Fire extinguisher status indicator
|
||||
- [ ] First aid kit availability marker
|
||||
- [ ] Emergency radio communication status
|
||||
|
||||
**Technical Tasks:**
|
||||
- Design safety equipment icon set (1h)
|
||||
- Add safety system status tracking (2h)
|
||||
- Implement emergency equipment checklist (1h)
|
||||
|
||||
#### US-007: Chart & Navigation Tools
|
||||
**As a** yacht navigator
|
||||
**I want** chart symbols and measurement tool icons
|
||||
**So that** I can perform navigation calculations and chart plotting
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Nautical chart icon for chart display mode
|
||||
- [ ] Ruler icon for distance measurements
|
||||
- [ ] Protractor icon for bearing calculations
|
||||
- [ ] Harbor and anchorage markers
|
||||
|
||||
**Technical Tasks:**
|
||||
- Create navigation tool icon set (1h)
|
||||
- Add measurement tool functionality (2h)
|
||||
- Implement chart symbol display (1h)
|
||||
|
||||
---
|
||||
|
||||
## Technical Debt & Infrastructure Tasks
|
||||
|
||||
### Track: All
|
||||
|
||||
#### TD-001: Asset Management System Enhancement
|
||||
**Priority:** High
|
||||
**Description:** Expand TextureAssets to support comprehensive icon loading system
|
||||
|
||||
**Tasks:**
|
||||
- [ ] Create icons subdirectory structure in assets/textures/
|
||||
- [ ] Update loading.rs with icon asset collections
|
||||
- [ ] Implement icon resource management system
|
||||
- [ ] Add icon preloading optimization
|
||||
- [ ] Create asset validation system
|
||||
|
||||
#### TD-002: Component Architecture Refactoring
|
||||
**Priority:** Medium
|
||||
**Description:** Optimize component structure for better performance and maintainability
|
||||
|
||||
**Tasks:**
|
||||
- [ ] Refactor instrument cluster components
|
||||
- [ ] Implement component pooling for status indicators
|
||||
- [ ] Add component lifecycle management
|
||||
- [ ] Optimize query systems for better performance
|
||||
|
||||
#### TD-003: Testing Infrastructure
|
||||
**Priority:** Medium
|
||||
**Description:** Expand test coverage for new components and systems
|
||||
|
||||
**Tasks:**
|
||||
- [ ] Add unit tests for new icon components
|
||||
- [ ] Create integration tests for system interactions
|
||||
- [ ] Implement visual regression tests for UI components
|
||||
- [ ] Add performance benchmarks for rendering systems
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done
|
||||
|
||||
### User Stories
|
||||
- [ ] All acceptance criteria met
|
||||
- [ ] Code reviewed and approved
|
||||
- [ ] Unit tests written and passing
|
||||
- [ ] Integration tests passing
|
||||
- [ ] Documentation updated
|
||||
- [ ] Icons meet design guidelines (high contrast, nautical theme)
|
||||
- [ ] Performance impact assessed and acceptable
|
||||
- [ ] Accessibility requirements met
|
||||
|
||||
### Technical Tasks
|
||||
- [ ] Code follows project conventions
|
||||
- [ ] No new compiler warnings
|
||||
- [ ] Memory usage within acceptable limits
|
||||
- [ ] Cross-platform compatibility verified
|
||||
- [ ] Asset optimization completed
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
### High Risk Items
|
||||
1. **Icon Design Consistency** - Risk of inconsistent visual style across 100+ icons
|
||||
- *Mitigation:* Create comprehensive style guide and icon templates
|
||||
|
||||
2. **Performance Impact** - Large number of icons may affect rendering performance
|
||||
- *Mitigation:* Implement icon atlasing and lazy loading
|
||||
|
||||
3. **Asset Loading Time** - Extensive icon set may increase initial load time
|
||||
- *Mitigation:* Progressive loading and asset compression
|
||||
|
||||
### Medium Risk Items
|
||||
1. **Cross-platform Icon Rendering** - Icons may render differently across platforms
|
||||
- *Mitigation:* Test on all target platforms early
|
||||
|
||||
2. **Memory Usage** - Icon textures may consume significant memory
|
||||
- *Mitigation:* Optimize icon sizes and use appropriate formats
|
||||
|
||||
---
|
||||
|
||||
## Next Retrospective
|
||||
|
||||
### Key Metrics to Track
|
||||
- Story points completed vs. planned
|
||||
- Icon implementation velocity
|
||||
- Performance impact measurements
|
||||
- User feedback on icon clarity and usability
|
||||
- Code quality metrics (test coverage, complexity)
|
||||
|
||||
### Success Criteria
|
||||
- High-priority user stories progress
|
||||
- Icon system foundation established
|
||||
- Performance remains within acceptable limits
|
||||
- User experience significantly improved
|
||||
- Technical debt reduced
|
||||
|
||||
---
|
||||
|
||||
## Notes for Development
|
||||
|
||||
## Personal Development Notes
|
||||
|
||||
### Current State Analysis
|
||||
- **Strengths:** Solid Bevy/Rust foundation, comprehensive component structure in player.rs
|
||||
- **Gaps:** Missing icon assets, limited TextureAssets configuration, no icon management system
|
||||
- **Opportunities:** Leverage existing YachtData structure, build on established plugin architecture
|
||||
|
||||
### Personal Development Approach
|
||||
1. Start with core navigation icons (US-001) as foundation
|
||||
2. Establish icon loading and management patterns early
|
||||
3. Implement status indicator system for immediate visual feedback
|
||||
4. Build comprehensive testing as icons are added
|
||||
5. Focus on performance optimization throughout development
|
||||
|
||||
### Resources & Tools Needed
|
||||
- Icon design resources (icon library or design tools)
|
||||
- Asset optimization tools for performance
|
||||
- Performance profiling setup for benchmarking
|
||||
- ~~Testing setup for cross-platform compatibility~~ This is done in CI.
|
5519
Cargo.lock
generated
Normal file
87
Cargo.toml
Normal file
@@ -0,0 +1,87 @@
|
||||
[package]
|
||||
name = "yachtpit"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
authors = ["seemueller-io <git@github.geoffsee>"]
|
||||
edition = "2021"
|
||||
exclude = ["dist", "build", "assets", "credits"]
|
||||
|
||||
[workspace]
|
||||
members = ["mobile"]
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
||||
# This is used by trunk as it doesn't support custom profiles: https://github.com/trunk-rs/trunk/issues/605
|
||||
# xbuild also uses this profile for building android AABs because I couldn't find a configuration for it
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = 'thin'
|
||||
codegen-units = 1
|
||||
strip = true
|
||||
|
||||
# Profile for distribution
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
strip = true
|
||||
|
||||
[features]
|
||||
dev = [
|
||||
"bevy/dynamic_linking",
|
||||
]
|
||||
|
||||
# All of Bevy's default features exept for the audio related ones (bevy_audio, vorbis), since they clash with bevy_kira_audio
|
||||
# and android_shared_stdcxx/android-game-activity, since those are covered in `mobile`
|
||||
[dependencies]
|
||||
bevy = { version = "0.16", default-features = false, features = [
|
||||
"animation",
|
||||
"bevy_asset",
|
||||
"bevy_color",
|
||||
"bevy_core_pipeline",
|
||||
"bevy_gilrs",
|
||||
"bevy_gizmos",
|
||||
"bevy_gltf",
|
||||
"bevy_log",
|
||||
"bevy_mesh_picking_backend",
|
||||
"bevy_pbr",
|
||||
"bevy_picking",
|
||||
"bevy_render",
|
||||
"bevy_scene",
|
||||
"bevy_sprite",
|
||||
"bevy_sprite_picking_backend",
|
||||
"bevy_state",
|
||||
"bevy_text",
|
||||
"bevy_ui",
|
||||
"bevy_ui_picking_backend",
|
||||
"bevy_window",
|
||||
"bevy_winit",
|
||||
"custom_cursor",
|
||||
"default_font",
|
||||
"hdr",
|
||||
"multi_threaded",
|
||||
"png",
|
||||
"smaa_luts",
|
||||
"sysinfo_plugin",
|
||||
"tonemapping_luts",
|
||||
"webgl2",
|
||||
"x11",
|
||||
] }
|
||||
bevy_kira_audio = { version = "0.23.0", features = ["android_shared_stdcxx"] }
|
||||
bevy_asset_loader = { version = "0.23.0" }
|
||||
rand = { version = "0.8.3" }
|
||||
webbrowser = { version = "1", features = ["hardened"] }
|
||||
|
||||
# keep the following in sync with Bevy's dependencies
|
||||
winit = { version = "0.30", default-features = false }
|
||||
image = { version = "0.25", default-features = false }
|
||||
## This greatly improves WGPU's performance due to its heavy use of trace! calls
|
||||
log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] }
|
||||
|
||||
[build-dependencies]
|
||||
embed-resource = "1"
|
161
ICONS_NEEDED.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Icons Required for Yacht yachtpit Application
|
||||
|
||||
This document lists all the icons that need to be generated for the yacht yachtpit application based on the current UI implementation.
|
||||
|
||||
## Navigation & Compass Icons
|
||||
|
||||
### Primary Navigation
|
||||
- **Compass Rose Icon** - For the central navigation display
|
||||
- **North Arrow Icon** - Directional indicator
|
||||
- **GPS Satellite Icon** - GPS status indicator
|
||||
- **Waypoint Icon** - Navigation waypoints
|
||||
- **Route Line Icon** - Planned route visualization
|
||||
|
||||
## Instrument Gauge Icons
|
||||
|
||||
### Speed Gauge
|
||||
- **Speedometer Icon** - Circular gauge background
|
||||
- **Speed Needle Icon** - Gauge pointer/needle
|
||||
- **Knots Unit Icon** - "KTS" stylized icon
|
||||
|
||||
### Depth Gauge
|
||||
- **Depth Sounder Icon** - Sonar/depth measurement icon
|
||||
- **Water Depth Icon** - Underwater depth visualization
|
||||
- **Meter Unit Icon** - "M" stylized icon
|
||||
|
||||
## Engine & Systems Icons
|
||||
|
||||
### Engine Status
|
||||
- **Engine Icon** - Marine engine representation
|
||||
- **Temperature Gauge Icon** - Engine temperature indicator
|
||||
- **Cooling System Icon** - Engine cooling status
|
||||
- **Engine Alert Icon** - Warning/alert indicator
|
||||
|
||||
### Fuel System
|
||||
- **Fuel Tank Icon** - Fuel level indicator
|
||||
- **Fuel Pump Icon** - Fuel system status
|
||||
- **Fuel Drop Icon** - Fuel consumption indicator
|
||||
|
||||
### Electrical System
|
||||
- **Battery Icon** - Battery level indicator
|
||||
- **Charging Icon** - Battery charging status
|
||||
- **Power Icon** - Electrical system status
|
||||
- **Voltage Meter Icon** - Electrical measurement
|
||||
|
||||
## Communication & Navigation Systems
|
||||
|
||||
### GPS System
|
||||
- **GPS Icon** - Global positioning system
|
||||
- **Satellite Signal Icon** - Signal strength indicator
|
||||
- **Location Pin Icon** - Current position marker
|
||||
|
||||
### Radar System
|
||||
- **Radar Dish Icon** - Radar antenna representation
|
||||
- **Radar Sweep Icon** - Radar scanning animation
|
||||
- **Target Blip Icon** - Radar contact indicator
|
||||
|
||||
### AIS (Automatic Identification System)
|
||||
- **AIS Icon** - Ship identification system
|
||||
- **Ship Icon** - Other vessel representation
|
||||
- **Radio Wave Icon** - Communication signal
|
||||
|
||||
## Weather & Environmental Icons
|
||||
|
||||
### Wind Information
|
||||
- **Wind Vane Icon** - Wind direction indicator
|
||||
- **Wind Speed Icon** - Anemometer representation
|
||||
- **Wind Arrow Icon** - Directional wind indicator
|
||||
- **Beaufort Scale Icon** - Wind force scale
|
||||
|
||||
### Weather Conditions
|
||||
- **Barometer Icon** - Atmospheric pressure
|
||||
- **Temperature Icon** - Air temperature
|
||||
- **Humidity Icon** - Relative humidity indicator
|
||||
|
||||
## Status & Alert Icons
|
||||
|
||||
### System Status Indicators
|
||||
- **Green Status Dot** - System operational
|
||||
- **Red Status Dot** - System fault/offline
|
||||
- **Yellow Status Dot** - System warning
|
||||
- **Blue Status Dot** - System standby
|
||||
|
||||
### Alert Icons
|
||||
- **Warning Triangle** - General warning
|
||||
- **Critical Alert** - Emergency situation
|
||||
- **Information Icon** - General information
|
||||
- **Maintenance Icon** - Service required
|
||||
|
||||
## UI Control Icons
|
||||
|
||||
### Navigation Controls
|
||||
- **Menu Icon** - Main menu access
|
||||
- **Settings Icon** - Configuration access
|
||||
- **Home Icon** - Return to main display
|
||||
- **Back Arrow** - Navigation back
|
||||
|
||||
### Display Controls
|
||||
- **Brightness Icon** - Screen brightness control
|
||||
- **Contrast Icon** - Display contrast
|
||||
- **Night Mode Icon** - Low-light display mode
|
||||
- **Full Screen Icon** - Display mode toggle
|
||||
|
||||
## Chart & Mapping Icons
|
||||
|
||||
### Chart Elements
|
||||
- **Chart Icon** - Nautical chart representation
|
||||
- **Depth Contour Icon** - Underwater topography
|
||||
- **Buoy Icon** - Navigation aids
|
||||
- **Harbor Icon** - Port/marina indicator
|
||||
- **Anchor Icon** - Anchorage areas
|
||||
|
||||
### Measurement Tools
|
||||
- **Ruler Icon** - Distance measurement
|
||||
- **Protractor Icon** - Bearing measurement
|
||||
- **Scale Icon** - Chart scale indicator
|
||||
|
||||
## Safety & Emergency Icons
|
||||
|
||||
### Safety Equipment
|
||||
- **Life Ring Icon** - Safety equipment
|
||||
- **Fire Extinguisher Icon** - Emergency equipment
|
||||
- **First Aid Icon** - Medical supplies
|
||||
- **Emergency Radio Icon** - Distress communication
|
||||
|
||||
### Emergency Procedures
|
||||
- **SOS Icon** - Distress signal
|
||||
- **Mayday Icon** - Emergency call
|
||||
- **Coast Guard Icon** - Emergency services
|
||||
- **Evacuation Icon** - Emergency procedures
|
||||
|
||||
## File Formats Required
|
||||
|
||||
All icons should be generated in the following formats:
|
||||
- **PNG**: 16x16, 24x24, 32x32, 48x48, 64x64, 128x128, 256x256 pixels
|
||||
- **SVG**: Scalable vector format for high-DPI displays
|
||||
- **ICO**: Windows icon format (for desktop application)
|
||||
|
||||
## Design Guidelines
|
||||
|
||||
### Style Requirements
|
||||
- **Nautical Theme**: Maritime-inspired design language
|
||||
- **High Contrast**: Suitable for marine lighting conditions
|
||||
- **Monochromatic**: Primary colors should be cyan/blue theme
|
||||
- **Clean Lines**: Minimalist, professional appearance
|
||||
- **Scalable**: Must remain legible at small sizes
|
||||
|
||||
### Color Palette
|
||||
- **Primary**: Cyan (#00CCFF) - Main UI elements
|
||||
- **Secondary**: Green (#00FF80) - Operational status
|
||||
- **Warning**: Orange (#FF8000) - Caution states
|
||||
- **Alert**: Red (#FF0040) - Critical alerts
|
||||
- **Neutral**: Gray (#999999) - Inactive elements
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
These icons will replace the current text-based placeholders in:
|
||||
- `src/player.rs` - Main instrument cluster
|
||||
- `src/menu.rs` - Menu system icons
|
||||
- `src/loading.rs` - Loading screen elements
|
||||
|
||||
The icons should be placed in the `assets/textures/icons/` directory and loaded through the existing `TextureAssets` resource system.
|
121
LICENSE
Normal file
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
5
README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# yachtpit
|
||||
|
||||
> Warning: Experimental and incomplete. Project is unfunded.
|
||||
|
||||
**yachtpit** is an open-source cross-platform cockpit for Boats, targeting Windows, macOS, Linux, WebAssembly, Android, and iOS.
|
5
Trunk.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
[build]
|
||||
public_url = "./"
|
||||
|
||||
[serve]
|
||||
port = 8080
|
BIN
assets/audio/flying.ogg
Normal file
BIN
assets/textures/bevy.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
assets/textures/github.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
10
build.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
extern crate embed_resource;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
if target.contains("windows") {
|
||||
// on windows we will set our game icon as icon for the executable
|
||||
embed_resource::compile("build/windows/icon.rc");
|
||||
}
|
||||
}
|
BIN
build/android/res/mipmap-mdpi/icon.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
build/icon_1024x1024.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
build/macos/AppIcon.iconset/icon_128x128.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
build/macos/AppIcon.iconset/icon_128x128@2x.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
build/macos/AppIcon.iconset/icon_16x16.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
build/macos/AppIcon.iconset/icon_16x16@2x.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
build/macos/AppIcon.iconset/icon_256x256.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
build/macos/AppIcon.iconset/icon_256x256@2x.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
build/macos/AppIcon.iconset/icon_32x32.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
build/macos/AppIcon.iconset/icon_32x32@2x.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
build/macos/AppIcon.iconset/icon_512x512.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
build/macos/AppIcon.iconset/icon_512x512@2x.png
Normal file
After Width: | Height: | Size: 99 KiB |
17
build/macos/create_icns.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
rm -rf AppIcon.iconset/*
|
||||
mkdir -p AppIcon.iconset
|
||||
sips -z 16 16 ../icon_1024x1024.png --out AppIcon.iconset/icon_16x16.png
|
||||
sips -z 32 32 ../icon_1024x1024.png --out AppIcon.iconset/icon_16x16@2x.png
|
||||
sips -z 32 32 ../icon_1024x1024.png --out AppIcon.iconset/icon_32x32.png
|
||||
sips -z 64 64 ../icon_1024x1024.png --out AppIcon.iconset/icon_32x32@2x.png
|
||||
sips -z 128 128 ../icon_1024x1024.png --out AppIcon.iconset/icon_128x128.png
|
||||
sips -z 256 256 ../icon_1024x1024.png --out AppIcon.iconset/icon_128x128@2x.png
|
||||
sips -z 256 256 ../icon_1024x1024.png --out AppIcon.iconset/icon_256x256.png
|
||||
sips -z 512 512 ../icon_1024x1024.png --out AppIcon.iconset/icon_256x256@2x.png
|
||||
sips -z 512 512 ../icon_1024x1024.png --out AppIcon.iconset/icon_512x512.png
|
||||
cp ../icon_1024x1024.png AppIcon.iconset/icon_512x512@2x.png
|
||||
iconutil -c icns AppIcon.iconset
|
||||
mkdir -p src/Game.app/Contents/Resources
|
||||
mv AppIcon.icns src/Game.app/Contents/Resources/
|
17
build/macos/create_icns_linux.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
rm -rf AppIcon.iconset/*
|
||||
mkdir -p AppIcon.iconset
|
||||
convert ../icon_1024x1024.png -resize 16x16 AppIcon.iconset/icon_16x16.png
|
||||
convert ../icon_1024x1024.png -resize 32x32 AppIcon.iconset/icon_16x16@2x.png
|
||||
convert ../icon_1024x1024.png -resize 32x32 AppIcon.iconset/icon_32x32.png
|
||||
convert ../icon_1024x1024.png -resize 64x64 AppIcon.iconset/icon_32x32@2x.png
|
||||
convert ../icon_1024x1024.png -resize 128x128 AppIcon.iconset/icon_128x128.png
|
||||
convert ../icon_1024x1024.png -resize 256x256 AppIcon.iconset/icon_128x128@2x.png
|
||||
convert ../icon_1024x1024.png -resize 256x256 AppIcon.iconset/icon_256x256.png
|
||||
convert ../icon_1024x1024.png -resize 512x512 AppIcon.iconset/icon_256x256@2x.png
|
||||
convert ../icon_1024x1024.png -resize 512x512 AppIcon.iconset/icon_512x512.png
|
||||
cp ../icon_1024x1024.png AppIcon.iconset/icon_512x512@2x.png
|
||||
png2icns ./AppIcon.icns AppIcon.iconset/icon_16x16.png AppIcon.iconset/icon_32x32.png AppIcon.iconset/icon_128x128.png AppIcon.iconset/icon_256x256.png AppIcon.iconset/icon_512x512.png
|
||||
mkdir -p src/Game.app/Contents/Resources
|
||||
mv AppIcon.icns src/Game.app/Contents/Resources/
|
29
build/macos/src/Game.app/Contents/Info.plist
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>yachtpit</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>yachtpit</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.gs.yachtpit</string> <!-- ToDo replace all instances with io.gs.yachtpit -->
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>yachtpit</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<!-- Version -->
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
BIN
build/macos/src/Game.app/Contents/Resources/AppIcon.icns
Normal file
62
build/web/sound.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// Insert hack to make sound autoplay on Chrome as soon as the user interacts with the tab:
|
||||
// https://developers.google.com/web/updates/2018/11/web-audio-autoplay#moving-forward
|
||||
|
||||
// the following function keeps track of all AudioContexts and resumes them on the first user
|
||||
// interaction with the page. If the function is called and all contexts are already running,
|
||||
// it will remove itself from all event listeners.
|
||||
(function () {
|
||||
// An array of all contexts to resume on the page
|
||||
const audioContextList = [];
|
||||
|
||||
// An array of various user interaction events we should listen for
|
||||
const userInputEventNames = [
|
||||
"click",
|
||||
"contextmenu",
|
||||
"auxclick",
|
||||
"dblclick",
|
||||
"mousedown",
|
||||
"mouseup",
|
||||
"pointerup",
|
||||
"touchend",
|
||||
"keydown",
|
||||
"keyup",
|
||||
];
|
||||
|
||||
// A proxy object to intercept AudioContexts and
|
||||
// add them to the array for tracking and resuming later
|
||||
self.AudioContext = new Proxy(self.AudioContext, {
|
||||
construct(target, args) {
|
||||
const result = new target(...args);
|
||||
audioContextList.push(result);
|
||||
return result;
|
||||
},
|
||||
});
|
||||
|
||||
// To resume all AudioContexts being tracked
|
||||
function resumeAllContexts(_event) {
|
||||
let count = 0;
|
||||
|
||||
audioContextList.forEach((context) => {
|
||||
if (context.state !== "running") {
|
||||
context.resume();
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
// If all the AudioContexts have now resumed then we unbind all
|
||||
// the event listeners from the page to prevent unnecessary resume attempts
|
||||
// Checking count > 0 ensures that the user interaction happens AFTER the game started up
|
||||
if (count > 0 && count === audioContextList.length) {
|
||||
userInputEventNames.forEach((eventName) => {
|
||||
document.removeEventListener(eventName, resumeAllContexts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// We bind the resume function for each user interaction
|
||||
// event on the page
|
||||
userInputEventNames.forEach((eventName) => {
|
||||
document.addEventListener(eventName, resumeAllContexts);
|
||||
});
|
||||
})();
|
53
build/web/styles.css
Normal file
@@ -0,0 +1,53 @@
|
||||
body, html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lds-dual-ring {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.lds-dual-ring:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
border: 6px solid #fff;
|
||||
border-color: #fff transparent #fff transparent;
|
||||
animation: lds-dual-ring 1.2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes lds-dual-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#bevy {
|
||||
z-index: 2;
|
||||
}
|
BIN
build/windows/icon.ico
Normal file
After Width: | Height: | Size: 153 KiB |
1
build/windows/icon.rc
Normal file
@@ -0,0 +1 @@
|
||||
app_icon ICON "icon.ico"
|
2
build/windows/installer/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/bin/
|
||||
/obj/
|
73
build/windows/installer/InstallDirUi.wxs
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
|
||||
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
<!-- Copied from https://github.com/wixtoolset/wix4/blob/6f2511f58f60e63a15357a2fe37f83343dea3090/src/ext/UI/wixlib/WixUI_InstallDir.wxs#L9
|
||||
but with the license dialog disabled.
|
||||
See: https://wixtoolset.org/docs/v3/wixui/wixui_customizations/#changing-the-ui-sequence-of-a-built-in-dialog-set. -->
|
||||
<Fragment>
|
||||
<UI Id="WixUI_CustomInstallDir">
|
||||
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
|
||||
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
|
||||
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
|
||||
|
||||
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
|
||||
|
||||
<DialogRef Id="BrowseDlg" />
|
||||
<DialogRef Id="DiskCostDlg" />
|
||||
<DialogRef Id="ErrorDlg" />
|
||||
<DialogRef Id="FatalError" />
|
||||
<DialogRef Id="FilesInUse" />
|
||||
<DialogRef Id="MsiRMFilesInUse" />
|
||||
<DialogRef Id="PrepareDlg" />
|
||||
<DialogRef Id="ProgressDlg" />
|
||||
<DialogRef Id="ResumeDlg" />
|
||||
<DialogRef Id="UserExit" />
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" />
|
||||
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
|
||||
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Condition="NOT Installed" />
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />
|
||||
|
||||
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4" Condition="WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"" />
|
||||
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
|
||||
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1" Condition="NOT Installed" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />
|
||||
|
||||
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
|
||||
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
|
||||
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
|
||||
<InstallUISequence>
|
||||
<Show Dialog="FatalError" OnExit="error" />
|
||||
<Show Dialog="UserExit" OnExit="cancel" />
|
||||
<Show Dialog="ExitDialog" OnExit="success" />
|
||||
</InstallUISequence>
|
||||
|
||||
<AdminUISequence>
|
||||
<Show Dialog="FatalError" OnExit="error" />
|
||||
<Show Dialog="UserExit" OnExit="cancel" />
|
||||
<Show Dialog="ExitDialog" OnExit="success" />
|
||||
</AdminUISequence>
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Common" />
|
||||
</Fragment>
|
||||
<?foreach WIXUIARCH in X86;X64;A64 ?>
|
||||
<Fragment>
|
||||
<UI Id="WixUI_CustomInstallDir_$(WIXUIARCH)">
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="2" Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||
</UI>
|
||||
</Fragment>
|
||||
<?endforeach?>
|
||||
</Wix>
|
40
build/windows/installer/Installer.sln
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Installer", "Installer.wixproj", "{340293B0-F46C-46A0-88D8-4BB2F3465C53}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x64.Build.0 = Debug|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x86.Build.0 = Debug|x86
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|Any CPU.Build.0 = Release|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x64.ActiveCfg = Release|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x64.Build.0 = Release|x64
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x86.ActiveCfg = Release|x86
|
||||
{340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
29
build/windows/installer/Installer.wixproj
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Sdk="WixToolset.Sdk/4.0.0">
|
||||
<PropertyGroup>
|
||||
<DebugType>none</DebugType>
|
||||
<OutputName>installer</OutputName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WixToolset.UI.wixext" Version="4.0.0" />
|
||||
<PackageReference Include="WixToolset.Heat" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<HarvestDirectory Include="..\..\..\assets">
|
||||
<ComponentGroupName>AssetsDirectory</ComponentGroupName>
|
||||
<DirectoryRefId>INSTALLFOLDER</DirectoryRefId>
|
||||
<SuppressRootDirectory>false</SuppressRootDirectory>
|
||||
</HarvestDirectory>
|
||||
<BindPath Include="..\..\..\assets" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<HarvestDirectory Include="..\..\..\credits">
|
||||
<ComponentGroupName>CreditsDirectory</ComponentGroupName>
|
||||
<DirectoryRefId>INSTALLFOLDER</DirectoryRefId>
|
||||
<SuppressRootDirectory>false</SuppressRootDirectory>
|
||||
</HarvestDirectory>
|
||||
<BindPath Include="..\..\..\credits" />
|
||||
</ItemGroup>
|
||||
</Project>
|
6
build/windows/installer/Package.en-us.wxl
Normal file
@@ -0,0 +1,6 @@
|
||||
<!--
|
||||
This file contains the declaration of all the localizable strings.
|
||||
-->
|
||||
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
|
||||
<String Id="DowngradeError" Value="A newer version of [ProductName] is already installed." />
|
||||
</WixLocalization>
|
50
build/windows/installer/Package.wxs
Normal file
@@ -0,0 +1,50 @@
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
<Package
|
||||
Name="yachtpit"
|
||||
Manufacturer="gsio"
|
||||
UpgradeCode="ac8709c2-1d6f-4440-b424-386e4e315425"
|
||||
Version="0.0.1"
|
||||
|
||||
Scope="perUserOrMachine">
|
||||
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
|
||||
|
||||
<Media Id="1" Cabinet="myapplication.cab" EmbedCab="yes" />
|
||||
|
||||
<Property Id="ApplicationFolderName" Value="!(bind.Property.ProductName)" />
|
||||
|
||||
<!-- Installer Icon -->
|
||||
<Icon Id="icon.ico" SourceFile="..\icon.ico"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
|
||||
|
||||
<!-- Sets the default installation folder -->
|
||||
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="!(bind.Property.ProductName)" />
|
||||
</StandardDirectory>
|
||||
|
||||
<!-- Shows a UI that allows customizing the install location -->
|
||||
<ui:WixUI Id="WixUI_CustomInstallDir" InstallDirectory="INSTALLFOLDER" />
|
||||
|
||||
<Feature Id="Main">
|
||||
<ComponentGroupRef Id="MainComponent" />
|
||||
<ComponentRef Id="StartMenuShortcut" />
|
||||
<ComponentGroupRef Id="AssetsDirectory" />
|
||||
<ComponentGroupRef Id="CreditsDirectory" />
|
||||
</Feature>
|
||||
|
||||
<!-- Installs the actual files -->
|
||||
<ComponentGroup Id="MainComponent" Directory="INSTALLFOLDER">
|
||||
<Component>
|
||||
<File Id="Executable" Source="..\..\..\target\dist\yachtpit.exe" Vital="true" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
|
||||
<!-- Start menu shortcut -->
|
||||
<!-- Source: https://wixtoolset.org/docs/v3/howtos/files_and_registry/create_start_menu_shortcut/ -->
|
||||
<StandardDirectory Id="ProgramMenuFolder">
|
||||
<Component Id="StartMenuShortcut" Guid="*">
|
||||
<Shortcut Id="ApplicationStartMenuShortcut" Name="!(bind.Property.ProductName)" Target="[!Executable]" WorkingDirectory="INSTALLFOLDER" />
|
||||
<RegistryValue Root="HKCU" Key="Software\!(bind.Property.ProductName)" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||
</Component>
|
||||
</StandardDirectory>
|
||||
</Package>
|
||||
</Wix>
|
6
build/windows/installer/global.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "9.0.100",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
9
cleanup.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Clean up build artifacts and temporary directories
|
||||
echo "Cleaning up build artifacts and temporary directories..."
|
||||
cargo clean
|
||||
# Remove persisted data
|
||||
find . -name "target" -type d -prune -exec rm -rf {} \;
|
||||
find . -name "dist" -type d -prune -exec rm -rf {} \;
|
||||
echo "Cleanup complete!"
|
5
credits/CREDITS.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Credits
|
||||
|
||||
## Assets
|
||||
|
||||
* Bevy icon: [MIT License](licenses/Bevy_MIT_License.md);
|
19
credits/licenses/Bevy_MIT_License.md
Normal file
@@ -0,0 +1,19 @@
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
22
index.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<title>yachtpit</title>
|
||||
<link data-trunk rel="copy-dir" href="assets"/>
|
||||
<link data-trunk rel="copy-dir" href="credits"/>
|
||||
<link data-trunk rel="copy-file" href="build/windows/icon.ico"/>
|
||||
<link rel="icon" href="icon.ico">
|
||||
<link data-trunk rel="inline" href="build/web/styles.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<link data-trunk rel="inline" href="build/web/sound.js"/>
|
||||
<div class="game-container">
|
||||
<div class="lds-dual-ring"></div>
|
||||
<canvas id="bevy">
|
||||
Javascript and support for canvas is required
|
||||
</canvas>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
5
mobile/.cargo/config.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
# Flag to notify the compiler we're building for the iOS simulator from an Apple silicon mac
|
||||
# This needs some workarounds for now
|
||||
# See https://github.com/bevyengine/bevy/pull/10178 - remove if it's not needed anymore.
|
||||
[target.aarch64-apple-ios-sim]
|
||||
rustflags = ["--cfg=ios_simulator"]
|
3
mobile/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
mobile.xcodeproj/xcuserdata/
|
||||
mobile.xcodeproj/project.xcworkspace/
|
||||
build/
|
34
mobile/Cargo.toml
Normal file
@@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "mobile"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
name = "mobile"
|
||||
crate-type = ["staticlib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
yachtpit = { path = ".." }
|
||||
bevy = { version = "0.16.0", default-features = false, features = ["android-native-activity"] }
|
||||
|
||||
[target."cfg(target_os = \"ios\")".dependencies]
|
||||
objc2-avf-audio = { version = "0.3.0", features = [
|
||||
"AVAudioSession",
|
||||
"AVAudioSessionTypes",
|
||||
] }
|
||||
|
||||
[package.metadata.android]
|
||||
package = "io.gs.yachtpit"
|
||||
apk_name = "yachtpit"
|
||||
assets = "../assets"
|
||||
strip = "strip"
|
||||
resources = "../build/android/res"
|
||||
build_targets = ["aarch64-linux-android"]
|
||||
|
||||
[package.metadata.android.sdk]
|
||||
target_sdk_version = 35
|
||||
|
||||
[package.metadata.android.application]
|
||||
icon = "@mipmap/icon"
|
||||
label = "yachtpit"
|
28
mobile/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
.PHONY: xcodebuild run install boot-sim generate clean
|
||||
|
||||
DEVICE = ${DEVICE_ID}
|
||||
ifndef DEVICE_ID
|
||||
DEVICE=$(shell xcrun simctl list devices 'iOS' | grep -v 'unavailable' | grep -v '^--' | grep -v '==' | head -n 1 | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})")
|
||||
endif
|
||||
|
||||
run: install
|
||||
xcrun simctl launch --console $(DEVICE) io.gs.yachtpit
|
||||
|
||||
boot-sim:
|
||||
xcrun simctl boot $(DEVICE) || true
|
||||
|
||||
install: xcodebuild-simulator boot-sim
|
||||
xcrun simctl install $(DEVICE) build/Build/Products/Debug-iphonesimulator/mobile.app
|
||||
|
||||
xcodebuild-simulator:
|
||||
IOS_TARGETS=x86_64-apple-ios xcodebuild -scheme mobile -configuration Debug -derivedDataPath build -destination "id=$(DEVICE)"
|
||||
|
||||
xcodebuild-iphone:
|
||||
IOS_TARGETS=aarch64-apple-ios xcodebuild -scheme mobile -configuration Debug -derivedDataPath build -arch arm64
|
||||
|
||||
xcodebuild-iphone-release:
|
||||
IOS_TARGETS=aarch64-apple-ios xcodebuild -scheme mobile -configuration Release -derivedDataPath build -arch arm64
|
||||
|
||||
clean:
|
||||
rm -r build
|
||||
cargo clean
|
55
mobile/build_rust_deps.sh
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# based on https://github.com/mozilla/glean/blob/main/build-scripts/xc-universal-binary.sh
|
||||
|
||||
set -eux
|
||||
|
||||
PATH=$PATH:$HOME/.cargo/bin
|
||||
|
||||
RELFLAG=
|
||||
if [[ "$CONFIGURATION" != "Debug" ]]; then
|
||||
RELFLAG="--profile dist"
|
||||
fi
|
||||
|
||||
set -euvx
|
||||
|
||||
if [[ -n "${DEVELOPER_SDK_DIR:-}" ]]; then
|
||||
# Assume we're in Xcode, which means we're probably cross-compiling.
|
||||
# In this case, we need to add an extra library search path for build scripts and proc-macros,
|
||||
# which run on the host instead of the target.
|
||||
# (macOS Big Sur does not have linkable libraries in /usr/lib/.)
|
||||
export LIBRARY_PATH="${DEVELOPER_SDK_DIR}/MacOSX.sdk/usr/lib:${LIBRARY_PATH:-}"
|
||||
fi
|
||||
|
||||
# add homebrew bin path, as it's the most commonly used package manager on macOS
|
||||
# this is needed for cmake on apple arm processors as it's not available by default
|
||||
export PATH="$PATH:/opt/homebrew/bin"
|
||||
|
||||
IS_SIMULATOR=0
|
||||
if [ "${LLVM_TARGET_TRIPLE_SUFFIX-}" = "-simulator" ]; then
|
||||
IS_SIMULATOR=1
|
||||
fi
|
||||
|
||||
for arch in $ARCHS; do
|
||||
case "$arch" in
|
||||
x86_64)
|
||||
if [ $IS_SIMULATOR -eq 0 ]; then
|
||||
echo "Building for x86_64, but not a simulator build. What's going on?" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Intel iOS simulator
|
||||
export CFLAGS_x86_64_apple_ios="-target x86_64-apple-ios"
|
||||
cargo rustc --crate-type staticlib --lib $RELFLAG --target x86_64-apple-ios
|
||||
;;
|
||||
|
||||
arm64)
|
||||
if [ $IS_SIMULATOR -eq 0 ]; then
|
||||
# Hardware iOS targets
|
||||
cargo rustc --crate-type staticlib --lib $RELFLAG --target aarch64-apple-ios
|
||||
else
|
||||
# M1 iOS simulator -- currently in Nightly only and requires to build `libstd`
|
||||
cargo rustc --crate-type staticlib --lib $RELFLAG --target aarch64-apple-ios-sim
|
||||
fi
|
||||
esac
|
||||
done
|
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_1024x1024.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 49 KiB |
6
mobile/ios-src/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
35
mobile/ios-src/Info.plist
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.1</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.1.1</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
37
mobile/ios-src/LaunchScreen.storyboard
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="842"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" image="bevy.png" translatesAutoresizingMaskIntoConstraints="NO" id="sic-lC-kjy">
|
||||
<rect key="frame" x="113" y="346" width="164" height="153"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<modalPageSheetSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="bevy.png" width="152.33058166503906" height="152.33058166503906"/>
|
||||
</resources>
|
||||
</document>
|
1
mobile/ios-src/bindings.h
Normal file
@@ -0,0 +1 @@
|
||||
void main_rs(void);
|
6
mobile/ios-src/main.m
Normal file
@@ -0,0 +1,6 @@
|
||||
#import "bindings.h"
|
||||
|
||||
int main() {
|
||||
main_rs();
|
||||
return 0;
|
||||
}
|
11
mobile/manifest.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
android:
|
||||
gradle: true
|
||||
# this assets configuration is currently only used without gradle!
|
||||
#assets:
|
||||
# - "../assets/*"
|
||||
icon: "ios-src/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png"
|
||||
manifest:
|
||||
package: "io.gs.yachtpit"
|
||||
version_code: 1
|
||||
application:
|
||||
label: "yachtpit"
|
473
mobile/mobile.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,473 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
134866208A035F8615C99114 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96A1E5B62F48B379829E8A0D /* Metal.framework */; };
|
||||
2469A4292A6F9AC200ACF4EF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2469A4282A6F9AC200ACF4EF /* Assets.xcassets */; };
|
||||
2469A42B2A6FAC7000ACF4EF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2469A42A2A6FAC7000ACF4EF /* LaunchScreen.storyboard */; };
|
||||
2604C99FAB5A8322EDCABB9F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE71FBCAA714DB4F42459106 /* UIKit.framework */; };
|
||||
442540D056ADB9AE61A0A590 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F1B41978FA53999AA836D0F /* Security.framework */; };
|
||||
55892F1396056740E1AF9685 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AF7DE91055EBD05ED77E57F9 /* main.m */; };
|
||||
55B7188F81C3C4183F81D3AE /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A39528EB2CCB182F5328223A /* libc++.tbd */; };
|
||||
57CD6306253C7A940098CD4A /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57CD6305253C7A940098CD4A /* AudioToolbox.framework */; };
|
||||
57CD630E253C80EC0098CD4A /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 57CD630A253C7F5F0098CD4A /* assets */; };
|
||||
6ADF1AB92CCDA73A00AF5F8E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ADF1AB82CCDA73A00AF5F8E /* QuartzCore.framework */; };
|
||||
D4A53EFF2DDD2FF70035BC01 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4A53EFE2DDD2FF70035BC01 /* AVFoundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
26BF2C4863C966DABAB40DC8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8DBF1E2B5C613DA41701F6D9 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = D08AEBE0B1A9C9A7B8C7B33F;
|
||||
remoteInfo = cargo_ios;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
160DB77300A3F1806F024D47 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = "<group>"; };
|
||||
2469A4282A6F9AC200ACF4EF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "ios-src/Assets.xcassets"; sourceTree = SOURCE_ROOT; };
|
||||
2469A42A2A6FAC7000ACF4EF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ios-src/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
55EAC02897847195D2F44C15 /* mobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mobile.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
57CD6305253C7A940098CD4A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
57CD630A253C7F5F0098CD4A /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = ../../assets; sourceTree = "<group>"; };
|
||||
6ADF1AB82CCDA73A00AF5F8E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||
8EE7F1E3B0303533925D7E33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
96A1E5B62F48B379829E8A0D /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
|
||||
9F1B41978FA53999AA836D0F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
A39528EB2CCB182F5328223A /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
AF7DE91055EBD05ED77E57F9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
D4A53EFE2DDD2FF70035BC01 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
FE71FBCAA714DB4F42459106 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
D5A822CB2D6847BA8800BE4C /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6ADF1AB92CCDA73A00AF5F8E /* QuartzCore.framework in Frameworks */,
|
||||
442540D056ADB9AE61A0A590 /* Security.framework in Frameworks */,
|
||||
134866208A035F8615C99114 /* Metal.framework in Frameworks */,
|
||||
2604C99FAB5A8322EDCABB9F /* UIKit.framework in Frameworks */,
|
||||
55B7188F81C3C4183F81D3AE /* libc++.tbd in Frameworks */,
|
||||
D4A53EFF2DDD2FF70035BC01 /* AVFoundation.framework in Frameworks */,
|
||||
57CD6306253C7A940098CD4A /* AudioToolbox.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
321F7D6A765B38E746C35105 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
55EAC02897847195D2F44C15 /* mobile.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4F1D6F28B8A5D1927AB0ADED /* ios-src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2469A4282A6F9AC200ACF4EF /* Assets.xcassets */,
|
||||
57CD630A253C7F5F0098CD4A /* assets */,
|
||||
160DB77300A3F1806F024D47 /* bindings.h */,
|
||||
8EE7F1E3B0303533925D7E33 /* Info.plist */,
|
||||
AF7DE91055EBD05ED77E57F9 /* main.m */,
|
||||
);
|
||||
path = "ios-src";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8F2E3E6040EAD2EC9F3FA530 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2469A42A2A6FAC7000ACF4EF /* LaunchScreen.storyboard */,
|
||||
4F1D6F28B8A5D1927AB0ADED /* ios-src */,
|
||||
EB028409C2D0655412DA6E44 /* Frameworks */,
|
||||
321F7D6A765B38E746C35105 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EB028409C2D0655412DA6E44 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4A53EFE2DDD2FF70035BC01 /* AVFoundation.framework */,
|
||||
6ADF1AB82CCDA73A00AF5F8E /* QuartzCore.framework */,
|
||||
57CD6305253C7A940098CD4A /* AudioToolbox.framework */,
|
||||
A39528EB2CCB182F5328223A /* libc++.tbd */,
|
||||
96A1E5B62F48B379829E8A0D /* Metal.framework */,
|
||||
9F1B41978FA53999AA836D0F /* Security.framework */,
|
||||
FE71FBCAA714DB4F42459106 /* UIKit.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXLegacyTarget section */
|
||||
D08AEBE0B1A9C9A7B8C7B33F /* cargo_ios */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = build_rust_deps.sh;
|
||||
buildConfigurationList = AA00A0CFDB11F37F2BA3FC2E /* Build configuration list for PBXLegacyTarget "cargo_ios" */;
|
||||
buildPhases = (
|
||||
FE045B3D04D57B713A565FF8 /* Sources */,
|
||||
);
|
||||
buildToolPath = /bin/sh;
|
||||
buildWorkingDirectory = .;
|
||||
dependencies = (
|
||||
);
|
||||
name = cargo_ios;
|
||||
passBuildSettingsInEnvironment = 1;
|
||||
productName = cargo_ios;
|
||||
};
|
||||
/* End PBXLegacyTarget section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
3BDB8152E4962373181B4FE5 /* mobile */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "mobile" */;
|
||||
buildPhases = (
|
||||
9F13800790AD9DBC2BC0F116 /* Sources */,
|
||||
D5A822CB2D6847BA8800BE4C /* Frameworks */,
|
||||
57CD630D253C80E60098CD4A /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
19D4B9C22ADC6705B5132B4C /* PBXTargetDependency */,
|
||||
);
|
||||
name = mobile;
|
||||
productName = mobile;
|
||||
productReference = 55EAC02897847195D2F44C15 /* mobile.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
8DBF1E2B5C613DA41701F6D9 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1240;
|
||||
};
|
||||
buildConfigurationList = 9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "mobile" */;
|
||||
compatibilityVersion = "Xcode 10.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 8F2E3E6040EAD2EC9F3FA530;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
3BDB8152E4962373181B4FE5 /* mobile */,
|
||||
D08AEBE0B1A9C9A7B8C7B33F /* cargo_ios */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
57CD630D253C80E60098CD4A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2469A42B2A6FAC7000ACF4EF /* LaunchScreen.storyboard in Resources */,
|
||||
2469A4292A6F9AC200ACF4EF /* Assets.xcassets in Resources */,
|
||||
57CD630E253C80EC0098CD4A /* assets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
9F13800790AD9DBC2BC0F116 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
55892F1396056740E1AF9685 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
FE045B3D04D57B713A565FF8 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
19D4B9C22ADC6705B5132B4C /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D08AEBE0B1A9C9A7B8C7B33F /* cargo_ios */;
|
||||
targetProxy = 26BF2C4863C966DABAB40DC8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
4AD7BC6FDD56FF18FA6DA7D7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"DEBUG=1",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5B14EC4ADC81FBF1F8CF20E9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++11";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"ios-src/",
|
||||
);
|
||||
INFOPLIST_FILE = "ios-src/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = (
|
||||
"$(inherited)",
|
||||
"../target/aarch64-apple-ios/dist",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = (
|
||||
"$(inherited)",
|
||||
"../target/aarch64-apple-ios-sim/dist",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = (
|
||||
"$(inherited)",
|
||||
"../target/x86_64-apple-ios/dist",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-lmobile",
|
||||
"-lc++abi",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER=io.gs.yachtpit;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
79E3C28F06346EA58420A93D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8265913A25816D964A847F1B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.rust.cargo-ios";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A2D5B73DD30D562B6F366526 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++11";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"ios-src/",
|
||||
);
|
||||
INFOPLIST_FILE = "ios-src/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = (
|
||||
"$(inherited)",
|
||||
"../target/aarch64-apple-ios/debug",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = (
|
||||
"$(inherited)",
|
||||
"../target/aarch64-apple-ios-sim/debug",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = (
|
||||
"$(inherited)",
|
||||
"../target/x86_64-apple-ios/debug",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-lmobile",
|
||||
"-lc++abi",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER=io.gs.yachtpit;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
FEA9B18D9236F9F6DC6DF799 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.rust.cargo-ios";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "mobile" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4AD7BC6FDD56FF18FA6DA7D7 /* Debug */,
|
||||
79E3C28F06346EA58420A93D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
AA00A0CFDB11F37F2BA3FC2E /* Build configuration list for PBXLegacyTarget "cargo_ios" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8265913A25816D964A847F1B /* Debug */,
|
||||
FEA9B18D9236F9F6DC6DF799 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "mobile" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A2D5B73DD30D562B6F366526 /* Debug */,
|
||||
5B14EC4ADC81FBF1F8CF20E9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 8DBF1E2B5C613DA41701F6D9 /* Project object */;
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1310"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3BDB8152E4962373181B4FE5"
|
||||
BuildableName = "mobile.app"
|
||||
BlueprintName = "mobile"
|
||||
ReferencedContainer = "container:mobile.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3BDB8152E4962373181B4FE5"
|
||||
BuildableName = "mobile.app"
|
||||
BlueprintName = "mobile"
|
||||
ReferencedContainer = "container:mobile.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3BDB8152E4962373181B4FE5"
|
||||
BuildableName = "mobile.app"
|
||||
BlueprintName = "mobile"
|
||||
ReferencedContainer = "container:mobile.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
41
mobile/src/lib.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy::window::WindowMode;
|
||||
use bevy::winit::WinitSettings;
|
||||
use yachtpit::GamePlugin;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn main_rs() {
|
||||
main();
|
||||
}
|
||||
|
||||
// this macro is a no-op on ios and only needed for anroid since bevy 0.16
|
||||
// see https://github.com/bevyengine/bevy/pull/14780
|
||||
#[bevy_main]
|
||||
fn main() {
|
||||
#[cfg(target_os = "ios")]
|
||||
unsafe {
|
||||
// Sets our audio session to Ambient mode to prevent background music from stopping.
|
||||
// The default for iOS apps is SoloAmbient, which stops background music.
|
||||
// See apple docs: https://developer.apple.com/documentation/avfaudio/avaudiosession/category-swift.struct/ambient
|
||||
if let Err(e) = objc2_avf_audio::AVAudioSession::sharedInstance()
|
||||
.setCategory_error(objc2_avf_audio::AVAudioSessionCategoryAmbient.unwrap())
|
||||
{
|
||||
println!("Error setting audio session category: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
App::new()
|
||||
.insert_resource(WinitSettings::mobile())
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
resizable: false,
|
||||
mode: WindowMode::BorderlessFullscreen(MonitorSelection::Current),
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}),
|
||||
GamePlugin,
|
||||
))
|
||||
.run();
|
||||
}
|
13
package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "yachtpit",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
35
src/actions/game_control.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use bevy::prelude::{ButtonInput, KeyCode, Res};
|
||||
|
||||
pub enum GameControl {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl GameControl {
|
||||
pub fn pressed(&self, keyboard_input: &Res<ButtonInput<KeyCode>>) -> bool {
|
||||
match self {
|
||||
GameControl::Up => {
|
||||
keyboard_input.pressed(KeyCode::KeyW) || keyboard_input.pressed(KeyCode::ArrowUp)
|
||||
}
|
||||
GameControl::Down => {
|
||||
keyboard_input.pressed(KeyCode::KeyS) || keyboard_input.pressed(KeyCode::ArrowDown)
|
||||
}
|
||||
GameControl::Left => {
|
||||
keyboard_input.pressed(KeyCode::KeyA) || keyboard_input.pressed(KeyCode::ArrowLeft)
|
||||
}
|
||||
GameControl::Right => {
|
||||
keyboard_input.pressed(KeyCode::KeyD) || keyboard_input.pressed(KeyCode::ArrowRight)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_movement(control: GameControl, input: &Res<ButtonInput<KeyCode>>) -> f32 {
|
||||
if control.pressed(input) {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
44
src/actions/mod.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::actions::game_control::{get_movement, GameControl};
|
||||
use crate::GameState;
|
||||
|
||||
mod game_control;
|
||||
|
||||
pub const FOLLOW_EPSILON: f32 = 5.;
|
||||
|
||||
pub struct ActionsPlugin;
|
||||
|
||||
// This plugin listens for keyboard input and converts the input into Actions.
|
||||
// Actions can then be used as a resource in other systems to act on the player input.
|
||||
impl Plugin for ActionsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<Actions>().add_systems(
|
||||
Update,
|
||||
set_movement_actions.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Resource)]
|
||||
pub struct Actions {
|
||||
pub player_movement: Option<Vec2>,
|
||||
}
|
||||
|
||||
pub fn set_movement_actions(
|
||||
mut actions: ResMut<Actions>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
) {
|
||||
let player_movement = Vec2::new(
|
||||
get_movement(GameControl::Right, &keyboard_input)
|
||||
- get_movement(GameControl::Left, &keyboard_input),
|
||||
get_movement(GameControl::Up, &keyboard_input)
|
||||
- get_movement(GameControl::Down, &keyboard_input),
|
||||
);
|
||||
|
||||
if player_movement != Vec2::ZERO {
|
||||
actions.player_movement = Some(player_movement.normalize());
|
||||
} else {
|
||||
actions.player_movement = None;
|
||||
}
|
||||
}
|
56
src/audio.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::actions::{set_movement_actions, Actions};
|
||||
use crate::loading::AudioAssets;
|
||||
use crate::GameState;
|
||||
use bevy::prelude::*;
|
||||
use bevy_kira_audio::prelude::*;
|
||||
|
||||
pub struct InternalAudioPlugin;
|
||||
|
||||
// This plugin is responsible to control the game audio
|
||||
impl Plugin for InternalAudioPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins(AudioPlugin)
|
||||
.add_systems(OnEnter(GameState::Playing), start_audio)
|
||||
.add_systems(
|
||||
Update,
|
||||
control_flying_sound
|
||||
.after(set_movement_actions)
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct FlyingAudio(Handle<AudioInstance>);
|
||||
|
||||
fn start_audio(mut commands: Commands, audio_assets: Res<AudioAssets>, audio: Res<Audio>) {
|
||||
audio.pause();
|
||||
let handle = audio
|
||||
.play(audio_assets.flying.clone())
|
||||
.looped()
|
||||
.with_volume(0.3)
|
||||
.handle();
|
||||
commands.insert_resource(FlyingAudio(handle));
|
||||
}
|
||||
|
||||
fn control_flying_sound(
|
||||
actions: Res<Actions>,
|
||||
audio: Res<FlyingAudio>,
|
||||
mut audio_instances: ResMut<Assets<AudioInstance>>,
|
||||
) {
|
||||
if let Some(instance) = audio_instances.get_mut(&audio.0) {
|
||||
match instance.state() {
|
||||
PlaybackState::Paused { .. } => {
|
||||
if actions.player_movement.is_some() {
|
||||
instance.resume(AudioTween::default());
|
||||
}
|
||||
}
|
||||
PlaybackState::Playing { .. } => {
|
||||
if actions.player_movement.is_none() {
|
||||
instance.pause(AudioTween::default());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
54
src/lib.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
mod actions;
|
||||
mod audio;
|
||||
mod loading;
|
||||
mod menu;
|
||||
mod player;
|
||||
|
||||
use crate::actions::ActionsPlugin;
|
||||
use crate::audio::InternalAudioPlugin;
|
||||
use crate::loading::LoadingPlugin;
|
||||
use crate::menu::MenuPlugin;
|
||||
use crate::player::PlayerPlugin;
|
||||
|
||||
use bevy::app::App;
|
||||
#[cfg(debug_assertions)]
|
||||
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
|
||||
use bevy::prelude::*;
|
||||
|
||||
// This example game uses States to separate logic
|
||||
// See https://bevy-cheatbook.github.io/programming/states.html
|
||||
// Or https://github.com/bevyengine/bevy/blob/main/examples/ecs/state.rs
|
||||
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
|
||||
enum GameState {
|
||||
// During the loading State the LoadingPlugin will load our assets
|
||||
#[default]
|
||||
Loading,
|
||||
// During this State the actual game logic is executed
|
||||
Playing,
|
||||
// Here the menu is drawn and waiting for player interaction
|
||||
Menu,
|
||||
}
|
||||
|
||||
pub struct GamePlugin;
|
||||
|
||||
impl Plugin for GamePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_state::<GameState>().add_plugins((
|
||||
LoadingPlugin,
|
||||
MenuPlugin,
|
||||
ActionsPlugin,
|
||||
InternalAudioPlugin,
|
||||
PlayerPlugin,
|
||||
));
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
app.add_plugins((
|
||||
FrameTimeDiagnosticsPlugin::default(),
|
||||
LogDiagnosticsPlugin::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
37
src/loading.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use crate::GameState;
|
||||
use bevy::prelude::*;
|
||||
use bevy_asset_loader::prelude::*;
|
||||
use bevy_kira_audio::AudioSource;
|
||||
|
||||
pub struct LoadingPlugin;
|
||||
|
||||
/// This plugin loads all assets using [`AssetLoader`] from a third party bevy plugin
|
||||
/// Alternatively you can write the logic to load assets yourself
|
||||
/// If interested, take a look at <https://bevy-cheatbook.github.io/features/assets.html>
|
||||
impl Plugin for LoadingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_loading_state(
|
||||
LoadingState::new(GameState::Loading)
|
||||
.continue_to_state(GameState::Playing)
|
||||
.load_collection::<AudioAssets>()
|
||||
.load_collection::<TextureAssets>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// the following asset collections will be loaded during the State `GameState::Loading`
|
||||
// when done loading, they will be inserted as resources (see <https://github.com/NiklasEi/bevy_asset_loader>)
|
||||
|
||||
#[derive(AssetCollection, Resource)]
|
||||
pub struct AudioAssets {
|
||||
#[asset(path = "audio/flying.ogg")]
|
||||
pub flying: Handle<AudioSource>,
|
||||
}
|
||||
|
||||
#[derive(AssetCollection, Resource)]
|
||||
pub struct TextureAssets {
|
||||
#[asset(path = "textures/bevy.png")]
|
||||
pub bevy: Handle<Image>,
|
||||
#[asset(path = "textures/github.png")]
|
||||
pub github: Handle<Image>,
|
||||
}
|
61
src/main.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
// disable console on windows for release builds
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use bevy::asset::AssetMetaCheck;
|
||||
use bevy::prelude::*;
|
||||
use bevy::window::PrimaryWindow;
|
||||
use bevy::winit::WinitWindows;
|
||||
use bevy::DefaultPlugins;
|
||||
use yachtpit::GamePlugin;
|
||||
use std::io::Cursor;
|
||||
use winit::window::Icon;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(ClearColor(Color::NONE))
|
||||
.add_plugins(
|
||||
DefaultPlugins
|
||||
.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: "yachtpit".to_string(),
|
||||
// Bind to canvas included in `index.html`
|
||||
canvas: Some("#bevy".to_owned()),
|
||||
fit_canvas_to_parent: true,
|
||||
// Tells wasm not to override default event handling, like F5 and Ctrl+R
|
||||
prevent_default_event_handling: false,
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
})
|
||||
.set(AssetPlugin {
|
||||
meta_check: AssetMetaCheck::Never,
|
||||
..default()
|
||||
}),
|
||||
)
|
||||
.add_plugins(GamePlugin)
|
||||
.add_systems(Startup, set_window_icon)
|
||||
.run();
|
||||
}
|
||||
|
||||
// Sets the icon on windows and X11
|
||||
fn set_window_icon(
|
||||
windows: NonSend<WinitWindows>,
|
||||
primary_window: Query<Entity, With<PrimaryWindow>>,
|
||||
) -> Result {
|
||||
let primary_entity = primary_window.single()?;
|
||||
let Some(primary) = windows.get_window(primary_entity) else {
|
||||
return Err(BevyError::from("No primary window!"));
|
||||
};
|
||||
let icon_buf = Cursor::new(include_bytes!(
|
||||
"../build/macos/AppIcon.iconset/icon_256x256.png"
|
||||
));
|
||||
if let Ok(image) = image::load(icon_buf, image::ImageFormat::Png) {
|
||||
let image = image.into_rgba8();
|
||||
let (width, height) = image.dimensions();
|
||||
let rgba = image.into_raw();
|
||||
let icon = Icon::from_rgba(rgba, width, height).unwrap();
|
||||
primary.set_window_icon(Some(icon));
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
209
src/menu.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use crate::loading::TextureAssets;
|
||||
use crate::GameState;
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub struct MenuPlugin;
|
||||
|
||||
/// This plugin is responsible for the game menu (containing only one button...)
|
||||
/// The menu is only drawn during the State `GameState::Menu` and is removed when that state is exited
|
||||
impl Plugin for MenuPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(OnEnter(GameState::Menu), setup_menu)
|
||||
.add_systems(Update, click_play_button.run_if(in_state(GameState::Menu)))
|
||||
.add_systems(OnExit(GameState::Menu), cleanup_menu);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct ButtonColors {
|
||||
normal: Color,
|
||||
hovered: Color,
|
||||
}
|
||||
|
||||
impl Default for ButtonColors {
|
||||
fn default() -> Self {
|
||||
ButtonColors {
|
||||
normal: Color::linear_rgb(0.15, 0.15, 0.15),
|
||||
hovered: Color::linear_rgb(0.25, 0.25, 0.25),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Menu;
|
||||
|
||||
fn setup_menu(mut commands: Commands, textures: Res<TextureAssets>) {
|
||||
info!("menu");
|
||||
commands.spawn((Camera2d, Msaa::Off));
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
Menu,
|
||||
))
|
||||
.with_children(|children| {
|
||||
let button_colors = ButtonColors::default();
|
||||
children
|
||||
.spawn((
|
||||
Button,
|
||||
Node {
|
||||
width: Val::Px(140.0),
|
||||
height: Val::Px(50.0),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..Default::default()
|
||||
},
|
||||
BackgroundColor(button_colors.normal),
|
||||
button_colors,
|
||||
ChangeState(GameState::Playing),
|
||||
))
|
||||
.with_child((
|
||||
Text::new("Play"),
|
||||
TextFont {
|
||||
font_size: 40.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.9, 0.9, 0.9)),
|
||||
));
|
||||
});
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
bottom: Val::Px(5.),
|
||||
width: Val::Percent(100.),
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
Menu,
|
||||
))
|
||||
.with_children(|children| {
|
||||
children
|
||||
.spawn((
|
||||
Button,
|
||||
Node {
|
||||
width: Val::Px(170.0),
|
||||
height: Val::Px(50.0),
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(5.)),
|
||||
..Default::default()
|
||||
},
|
||||
BackgroundColor(Color::NONE),
|
||||
ButtonColors {
|
||||
normal: Color::NONE,
|
||||
..default()
|
||||
},
|
||||
OpenLink("https://bevyengine.org"),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
Text::new("Made with Bevy"),
|
||||
TextFont {
|
||||
font_size: 15.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.9, 0.9, 0.9)),
|
||||
));
|
||||
parent.spawn((
|
||||
ImageNode {
|
||||
image: textures.bevy.clone(),
|
||||
..default()
|
||||
},
|
||||
Node {
|
||||
width: Val::Px(32.),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
children
|
||||
.spawn((
|
||||
Button,
|
||||
Node {
|
||||
width: Val::Px(170.0),
|
||||
height: Val::Px(50.0),
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(5.)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::NONE),
|
||||
ButtonColors {
|
||||
normal: Color::NONE,
|
||||
hovered: Color::linear_rgb(0.25, 0.25, 0.25),
|
||||
},
|
||||
OpenLink("https://github.com/NiklasEi/bevy_game_template"),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
Text::new("Open source"),
|
||||
TextFont {
|
||||
font_size: 15.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.9, 0.9, 0.9)),
|
||||
));
|
||||
parent.spawn((
|
||||
ImageNode::new(textures.github.clone()),
|
||||
Node {
|
||||
width: Val::Px(32.),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct ChangeState(GameState);
|
||||
|
||||
#[derive(Component)]
|
||||
struct OpenLink(&'static str);
|
||||
|
||||
fn click_play_button(
|
||||
mut next_state: ResMut<NextState<GameState>>,
|
||||
mut interaction_query: Query<
|
||||
(
|
||||
&Interaction,
|
||||
&mut BackgroundColor,
|
||||
&ButtonColors,
|
||||
Option<&ChangeState>,
|
||||
Option<&OpenLink>,
|
||||
),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
) {
|
||||
for (interaction, mut color, button_colors, change_state, open_link) in &mut interaction_query {
|
||||
match *interaction {
|
||||
Interaction::Pressed => {
|
||||
if let Some(state) = change_state {
|
||||
next_state.set(state.0.clone());
|
||||
} else if let Some(link) = open_link {
|
||||
if let Err(error) = webbrowser::open(link.0) {
|
||||
warn!("Failed to open link {error:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Interaction::Hovered => {
|
||||
*color = button_colors.hovered.into();
|
||||
}
|
||||
Interaction::None => {
|
||||
*color = button_colors.normal.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanup_menu(mut commands: Commands, menu: Query<Entity, With<Menu>>) {
|
||||
for entity in menu.iter() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
799
src/player.rs
Normal file
@@ -0,0 +1,799 @@
|
||||
use crate::GameState;
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub struct PlayerPlugin;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct InstrumentCluster;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct SpeedGauge;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct DepthGauge;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct CompassGauge;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct EngineStatus;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct NavigationDisplay;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct GpsIndicator;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct RadarIndicator;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct AisIndicator;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct SystemDisplay;
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct SelectedSystem {
|
||||
pub current: Option<SystemType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum SystemType {
|
||||
Gps,
|
||||
Radar,
|
||||
Ais,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct YachtData {
|
||||
pub speed: f32, // knots
|
||||
pub depth: f32, // meters
|
||||
pub heading: f32, // degrees
|
||||
pub engine_temp: f32, // celsius
|
||||
pub fuel_level: f32, // percentage
|
||||
pub battery_level: f32, // percentage
|
||||
pub wind_speed: f32, // knots
|
||||
pub wind_direction: f32, // degrees
|
||||
}
|
||||
|
||||
impl Default for YachtData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
speed: 12.5,
|
||||
depth: 15.2,
|
||||
heading: 045.0,
|
||||
engine_temp: 82.0,
|
||||
fuel_level: 75.0,
|
||||
battery_level: 88.0,
|
||||
wind_speed: 8.3,
|
||||
wind_direction: 120.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This plugin handles the futuristic yacht instrument cluster
|
||||
/// Instrument cluster is only active during the State `GameState::Playing`
|
||||
impl Plugin for PlayerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<YachtData>()
|
||||
.init_resource::<SelectedSystem>()
|
||||
.add_systems(OnEnter(GameState::Playing), setup_instrument_cluster)
|
||||
.add_systems(
|
||||
Update,
|
||||
(update_yacht_data, update_instrument_displays, handle_system_interactions, update_system_display)
|
||||
.run_if(in_state(GameState::Playing))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_instrument_cluster(mut commands: Commands) {
|
||||
// Spawn camera since we're bypassing the menu system
|
||||
commands.spawn((Camera2d, Msaa::Off));
|
||||
|
||||
// Main container for the instrument cluster
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
flex_direction: FlexDirection::Column,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.05, 0.05, 0.1)),
|
||||
InstrumentCluster,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
// Top row - Main navigation and speed
|
||||
parent
|
||||
.spawn(Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(60.0),
|
||||
flex_direction: FlexDirection::Row,
|
||||
justify_content: JustifyContent::SpaceEvenly,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(20.0)),
|
||||
..default()
|
||||
})
|
||||
.with_children(|row| {
|
||||
// Speed Gauge
|
||||
row.spawn((
|
||||
Node {
|
||||
width: Val::Px(180.0),
|
||||
height: Val::Px(180.0),
|
||||
border: UiRect::all(Val::Px(2.0)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15)),
|
||||
BorderColor(Color::linear_rgb(0.0, 0.8, 1.0)),
|
||||
SpeedGauge,
|
||||
))
|
||||
.with_children(|gauge| {
|
||||
gauge.spawn((
|
||||
Text::new("SPEED"),
|
||||
TextFont {
|
||||
font_size: 12.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 0.8, 1.0)),
|
||||
));
|
||||
gauge.spawn((
|
||||
Text::new("12.5"),
|
||||
TextFont {
|
||||
font_size: 32.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 1.0, 0.8)),
|
||||
));
|
||||
gauge.spawn((
|
||||
Text::new("KTS"),
|
||||
TextFont {
|
||||
font_size: 10.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.6, 0.6)),
|
||||
));
|
||||
});
|
||||
|
||||
// Central Navigation Display
|
||||
row.spawn((
|
||||
Node {
|
||||
width: Val::Px(300.0),
|
||||
height: Val::Px(300.0),
|
||||
border: UiRect::all(Val::Px(2.0)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.15, 0.2)),
|
||||
BorderColor(Color::linear_rgb(0.0, 0.8, 1.0)),
|
||||
NavigationDisplay,
|
||||
))
|
||||
.with_children(|nav| {
|
||||
nav.spawn((
|
||||
Text::new("NAVIGATION"),
|
||||
TextFont {
|
||||
font_size: 16.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 0.8, 1.0)),
|
||||
));
|
||||
nav.spawn((
|
||||
Text::new("045°"),
|
||||
TextFont {
|
||||
font_size: 48.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 1.0, 0.8)),
|
||||
CompassGauge,
|
||||
));
|
||||
nav.spawn((
|
||||
Text::new("HEADING"),
|
||||
TextFont {
|
||||
font_size: 14.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.6, 0.6)),
|
||||
));
|
||||
});
|
||||
|
||||
// Depth Gauge
|
||||
row.spawn((
|
||||
Node {
|
||||
width: Val::Px(180.0),
|
||||
height: Val::Px(180.0),
|
||||
border: UiRect::all(Val::Px(2.0)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15)),
|
||||
BorderColor(Color::linear_rgb(0.0, 0.8, 1.0)),
|
||||
DepthGauge,
|
||||
))
|
||||
.with_children(|gauge| {
|
||||
gauge.spawn((
|
||||
Text::new("DEPTH"),
|
||||
TextFont {
|
||||
font_size: 12.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 0.8, 1.0)),
|
||||
));
|
||||
gauge.spawn((
|
||||
Text::new("15.2"),
|
||||
TextFont {
|
||||
font_size: 32.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 1.0, 0.8)),
|
||||
));
|
||||
gauge.spawn((
|
||||
Text::new("M"),
|
||||
TextFont {
|
||||
font_size: 10.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.6, 0.6)),
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
// Bottom row - Engine and system status
|
||||
parent
|
||||
.spawn(Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(40.0),
|
||||
flex_direction: FlexDirection::Row,
|
||||
justify_content: JustifyContent::SpaceEvenly,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(20.0)),
|
||||
..default()
|
||||
})
|
||||
.with_children(|row| {
|
||||
// Engine Status Panel
|
||||
row.spawn((
|
||||
Node {
|
||||
width: Val::Px(200.0),
|
||||
height: Val::Px(150.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::SpaceEvenly,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(10.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15)),
|
||||
BorderColor(Color::linear_rgb(0.8, 0.4, 0.0)),
|
||||
EngineStatus,
|
||||
))
|
||||
.with_children(|engine| {
|
||||
engine.spawn((
|
||||
Text::new("ENGINE"),
|
||||
TextFont {
|
||||
font_size: 14.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.8, 0.4, 0.0)),
|
||||
));
|
||||
engine.spawn((
|
||||
Text::new("82°C"),
|
||||
TextFont {
|
||||
font_size: 24.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 1.0, 0.0)),
|
||||
));
|
||||
engine.spawn((
|
||||
Text::new("TEMP NORMAL"),
|
||||
TextFont {
|
||||
font_size: 10.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.6, 0.6)),
|
||||
));
|
||||
});
|
||||
|
||||
// System Status Grid
|
||||
row.spawn((
|
||||
Node {
|
||||
width: Val::Px(250.0),
|
||||
height: Val::Px(150.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::SpaceEvenly,
|
||||
padding: UiRect::all(Val::Px(10.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.08, 0.08, 0.12)),
|
||||
BorderColor(Color::linear_rgb(0.4, 0.4, 0.6)),
|
||||
))
|
||||
.with_children(|grid| {
|
||||
grid.spawn((
|
||||
Text::new("SYSTEMS"),
|
||||
TextFont {
|
||||
font_size: 12.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.4, 0.4, 0.8)),
|
||||
));
|
||||
|
||||
// Fuel Level Bar
|
||||
grid.spawn(Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceBetween,
|
||||
width: Val::Percent(100.0),
|
||||
..default()
|
||||
})
|
||||
.with_children(|bar| {
|
||||
bar.spawn((
|
||||
Text::new("FUEL"),
|
||||
TextFont {
|
||||
font_size: 10.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.7, 0.7, 0.7)),
|
||||
));
|
||||
|
||||
bar.spawn((
|
||||
Node {
|
||||
width: Val::Px(80.0),
|
||||
height: Val::Px(8.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.2, 0.2, 0.2)),
|
||||
BorderColor(Color::linear_rgb(0.4, 0.4, 0.4)),
|
||||
))
|
||||
.with_children(|bar_bg| {
|
||||
bar_bg.spawn((
|
||||
Node {
|
||||
width: Val::Percent(75.0),
|
||||
height: Val::Percent(100.0),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.0, 0.8, 0.0)),
|
||||
));
|
||||
});
|
||||
|
||||
bar.spawn((
|
||||
Text::new("75%"),
|
||||
TextFont {
|
||||
font_size: 9.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.8, 0.8, 0.8)),
|
||||
));
|
||||
});
|
||||
|
||||
// Battery Level Bar
|
||||
grid.spawn(Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceBetween,
|
||||
width: Val::Percent(100.0),
|
||||
..default()
|
||||
})
|
||||
.with_children(|bar| {
|
||||
bar.spawn((
|
||||
Text::new("BATTERY"),
|
||||
TextFont {
|
||||
font_size: 10.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.7, 0.7, 0.7)),
|
||||
));
|
||||
|
||||
bar.spawn((
|
||||
Node {
|
||||
width: Val::Px(80.0),
|
||||
height: Val::Px(8.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.2, 0.2, 0.2)),
|
||||
BorderColor(Color::linear_rgb(0.4, 0.4, 0.4)),
|
||||
))
|
||||
.with_children(|bar_bg| {
|
||||
bar_bg.spawn((
|
||||
Node {
|
||||
width: Val::Percent(88.0),
|
||||
height: Val::Percent(100.0),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.0, 0.6, 1.0)),
|
||||
));
|
||||
});
|
||||
|
||||
bar.spawn((
|
||||
Text::new("88%"),
|
||||
TextFont {
|
||||
font_size: 9.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.8, 0.8, 0.8)),
|
||||
));
|
||||
});
|
||||
|
||||
// System Status Indicators
|
||||
grid.spawn(Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
justify_content: JustifyContent::SpaceBetween,
|
||||
width: Val::Percent(100.0),
|
||||
..default()
|
||||
})
|
||||
.with_children(|indicators| {
|
||||
// GPS Indicator
|
||||
indicators.spawn((
|
||||
Button,
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
padding: UiRect::all(Val::Px(8.0)),
|
||||
width: Val::Px(60.0),
|
||||
height: Val::Px(40.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15)),
|
||||
BorderColor(Color::linear_rgb(0.3, 0.3, 0.4)),
|
||||
GpsIndicator,
|
||||
))
|
||||
.with_children(|indicator| {
|
||||
indicator.spawn((
|
||||
Text::new("🛰️"),
|
||||
TextFont {
|
||||
font_size: 16.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 1.0, 0.0)),
|
||||
));
|
||||
indicator.spawn((
|
||||
Text::new("GPS"),
|
||||
TextFont {
|
||||
font_size: 8.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.6, 0.6)),
|
||||
));
|
||||
});
|
||||
|
||||
// RADAR Indicator
|
||||
indicators.spawn((
|
||||
Button,
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
padding: UiRect::all(Val::Px(8.0)),
|
||||
width: Val::Px(60.0),
|
||||
height: Val::Px(40.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15)),
|
||||
BorderColor(Color::linear_rgb(0.3, 0.3, 0.4)),
|
||||
RadarIndicator,
|
||||
))
|
||||
.with_children(|indicator| {
|
||||
indicator.spawn((
|
||||
Text::new("📡"),
|
||||
TextFont {
|
||||
font_size: 16.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 1.0, 0.0)),
|
||||
));
|
||||
indicator.spawn((
|
||||
Text::new("RADAR"),
|
||||
TextFont {
|
||||
font_size: 8.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.6, 0.6)),
|
||||
));
|
||||
});
|
||||
|
||||
// AIS Indicator
|
||||
indicators.spawn((
|
||||
Button,
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
padding: UiRect::all(Val::Px(8.0)),
|
||||
width: Val::Px(60.0),
|
||||
height: Val::Px(40.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15)),
|
||||
BorderColor(Color::linear_rgb(0.3, 0.3, 0.4)),
|
||||
AisIndicator,
|
||||
))
|
||||
.with_children(|indicator| {
|
||||
indicator.spawn((
|
||||
Text::new("🚢"),
|
||||
TextFont {
|
||||
font_size: 16.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.8, 0.0, 0.0)),
|
||||
));
|
||||
indicator.spawn((
|
||||
Text::new("AIS"),
|
||||
TextFont {
|
||||
font_size: 8.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.6, 0.6)),
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Wind Information
|
||||
row.spawn((
|
||||
Node {
|
||||
width: Val::Px(200.0),
|
||||
height: Val::Px(150.0),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::SpaceEvenly,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(10.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.1, 0.15, 0.1)),
|
||||
BorderColor(Color::linear_rgb(0.0, 0.8, 0.4)),
|
||||
))
|
||||
.with_children(|wind| {
|
||||
wind.spawn((
|
||||
Text::new("WIND"),
|
||||
TextFont {
|
||||
font_size: 14.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 0.8, 0.4)),
|
||||
));
|
||||
wind.spawn((
|
||||
Text::new("8.3 KTS"),
|
||||
TextFont {
|
||||
font_size: 18.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.0, 1.0, 0.6)),
|
||||
));
|
||||
wind.spawn((
|
||||
Text::new("120° REL"),
|
||||
TextFont {
|
||||
font_size: 14.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.6, 0.8, 0.6)),
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
// System Display Area
|
||||
parent
|
||||
.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Px(200.0),
|
||||
border: UiRect::all(Val::Px(2.0)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(20.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::linear_rgb(0.05, 0.05, 0.1)),
|
||||
BorderColor(Color::linear_rgb(0.2, 0.2, 0.3)),
|
||||
SystemDisplay,
|
||||
))
|
||||
.with_children(|display| {
|
||||
display.spawn((
|
||||
Text::new("Select a system above to view details"),
|
||||
TextFont {
|
||||
font_size: 16.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::linear_rgb(0.5, 0.5, 0.6)),
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
fn update_yacht_data(mut yacht_data: ResMut<YachtData>, time: Res<Time>) {
|
||||
let t = time.elapsed_secs();
|
||||
|
||||
// Simulate realistic yacht data with some variation
|
||||
yacht_data.speed = 12.5 + (t * 0.3).sin() * 2.0;
|
||||
yacht_data.depth = 15.2 + (t * 0.1).sin() * 3.0;
|
||||
yacht_data.heading = (yacht_data.heading + time.delta_secs() * 5.0) % 360.0;
|
||||
yacht_data.engine_temp = 82.0 + (t * 0.2).sin() * 3.0;
|
||||
yacht_data.wind_speed = 8.3 + (t * 0.4).sin() * 1.5;
|
||||
yacht_data.wind_direction = (yacht_data.wind_direction + time.delta_secs() * 10.0) % 360.0;
|
||||
|
||||
// Slowly drain fuel and battery (very slowly for demo purposes)
|
||||
yacht_data.fuel_level = (yacht_data.fuel_level - time.delta_secs() * 0.01).max(0.0);
|
||||
yacht_data.battery_level = (yacht_data.battery_level - time.delta_secs() * 0.005).max(0.0);
|
||||
}
|
||||
|
||||
fn update_instrument_displays(
|
||||
yacht_data: Res<YachtData>,
|
||||
mut speed_query: Query<&mut Text, (With<SpeedGauge>, Without<DepthGauge>, Without<CompassGauge>)>,
|
||||
mut depth_query: Query<&mut Text, (With<DepthGauge>, Without<SpeedGauge>, Without<CompassGauge>)>,
|
||||
mut compass_query: Query<&mut Text, (With<CompassGauge>, Without<SpeedGauge>, Without<DepthGauge>)>,
|
||||
) {
|
||||
// Update speed display
|
||||
for mut text in speed_query.iter_mut() {
|
||||
if text.0.contains('.') {
|
||||
text.0 = format!("{:.1}", yacht_data.speed);
|
||||
}
|
||||
}
|
||||
|
||||
// Update depth display
|
||||
for mut text in depth_query.iter_mut() {
|
||||
if text.0.contains('.') {
|
||||
text.0 = format!("{:.1}", yacht_data.depth);
|
||||
}
|
||||
}
|
||||
|
||||
// Update compass display
|
||||
for mut text in compass_query.iter_mut() {
|
||||
if text.0.contains('°') {
|
||||
text.0 = format!("{:03.0}°", yacht_data.heading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_system_interactions(
|
||||
mut selected_system: ResMut<SelectedSystem>,
|
||||
mut interaction_query: Query<
|
||||
(&Interaction, &mut BackgroundColor, Option<&GpsIndicator>, Option<&RadarIndicator>, Option<&AisIndicator>),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
) {
|
||||
for (interaction, mut background_color, gps, radar, ais) in &mut interaction_query {
|
||||
match *interaction {
|
||||
Interaction::Pressed => {
|
||||
if gps.is_some() {
|
||||
selected_system.current = Some(SystemType::Gps);
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
||||
} else if radar.is_some() {
|
||||
selected_system.current = Some(SystemType::Radar);
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
||||
} else if ais.is_some() {
|
||||
selected_system.current = Some(SystemType::Ais);
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
||||
}
|
||||
}
|
||||
Interaction::Hovered => {
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.15, 0.15, 0.2));
|
||||
}
|
||||
Interaction::None => {
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_system_display(
|
||||
selected_system: Res<SelectedSystem>,
|
||||
mut display_query: Query<&mut Text, With<SystemDisplay>>,
|
||||
yacht_data: Res<YachtData>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if let Ok(mut text) = display_query.single_mut() {
|
||||
match selected_system.current {
|
||||
Some(SystemType::Gps) => {
|
||||
text.0 = format!(
|
||||
"GPS NAVIGATION SYSTEM\n\n\
|
||||
Position: 43°38'19.5\"N 1°26'58.3\"W\n\
|
||||
Heading: {:.0}°\n\
|
||||
Speed: {:.1} knots\n\
|
||||
Course Over Ground: {:.0}°\n\
|
||||
Satellites: 12 connected\n\
|
||||
HDOP: 0.8 (Excellent)\n\
|
||||
\n\
|
||||
Next Waypoint: MONACO HARBOR\n\
|
||||
Distance: 127.3 NM\n\
|
||||
ETA: 10h 12m",
|
||||
yacht_data.heading,
|
||||
yacht_data.speed,
|
||||
yacht_data.heading + 5.0
|
||||
);
|
||||
}
|
||||
Some(SystemType::Radar) => {
|
||||
let sweep_angle = (time.elapsed_secs() * 60.0) % 360.0;
|
||||
text.0 = format!(
|
||||
"RADAR SYSTEM - 12 NM RANGE\n\n\
|
||||
Status: ACTIVE\n\
|
||||
Sweep: {:.0}°\n\
|
||||
Gain: AUTO\n\
|
||||
Sea Clutter: -15 dB\n\
|
||||
Rain Clutter: OFF\n\
|
||||
\n\
|
||||
CONTACTS DETECTED:\n\
|
||||
• Vessel 1: 2.3 NM @ 045° (15 kts)\n\
|
||||
• Vessel 2: 5.7 NM @ 180° (8 kts)\n\
|
||||
• Land Mass: 8.2 NM @ 270°\n\
|
||||
• Buoy: 1.1 NM @ 315°",
|
||||
sweep_angle
|
||||
);
|
||||
}
|
||||
Some(SystemType::Ais) => {
|
||||
text.0 = format!(
|
||||
"AIS - AUTOMATIC IDENTIFICATION SYSTEM\n\n\
|
||||
Status: RECEIVING\n\
|
||||
Own Ship MMSI: 123456789\n\
|
||||
\n\
|
||||
NEARBY VESSELS:\n\
|
||||
\n\
|
||||
🛥️ M/Y SERENITY\n\
|
||||
MMSI: 987654321\n\
|
||||
Distance: 2.1 NM @ 045°\n\
|
||||
Speed: 12.5 kts\n\
|
||||
Course: 180°\n\
|
||||
\n\
|
||||
🚢 CARGO VESSEL ATLANTIS\n\
|
||||
MMSI: 456789123\n\
|
||||
Distance: 5.8 NM @ 270°\n\
|
||||
Speed: 18.2 kts\n\
|
||||
Course: 090°\n\
|
||||
\n\
|
||||
⛵ S/Y WIND DANCER\n\
|
||||
MMSI: 789123456\n\
|
||||
Distance: 1.3 NM @ 135°\n\
|
||||
Speed: 6.8 kts\n\
|
||||
Course: 225°"
|
||||
);
|
||||
}
|
||||
None => {
|
||||
text.0 = "Select a system above to view details".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_system_type_enum() {
|
||||
let gps = SystemType::Gps;
|
||||
let radar = SystemType::Radar;
|
||||
let ais = SystemType::Ais;
|
||||
|
||||
assert_ne!(gps, radar);
|
||||
assert_ne!(radar, ais);
|
||||
assert_ne!(ais, gps);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_selected_system_default() {
|
||||
let selected_system = SelectedSystem::default();
|
||||
assert_eq!(selected_system.current, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_yacht_data_default() {
|
||||
let yacht_data = YachtData::default();
|
||||
assert_eq!(yacht_data.speed, 12.5);
|
||||
assert_eq!(yacht_data.depth, 15.2);
|
||||
assert_eq!(yacht_data.heading, 45.0);
|
||||
assert_eq!(yacht_data.fuel_level, 75.0);
|
||||
assert_eq!(yacht_data.battery_level, 88.0);
|
||||
}
|
||||
}
|