mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-26 19:47:44 +00:00
Add resizable trainer windows and optional fakie turn hold
This commit is contained in:
1 parent
a2366128f4
commit
9fa682a0c4
13 files changed
+242
-15
No files matched your search
@@ -0,0 +1,56 @@
|
||||
param([switch]$StageOnly, [string]$DependencyTargetDirectory = (Join-Path $PSScriptRoot 'target'))
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$privateDirectory = Join-Path $PSScriptRoot '.local/fakie-trainer'
|
||||
$buildDirectory = Join-Path $privateDirectory 'build'
|
||||
$artifact = Join-Path $buildDirectory 'skate3rust-fakie-trainer.exe'
|
||||
$destination = Join-Path $privateDirectory 'skate3-fakie-trainer.exe'
|
||||
Push-Location $PSScriptRoot
|
||||
$previousFlags = $env:CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS
|
||||
try {
|
||||
New-Item -ItemType Directory -Force -Path $buildDirectory | Out-Null
|
||||
if (-not $StageOnly) {
|
||||
$env:CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS = '-C target-feature=+crt-static'
|
||||
# Dependencies may use a shared cache. The linker writes this task's
|
||||
# artifact directly, never staging the cache's generic skate3rust.exe.
|
||||
# Shared target fingerprints can refer to another worktree's local crates.
|
||||
# Refresh only our local source timestamps; never clean the shared cache.
|
||||
foreach ($crate in @('skate-core', 'skate-data', 'skate-mods')) {
|
||||
(Get-Item -LiteralPath (Join-Path $PSScriptRoot "crates/$crate/src/lib.rs")).LastWriteTime = Get-Date
|
||||
}
|
||||
& cargo rustc --release --locked --target x86_64-pc-windows-msvc `
|
||||
--target-dir $DependencyTargetDirectory -p skate-game --no-default-features `
|
||||
--bin skate3rust -- -C extra-filename=-fakie-trainer `
|
||||
-o (Join-Path $buildDirectory 'skate3rust.exe')
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Lua SDK build failed.' }
|
||||
}
|
||||
$bytes = [System.IO.File]::ReadAllBytes($artifact)
|
||||
$text = [System.Text.Encoding]::ASCII.GetString($bytes)
|
||||
foreach ($required in @('--start-paused', 'Scroll wheel: more settings', 'Drag // to resize', 'hold_fakie', 'Native trainer controls are already owned by another mod')) {
|
||||
if (-not $text.Contains($required)) { throw "Wrong fakie-trainer artifact: missing $required" }
|
||||
}
|
||||
Copy-Item -LiteralPath $artifact -Destination $destination -Force
|
||||
# Only original SDK example sources are staged. Never copy extracted assets.
|
||||
$modDestination = Join-Path $privateDirectory 'mods/native-trainer'
|
||||
New-Item -ItemType Directory -Force -Path $modDestination | Out-Null
|
||||
foreach ($name in @('mod.json','main.lua','help.txt','README.md')) {
|
||||
$source = Join-Path $PSScriptRoot "mods/native-trainer/$name"
|
||||
$target = Join-Path $modDestination $name
|
||||
if (-not (Test-Path -LiteralPath $target)) { Copy-Item -LiteralPath $source -Destination $target }
|
||||
elseif ((Get-FileHash -LiteralPath $source).Hash -ne (Get-FileHash -LiteralPath $target).Hash) {
|
||||
Write-Warning "Preserving existing mod file: $target. Copy the updated source there when ready."
|
||||
}
|
||||
}
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'mods/README.md') -Destination (Join-Path $privateDirectory 'mods/README.md') -Force
|
||||
$hash = (Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash
|
||||
if ($hash -ne (Get-FileHash -LiteralPath $artifact -Algorithm SHA256).Hash) {
|
||||
throw 'Staged executable hash mismatch.'
|
||||
}
|
||||
@{ executable = $destination; sha256 = $hash; source = $PSScriptRoot;
|
||||
revision = (& git rev-parse HEAD); staticChecks = 'CLI, Mods UI, Lua quota diagnostics' } |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $privateDirectory 'build-manifest.json') -Encoding UTF8
|
||||
Write-Host "Ready for manual launch: $destination"
|
||||
Write-Host "SHA256: $hash"
|
||||
} finally {
|
||||
$env:CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS = $previousFlags
|
||||
Pop-Location
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@echo off
|
||||
setlocal
|
||||
cd /d "%~dp0"
|
||||
if not defined SKATE3_ASSETS (
|
||||
if exist "%~dp0.local\fakie-trainer\assets.path" set /p "SKATE3_ASSETS="<"%~dp0.local\fakie-trainer\assets.path"
|
||||
)
|
||||
if not defined SKATE3_ASSETS (
|
||||
echo Set SKATE3_ASSETS to your prepared assets directory.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "%~dp0.local\fakie-trainer\skate3-fakie-trainer.exe" (
|
||||
echo Build missing. Run Build-FakieTrainer.ps1 first.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
set "SKATE3_MOD_SETTINGS=%~dp0.local\fakie-trainer\settings"
|
||||
if exist "%~dp0.local\fakie-trainer\overlay.path" set /p "SKATE3_SESSION_MARKER_OVERLAY="<"%~dp0.local\fakie-trainer\overlay.path"
|
||||
echo Open Mods and ENABLE Native Trainer. Escape shows draggable settings windows. Drag the title to move, drag // to resize, or scroll for more settings.
|
||||
echo Enable Hold fakie stance in the trainer settings to stop automatic fakie correction.
|
||||
echo Mods folder: %~dp0.local\fakie-trainer\mods
|
||||
echo Trainer log: %~dp0.local\fakie-trainer\session.log
|
||||
"%~dp0.local\fakie-trainer\skate3-fakie-trainer.exe" --assets "%SKATE3_ASSETS%" --start-paused %* > "%~dp0.local\fakie-trainer\session.log" 2>&1
|
||||
if errorlevel 1 pause
|
||||
@@ -177,6 +177,7 @@ pub struct MotionHost {
|
||||
pub applying_body_tilt: bool,
|
||||
pub crouching_physical: Option<skate_core::animation::crouching::Physical>,
|
||||
pub body_tilt_physical: Option<skate_core::animation::body_tilt::Physical>,
|
||||
pub hold_fakie: bool,
|
||||
pub fakie_physical: Option<skate_core::animation::riding_fakie::Physical>,
|
||||
/// PhysOutAnimation158/157, used directly by IsRidingGoofy82BA5AA8.
|
||||
pub physical_stance: Option<(bool, bool)>,
|
||||
@@ -298,6 +299,7 @@ impl MotionHost {
|
||||
applying_body_tilt: false,
|
||||
crouching_physical: None,
|
||||
body_tilt_physical: None,
|
||||
hold_fakie: false,
|
||||
fakie_physical: None,
|
||||
physical_stance: None,
|
||||
flags: Default::default(),
|
||||
|
||||
@@ -553,7 +553,9 @@ impl MotionHost {
|
||||
.physical
|
||||
.ok_or("SetTurning requires actual PhysOutAnimation and stance outputs")?;
|
||||
let intents = set_turning::Intents {
|
||||
fakie_turn: self.animation.motion_intent("FakieTurn"),
|
||||
// Trainer suppresses only the automatic fakie correction.
|
||||
// Slide branches and ordinary steering retain their native inputs.
|
||||
fakie_turn: if self.hold_fakie { None } else { self.animation.motion_intent("FakieTurn") },
|
||||
mode_0_slide: self.animation.motion_intent("LeftSlide"),
|
||||
mode_1_slide: self.animation.motion_intent("RightSlide"),
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::collections::BTreeMap;
|
||||
#[derive(Default)]
|
||||
struct Layout {
|
||||
position: Vec2,
|
||||
size: Vec2,
|
||||
collapsed: bool,
|
||||
selected: usize,
|
||||
status: String,
|
||||
@@ -18,11 +19,12 @@ pub(crate) struct EnabledPanel {
|
||||
active: Option<String>,
|
||||
layouts: BTreeMap<String, Layout>,
|
||||
drag: Option<(String, Vec2)>,
|
||||
resize: Option<(String, Vec2)>,
|
||||
signature: Vec<(String, Vec<String>)>,
|
||||
}
|
||||
impl EnabledPanel {
|
||||
pub fn dragging(&self) -> bool {
|
||||
self.drag.is_some()
|
||||
self.drag.is_some() || self.resize.is_some()
|
||||
}
|
||||
}
|
||||
#[derive(Component)]
|
||||
@@ -34,6 +36,8 @@ struct Viewport(String);
|
||||
#[derive(Component)]
|
||||
struct Header(String);
|
||||
#[derive(Component)]
|
||||
struct ResizeGrip(String);
|
||||
#[derive(Component)]
|
||||
struct Row(String, usize);
|
||||
#[derive(Component)]
|
||||
struct Label(String, usize);
|
||||
@@ -113,6 +117,7 @@ fn sync(
|
||||
}
|
||||
panel.signature = signature.clone();
|
||||
panel.drag = None;
|
||||
panel.resize = None;
|
||||
if !signature
|
||||
.iter()
|
||||
.any(|(id, _)| panel.active.as_ref() == Some(id))
|
||||
@@ -122,6 +127,7 @@ fn sync(
|
||||
}
|
||||
for (index, (id, settings)) in signature.iter().enumerate() {
|
||||
panel.layouts.entry(id.clone()).or_insert_with(|| Layout {
|
||||
size: Vec2::new(360., 760.),
|
||||
position: Vec2::new(16. + index as f32 * 22., 64. + index as f32 * 34.),
|
||||
..default()
|
||||
});
|
||||
@@ -133,7 +139,8 @@ fn sync(
|
||||
Node {
|
||||
display: Display::None,
|
||||
position_type: PositionType::Absolute,
|
||||
width: px(320.),
|
||||
width: px(360.),
|
||||
height: px(760.),
|
||||
max_width: percent(95),
|
||||
padding: UiRect::all(px(10.)),
|
||||
row_gap: px(8.),
|
||||
@@ -199,6 +206,8 @@ fn sync(
|
||||
.spawn((
|
||||
Body(id.clone()),
|
||||
Node {
|
||||
flex_grow: 1.,
|
||||
min_height: px(0.),
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: px(6.),
|
||||
..default()
|
||||
@@ -209,8 +218,9 @@ fn sync(
|
||||
Viewport(id.clone()),
|
||||
ScrollPosition::default(),
|
||||
Node {
|
||||
height: px(280.),
|
||||
max_height: Val::Vh(45.),
|
||||
flex_grow: 1.,
|
||||
flex_basis: px(0.),
|
||||
min_height: px(0.),
|
||||
overflow: Overflow::scroll_y(),
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: px(6.),
|
||||
@@ -279,6 +289,30 @@ fn sync(
|
||||
},
|
||||
TextColor(Color::srgb(0.65, 0.85, 0.85)),
|
||||
));
|
||||
body.spawn((
|
||||
Button,
|
||||
ResizeGrip(id.clone()),
|
||||
Node {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
width: px(28.),
|
||||
height: px(22.),
|
||||
flex_shrink: 0.,
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::srgb(0.12, 0.20, 0.26)),
|
||||
))
|
||||
.with_children(|grip| {
|
||||
grip.spawn((
|
||||
Text::new("//"),
|
||||
TextFont {
|
||||
font_size: 18.,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::WHITE),
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -293,12 +327,14 @@ fn input(
|
||||
mouse: Res<ButtonInput<MouseButton>>,
|
||||
nav: Res<crate::customiser::Navigation>,
|
||||
window: Single<&Window, With<bevy::window::PrimaryWindow>>,
|
||||
grips: Query<(&Interaction, &ResizeGrip), Changed<Interaction>>,
|
||||
headers: Query<(&Interaction, &Header), Changed<Interaction>>,
|
||||
buttons: Query<(&Interaction, &Action), Changed<Interaction>>,
|
||||
) {
|
||||
if !pause.open || menu.open || custom.open {
|
||||
panel.focused = false;
|
||||
panel.drag = None;
|
||||
panel.resize = None;
|
||||
return;
|
||||
}
|
||||
let ids: Vec<_> = mods
|
||||
@@ -311,6 +347,7 @@ fn input(
|
||||
if ids.is_empty() {
|
||||
panel.focused = false;
|
||||
panel.drag = None;
|
||||
panel.resize = None;
|
||||
return;
|
||||
}
|
||||
if keys.just_pressed(KeyCode::Tab) || nav.pressed & 0x4000 != 0 {
|
||||
@@ -331,25 +368,53 @@ fn input(
|
||||
}
|
||||
if !mouse.pressed(MouseButton::Left) {
|
||||
panel.drag = None;
|
||||
panel.resize = None;
|
||||
}
|
||||
for (interaction, header) in &headers {
|
||||
if *interaction == Interaction::Pressed && mouse.pressed(MouseButton::Left) {
|
||||
if let (Some(cursor), Some(layout)) =
|
||||
(window.cursor_position(), panel.layouts.get(&header.0))
|
||||
{
|
||||
panel.drag = Some((header.0.clone(), cursor - layout.position));
|
||||
panel.drag = Some((header.0.clone(), cursor - fitted_position(layout, &window)));
|
||||
panel.active = Some(header.0.clone());
|
||||
panel.focused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (interaction, grip) in &grips {
|
||||
if *interaction == Interaction::Pressed && mouse.pressed(MouseButton::Left) {
|
||||
if let (Some(cursor), Some(layout)) =
|
||||
(window.cursor_position(), panel.layouts.get(&grip.0))
|
||||
{
|
||||
let size = fit_size(layout.size, &window);
|
||||
let position = fitted_position(layout, &window);
|
||||
panel.layouts.get_mut(&grip.0).unwrap().position = position;
|
||||
panel.resize = Some((grip.0.clone(), size - cursor));
|
||||
panel.drag = None;
|
||||
panel.active = Some(grip.0.clone());
|
||||
panel.focused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some((id, offset)), Some(cursor)) = (panel.resize.clone(), window.cursor_position()) {
|
||||
if let Some(layout) = panel.layouts.get_mut(&id) {
|
||||
layout.size = fit_size(cursor + offset, &window)
|
||||
.min(Vec2::new(window.width(), window.height()) - layout.position);
|
||||
}
|
||||
}
|
||||
if let (Some((id, offset)), Some(cursor)) = (panel.drag.clone(), window.cursor_position()) {
|
||||
if let Some(layout) = panel.layouts.get_mut(&id) {
|
||||
layout.position = (cursor - offset).clamp(
|
||||
Vec2::ZERO,
|
||||
Vec2::new(
|
||||
(window.width() - 320.).max(0.),
|
||||
(window.height() - 48.).max(0.),
|
||||
(window.width() - fit_size(layout.size, &window).x).max(0.),
|
||||
(window.height()
|
||||
- if layout.collapsed {
|
||||
48.
|
||||
} else {
|
||||
fit_size(layout.size, &window).y
|
||||
})
|
||||
.max(0.),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -479,8 +544,21 @@ fn draw(
|
||||
} else {
|
||||
Display::None
|
||||
};
|
||||
node.left = px(layout.position.x.clamp(0., (window.width() - 320.).max(0.)));
|
||||
node.top = px(layout.position.y.clamp(0., (window.height() - 48.).max(0.)));
|
||||
let size = fit_size(layout.size, &window);
|
||||
node.width = px(size.x);
|
||||
node.height = if layout.collapsed {
|
||||
Val::Auto
|
||||
} else {
|
||||
px(size.y)
|
||||
};
|
||||
node.left = px(layout
|
||||
.position
|
||||
.x
|
||||
.clamp(0., (window.width() - size.x).max(0.)));
|
||||
node.top = px(layout.position.y.clamp(
|
||||
0.,
|
||||
(window.height() - if layout.collapsed { 48. } else { size.y }).max(0.),
|
||||
));
|
||||
z.0 = if panel.active.as_ref() == Some(&root.0) {
|
||||
14
|
||||
} else {
|
||||
@@ -526,7 +604,7 @@ fn draw(
|
||||
if let Some(h) = hint {
|
||||
if let Some(l) = panel.layouts.get(&h.0) {
|
||||
**t = format!(
|
||||
"Drag title to move | +/- change values\nScroll wheel: more settings | Tab / X: focus\n{}",
|
||||
"Drag title to move | Drag // to resize\nScroll wheel: more settings | Tab / X: focus\n{}",
|
||||
l.status
|
||||
);
|
||||
}
|
||||
@@ -583,7 +661,7 @@ fn scroll(
|
||||
MouseScrollUnit::Pixel => event.y,
|
||||
})
|
||||
.sum();
|
||||
if !pause.open || menu.open || custom.open || panel.drag.is_some() {
|
||||
if !pause.open || menu.open || custom.open || panel.dragging() {
|
||||
return;
|
||||
}
|
||||
let hovered = window.physical_cursor_position().and_then(|cursor| {
|
||||
@@ -629,3 +707,22 @@ fn scroll(
|
||||
position.0.y = position.0.y.clamp(0., max);
|
||||
}
|
||||
}
|
||||
|
||||
fn fit_size(size: Vec2, window: &Window) -> Vec2 {
|
||||
let maximum = Vec2::new(
|
||||
(window.width() - 16.).max(1.),
|
||||
(window.height() - 32.).max(1.),
|
||||
);
|
||||
size.clamp(Vec2::new(320., 260.).min(maximum), maximum)
|
||||
}
|
||||
|
||||
fn fitted_position(layout: &Layout, window: &Window) -> Vec2 {
|
||||
let mut size = fit_size(layout.size, window);
|
||||
if layout.collapsed {
|
||||
size.y = 48.;
|
||||
}
|
||||
layout.position.clamp(
|
||||
Vec2::ZERO,
|
||||
(Vec2::new(window.width(), window.height()) - size).max(Vec2::ZERO),
|
||||
)
|
||||
}
|
||||
@@ -247,6 +247,7 @@ pub(crate) fn advance(
|
||||
surface_up_y: physics.riding.ground.wheel_normal.y,
|
||||
},
|
||||
);
|
||||
skater.animation.motion.hold_fakie = physics.trainer.hold_fakie;
|
||||
let observations = AnimationPhysical {
|
||||
conditions,
|
||||
feedback,
|
||||
|
||||
@@ -148,6 +148,7 @@ pub struct TrainerTuning {
|
||||
pub grip: f32,
|
||||
pub turn_power: f32,
|
||||
pub manual_drag: f32,
|
||||
pub hold_fakie: bool,
|
||||
}
|
||||
impl Default for TrainerTuning {
|
||||
fn default() -> Self {
|
||||
@@ -163,6 +164,7 @@ impl Default for TrainerTuning {
|
||||
grip: 1.,
|
||||
turn_power: 1.,
|
||||
manual_drag: 1.,
|
||||
hold_fakie: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,3 +383,26 @@ fn showcase_reuses_trail_keys_and_updates_hud_settings() {
|
||||
.any(|(_, c)| matches!(c,Command::Trainer{tuning} if tuning.grip==2.))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn showcase_hold_fakie_is_optional_live_and_reversible() {
|
||||
let f = Fixture::new("return {}");
|
||||
let mut m = showcase_manager(&f);
|
||||
let id = "community.native-trainer";
|
||||
assert!(!TrainerTuning::default().hold_fakie);
|
||||
assert_eq!(m.packages[id].settings["hold_fakie"], json!(false));
|
||||
for enabled in [true, false] {
|
||||
m.commands.clear();
|
||||
m.setting(id, "hold_fakie", json!(enabled)).unwrap();
|
||||
let tuning = m.commands.iter().find_map(|(_, command)| match command {
|
||||
Command::Trainer { tuning } => Some(tuning),
|
||||
_ => None,
|
||||
}).expect("setting update must emit native trainer tuning");
|
||||
assert_eq!(tuning.hold_fakie, enabled);
|
||||
assert_eq!(tuning.pop, 1.);
|
||||
assert!(tuning.valid());
|
||||
}
|
||||
m.setting(id, "hold_fakie", json!(true)).unwrap();
|
||||
m.enable(id, false).unwrap();
|
||||
assert!(m.retired.iter().any(|owner| owner == id));
|
||||
}
|
||||
@@ -265,3 +265,14 @@ package-relative text loading. No private game assets are shipped.
|
||||
Standalone Lua tests exercise save/return command payloads, stopwatch creation,
|
||||
world-change cleanup, bounded trail keys and immediate HUD/tuning settings. Actual
|
||||
native gameplay effects and visuals remain user-run; no game process was launched.
|
||||
|
||||
|
||||
Trainer 2.1 adds `sdk.trainer.apply { hold_fakie = true }` (default false).
|
||||
It suppresses the native `FakieTurn` correction input in SetTurning; normal
|
||||
steering, slide overrides and trick inputs remain native. Turning the setting
|
||||
off, disabling the mod or retiring its owner restores stock behavior.
|
||||
|
||||
Enabled-mod windows default to 360 by 760 pixels, bounded by the display.
|
||||
Drag the bottom-right `//` grip to resize (minimum 320 by 260); the settings
|
||||
viewport grows with the window and keeps mouse-wheel scrolling. Window sizes
|
||||
are retained in memory while the game runs, alongside position/collapse state.
|
||||
@@ -46,3 +46,5 @@ HUD, visual objects), on_event (map/bail/grind observations), and timer cleanup.
|
||||
Use this as a practical starting point for training tools, navigation helpers,
|
||||
telemetry overlays and original visual extensions. The engine's full API reference
|
||||
is docs/lua-modding.md and language-server annotations are sdk/skate.lua.
|
||||
|
||||
Hold fakie stance disables automatic fakie turn correction; normal steering and slides still work. Off by default. Escape settings windows can be resized by dragging the bottom-right // grip.
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Native Trainer Showcase: original Lua, no game assets.
|
||||
local tuning_keys={'pop','grind_pop','push_speed','push_power','braking','steering','wobble','offboard_jump','grip','turn_power','manual_drag'}
|
||||
local tuning_keys={'pop','grind_pop','push_speed','push_power','braking','steering','wobble','offboard_jump','grip','turn_power','manual_drag','hold_fakie'}
|
||||
local previous, checkpoint, last_position, last_trail = {}, nil, nil, nil
|
||||
local trail_index, beacon_index = 0, 0
|
||||
local stats = {distance=0,peak=0,bails=0,grinds=0,grind_time=0}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"id": "community.native-trainer",
|
||||
"api": 1,
|
||||
"name": "Native Trainer Showcase",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"author": "Skate Rust Engine",
|
||||
"description": "Native tuning, telemetry, checkpoint return, stopwatch, breadcrumbs and beacons in one original Lua mod. Manage options here; default keyboard shortcuts F5 save, F6 return, F7 stopwatch, F8 reset, F9 beacon. Stock tuning is 1x.",
|
||||
"entry": "main.lua",
|
||||
@@ -225,6 +225,12 @@
|
||||
"KeyL"
|
||||
],
|
||||
"description": "Choose distinct shortcuts. If keys coincide, save, return, timer, reset, beacon run in that order."
|
||||
},
|
||||
"hold_fakie": {
|
||||
"type": "boolean",
|
||||
"label": "Hold fakie stance",
|
||||
"description": "Disables automatic FakieTurn correction. Normal steering, slides and tricks remain available. Turn off to restore stock correction.",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -93,7 +93,7 @@ function sdk.animation.info() end
|
||||
---@param path string Package-relative original body-clip JSON, <=4 MiB; host validates supported slots and exact timing.
|
||||
function sdk.animation.replace(path) end
|
||||
|
||||
---@class TrainerTuning All fields default to 1; multipliers of current stock settings.
|
||||
---@class TrainerTuning Numeric fields default to 1; multipliers of current stock settings.
|
||||
---@field pop? number 0.25..4, native ground pop heights.
|
||||
---@field grind_pop? number 0.25..4, native grind launch height.
|
||||
---@field push_speed? number 0.25..4, push target and maximum pushable speed.
|
||||
@@ -104,6 +104,7 @@ function sdk.animation.replace(path) end
|
||||
---@field offboard_jump? number 0.25..4, native biped launch height.
|
||||
---@field grip? number 0.25..4, wheel static/dynamic and sideways slide friction.
|
||||
---@field turn_power? number 0.25..4, native heading turn strength.
|
||||
---@field hold_fakie? boolean Defaults false; suppress automatic fakie turn correction.
|
||||
---@field manual_drag? number 0.25..4, manual balance linear drag.
|
||||
sdk.trainer = {}
|
||||
---@param tuning TrainerTuning Owned, reversible native tuning. Conflicting owner is rejected.
|
||||
|
||||
Reference in new issue
Block a user