mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 20:00:25 +02:00
With input.touch_aim_assist false, walking with the touch stick still let the view follow a target: the touch controls now count as aiming without the assist until a stick aims again (halo_linux_touch_aiming), as the mouse does. The stick's diagonal is full on both axes, as a controller's square stick gives. The gyroscope reaches the game apart from the swipe (nativeGyro), so the profile's invert look turns only the swipe. The overlay shows once the game has been in play for 200 ms, so it does not flash at a level's first frame. The options dialogs are counted, so a dialog opened from another keeps the controls released, and the layout is saved when the last one closes (a slider moved without a finger was not). A floating stick rests where the thumb lands, even near an edge, and not in the bottom gesture zone. A finger's width is at most 34 logical units, so the D-pad's buttons stay apart on a small dense display. A held button that takes over the swipe from another finger starts from where it is, not from where it went down. The system bars coming and going no longer release every finger. Hide stops the gyroscope. The libtiff comments are corrected: the screenshot_count and screenshot_size globals also reached tiff_export, but read back-buffer memory the port never draws into, so they never had the picture. Stale comments are fixed and the source exclusion test covers folders.
152 lines
9.4 KiB
Java
152 lines
9.4 KiB
Java
package com.halo.decomp;
|
|
|
|
/** Standalone geometry/persistence regression checks; no phone required. */
|
|
public final class TouchLayoutTest {
|
|
private static void check(boolean condition, String message) {
|
|
if (!condition) throw new AssertionError(message);
|
|
}
|
|
public static void main(String[] args) {
|
|
TouchLayout layout = new TouchLayout();
|
|
check(layout.size() == 18, "All 17 buttons and the movement stick must be editable");
|
|
check(layout.x(TouchLayout.LEFT) == 115 && layout.y(TouchLayout.FIRE_LEFT) == 239,
|
|
"Existing layouts must start with the original controls");
|
|
layout.move(TouchLayout.LEFT, -500, -500);
|
|
check(layout.x(TouchLayout.LEFT) >= 64 && layout.y(TouchLayout.LEFT) == 64,
|
|
"Dragging must keep the full stick inside the screen without a toolbar exclusion");
|
|
layout.move(TouchLayout.FIRE_LEFT, 10000, 10000);
|
|
check(layout.x(TouchLayout.FIRE_LEFT)+39 <= 960 && layout.y(TouchLayout.FIRE_LEFT)+39 <= 540,
|
|
"Dragging beyond the bottom/right edge must keep the stick reachable");
|
|
layout.move(0, 230, 320);
|
|
layout.move(5, 440, 390);
|
|
TouchLayout reopened = new TouchLayout();
|
|
for (int i = 0; i < layout.size(); i++) reopened.restore(i, layout.x(i), layout.y(i));
|
|
for (int i = 0; i < layout.size(); i++) {
|
|
check(layout.x(i) == reopened.x(i) && layout.y(i) == reopened.y(i),
|
|
"Saved coordinates must round-trip for control "+i);
|
|
}
|
|
float before = reopened.x(0);
|
|
reopened.restore(0, Float.NaN, 200);
|
|
reopened.restore(0, Float.POSITIVE_INFINITY, 200);
|
|
reopened.restore(0, -20, 200);
|
|
check(reopened.x(0) == before, "Invalid saved coordinates must not corrupt the layout");
|
|
reopened.move(0, Float.NaN, 200);
|
|
check(reopened.x(0) == before, "Invalid drag coordinates must not corrupt the layout");
|
|
// A saved top-row Pause button is valid even before it has been moved.
|
|
check(reopened.y(10) == 36, "Reopening must preserve unmoved top-row controls");
|
|
reopened.bounds(1200, 540);
|
|
reopened.move(0, 9999, 1);
|
|
check(reopened.x(0) == 1164 && reopened.y(0) == 36,
|
|
"Controls must reach the full widescreen edge and top edge");
|
|
TouchLayout wide = new TouchLayout();
|
|
wide.restore(0, reopened.savedX(0), reopened.savedY(0));
|
|
wide.bounds(1200, 540);
|
|
check(wide.x(0) == reopened.x(0), "Widescreen positions must survive reload");
|
|
// Duplicates retain their action and are independently placed and hidden.
|
|
int copy = wide.duplicate(4);
|
|
check(copy == 18 && wide.type(copy) == 4, "Fire copies must retain the fire action");
|
|
wide.setShown(4, false);
|
|
wide.move(copy, 300, 100);
|
|
check(!wide.shown(4) && wide.shown(copy), "Hiding an original must not hide its copy");
|
|
check(wide.duplicate(TouchLayout.LEFT) == -1, "There can only be one movement stick");
|
|
String exported = wide.exportConfiguration(2.25f);
|
|
TouchLayout.Configuration imported = TouchLayout.importConfiguration(exported);
|
|
imported.layout.bounds(1200, 540);
|
|
check(imported.sensitivity == 2.25f && imported.layout.size() == 19,
|
|
"Configuration must preserve sensitivity and duplicates");
|
|
for (int i = 0; i < wide.size(); i++) {
|
|
check(imported.layout.type(i) == wide.type(i) && imported.layout.shown(i) == wide.shown(i)
|
|
&& Math.abs(imported.layout.x(i)-wide.x(i)) < 0.001f
|
|
&& Math.abs(imported.layout.y(i)-wide.y(i)) < 0.001f,
|
|
"Export/import must preserve each control on widescreen");
|
|
}
|
|
check(wide.add(4) == 4 && wide.shown(4), "Add must restore a hidden button first");
|
|
wide.setShown(TouchLayout.LEFT, false);
|
|
check(wide.add(TouchLayout.LEFT) == TouchLayout.LEFT, "Hidden move stick must be restorable");
|
|
reject(exported.replace("version=3", "version=9"));
|
|
reject(exported.replace("control.18.type=4", "control.18.type=16"));
|
|
reject(exported.replace("control.18.x=240.0", "control.18.x=NaN"));
|
|
reject(exported.replace("count=19", "count=10000"));
|
|
reject(exported.replace("sensitivity=2.25", "sensitivity=Infinity"));
|
|
reject(exported.replace("control.0.visible=false", "control.0.visible=maybe")
|
|
.replace("control.0.visible=true", "control.0.visible=maybe"));
|
|
reject("not a layout");
|
|
wide.setSize(copy, 1.6f);
|
|
wide.rumbleEnabled = false; wide.gyroscopeEnabled = true;
|
|
TouchLayout.Configuration sized = TouchLayout.importConfiguration(wide.exportConfiguration(2.25f));
|
|
check(sized.layout.sizeScale(copy) == 1.6f && !sized.layout.rumbleEnabled && sized.layout.gyroscopeEnabled,
|
|
"Sizes, rumble and gyro settings must survive export/import");
|
|
int sizedCopy = wide.duplicate(copy);
|
|
check(wide.sizeScale(sizedCopy) == 1.6f, "Duplicates inherit their source size");
|
|
wide.move(copy, 99999, 99999);
|
|
wide.setSize(copy, 2f);
|
|
check(wide.x(copy)+wide.radius(copy) <= 1200 && wide.y(copy)+wide.radius(copy) <= 540,
|
|
"Growing a button at the edge must keep it reachable");
|
|
String sizedText = sized.layout.exportConfiguration(2.25f);
|
|
reject(sizedText.replace("control.18.size=1.6", "control.18.size=NaN"));
|
|
reject(sizedText.replace("control.18.size=1.6", "control.18.size=3.0"));
|
|
reject(sizedText.replace("rumble=false", "rumble=invalid"));
|
|
reject(sizedText.replace("gyroscope=true", "gyroscope=invalid"));
|
|
String legacy = sizedText.replace("version=3", "version=1").replaceAll("(?m)^.*\\.size=.*\\R", "")
|
|
.replaceAll("(?m)^(rumble|gyroscope|opacity|floating-stick)=.*\\R", "");
|
|
TouchLayout.Configuration old = TouchLayout.importConfiguration(legacy);
|
|
check(old.layout.sizeScale(copy) == 1 && old.layout.rumbleEnabled && !old.layout.gyroscopeEnabled,
|
|
"Legacy layouts must load with default sizes and gyro disabled");
|
|
// Opacity (version 3) round-trips; version 2 files load fully opaque.
|
|
sized.layout.setOpacity(0.45f);
|
|
String translucent = sized.layout.exportConfiguration(2.25f);
|
|
check(TouchLayout.importConfiguration(translucent).layout.opacity == 0.45f, "Opacity must survive export/import");
|
|
reject(translucent.replace("opacity=0.45", "opacity=0.05"));
|
|
reject(translucent.replace("opacity=0.45", "opacity=NaN"));
|
|
sized.layout.floatingStick = true;
|
|
check(TouchLayout.importConfiguration(sized.layout.exportConfiguration(2.25f)).layout.floatingStick,
|
|
"The floating stick must survive export/import");
|
|
sized.layout.floatingStick = false;
|
|
reject(translucent.replace("floating-stick=false", "floating-stick=maybe"));
|
|
String version2 = translucent.replace("version=3", "version=2").replaceAll("(?m)^(opacity|floating-stick)=.*\\R", "");
|
|
check(TouchLayout.importConfiguration(version2).layout.opacity == 1f
|
|
&& !TouchLayout.importConfiguration(version2).layout.floatingStick,
|
|
"Version 2 layouts load fully opaque, with the stick in its place");
|
|
reject(translucent.replaceAll("(?m)^opacity=.*\\R", ""));
|
|
try {
|
|
sized.layout.setOpacity(1.5f);
|
|
throw new AssertionError("Out-of-range opacity was accepted");
|
|
} catch (IllegalArgumentException expected) {
|
|
// refused
|
|
}
|
|
// A finger-sized minimum radius enlarges small buttons and keeps them on the display.
|
|
TouchLayout fingers = new TouchLayout();
|
|
fingers.setMinimumRadius(30);
|
|
check(fingers.radius(12) == 30 && fingers.radius(TouchLayout.LEFT) == 64,
|
|
"The minimum radius enlarges small controls only");
|
|
fingers.move(12, 0, 0);
|
|
check(fingers.x(12) == 30 && fingers.y(12) == 30, "Enlarged controls stay inside the display");
|
|
check(fingers.savedX(12) == 30, "The minimum radius does not change saved places");
|
|
fingers.setMinimumRadius(80);
|
|
check(fingers.radius(12) == TouchLayout.MAX_MINIMUM_RADIUS, "The minimum radius is capped");
|
|
TouchLayout dense = new TouchLayout();
|
|
dense.setMinimumRadius(80);
|
|
for (int a = 12; a <= 15; a++) {
|
|
for (int b = a + 1; b <= 15; b++) {
|
|
check(Math.hypot(dense.x(a) - dense.x(b), dense.y(a) - dense.y(b)) >= dense.radius(a) + dense.radius(b),
|
|
"At the capped minimum radius the D-pad's buttons stay apart");
|
|
}
|
|
}
|
|
fingers.setMinimumRadius(Float.NaN);
|
|
check(fingers.radius(12) == 25, "An invalid minimum radius is none");
|
|
wide.resetDefaults();
|
|
check(wide.size() == 18 && wide.shown(4) && wide.x(4) == 915*1200f/960,
|
|
"Reset restores defaults on the current display and removes all copies");
|
|
check(wide.sizeScale(4) == 1f && !wide.rumbleEnabled && wide.gyroscopeEnabled,
|
|
"Button reset must restore sizes without changing General settings");
|
|
while (wide.duplicate(4) >= 0) {}
|
|
check(wide.size() == TouchLayout.MAX_CONTROLS, "Duplicate count must be bounded");
|
|
System.out.println("Touch layout, visibility, duplication and import/export checks passed");
|
|
}
|
|
|
|
private static void reject(String text) {
|
|
try { TouchLayout.importConfiguration(text); }
|
|
catch (IllegalArgumentException expected) { return; }
|
|
throw new AssertionError("Invalid configuration was accepted");
|
|
}
|
|
}
|