Skip to content

Instantly share code, notes, and snippets.

@CryZe
Created January 16, 2019 20:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CryZe/a9ec8090c0597b93d69a9f1054713555 to your computer and use it in GitHub Desktop.
Save CryZe/a9ec8090c0597b93d69a9f1054713555 to your computer and use it in GitHub Desktop.
WebAssembly based Auto Splitter for A Hat in Time written in Rust.
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