mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-22 01:00:28 +02:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35554cd243 | ||
|
|
d4687b24e2 | ||
|
|
9d65e7e63f | ||
|
|
f3ce8c3bf0 | ||
|
|
bce5c2b560 |
No files matched your search
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: How to install
|
||||
url: https://github.com/Cesarus85/ringfall-mjolnir-vr#install-on-meta-quest-3--3s
|
||||
url: https://github.com/Cesarus85/mjolnir-vr#install-on-meta-quest-3--3s
|
||||
about: Step-by-step installation for the Quest 3 / 3S and the Steam Frame.
|
||||
+23
-15
@@ -13,7 +13,7 @@ name: Build
|
||||
# - a newer push to the same branch cancels a run still going;
|
||||
# - pushes that only change documentation do not build;
|
||||
# - Windows, whose minutes cost twice Linux's, builds only Windows;
|
||||
# - Ringfall: Mjolnir VR builds only Android (the VR app): the Linux and
|
||||
# - Mjolnir VR builds only Android (the VR app): the Linux and
|
||||
# Windows jobs are kept, off (if: false), so that upstream's changes to
|
||||
# them still merge;
|
||||
# - artifacts are kept for three days.
|
||||
@@ -24,7 +24,7 @@ name: Build
|
||||
# gives the latest build; the last five are kept. Its web build is then the
|
||||
# site on GitHub Pages (https://halocombatevolved.com/, port/web/README.md).
|
||||
on:
|
||||
# (Ringfall: Mjolnir VR: main only; its branches are tried on the headsets
|
||||
# (Mjolnir VR: main only; its branches are tried on the headsets
|
||||
# from local builds)
|
||||
push:
|
||||
branches: [main]
|
||||
@@ -52,7 +52,7 @@ env:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
# (off: Ringfall: Mjolnir VR ships the Android app only)
|
||||
# (off: Mjolnir VR ships the Android app only)
|
||||
if: ${{ false }}
|
||||
# Arch Linux has what the build needs as packages: clang 22 (for the
|
||||
# profile-guided optimisation in pgo/), and CMake, pkg-config and
|
||||
@@ -203,7 +203,7 @@ jobs:
|
||||
retention-days: 3
|
||||
|
||||
windows:
|
||||
# (off: Ringfall: Mjolnir VR ships the Android app only)
|
||||
# (off: Mjolnir VR ships the Android app only)
|
||||
if: ${{ false }}
|
||||
# The runner image has LLVM, Python and Visual Studio's x86 libraries and
|
||||
# Windows SDK.
|
||||
@@ -279,7 +279,7 @@ jobs:
|
||||
retention-days: 3
|
||||
|
||||
web:
|
||||
# (off: Ringfall: Mjolnir VR ships the Android app only)
|
||||
# (off: Mjolnir VR ships the Android app only)
|
||||
if: ${{ false }}
|
||||
# Emscripten (emsdk, the version below, cached) builds the web port's
|
||||
# site (port/web/README.md); the tests check its graph, the page's
|
||||
@@ -389,18 +389,26 @@ jobs:
|
||||
- name: Package
|
||||
run: |
|
||||
mkdir release
|
||||
# (Ringfall: Mjolnir VR publishes its Android builds only, under
|
||||
# (Mjolnir VR publishes its Android builds only, under
|
||||
# its own name: Updater.java looks for them; the desktop ports are
|
||||
# built to keep them building)
|
||||
for config in debug release; do
|
||||
(cd "artifacts/halo-android-$config" &&
|
||||
zip -qr "$GITHUB_WORKSPACE/release/ringfall-mjolnir-vr-android-$config.zip" .)
|
||||
zip -qr "$GITHUB_WORKSPACE/release/mjolnir-vr-android-$config.zip" .)
|
||||
done
|
||||
# the Steam Frame's installer (port/android/STEAM_FRAME.md): its
|
||||
# bundle with the release APK, and the desktop file that fetches it
|
||||
python3 tools/steam_frame_bundle.py artifacts/halo-android-release/app-release.apk release "$GH_REPO"
|
||||
# the APK itself, for sideloading on the Quest (SideQuest, adb)
|
||||
cp artifacts/halo-android-release/app-release.apk release/RingfallMjolnirVR.apk
|
||||
cp artifacts/halo-android-release/app-release.apk release/MjolnirVR.apk
|
||||
# the same under the names before "Mjolnir VR" (Ringfall: Mjolnir
|
||||
# VR, builds 302 to 313): their updaters (Updater.java, the Frame's
|
||||
# launcher) and the links already shared ask for them
|
||||
cp release/mjolnir-vr-android-debug.zip release/ringfall-mjolnir-vr-android-debug.zip
|
||||
cp release/mjolnir-vr-android-release.zip release/ringfall-mjolnir-vr-android-release.zip
|
||||
cp release/mjolnir-vr-steam-frame.zip release/ringfall-mjolnir-vr-steam-frame.zip
|
||||
cp release/Install-Mjolnir-VR.desktop release/Install-Ringfall-Mjolnir-VR.desktop
|
||||
cp release/MjolnirVR.apk release/RingfallMjolnirVR.apk
|
||||
ls -l release
|
||||
|
||||
- name: Publish
|
||||
@@ -415,15 +423,15 @@ jobs:
|
||||
readme="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
|
||||
{
|
||||
printf '%s\n\n' "$subject"
|
||||
printf '**Ringfall: Mjolnir VR** is an unofficial VR mod for Halo: Combat Evolved. It needs a disc image of your own, purchased Xbox copy of the game; no game files are included.\n\n'
|
||||
printf '**Mjolnir VR** is an unofficial VR mod for Halo: Combat Evolved. It needs a disc image of your own, purchased Xbox copy of the game; no game files are included.\n\n'
|
||||
printf '| Headset | Download | How to install |\n| --- | --- | --- |\n'
|
||||
printf '| Meta Quest 3 / 3S | `RingfallMjolnirVR.apk` | [Install on Quest](%s#install-on-meta-quest-3--3s) |\n' "$readme"
|
||||
printf '| Steam Frame | `Install-Ringfall-Mjolnir-VR.desktop` (open it in Desktop Mode) | [Install on Steam Frame](%s#install-on-steam-frame) |\n\n' "$readme"
|
||||
printf 'The other files are for the built-in updater and the Steam Frame installer.\n\n'
|
||||
printf '| Meta Quest 3 / 3S | `MjolnirVR.apk` | [Install on Quest](%s#install-on-meta-quest-3--3s) |\n' "$readme"
|
||||
printf '| Steam Frame | `Install-Mjolnir-VR.desktop` (open it in Desktop Mode) | [Install on Steam Frame](%s#install-on-steam-frame) |\n\n' "$readme"
|
||||
printf 'The other files are for the built-in updater and the Steam Frame installer (those named `ringfall-…` / `Ringfall…`: the same, for the builds before the name Mjolnir VR).\n\n'
|
||||
printf 'Commit: %s\nBuild: %s\n' "$COMMIT_URL" "$RUN_URL"
|
||||
} > notes.md
|
||||
gh release create "$tag" release/* --target "$GITHUB_SHA" --latest \
|
||||
--title "Ringfall: Mjolnir VR, build $GITHUB_RUN_NUMBER (${GITHUB_SHA:0:7})" --notes-file notes.md
|
||||
--title "Mjolnir VR, build $GITHUB_RUN_NUMBER (${GITHUB_SHA:0:7})" --notes-file notes.md
|
||||
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# The Windows builds' executables and PDBs to Sentry, which turns the
|
||||
@@ -465,7 +473,7 @@ jobs:
|
||||
done
|
||||
|
||||
pages:
|
||||
# (off: Ringfall: Mjolnir VR ships the Android app only)
|
||||
# (off: Mjolnir VR ships the Android app only)
|
||||
if: ${{ false }}
|
||||
# The release's web build to GitHub Pages, which serves it at
|
||||
# https://halocombatevolved.com/ (port/web/README.md, "Hosting"). Only
|
||||
@@ -586,7 +594,7 @@ jobs:
|
||||
title="Build failed"; color=15548997
|
||||
fi
|
||||
latest="$SERVER/$REPOSITORY/releases/latest"
|
||||
downloads="[Quest]($latest/download/ringfall-mjolnir-vr-android-release.zip) · [Steam Frame]($latest/download/Install-Ringfall-Mjolnir-VR.desktop) ([all builds]($latest))"
|
||||
downloads="[Quest]($latest/download/mjolnir-vr-android-release.zip) · [Steam Frame]($latest/download/Install-Mjolnir-VR.desktop) ([all builds]($latest))"
|
||||
if [ -n "$RELEASE_TAG" ]; then
|
||||
release="[$RELEASE_TAG]($SERVER/$REPOSITORY/releases/tag/$RELEASE_TAG)"
|
||||
if [ "$PAGES" = success ]; then
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
<p align="center">
|
||||
<img src="port/android/steam_frame_grid/library.png" width="640" alt="Ringfall: Mjolnir VR">
|
||||
<img src="port/android/steam_frame_grid/library.png" width="640" alt="Mjolnir VR">
|
||||
</p>
|
||||
|
||||
# Ringfall: Mjolnir VR
|
||||
# Mjolnir VR
|
||||
|
||||
**A VR mod for Halo: Combat Evolved** (the original Xbox game) on the
|
||||
**Meta Quest 3 / 3S** and the **Steam Frame**. Play the campaign and
|
||||
multiplayer in full 6DoF virtual reality, with your hands in the game.
|
||||
|
||||
Ringfall: Mjolnir VR runs on your headset by itself, without a PC. It
|
||||
Mjolnir VR runs on your headset by itself, without a PC. It
|
||||
brings no game files: it uses a disc image of **your own, purchased copy**
|
||||
of the game.
|
||||
|
||||
> **Unofficial fan project.** Ringfall: Mjolnir VR is a free,
|
||||
> **Unofficial fan project.** Mjolnir VR is a free,
|
||||
> non-commercial fan project. It is not affiliated with, endorsed,
|
||||
> sponsored or approved by Microsoft, Xbox Game Studios, Halo Studios or
|
||||
> Bungie. See [Legal](#legal).
|
||||
|
||||
Until build 313 it was called *Ringfall: Mjolnir VR*. Installed builds
|
||||
update to the new name by themselves, and the old links lead here.
|
||||
|
||||
## Contents
|
||||
|
||||
- [What you get](#what-you-get)
|
||||
@@ -86,14 +89,14 @@ that it updates itself.
|
||||
2. **Install [SideQuest](https://sidequestvr.com/setup-howto)** (the
|
||||
"Advanced Installer") on your computer and connect the Quest with a
|
||||
USB cable. In the headset, allow USB debugging for this computer.
|
||||
3. **Download `RingfallMjolnirVR.apk`** from the
|
||||
3. **Download `MjolnirVR.apk`** from the
|
||||
[latest release](../../releases/latest) and drag it onto the SideQuest
|
||||
window. It installs the game.
|
||||
4. **Copy your disc image** to the Quest's `Download` folder: in
|
||||
SideQuest, open the file manager (the folder icon), go to `Download`
|
||||
and drag the `.iso` file in.
|
||||
5. **In the headset**, open your *Library*, choose *Unknown Sources* and
|
||||
start **Ringfall: Mjolnir VR**. Select **Choose disc image** and pick
|
||||
start **Mjolnir VR**. Select **Choose disc image** and pick
|
||||
your file in `Download`. The game copies what it needs (about 1.8 GB,
|
||||
a minute or two) and starts.
|
||||
|
||||
@@ -108,14 +111,14 @@ No computer and no keyboard needed.
|
||||
*Switch to Desktop*.
|
||||
2. **Put your disc image** in the `Downloads` folder (for example from a
|
||||
USB stick or memory card with the file manager, Dolphin).
|
||||
3. **Download `Install-Ringfall-Mjolnir-VR.desktop`** from the
|
||||
3. **Download `Install-Mjolnir-VR.desktop`** from the
|
||||
[latest release](../../releases/latest) with the web browser.
|
||||
4. **Open it**: in Dolphin, open `Downloads` and double-click the file.
|
||||
If you are asked whether to run it, choose to continue.
|
||||
5. **Answer the dialogs.** The installer finds your disc image, copies
|
||||
what the game needs, adds **Ringfall: Mjolnir VR** to your Steam
|
||||
what the game needs, adds **Mjolnir VR** to your Steam
|
||||
library and offers to switch back to Gaming Mode.
|
||||
6. Start **Ringfall: Mjolnir VR** from your library (under *Non-Steam*).
|
||||
6. Start **Mjolnir VR** from your library (under *Non-Steam*).
|
||||
|
||||
The game updates itself: when a new version is out, it is downloaded
|
||||
while you play and used from the next start on. Your settings, saves and
|
||||
@@ -155,7 +158,7 @@ telemetry.
|
||||
|
||||
## Credits
|
||||
|
||||
Ringfall: Mjolnir VR stands on the work of others:
|
||||
Mjolnir VR stands on the work of others:
|
||||
|
||||
- **[OpenCE](https://github.com/OpenCommunityEdition/OpenCE)** (Open
|
||||
Community Edition): the port of the decompiled game to Linux, Windows
|
||||
@@ -186,7 +189,7 @@ port it is built on.
|
||||
|
||||
## Legal
|
||||
|
||||
Ringfall: Mjolnir VR is an unofficial, free, non-commercial fan project.
|
||||
Mjolnir VR is an unofficial, free, non-commercial fan project.
|
||||
It is not affiliated with, endorsed, sponsored or approved by Microsoft
|
||||
Corporation, Xbox Game Studios, Halo Studios or Bungie. Halo, Halo: Combat
|
||||
Evolved and Xbox are trademarks of Microsoft Corporation; they are named
|
||||
@@ -201,7 +204,7 @@ their games.
|
||||
|
||||
### Licence
|
||||
|
||||
Ringfall: Mjolnir VR is free software: you can give it to others and change
|
||||
Mjolnir VR is free software: you can give it to others and change
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License or (at your option)
|
||||
any later version (`AGPL-3.0-or-later`), as OpenCE, which it builds on, is.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Third-party notices
|
||||
|
||||
Ringfall: Mjolnir VR is released under the GNU Affero General Public
|
||||
Mjolnir VR is released under the GNU Affero General Public
|
||||
License, version 3 or later ([LICENSE](LICENSE)); releases before build 312
|
||||
were released under [CC0 1.0](LICENSES/CC0-1.0.txt). It includes or builds
|
||||
on the following, under their own licenses; each license's full text is in
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
> This is the README of [OpenCE](https://github.com/OpenCommunityEdition/OpenCE),
|
||||
> the port Ringfall: Mjolnir VR is built on, kept for its build and
|
||||
> the port Mjolnir VR is built on, kept for its build and
|
||||
> development notes. Its download links are OpenCE's. For the VR mod, see
|
||||
> the [main README](../README.md).
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Android
|
||||
|
||||
The app of Ringfall: Mjolnir VR (application id `io.github.cesarus85.ringfall`;
|
||||
The app of Mjolnir VR (application id `io.github.cesarus85.ringfall`, from
|
||||
the name before, "Ringfall: Mjolnir VR", kept so that installed apps update;
|
||||
its Java classes keep the decompilation's package, `com.halo.decomp`).
|
||||
`ninja android` builds the game for 64-bit ARM Android (arm64-v8a).
|
||||
`ninja android_apk` makes an app from it:
|
||||
|
||||
@@ -10,10 +10,10 @@ On the Frame, in Desktop Mode (Steam menu > Power > Switch to Desktop):
|
||||
1. Put your own disc image of Halo: Combat Evolved for the original Xbox (`.iso` or
|
||||
`.xiso`, any version) in Downloads (or Documents, the Desktop, a memory
|
||||
card).
|
||||
2. Download `Install-Ringfall-Mjolnir-VR.desktop` from the latest release and open it
|
||||
2. Download `Install-Mjolnir-VR.desktop` from the latest release and open it
|
||||
(double-click; confirm running it).
|
||||
3. Answer the dialogs: the installer finds the disc image (or asks for it),
|
||||
copies the game data out of it, adds "Ringfall: Mjolnir VR" to the Steam library
|
||||
copies the game data out of it, adds "Mjolnir VR" to the Steam library
|
||||
and switches back to Gaming Mode. Start it from the library there.
|
||||
|
||||
The game updates itself: while it runs, its launcher looks for a newer
|
||||
@@ -23,18 +23,20 @@ release, fetches it and puts it in place as the next start begins
|
||||
too (settings, saves and game data stay).
|
||||
|
||||
The desktop file (`steam_frame_installer.desktop`) downloads the latest
|
||||
release's `ringfall-mjolnir-vr-steam-frame.zip` (`tools/steam_frame_bundle.py`, made by
|
||||
release's `mjolnir-vr-steam-frame.zip` (`tools/steam_frame_bundle.py`, made by
|
||||
the release job of `.github/workflows/build.yml`) and runs its
|
||||
`install.sh` (`steam_frame_install.sh`), with zenity dialogs:
|
||||
- `~/Documents/RingfallMjolnirVR`: the launcher (`ringfall-mjolnir-vr.sh`)
|
||||
and the release APK (`RingfallMjolnirVR.apk`); the folder of the builds
|
||||
before the name, `~/Documents/HaloCE`, renamed to it, and their "Halo VR"
|
||||
shortcut replaced;
|
||||
shortcut replaced. (These keep the name of builds 302 to 313, "Ringfall:
|
||||
Mjolnir VR": the Steam shortcut of an installed game starts the launcher
|
||||
there, and the launcher's update looks for them in the bundle.);
|
||||
- the maps folder out of the disc image (`steam_frame_xiso.py`, the app's
|
||||
`XisoExtractor.java` in Python), unless `maps/ui.map` is there;
|
||||
- the Steam shortcut, unless it is there: added by Steam itself
|
||||
(`steam://addnonsteamgame`, as SteamOS's "Add to Steam" does) from
|
||||
`Ringfall Mjolnir VR.desktop` beside the game, then the library pictures
|
||||
`Mjolnir VR.desktop` beside the game, then the library pictures
|
||||
under the id Steam gave it. Steam is not closed: on the Frame, Desktop
|
||||
Mode runs inside Gaming Mode's session, and closing Steam ends it (and the
|
||||
installer with it). An old "Halo VR" shortcut is left for the player to
|
||||
@@ -83,7 +85,7 @@ dialogs, for testing over adb; it leaves Steam and its shortcuts alone.
|
||||
|
||||
## Starting it on the Frame
|
||||
|
||||
A button in the Steam library ("Ringfall: Mjolnir VR", a non-Steam game)
|
||||
A button in the Steam library ("Mjolnir VR", a non-Steam game)
|
||||
runs `~/Documents/RingfallMjolnirVR/ringfall-mjolnir-vr.sh` (`port/android/steam_frame_launch.sh`),
|
||||
which starts the game as Steam starts an Android title of its own:
|
||||
`lepton waitforexitandrun -- ~/Documents/RingfallMjolnirVR/RingfallMjolnirVR.apk`, in a container
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# VR roadmap
|
||||
|
||||
What Ringfall: Mjolnir VR (Quest 3, Steam Frame) has, and what comes next, in the
|
||||
What Mjolnir VR (Quest 3, Steam Frame) has, and what comes next, in the
|
||||
agreed order. Updated with every step: a step is **done** once the user has
|
||||
tried it in the headset and it is merged into `main`; **built** means in a
|
||||
branch, not yet tried in the headset.
|
||||
@@ -24,8 +24,8 @@ named where code is taken over.
|
||||
| Scores | The menu button held shows the scores on the Quest (a press pauses, as it is let go); the Steam Frame's View button as before; the text chat's lines where the desktop has them (2026-10-18) |
|
||||
| Comfort | Snap or smooth turning, a comfort vignette in vehicles, VR options in the main and pause menus (Y), a page of every controller button |
|
||||
| Menus | The PC version's menus in VR (`vr.menus`), clicked with a laser; text typed on the on-screen keyboard |
|
||||
| Multiplayer | Up to date with OpenCE (network version 29, 2026-10-14): cross-play, its server browser, co-op campaign, password lobbies; join codes and the VR online list |
|
||||
| Bots | Bots in multiplayer: on foot (paths, items, objectives), in vehicles and at turrets; they make room for a joining person, on the LAN too (a lobby they fill is advertised open, 2026-10-18) |
|
||||
| Multiplayer | Up to date with OpenCE (network version 32, 2026-10-20; HDR kept off in VR): cross-play, its server browser, co-op campaign, password lobbies; join codes and the VR online list |
|
||||
| Bots | Bots in multiplayer (`host_bots.c`; OpenCE's own, `bots.c`, are local split screen games' and off in VR): on foot (paths, items, objectives), in vehicles and at turrets; they make room for a joining person, on the LAN too (2026-10-18); they climb ladders (Damnation's) and take paths as near their goal as a search reaches, as OpenCE's do: on Damnation 0 searches of 1747 failed, 1078 of 1605 before (2026-10-20). Later: OpenCE's bots' aiming (lead, bursts, taps for the pistol and sniper rifle, field of view), learnt obstacles and map fixes into ours |
|
||||
| Voice chat | Push to talk on any button (`vr.button_talk`, BUTTONS page; the free hand's grip by default, sending nothing while it reaches for the foregrip or the wheel), open mic, off (2026-10-13) |
|
||||
| Builds | The Android build's assembly conversion (`tools/android_asm_convert.py`) keeps string data as it is: strings and tables starting with "_" or holding \" lost a "_" (Opus's pulse cache among them: the Steam Frame's crashes in online games while talking), 2026-10-13 |
|
||||
| Languages | The European disc in Spanish and Italian (a maps folder for each, `maps_es` and `maps_it`, none called `maps`) imported on the Quest and the Steam Frame: the headset's language's, its language the game's and the menus' (`maps/language.txt`, unless `game.language` is set), 2026-10-17; not yet tried with that disc |
|
||||
@@ -46,6 +46,7 @@ named where code is taken over.
|
||||
| 4 | Later: disc image import in the launcher; moving by swinging the arms (in place of the stick, never faster than it: online too), and a sprint by swinging them harder (faster than the game allows: offline only) | import **done** (in `main` 2026-10-06): the app's import (tried on the Quest 3: all 24 maps; on the Frame kept in `~/Documents/RingfallMjolnirVR`), a Steam Frame installer from Desktop Mode (`STEAM_FRAME.md`; tried on the Frame: the old folder renamed, the shortcut added by Steam), a release signing key (CI and local builds the same). Arm swinging and its offline sprint still open |
|
||||
| 5 | Later: multiview again (both eyes in one pass, half the drawing's CPU cost): find why its eyes do not fuse in big online games on the Quest 3 (and now and then on the Steam Frame), then turn it back on | open: off by default since 2026-10-16 (`debug.multiview`); findings in the open points |
|
||||
| R | Release as **Ringfall: Mjolnir VR** (no "Halo" in the product's name): app name, id `io.github.cesarus85.ringfall`, icon and library pictures (`tools/ringfall_art.py`), Frame folder and installer, release assets, README; the repository renamed | **done** (in `main` 2026-10-06; Quest 3 and Steam Frame good) |
|
||||
| R2 | Renamed **Mjolnir VR** ("Ringfall" is another game's name): app and Steam names, logo (`tools/mjolnir_art.py`), release assets (also under the old names for the updaters of builds 302 to 313), updaters, README; the repository renamed `Cesarus85/mjolnir-vr` (GitHub forwards the old links). Kept: the app id, the signing key's alias, the Frame's `~/Documents/RingfallMjolnirVR` and the names inside its bundle (installed games and their shortcuts) | in `feature/rename-mjolnir-vr` (2026-10-21) |
|
||||
|
||||
## Step 2, first part (done)
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ public class LauncherActivity extends Activity {
|
||||
layout.setBackgroundColor(Color.rgb(12, 16, 20));
|
||||
|
||||
TextView title = new TextView(this);
|
||||
title.setText("Ringfall: Mjolnir VR needs game data");
|
||||
title.setText("Mjolnir VR needs game data");
|
||||
title.setTextColor(Color.WHITE);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
|
||||
title.setGravity(Gravity.CENTER);
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.zip.ZipInputStream;
|
||||
* the latest release when it starts, on a thread of its own, and if it is
|
||||
* newer asks the player whether to update:
|
||||
*
|
||||
* - Yes: the release's app (ringfall-mjolnir-vr-android-release.zip or
|
||||
* - Yes: the release's app (mjolnir-vr-android-release.zip or
|
||||
* -debug.zip) is
|
||||
* downloaded and handed to Android's package installer, which replaces the
|
||||
* game (closing it) and offers to open the new version.
|
||||
@@ -53,8 +53,8 @@ import java.util.zip.ZipInputStream;
|
||||
final class Updater {
|
||||
/* the VR port's own releases: the original's, made without VR, must
|
||||
never replace it (they share the package name) */
|
||||
private static final String APP_NAME = "Ringfall: Mjolnir VR";
|
||||
private static final String REPOSITORY = "Cesarus85/ringfall-mjolnir-vr";
|
||||
private static final String APP_NAME = "Mjolnir VR";
|
||||
private static final String REPOSITORY = "Cesarus85/mjolnir-vr";
|
||||
private static final String USER_AGENT = "halo-ce-universal-updater";
|
||||
private static final int TIMEOUT_MILLISECONDS = 20000;
|
||||
/** the most a download (a release's zip, about 35 MB) or the app in it may be */
|
||||
@@ -240,7 +240,7 @@ final class Updater {
|
||||
/* ---------- updating */
|
||||
|
||||
private static void update(Activity activity, int latest) {
|
||||
String asset = "ringfall-mjolnir-vr-android-" + (BuildConfig.DEBUG ? "debug" : "release") + ".zip";
|
||||
String asset = "mjolnir-vr-android-" + (BuildConfig.DEBUG ? "debug" : "release") + ".zip";
|
||||
File directory = new File(activity.getCacheDir(), UpdateProvider.DIRECTORY);
|
||||
LinearLayout layout = new LinearLayout(activity);
|
||||
TextView status = new TextView(activity);
|
||||
|
||||
@@ -153,7 +153,7 @@ final class XisoExtractor {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// (Ringfall: a European disc in two languages, Spanish and Italian,
|
||||
// (Mjolnir VR: a European disc in two languages, Spanish and Italian,
|
||||
// has no maps folder but one for each, maps_es and maps_it: the
|
||||
// device's language's is taken, else English's, else the first, and
|
||||
// its language written to maps/language.txt, which the game asks the
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Ringfall: Mjolnir VR</string>
|
||||
<string name="app_name">Mjolnir VR</string>
|
||||
</resources>
|
||||
@@ -65,7 +65,7 @@ void host_fatal(const char *format, ...)
|
||||
vsnprintf(message, sizeof(message), format, arguments);
|
||||
va_end(arguments);
|
||||
__android_log_write(ANDROID_LOG_FATAL, "halo", message);
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Ringfall: Mjolnir VR", message, NULL);
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Mjolnir VR", message, NULL);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
host_logf(HOST_LOG_INFO, "Ringfall: Mjolnir VR starting");
|
||||
host_logf(HOST_LOG_INFO, "Mjolnir VR starting");
|
||||
host_install_signal_handlers();
|
||||
if (host_native_thread_create(game_main, NULL, MAIN_STACK_SIZE) != 0)
|
||||
host_fatal("cannot start the game thread");
|
||||
|
||||
@@ -1256,7 +1256,7 @@ with vr.log_performance: host_main.c) */
|
||||
android.applicationVM = machine;
|
||||
android.applicationActivity = activity;
|
||||
instance.next = &android;
|
||||
strcpy(instance.applicationInfo.applicationName, "Ringfall Mjolnir VR");
|
||||
strcpy(instance.applicationInfo.applicationName, "Mjolnir VR");
|
||||
instance.applicationInfo.applicationVersion = 1;
|
||||
strcpy(instance.applicationInfo.engineName, "halo-ce-universal");
|
||||
instance.applicationInfo.engineVersion = 1;
|
||||
@@ -2275,7 +2275,7 @@ void host_xr_test(void)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
window = SDL_CreateWindow("Ringfall: Mjolnir VR", 640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
|
||||
window = SDL_CreateWindow("Mjolnir VR", 640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
|
||||
context = window ? SDL_GL_CreateContext(window) : NULL;
|
||||
if (!context || !SDL_GL_MakeCurrent(window, context))
|
||||
host_fatal("VR test: no OpenGL ES context: %s", SDL_GetError());
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 70 KiB |
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Ringfall: Mjolnir VR's installer for the Steam Frame, run in its Desktop
|
||||
# Mode from the bundle the release's "Install Ringfall: Mjolnir VR" desktop
|
||||
# Mjolnir VR's installer for the Steam Frame, run in its Desktop
|
||||
# Mode from the bundle the release's "Install Mjolnir VR" desktop
|
||||
# file downloads (steam_frame_installer.desktop; the bundle: this script,
|
||||
# RingfallMjolnirVR.apk, ringfall-mjolnir-vr.sh = steam_frame_launch.sh,
|
||||
# steam_frame_xiso.py and the library pictures in grid/). Everything is
|
||||
@@ -19,28 +19,31 @@
|
||||
# Steam Frame Desktop Mode runs inside Gaming Mode's session, which ends
|
||||
# with Steam. The shortcut of the builds before the name ("Halo VR") is
|
||||
# left for the player to remove (Steam would write it back).
|
||||
# RINGFALL_UNATTENDED=1 answers every question with its default and prints
|
||||
# instead of showing dialogs (RINGFALL_IMAGE: the disc image to use), for
|
||||
# MJOLNIR_UNATTENDED=1 answers every question with its default and prints
|
||||
# instead of showing dialogs (MJOLNIR_IMAGE: the disc image to use), for
|
||||
# testing over adb; it never closes Steam.
|
||||
|
||||
BUNDLE="$(cd "$(dirname "$0")" && pwd)"
|
||||
GAME_DIR="$HOME/Documents/RingfallMjolnirVR"
|
||||
STEAM_DIR="$HOME/.local/share/Steam"
|
||||
LEPTON="$STEAM_DIR/steamapps/common/Lepton/lepton"
|
||||
TITLE="Ringfall: Mjolnir VR"
|
||||
NAME="Ringfall: Mjolnir VR"
|
||||
TITLE="Mjolnir VR"
|
||||
NAME="Mjolnir VR"
|
||||
APK_NAME="RingfallMjolnirVR.apk"
|
||||
LAUNCHER="ringfall-mjolnir-vr.sh"
|
||||
EXE="\"$GAME_DIR/$LAUNCHER\""
|
||||
# (the desktop file Steam adds the game from)
|
||||
NAME_FILE="Ringfall Mjolnir VR"
|
||||
NAME_FILE="Mjolnir VR"
|
||||
# the builds before the name: their folder and Steam shortcut
|
||||
OLD_DIR="$HOME/Documents/HaloCE"
|
||||
OLD_NAME="Halo VR"
|
||||
# the name of builds 302 to 313, which their shortcut keeps (Steam writes
|
||||
# its shortcuts back from memory: the player renames it)
|
||||
FORMER_NAME="Ringfall: Mjolnir VR"
|
||||
|
||||
# what the installer did, for looking into problems: written to /tmp while
|
||||
# it runs, then kept beside the game as install.log
|
||||
LOG="/tmp/ringfall-mjolnir-vr-install.log"
|
||||
LOG="/tmp/mjolnir-vr-install.log"
|
||||
: > "$LOG"
|
||||
log() { echo "$(date '+%F %T') $*" >> "$LOG"; }
|
||||
keep_log() { [ -d "$GAME_DIR" ] && cp -f "$LOG" "$GAME_DIR/install.log" 2>/dev/null; }
|
||||
@@ -59,7 +62,7 @@ steam_running()
|
||||
pgrep -f 'ubuntu12_32/steam( |$)' >/dev/null || pgrep -x steam >/dev/null
|
||||
}
|
||||
|
||||
unattended() { [ "${RINGFALL_UNATTENDED:-0}" = 1 ] || ! command -v zenity >/dev/null; }
|
||||
unattended() { [ "${MJOLNIR_UNATTENDED:-0}" = 1 ] || ! command -v zenity >/dev/null; }
|
||||
|
||||
info()
|
||||
{
|
||||
@@ -185,7 +188,7 @@ PYTHON
|
||||
# must run), from a desktop file beside the game; then its pictures
|
||||
add_shortcut()
|
||||
{
|
||||
local desktop="$GAME_DIR/$NAME_FILE.desktop" url file id
|
||||
local desktop="$GAME_DIR/$NAME_FILE.desktop" url
|
||||
printf '[Desktop Entry]\nType=Application\nName=%s\nExec=%s\nPath=%s\nIcon=steam\nTerminal=false\n' \
|
||||
"$NAME" "$GAME_DIR/$LAUNCHER" "$GAME_DIR" > "$desktop" && chmod 755 "$desktop" || return 1
|
||||
url="steam://addnonsteamgame/$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$desktop")"
|
||||
@@ -195,6 +198,16 @@ add_shortcut()
|
||||
steam "$url" >/dev/null 2>&1 &
|
||||
for _ in $(seq 30); do have_shortcut && break; sleep 1; done
|
||||
have_shortcut || return 1
|
||||
add_pictures
|
||||
return 0
|
||||
}
|
||||
|
||||
# the library pictures of the game's shortcut, for every Steam user who has
|
||||
# it (again on an update: the builds before the name "Mjolnir VR" had
|
||||
# Ringfall's)
|
||||
add_pictures()
|
||||
{
|
||||
local file id
|
||||
while read -r file; do
|
||||
id="$(shortcut_id "$file" "$EXE")" || continue
|
||||
log "shortcut $id in $file"
|
||||
@@ -204,7 +217,6 @@ add_shortcut()
|
||||
cp "$BUNDLE/grid/library_hero.png" "$(dirname "$file")/grid/${id}_hero.png"
|
||||
cp "$BUNDLE/grid/library_logo.png" "$(dirname "$file")/grid/${id}_logo.png"
|
||||
done < <(shortcut_files)
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- the game
|
||||
@@ -245,7 +257,7 @@ rm -rf "$GAME_DIR/update"
|
||||
# --- the game data
|
||||
|
||||
if [ ! -f "$GAME_DIR/maps/ui.map" ]; then
|
||||
image="${RINGFALL_IMAGE:-}"
|
||||
image="${MJOLNIR_IMAGE:-}"
|
||||
if [ -z "$image" ]; then
|
||||
mapfile -t images < <(find_images)
|
||||
for candidate in "${images[@]}"; do
|
||||
@@ -271,6 +283,7 @@ fi
|
||||
|
||||
if have_shortcut; then
|
||||
log "the Steam shortcut is there"
|
||||
add_pictures
|
||||
elif unattended; then
|
||||
echo "info: the Steam shortcut is missing (not added unattended)"
|
||||
elif ! steam_running; then
|
||||
@@ -286,6 +299,9 @@ fi
|
||||
if have_shortcut_to "$OLD_DIR/halo-vr.sh"; then
|
||||
info "The library still shows the old \"$OLD_NAME\" from before the game's new name. It no longer starts: remove it in Steam (its gear or Manage > Remove non-Steam game)."
|
||||
fi
|
||||
if have_shortcut_to "$FORMER_NAME"; then
|
||||
info "The library shows the game by its former name, \"$FORMER_NAME\". To call it $NAME there: its gear > Properties, then its name at the top."
|
||||
fi
|
||||
if ask "$NAME is installed.${image_note:-}\n\nSwitch to Gaming Mode now? Start $NAME from your library there (its pictures may show once Steam starts again)." yes; then
|
||||
steamos-session-select gamescope >/dev/null 2>&1 ||
|
||||
info "Switch to Gaming Mode yourself (the \"Return to Gaming Mode\" icon or the power menu)."
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[Desktop Entry]
|
||||
# Ringfall: Mjolnir VR's installer for the Steam Frame
|
||||
# Mjolnir VR's installer for the Steam Frame
|
||||
# (port/android/STEAM_FRAME.md): a release asset,
|
||||
# "Install-Ringfall-Mjolnir-VR.desktop", opened in Desktop Mode. It downloads
|
||||
# "Install-Mjolnir-VR.desktop", opened in Desktop Mode. It downloads
|
||||
# the latest release's bundle (tools/steam_frame_bundle.py, which fills in
|
||||
# the repository) and runs its installer, steam_frame_install.sh.
|
||||
# (no "$", "\" or "%" in Exec: the desktop entry format would need them
|
||||
# escaped)
|
||||
Type=Application
|
||||
Name=Install Ringfall: Mjolnir VR
|
||||
Comment=Installs Ringfall: Mjolnir VR for the Steam Frame, or updates it
|
||||
Name=Install Mjolnir VR
|
||||
Comment=Installs Mjolnir VR for the Steam Frame, or updates it
|
||||
Icon=steam
|
||||
Terminal=false
|
||||
Exec=bash -c "if curl -fL --retry 3 -o /tmp/ringfall-mjolnir-vr-steam-frame.zip https://github.com/@REPOSITORY@/releases/latest/download/ringfall-mjolnir-vr-steam-frame.zip && rm -rf /tmp/ringfall-mjolnir-vr-install && python3 -m zipfile -e /tmp/ringfall-mjolnir-vr-steam-frame.zip /tmp/ringfall-mjolnir-vr-install; then exec bash /tmp/ringfall-mjolnir-vr-install/install.sh; else zenity --error --width=460 --title='Ringfall: Mjolnir VR' --text='Ringfall: Mjolnir VR could not be downloaded. Is the Steam Frame online?'; fi"
|
||||
Exec=bash -c "if curl -fL --retry 3 -o /tmp/mjolnir-vr-steam-frame.zip https://github.com/@REPOSITORY@/releases/latest/download/mjolnir-vr-steam-frame.zip && rm -rf /tmp/mjolnir-vr-install && python3 -m zipfile -e /tmp/mjolnir-vr-steam-frame.zip /tmp/mjolnir-vr-install; then exec bash /tmp/mjolnir-vr-install/install.sh; else zenity --error --width=460 --title='Mjolnir VR' --text='Mjolnir VR could not be downloaded. Is the Steam Frame online?'; fi"
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Ringfall: Mjolnir VR on the Steam Frame: Lepton runs the APK in a container of its own,
|
||||
# Mjolnir VR on the Steam Frame: Lepton runs the APK in a container of its own,
|
||||
# and installs it anew whenever the APK is newer than the installed one
|
||||
# (port/android/STEAM_FRAME.md). The game's data: ~/Documents/RingfallMjolnirVR.
|
||||
# (a container of its own, not the shortcut's id: with that, SteamVR's
|
||||
@@ -121,7 +121,7 @@ update_check()
|
||||
log "update: build $latest found (this is $installed), fetching it"
|
||||
rm -rf "$UPDATE" && mkdir -p "$UPDATE" || return 0
|
||||
if curl -fsSL --retry 2 --max-time 900 -o "$UPDATE/bundle.zip" \
|
||||
"https://github.com/$REPOSITORY/releases/download/build-$latest/ringfall-mjolnir-vr-steam-frame.zip" &&
|
||||
"https://github.com/$REPOSITORY/releases/download/build-$latest/mjolnir-vr-steam-frame.zip" &&
|
||||
python3 -I -m zipfile -e "$UPDATE/bundle.zip" "$UPDATE/bundle" &&
|
||||
[ "$(cat "$UPDATE/bundle/build.txt" 2>/dev/null)" = "$latest" ] &&
|
||||
[ -s "$UPDATE/bundle/RingfallMjolnirVR.apk" ] && [ -s "$UPDATE/bundle/ringfall-mjolnir-vr.sh" ]
|
||||
|
||||
@@ -42,7 +42,7 @@ name that no file before it had (ignoring case).
|
||||
|
||||
A European disc in two languages (Spanish and Italian) has no maps folder
|
||||
but one for each, maps_es and maps_it: the one for the language asked for
|
||||
(RINGFALL_LANGUAGE, else the system's) is taken, else English's, else the
|
||||
(MJOLNIR_LANGUAGE, else the system's) is taken, else English's, else the
|
||||
first, and its language written to maps/language.txt, which the game takes
|
||||
as the Xbox's language while game.language is unset.
|
||||
"""
|
||||
@@ -161,7 +161,7 @@ def choose_maps(folders):
|
||||
if folder[0].lower() == "maps":
|
||||
return folder, None
|
||||
languages = [(maps_language(folder[0]), folder) for folder in folders if maps_language(folder[0])]
|
||||
wanted = (os.environ.get("RINGFALL_LANGUAGE") or os.environ.get("LC_ALL") or
|
||||
wanted = (os.environ.get("MJOLNIR_LANGUAGE") or os.environ.get("LC_ALL") or
|
||||
os.environ.get("LC_MESSAGES") or os.environ.get("LANG") or "")[:2].lower()
|
||||
for preferred in (wanted, "en"):
|
||||
for language, folder in languages:
|
||||
|
||||
+251
-6
@@ -67,6 +67,9 @@ nearest person on its team.
|
||||
#include "game/player_queues_new.h"
|
||||
#include "objects/objects.h"
|
||||
#include "physics/collisions.h"
|
||||
#include "physics/bsp3d.h"
|
||||
#include "physics/collision_bsp.h"
|
||||
#include "physics/collision_bsp_definitions.h"
|
||||
#include "ai/path.h"
|
||||
#include "units/bipeds.h"
|
||||
#include "items/items.h"
|
||||
@@ -127,6 +130,15 @@ enum
|
||||
struct bot
|
||||
{
|
||||
long player_index;
|
||||
/* the ladder it climbs (bot_ladders: its index and one, 0 none), how
|
||||
long it has, how long past its top, and when it may take one again */
|
||||
short ladder_plus_one;
|
||||
/* its path ends short of its goal (no way all the way: the search's
|
||||
nearest) */
|
||||
boolean path_partial;
|
||||
short ladder_ticks;
|
||||
short ladder_top_ticks;
|
||||
long ladder_again_time;
|
||||
long target_unit_index;
|
||||
short target_seen_ticks;
|
||||
real yaw;
|
||||
@@ -240,6 +252,11 @@ static struct
|
||||
{
|
||||
boolean map_logged;
|
||||
boolean vehicles_logged;
|
||||
/* the level's ladders (bot_ladders_find), found as the game began */
|
||||
boolean ladders_found;
|
||||
short ladder_count;
|
||||
long climbs;
|
||||
long partial_paths;
|
||||
struct bot bots[MAXIMUM_BOTS];
|
||||
long seed;
|
||||
long logged_time;
|
||||
@@ -497,7 +514,9 @@ static boolean bot_find_path(
|
||||
/* (the goal's surface: under it, as a biped would stand there) */
|
||||
{
|
||||
struct collision_result collision;
|
||||
real_vector3d down = { 0.0f, 0.0f, -3.0f };
|
||||
/* (as far down as OpenCE's bots look, bots.c: a goal over a drop
|
||||
was none) */
|
||||
real_vector3d down = { 0.0f, 0.0f, -6.0f };
|
||||
real_point3d above = goal;
|
||||
|
||||
above.z += 0.5f;
|
||||
@@ -520,8 +539,17 @@ static boolean bot_find_path(
|
||||
bot_path_debug.path_traverse_result = 0;
|
||||
bot_path_debug.path_build_result = 0;
|
||||
path_state_new(&input, &bot_path_state, &bot_path_debug);
|
||||
path_state_destination(&bot_path_state, &goal, goal_surface, BOT_GOAL_RADIUS);
|
||||
if (path_state_find(&bot_path_state) && !path_state_build_path(&bot_path_state, &bot->path) &&
|
||||
/* (to as near the goal as the search reaches, as OpenCE's bots do,
|
||||
bots.c: a long way across a large level is more surfaces than a search
|
||||
holds, and a goal up where no walkable surface leads, as up a ladder,
|
||||
is reached nearer; the bot searches again from there) */
|
||||
path_state_destination(&bot_path_state, &goal, goal_surface, 1000.0f);
|
||||
path_state_find(&bot_path_state);
|
||||
bot->path_partial = bot_path_state.node_count > 0 &&
|
||||
path_node_from_hash_table(&bot_path_state, goal_surface) == NONE;
|
||||
if (bot->path_partial)
|
||||
bots.partial_paths++;
|
||||
if (bot_path_state.node_count > 0 && !path_state_build_path(&bot_path_state, &bot->path) &&
|
||||
bot_path_debug.path_build_result == _path_build_result_obstacle_avoidance_failed &&
|
||||
bot_path_debug.smoothed_step_count > 0)
|
||||
{
|
||||
@@ -2332,6 +2360,192 @@ static void bot_walk_to(
|
||||
action->throttle.j = -dx * (real)sin(yaw) + dy * (real)cos(yaw);
|
||||
}
|
||||
|
||||
/* ---------- ladders
|
||||
|
||||
The level's ladders (Damnation's): surfaces it marks climbable, which a
|
||||
biped walking into, facing them, climbs (bipeds.c), and which the game's
|
||||
pathfinding does not take. A bot whose way is not found, or that is stuck,
|
||||
with the foot of one going up near it, climbs it: walks to the foot, turns
|
||||
to it and walks into it, and past its top a little; then it finds its way
|
||||
anew. Each ladder: the climbable surfaces of a near place and facing,
|
||||
merged. */
|
||||
|
||||
#define BOT_LADDERS 64
|
||||
/* collision_surface flags' climbable bit (collision_bsp.c) */
|
||||
#define BOT_SURFACE_CLIMBABLE 0x04
|
||||
/* how near its foot a bot takes a ladder (world units), and how long it
|
||||
tries before giving up on it (ticks), and on ladders a while */
|
||||
#define BOT_LADDER_RANGE 8.0f
|
||||
#define BOT_LADDER_TIMEOUT_TICKS 300
|
||||
#define BOT_LADDER_AGAIN_TICKS 450
|
||||
|
||||
static struct bot_ladder
|
||||
{
|
||||
/* the climbable surfaces' bounds, and the way into them (flat, unit) */
|
||||
real_point3d low, high;
|
||||
real into_x, into_y;
|
||||
} bot_ladders[BOT_LADDERS];
|
||||
|
||||
static void bot_ladders_find(void)
|
||||
{
|
||||
struct collision_bsp *bsp = global_collision_bsp_get();
|
||||
long surface_index;
|
||||
|
||||
bots.ladders_found = TRUE;
|
||||
bots.ladder_count = 0;
|
||||
if (!bsp)
|
||||
return;
|
||||
for (surface_index = 0; surface_index < bsp->surfaces.count; surface_index++)
|
||||
{
|
||||
struct collision_surface const *surface = TAG_BLOCK_GET_ELEMENT(&bsp->surfaces, surface_index,
|
||||
struct collision_surface);
|
||||
real_point3d points[MAXIMUM_VERTICES_PER_COLLISION_SURFACE];
|
||||
real_point3d low, high;
|
||||
real_plane3d plane;
|
||||
real flat;
|
||||
short count, point, ladder;
|
||||
|
||||
if (!(surface->flags & BOT_SURFACE_CLIMBABLE))
|
||||
continue;
|
||||
bsp3d_get_plane_from_designator(&bsp->bsp3d, surface->plane_designator, &plane);
|
||||
/* (upright: a ladder's face, not its rungs' tops) */
|
||||
flat = (real)sqrt(plane.n.i * plane.n.i + plane.n.j * plane.n.j);
|
||||
if (flat < 0.7f)
|
||||
continue;
|
||||
count = collision_surface_polygon(bsp, surface_index, points);
|
||||
if (count < 3)
|
||||
continue;
|
||||
low = high = points[0];
|
||||
for (point = 1; point < count; point++)
|
||||
{
|
||||
low.x = MIN(low.x, points[point].x);
|
||||
low.y = MIN(low.y, points[point].y);
|
||||
low.z = MIN(low.z, points[point].z);
|
||||
high.x = MAX(high.x, points[point].x);
|
||||
high.y = MAX(high.y, points[point].y);
|
||||
high.z = MAX(high.z, points[point].z);
|
||||
}
|
||||
/* (one of a ladder found already, near and facing alike: merged) */
|
||||
for (ladder = 0; ladder < bots.ladder_count; ladder++)
|
||||
{
|
||||
struct bot_ladder *found = &bot_ladders[ladder];
|
||||
|
||||
if (low.x < found->high.x + 0.5f && high.x > found->low.x - 0.5f &&
|
||||
low.y < found->high.y + 0.5f && high.y > found->low.y - 0.5f &&
|
||||
low.z < found->high.z + 0.5f && high.z > found->low.z - 0.5f &&
|
||||
found->into_x * -plane.n.i / flat + found->into_y * -plane.n.j / flat > 0.8f)
|
||||
{
|
||||
found->low.x = MIN(found->low.x, low.x);
|
||||
found->low.y = MIN(found->low.y, low.y);
|
||||
found->low.z = MIN(found->low.z, low.z);
|
||||
found->high.x = MAX(found->high.x, high.x);
|
||||
found->high.y = MAX(found->high.y, high.y);
|
||||
found->high.z = MAX(found->high.z, high.z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ladder < bots.ladder_count || bots.ladder_count >= BOT_LADDERS)
|
||||
continue;
|
||||
bot_ladders[bots.ladder_count].low = low;
|
||||
bot_ladders[bots.ladder_count].high = high;
|
||||
bot_ladders[bots.ladder_count].into_x = -plane.n.i / flat;
|
||||
bot_ladders[bots.ladder_count].into_y = -plane.n.j / flat;
|
||||
bots.ladder_count++;
|
||||
}
|
||||
if (bots.ladder_count)
|
||||
platform_log("bots: %d ladders on this map", bots.ladder_count);
|
||||
}
|
||||
|
||||
/* a ladder's foot, before it on the floor its bottom is at */
|
||||
static void bot_ladder_foot(
|
||||
struct bot_ladder const *ladder,
|
||||
real_point3d *foot)
|
||||
{
|
||||
foot->x = (ladder->low.x + ladder->high.x) * 0.5f - ladder->into_x * 0.4f;
|
||||
foot->y = (ladder->low.y + ladder->high.y) * 0.5f - ladder->into_y * 0.4f;
|
||||
foot->z = ladder->low.z;
|
||||
}
|
||||
|
||||
/* the nearest ladder going up from where the bot stands, its foot in reach;
|
||||
NONE */
|
||||
static short bot_ladder_near(
|
||||
real_point3d const *position)
|
||||
{
|
||||
short ladder, best = NONE;
|
||||
real best_distance = BOT_LADDER_RANGE;
|
||||
|
||||
if (!bots.ladders_found)
|
||||
bot_ladders_find();
|
||||
for (ladder = 0; ladder < bots.ladder_count; ladder++)
|
||||
{
|
||||
real_point3d foot;
|
||||
real distance;
|
||||
|
||||
bot_ladder_foot(&bot_ladders[ladder], &foot);
|
||||
/* (its foot about the floor the bot is on, its top well above) */
|
||||
if (fabsf(foot.z - position->z) > 1.0f || bot_ladders[ladder].high.z < position->z + 0.8f)
|
||||
continue;
|
||||
distance = (real)sqrt((foot.x - position->x) * (foot.x - position->x) +
|
||||
(foot.y - position->y) * (foot.y - position->y));
|
||||
if (distance < best_distance)
|
||||
{
|
||||
best_distance = distance;
|
||||
best = ladder;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/* climbing: the way it walks and faces this tick; FALSE once it is up (or
|
||||
gives up), its way to be found anew */
|
||||
static boolean bot_climb(
|
||||
struct bot *bot,
|
||||
real_point3d const *position,
|
||||
struct player_action *action,
|
||||
real *wanted_yaw)
|
||||
{
|
||||
struct bot_ladder const *ladder = &bot_ladders[bot->ladder_plus_one - 1];
|
||||
real_point3d foot;
|
||||
real dx, dy, distance;
|
||||
|
||||
bot_ladder_foot(ladder, &foot);
|
||||
dx = foot.x - position->x;
|
||||
dy = foot.y - position->y;
|
||||
distance = (real)sqrt(dx * dx + dy * dy);
|
||||
if (++bot->ladder_ticks > BOT_LADDER_TIMEOUT_TICKS)
|
||||
{
|
||||
bot->ladder_again_time = game_time_get() + BOT_LADDER_AGAIN_TICKS;
|
||||
return FALSE;
|
||||
}
|
||||
/* (up: on past the top a moment, onto the floor there) */
|
||||
if (position->z > ladder->high.z - 0.25f || bot->ladder_top_ticks > 0)
|
||||
{
|
||||
if (++bot->ladder_top_ticks > 20)
|
||||
{
|
||||
bots.climbs++;
|
||||
bot->ladder_again_time = game_time_get() + 90;
|
||||
return FALSE;
|
||||
}
|
||||
*wanted_yaw = (real)atan2(ladder->into_y, ladder->into_x);
|
||||
action->throttle.i = 1.0f;
|
||||
action->throttle.j = 0.0f;
|
||||
return TRUE;
|
||||
}
|
||||
/* (to its foot, then into it, facing it) */
|
||||
if (distance > 0.35f && position->z < ladder->low.z + 0.4f)
|
||||
{
|
||||
*wanted_yaw = (real)atan2(dy, dx);
|
||||
bot_walk_to(action, bot->yaw, position, &foot);
|
||||
return TRUE;
|
||||
}
|
||||
*wanted_yaw = (real)atan2(ladder->into_y, ladder->into_x);
|
||||
action->throttle.i = (real)cos(bot->yaw - *wanted_yaw);
|
||||
action->throttle.j = 0.0f;
|
||||
if (action->throttle.i < 0.3f)
|
||||
action->throttle.i = 0.3f;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void bot_think(
|
||||
struct bot *bot,
|
||||
struct player_datum *player,
|
||||
@@ -2751,6 +2965,35 @@ static void bot_think(
|
||||
bot->has_goal = TRUE;
|
||||
bot->goal_time = game_time_get();
|
||||
}
|
||||
/* (no way found, or stuck, by a ladder going up: up it) */
|
||||
if (!bot->ladder_plus_one &&
|
||||
(bot->path_failures >= 2 || bot->stuck_ticks >= 6 ||
|
||||
(bot->path_partial && bot->has_goal && bot->goal.z > position.z + 0.8f)) &&
|
||||
(game_time_get() >= bot->ladder_again_time || game_time_get() < bot->ladder_again_time - 1000))
|
||||
{
|
||||
short ladder = bot_ladder_near(&position);
|
||||
|
||||
if (ladder != NONE)
|
||||
{
|
||||
bot->ladder_plus_one = (short)(ladder + 1);
|
||||
bot->ladder_ticks = 0;
|
||||
bot->ladder_top_ticks = 0;
|
||||
}
|
||||
}
|
||||
if (bot->ladder_plus_one)
|
||||
{
|
||||
if (bot_climb(bot, &position, action, &wanted_yaw))
|
||||
{
|
||||
bot->stuck_ticks = 0;
|
||||
bot->sidestep_ticks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bot->ladder_plus_one = 0;
|
||||
bot->path_failures = 0;
|
||||
bot->path_time = game_time_get() - PATH_REFRESH_TICKS;
|
||||
}
|
||||
}
|
||||
if (bot->stuck_ticks > 20)
|
||||
{
|
||||
/* (stuck: somewhere else) */
|
||||
@@ -2954,6 +3197,8 @@ void bots_new_game(
|
||||
|
||||
bots.map_logged = FALSE;
|
||||
bots.vehicles_logged = FALSE;
|
||||
bots.ladders_found = FALSE;
|
||||
bots.ladder_count = 0;
|
||||
for (index = 0; index < MAXIMUM_BOTS; index++)
|
||||
bots.bots[index].player_index = NONE;
|
||||
bots.seed = 12345;
|
||||
@@ -3025,12 +3270,12 @@ void bots_tick(
|
||||
}
|
||||
}
|
||||
bots.logged_time = game_time_get();
|
||||
platform_log("bots: driving %d; paths found %ld, not %ld; weapon swaps asked %ld; grenades %ld; changes %ld; stuck %ld, long %ld, gave up %ld (no start %ld, no goal %ld, no way %ld); vehicles: in %ld, out %ld",
|
||||
(int)count, bots.paths_found, bots.paths_failed, bots.pickups, bots.grenades, bots.switches, bots.stucks, bots.long_stucks, bots.gave_up, bots.fail_start, bots.fail_goal, bots.fail_search,
|
||||
platform_log("bots: driving %d; paths found %ld, not %ld; weapon swaps asked %ld; grenades %ld; changes %ld; stuck %ld, long %ld, gave up %ld (no start %ld, no goal %ld, no way %ld); ladders climbed %ld; partial paths %ld; vehicles: in %ld, out %ld",
|
||||
(int)count, bots.paths_found, bots.paths_failed, bots.pickups, bots.grenades, bots.switches, bots.stucks, bots.long_stucks, bots.gave_up, bots.fail_start, bots.fail_goal, bots.fail_search, bots.climbs, bots.partial_paths,
|
||||
bots.vehicle_entries, bots.vehicle_exits);
|
||||
bots.vehicle_entries = bots.vehicle_exits = 0;
|
||||
bots.fail_start = bots.fail_goal = bots.fail_search = 0;
|
||||
bots.paths_found = bots.paths_failed = bots.pickups = bots.grenades = bots.switches = bots.stucks = bots.long_stucks = bots.gave_up = 0;
|
||||
bots.paths_found = bots.paths_failed = bots.pickups = bots.grenades = bots.switches = bots.stucks = bots.long_stucks = bots.gave_up = bots.climbs = bots.partial_paths = 0;
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *player = distributed_player(index);
|
||||
|
||||
@@ -2290,7 +2290,7 @@ static void hdr_read(void)
|
||||
exposure = config_real("display.hdr_exposure");
|
||||
wanted = config_boolean("display.hdr");
|
||||
#ifdef HALO_ANDROID
|
||||
/* (port, Ringfall: never in VR, whose eyes' pictures go to the headset
|
||||
/* (port, Mjolnir VR: never in VR, whose eyes' pictures go to the headset
|
||||
as they are, and whose GPU time is short already) */
|
||||
if (halo_vr_active())
|
||||
wanted = FALSE;
|
||||
|
||||
@@ -564,7 +564,7 @@ unsigned int hud_hires_png_texture(const void *png, unsigned long size, unsigned
|
||||
return png_pixels_texture(pixels, width, height, levels);
|
||||
}
|
||||
|
||||
/* (port, Ringfall: the same in two steps, the decode on any thread:
|
||||
/* (port, Mjolnir VR: the same in two steps, the decode on any thread:
|
||||
menu_files.c's menu pictures) */
|
||||
unsigned char *hud_hires_png_pixels(const void *png, unsigned long size, unsigned long *width, unsigned long *height)
|
||||
{
|
||||
|
||||
@@ -648,7 +648,7 @@ static const struct config_setting config_settings[] =
|
||||
"as CUSTOM SINGLEPLAYER and CUSTOM MULTIPLAYER. Their tags are checked\n"
|
||||
"as the game's own maps' are before they run; false refuses them\n"
|
||||
"(docs/custom_edition_caches.md)." },
|
||||
/* (the desktop's local bots, bots.c; Ringfall's host bots have their own
|
||||
/* (the desktop's local bots, bots.c; Mjolnir VR's host bots have their own
|
||||
bots.count and bots.skill, Android's, below: host_bots.c) */
|
||||
{ "bots.count", _config_integer, "0", "HALO_BOTS", _environment_value, _platform_desktop,
|
||||
"Computer players (bots) in a local (split screen) Slayer game: 0 to 15\n"
|
||||
|
||||
@@ -47,7 +47,7 @@ update.h's: posix_update.c on Linux, win32_update.c on Windows.
|
||||
#endif
|
||||
|
||||
/* the VR port's own releases (the original's must not replace it) */
|
||||
#define UPDATE_REPOSITORY "Cesarus85/ringfall-mjolnir-vr"
|
||||
#define UPDATE_REPOSITORY "Cesarus85/mjolnir-vr"
|
||||
#ifdef _WIN32
|
||||
#define UPDATE_PLATFORM "windows"
|
||||
#define PATH_SEPARATOR "\\"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Ringfall: Mjolnir VR's pictures, drawn from nothing but shapes and the
|
||||
Mjolnir VR's pictures, drawn from nothing but shapes and the
|
||||
port's Overpass font (port/assets/fonts, SIL Open Font License):
|
||||
|
||||
python3 tools/ringfall_art.py
|
||||
python3 tools/mjolnir_art.py
|
||||
|
||||
writes
|
||||
- port/android/art/android-icon.png, the launcher icon's artwork (then run
|
||||
@@ -99,11 +99,11 @@ def italic_text(text, height, fill):
|
||||
|
||||
|
||||
def logo(width):
|
||||
""""RINGFALL" over "MJOLNIR VR", as wide as asked, transparent"""
|
||||
title = italic_text("RINGFALL", 200, LIGHT)
|
||||
""""MJOLNIR" over "VR", as wide as asked, transparent"""
|
||||
title = italic_text("MJOLNIR", 200, LIGHT)
|
||||
title = title.resize((width, int(title.height * width / title.width)), Image.LANCZOS)
|
||||
subtitle = italic_text("MJOLNIR VR", 200, ACCENT)
|
||||
sub_width = int(width * 0.62)
|
||||
subtitle = italic_text("VR", 200, ACCENT)
|
||||
sub_width = int(width * 0.30)
|
||||
subtitle = subtitle.resize((sub_width, int(subtitle.height * sub_width / subtitle.width)), Image.LANCZOS)
|
||||
gap = int(title.height * 0.16)
|
||||
image = Image.new("RGBA", (width, title.height + gap + subtitle.height), (0, 0, 0, 0))
|
||||
@@ -5,14 +5,14 @@ The Steam Frame's installer as release assets (port/android/STEAM_FRAME.md):
|
||||
python3 tools/steam_frame_bundle.py <apk> <output folder> <owner/repository>
|
||||
|
||||
writes into the output folder
|
||||
- ringfall-mjolnir-vr-steam-frame.zip: the installer (port/android/steam_frame_install.sh,
|
||||
- mjolnir-vr-steam-frame.zip: the installer (port/android/steam_frame_install.sh,
|
||||
as install.sh), the APK (RingfallMjolnirVR.apk), the launcher
|
||||
(steam_frame_launch.sh, as ringfall-mjolnir-vr.sh, which updates the game
|
||||
from the repository named), the build's number (build.txt: the workflow's
|
||||
run number, HALO_BUILD_NUMBER or GITHUB_RUN_NUMBER; 0, which never
|
||||
updates, without one), the disc image reader (steam_frame_xiso.py) and
|
||||
the library pictures (grid/);
|
||||
- Install-Ringfall-Mjolnir-VR.desktop (port/android/steam_frame_installer.desktop): what
|
||||
- Install-Mjolnir-VR.desktop (port/android/steam_frame_installer.desktop): what
|
||||
the player downloads and opens in Desktop Mode; it fetches the latest
|
||||
release's zip from the repository named.
|
||||
"""
|
||||
@@ -44,7 +44,7 @@ def main():
|
||||
build = os.environ.get("HALO_BUILD_NUMBER") or os.environ.get("GITHUB_RUN_NUMBER") or "0"
|
||||
if not build.isdigit():
|
||||
build = "0"
|
||||
bundle = output / "ringfall-mjolnir-vr-steam-frame.zip"
|
||||
bundle = output / "mjolnir-vr-steam-frame.zip"
|
||||
with zipfile.ZipFile(bundle, "w", zipfile.ZIP_DEFLATED) as archive:
|
||||
for path, name, mode in files:
|
||||
info = zipfile.ZipInfo.from_file(path, name)
|
||||
@@ -60,7 +60,7 @@ def main():
|
||||
print(f"{bundle.relative_to(Path.cwd()) if bundle.is_relative_to(Path.cwd()) else bundle}")
|
||||
|
||||
desktop = (ANDROID / "steam_frame_installer.desktop").read_text().replace("@REPOSITORY@", repository)
|
||||
installer = output / "Install-Ringfall-Mjolnir-VR.desktop"
|
||||
installer = output / "Install-Mjolnir-VR.desktop"
|
||||
installer.write_text(desktop)
|
||||
installer.chmod(0o755)
|
||||
print(installer)
|
||||
|
||||
Reference in new issue
Block a user