Created
January 16, 2019 20:07
-
-
Save CryZe/a9ec8090c0597b93d69a9f1054713555 to your computer and use it in GitHub Desktop.
WebAssembly based Auto Splitter for A Hat in Time written in Rust.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use asl::{ASLState, Address}; | |
use parking_lot::Mutex; | |
use scroll::{Pread, LE}; | |
#[derive(ASLState)] | |
#[Process = "HatinTimeGame.exe"] | |
#[allow(dead_code)] | |
struct EmptyState {} | |
#[derive(Debug, Default)] | |
struct State { | |
base_address: Option<Address>, | |
current: Option<GameState>, | |
old: Option<GameState>, | |
} | |
impl State { | |
fn update_game_state(&mut self) { | |
if let Some(base) = self.base_address { | |
let mut buf = [0; 68]; | |
asl::read_into_buf(base, &mut buf); | |
let current_state: GameState = buf.pread_with(0, LE).unwrap(); | |
self.old = self.current.replace(current_state); | |
} | |
} | |
fn get(&self) -> Option<(&GameState, &GameState)> { | |
Some((self.current.as_ref()?, self.old.as_ref()?)) | |
} | |
} | |
static STATE: Mutex<State> = Mutex::new(State { | |
base_address: None, | |
current: None, | |
old: None, | |
}); | |
#[derive(Debug, Clone, Pread)] | |
struct GameState { | |
timer_state: i32, | |
unpause_time: f64, | |
game_timer_is_paused: i32, | |
act_timer_is_paused: i32, | |
act_timer_is_visible: i32, | |
unpause_time_is_dirty: i32, | |
just_got_time_piece: i32, | |
game_time: f64, | |
act_time: f64, | |
real_game_time: f64, | |
real_act_time: f64, | |
time_piece_count: i32, | |
} | |
#[no_mangle] | |
pub extern "C" fn disconnect() { | |
*STATE.lock() = State::default(); | |
} | |
#[no_mangle] | |
pub extern "C" fn update() { | |
let mut state = STATE.lock(); | |
if state.base_address.is_none() { | |
state.base_address = asl::scan_signature( | |
"54 49 4D 52 | |
?? ?? ?? ?? | |
?? ?? ?? ?? ?? ?? ?? ?? | |
?? ?? ?? ?? | |
?? ?? ?? ?? | |
?? ?? ?? ?? | |
?? ?? ?? ?? | |
?? ?? ?? ?? | |
?? ?? ?? ?? ?? ?? ?? ?? | |
?? ?? ?? ?? ?? ?? ?? ?? | |
?? ?? ?? ?? ?? ?? ?? ?? | |
?? ?? ?? ?? ?? ?? ?? ?? | |
?? ?? ?? ?? | |
45 4E 44 20", | |
); | |
if let Some(Address(base)) = &mut state.base_address { | |
*base += 4; | |
state.current = None; | |
state.old = None; | |
asl::set_tick_rate(60.0); | |
} else { | |
asl::set_tick_rate(1.0); | |
} | |
} | |
state.update_game_state(); | |
} | |
#[no_mangle] | |
pub extern "C" fn should_start() -> bool { | |
let state = STATE.lock(); | |
if let Some((current, old)) = state.get() { | |
current.timer_state == 1 && old.timer_state == 0 | |
} else { | |
false | |
} | |
} | |
#[no_mangle] | |
pub extern "C" fn should_split() -> bool { | |
let state = STATE.lock(); | |
if let Some((current, old)) = state.get() { | |
let just_got_time_piece = current.time_piece_count == old.time_piece_count + 1 | |
&& current.act_timer_is_visible == 1; | |
let is_in_credits = current.timer_state == 2; | |
just_got_time_piece || is_in_credits | |
} else { | |
false | |
} | |
} | |
#[no_mangle] | |
pub extern "C" fn should_reset() -> bool { | |
let state = STATE.lock(); | |
if let Some((current, old)) = state.get() { | |
current.timer_state == 0 && old.timer_state == 1 | |
} else { | |
false | |
} | |
} | |
#[no_mangle] | |
pub extern "C" fn is_loading() -> bool { | |
true | |
} | |
#[no_mangle] | |
pub extern "C" fn game_time() -> f64 { | |
let state = STATE.lock(); | |
if let Some(current) = &state.current { | |
current.real_game_time | |
} else { | |
0.0 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment