diff --git a/src/hooks/bxt.rs b/src/hooks/bxt.rs index 093b850..9d62fe4 100644 --- a/src/hooks/bxt.rs +++ b/src/hooks/bxt.rs @@ -23,6 +23,10 @@ pub static BXT_IS_TAS_EDITOR_ACTIVE: Pointer c_int> = pub static BXT_TAS_NEW: Pointer< unsafe extern "C" fn(*const c_char, *const c_char, *const c_char, c_int), > = Pointer::empty(b"bxt_tas_new\0"); +pub static BXT_TAS_NOREFRESH_UNTIL_LAST_FRAMES: Pointer c_int> = + Pointer::empty(b"bxt_tas_norefresh_until_last_frames\0"); +pub static BXT_TAS_STUDIO_NOREFRESH_OVERRIDE: Pointer = + Pointer::empty(b"bxt_tas_studio_norefresh_override\0"); static POINTERS: &[&dyn PointerTrait] = &[ &BXT_ON_TAS_PLAYBACK_FRAME, @@ -31,6 +35,8 @@ static POINTERS: &[&dyn PointerTrait] = &[ &BXT_TAS_LOAD_SCRIPT_FROM_STRING, &BXT_IS_TAS_EDITOR_ACTIVE, &BXT_TAS_NEW, + &BXT_TAS_NOREFRESH_UNTIL_LAST_FRAMES, + &BXT_TAS_STUDIO_NOREFRESH_OVERRIDE, ]; #[cfg(unix)] diff --git a/src/modules/tas_studio/editor/mod.rs b/src/modules/tas_studio/editor/mod.rs index ce92224..411de74 100644 --- a/src/modules/tas_studio/editor/mod.rs +++ b/src/modules/tas_studio/editor/mod.rs @@ -83,6 +83,9 @@ pub struct Editor { /// Whether the editor is in the camera editor mode. in_camera_editor: bool, + /// Frame index calculated from bxt_tas_studio_norefresh_until_stop_frame. + norefresh_until_stop_frame_frame_idx: usize, + // ============================================== // Movement-editor-specific state. /// Index of the hovered frame bulk. @@ -415,6 +418,7 @@ impl Editor { smooth_window_s: 0.15, smooth_small_window_s: 0.03, smooth_small_window_multiplier: 3., + norefresh_until_stop_frame_frame_idx: 0, }) } @@ -521,6 +525,10 @@ impl Editor { self.show_player_bbox = value; } + pub fn set_norefresh_until_stop_frame(&mut self, value: usize) { + self.norefresh_until_stop_frame_frame_idx = value; + } + /// Invalidates frames starting from given. /// /// Erases cached frame data and adjusts the first predicted frame index if needed. @@ -3361,6 +3369,9 @@ impl Editor { let is_hovered = self.hovered_frame_idx == Some(idx); // If frame is the stop frame. let is_stop_frame = branch.branch.stop_frame as usize == idx; + // If frame is the end of norefresh for norefresh until stop frame. + let is_norefresh_until_stop_frame = + !is_stop_frame && self.norefresh_until_stop_frame_frame_idx == idx; // If frame is in the smoothing input region. let in_smoothing_input_region = @@ -3681,6 +3692,24 @@ impl Editor { }); } + // If bxt_tas_studio_norefresh_until_stop_frame is set, draw another indicator. + if is_norefresh_until_stop_frame { + let perp = perpendicular(prev_pos, pos) * 2.; + let diff = (pos - prev_pos).normalize_or_zero() * 2.; + + // Draw the arrow. + draw(DrawLine { + start: pos - perp + diff, + end: pos, + color: Vec3::new(1., 1., 0.5), + }); + draw(DrawLine { + start: pos, + end: pos + perp + diff, + color: Vec3::new(1., 1., 0.5), + }); + } + if is_last_in_bulk { // Reset flag for next iteration. collided_this_bulk = false; diff --git a/src/modules/tas_studio/mod.rs b/src/modules/tas_studio/mod.rs index d0ced03..cf2c498 100644 --- a/src/modules/tas_studio/mod.rs +++ b/src/modules/tas_studio/mod.rs @@ -120,6 +120,8 @@ impl Module for TasStudio { && bxt::BXT_ON_TAS_PLAYBACK_FRAME.is_set(marker) && bxt::BXT_ON_TAS_PLAYBACK_STOPPED.is_set(marker) && bxt::BXT_TAS_NEW.is_set(marker) + && bxt::BXT_TAS_NOREFRESH_UNTIL_LAST_FRAMES.is_set(marker) + && bxt::BXT_TAS_STUDIO_NOREFRESH_OVERRIDE.is_set(marker) && TriangleDrawing.is_enabled(marker) && Commands.is_enabled(marker) && PlayerMovementTracing.is_enabled(marker) @@ -385,6 +387,24 @@ fn convert_hltas_from_bxt_tas_new(marker: MainThreadMarker, mut path: String) { ) } +fn norefresh_until_stop_frame_frame_idx(marker: MainThreadMarker, editor: &Editor) -> usize { + let norefresh_last_frames = + unsafe { bxt::BXT_TAS_NOREFRESH_UNTIL_LAST_FRAMES.get(marker)() } as usize; + + if editor.stop_frame() == 0 { + (editor.branch().frames.len() - 1).saturating_sub(norefresh_last_frames) + } else { + (editor.stop_frame() as usize).saturating_sub(norefresh_last_frames) + } +} + +fn set_effective_norefresh_until_stop_frame(marker: MainThreadMarker, editor: &Editor) { + let stop_frame = norefresh_until_stop_frame_frame_idx(marker, editor); + let value = (editor.branch().frames.len() - 1).saturating_sub(stop_frame); + + unsafe { bxt::BXT_TAS_STUDIO_NOREFRESH_OVERRIDE.get(marker)(value as i32) }; +} + static BXT_TAS_STUDIO_REPLAY: Command = Command::new( b"bxt_tas_studio_replay\0", handler!( @@ -402,9 +422,11 @@ fn replay(marker: MainThreadMarker) { mut editor, bridge, .. } => { editor.cancel_ongoing_adjustments(); + set_effective_norefresh_until_stop_frame(marker, &editor); State::PreparingToPlayToEditor(editor, bridge, true) } State::PlayingToEditor { editor, bridge, .. } => { + set_effective_norefresh_until_stop_frame(marker, &editor); State::PreparingToPlayToEditor(editor, bridge, true) } other => other, @@ -1767,6 +1789,7 @@ pub fn draw(marker: MainThreadMarker, tri: &TriangleApi) { editor.set_smooth_small_window_multiplier( BXT_TAS_STUDIO_SMOOTH_SMALL_WINDOW_MULTIPLIER.as_f32(marker), ); + editor.set_norefresh_until_stop_frame(norefresh_until_stop_frame_frame_idx(marker, editor)); // SAFETY: if we have access to TriangleApi, it's safe to do player tracing too. let tracer = unsafe { Tracer::new(marker, true) }.unwrap();