Skip to content

Commit

Permalink
Merge pull request #58 from AlexKnauth/cloth
Browse files Browse the repository at this point in the history
Splits OnObtainGhostCloth, OnObtainGhostVespa
  • Loading branch information
AlexKnauth authored Feb 19, 2024
2 parents 13271c6 + b604b10 commit faae620
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 37 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ crate-type = ["cdylib"]
lto = true
panic = "abort"
codegen-units = 1
strip = true
# strip = true
# debug = true

[profile.release.build-override]
opt-level = 0
62 changes: 52 additions & 10 deletions src/hollow_knight_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub static GODHOME_LORE_SCENES: &[&str] = &[

// --------------------------------------------------------

// const VERSION_VEC_MAJOR: usize = 0;
const VERSION_VEC_MAJOR: usize = 0;
const VERSION_VEC_MINOR: usize = 1;
// const VERSION_VEC_BUILD: usize = 2;
// const VERSION_VEC_REVISION: usize = 3;
Expand Down Expand Up @@ -989,25 +989,27 @@ pub struct BossSequenceDoorCompletion {
// --------------------------------------------------------

pub struct GameManagerFinder {
string_list_offests: StringListOffsets,
module: mono::Module,
string_list_offests: Box<StringListOffsets>,
module: Box<mono::Module>,
image: mono::Image,
pointers: Box<GameManagerPointers>,
player_data_pointers: Box<PlayerDataPointers>,
completion_pointers: Box<CompletionPointers>,
ui_state_offset: OnceCell<u32>,
modded: OnceCell<bool>,
}

impl GameManagerFinder {
fn new(pointer_size: PointerSize, module: mono::Module, image: mono::Image) -> GameManagerFinder {
GameManagerFinder {
string_list_offests: StringListOffsets::new(pointer_size),
module,
string_list_offests: Box::new(StringListOffsets::new(pointer_size)),
module: Box::new(module),
image,
pointers: Box::new(GameManagerPointers::new()),
player_data_pointers: Box::new(PlayerDataPointers::new()),
completion_pointers: Box::new(CompletionPointers::new()),
ui_state_offset: OnceCell::new(),
modded: OnceCell::new(),
}
}

Expand Down Expand Up @@ -1077,12 +1079,22 @@ impl GameManagerFinder {
let ui_state_offset = ui_manager_class.get_field_offset(process, &self.module, "uiState")?;
self.ui_state_offset.get_or_init(|| ui_state_offset)
};
let ui = if let Ok(ui) = self.pointers.ui_state_vanilla.deref(process, &self.module, &self.image) {
ui
} else if let Ok(ui) = self.pointers.ui_state_modded.deref(process, &self.module, &self.image) {
ui
let ui = if let Some(&modded) = self.modded.get() {
if modded {
self.pointers.ui_state_modded.deref(process, &self.module, &self.image).ok()?
} else {
self.pointers.ui_state_vanilla.deref(process, &self.module, &self.image).ok()?
}
} else {

Check warning on line 1088 in src/hollow_knight_memory.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

this `else { if .. }` block can be collapsed
return None;
if let Ok(ui) = self.pointers.ui_state_vanilla.deref(process, &self.module, &self.image) {
self.modded.set(false).ok();
ui
} else if let Ok(ui) = self.pointers.ui_state_modded.deref(process, &self.module, &self.image) {
self.modded.set(true).ok();
ui
} else {
return None;
}
};
if ui_state_offset != &0x124 && ui >= 2 {
Some(ui + 2)
Expand Down Expand Up @@ -2185,6 +2197,11 @@ impl GameManagerFinder {
self.player_data_pointers.visited_hive.deref(process, &self.module, &self.image).ok()
}

fn hive_knight_doesnt_exist(&self, process: &Process) -> Option<bool> {
let v = self.get_version_vec(process)?;
Some((*v.get(VERSION_VEC_MAJOR)? <= 1) && (*v.get(VERSION_VEC_MINOR)? <= 2))
}

pub fn killed_hive_knight(&self, process: &Process) -> Option<bool> {
self.player_data_pointers.killed_hive_knight.deref(process, &self.module, &self.image).ok()
}
Expand Down Expand Up @@ -2828,6 +2845,31 @@ impl PlayerDataStore {
self.increased_i32(process, game_manager_finder, "royal_charm_state", &game_manager_finder.player_data_pointers.royal_charm_state)
}

fn been_dead_for_a_tick<const N: usize>(&mut self, prc: &Process, gmf: &GameManagerFinder, key: &'static str, pointer: &UnityPointer<N>) -> bool {
if gmf.is_game_state_non_continuous(prc) {
self.map_bool.remove(key);
return false;
}
match self.map_bool.get(key) {
None | Some(false) => {
if let Ok(dead_now) = pointer.deref(prc, &gmf.module, &gmf.image) {
self.map_bool.insert(key, dead_now);
}
false
}
Some(true) => true,
}
}

pub fn traitor_lord_been_dead_for_a_tick(&mut self, prc: &Process, gmf: &GameManagerFinder) -> bool {
self.been_dead_for_a_tick(prc, gmf, "killed_traitor_lord", &gmf.player_data_pointers.killed_traitor_lord)
}

pub fn hive_knight_been_dead_for_a_tick(&mut self, prc: &Process, gmf: &GameManagerFinder) -> bool {
gmf.hive_knight_doesnt_exist(prc).is_some_and(|d| d)
|| self.been_dead_for_a_tick(prc, gmf, "killed_hive_knight", &gmf.player_data_pointers.killed_hive_knight)
}

fn kills_on_entry<const N: usize>(&mut self, prc: &Process, gmf: &GameManagerFinder, key: &'static str, pointer: &UnityPointer<N>) -> Option<i32> {
if gmf.is_game_state_non_continuous(prc) {
self.map_i32.remove(key);
Expand Down
14 changes: 7 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async fn main() {

asr::print_message("Hello, World!");

let mut gui = SettingsGui::wait_load_merge_register().await;
let mut gui = Box::new(SettingsGui::wait_load_merge_register().await);

let mut ticks_since_gui = 0;
let mut splits = gui.get_splits();
Expand All @@ -40,17 +40,17 @@ async fn main() {
let mut auto_reset = splits::auto_reset_safe(&splits);

loop {
let process = wait_attach_hollow_knight(&mut gui).await;
let process = wait_attach_hollow_knight(&mut *gui).await;
process
.until_closes(async {
// TODO: Load some initial information from the process.
let mut scene_store = SceneStore::new();
let mut scene_store = Box::new(SceneStore::new());
let mut timing_method = gui.get_timing_method();
let mut load_remover = TimingMethodLoadRemover::new(timing_method);
let mut load_remover = Box::new(TimingMethodLoadRemover::new(timing_method));

next_tick().await;
let game_manager_finder = GameManagerFinder::wait_attach(&process).await;
let mut player_data_store = PlayerDataStore::new();
let game_manager_finder = Box::new(GameManagerFinder::wait_attach(&process).await);
let mut player_data_store = Box::new(PlayerDataStore::new());

#[cfg(debug_assertions)]
asr::print_message(&format!("geo: {:?}", game_manager_finder.get_geo(&process)));
Expand Down Expand Up @@ -88,7 +88,7 @@ async fn main() {
let new_timing_method = gui.get_timing_method();
if new_timing_method != timing_method {
timing_method = new_timing_method;
load_remover = TimingMethodLoadRemover::new(timing_method);
*load_remover = TimingMethodLoadRemover::new(timing_method);
asr::print_message(&format!("timing_method: {:?}", timing_method));
}
}
Expand Down
58 changes: 39 additions & 19 deletions src/splits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,14 @@ pub enum Split {
/// Splits when obtaining the essence from Blue Child Joni
OnObtainGhostJoni,
// TODO: resolve possible confounding essence sources for Cloth, Vespa, and Revek
/// Dream Nail Cloth (Obtain)
///
/// Splits when obtaining the essence from Cloth
OnObtainGhostCloth,
/// Dream Nail Vespa (Obtain)
///
/// Splits when obtaining the essence from Hive Queen Vespa
OnObtainGhostVespa,
// endregion: Essence, Trees, and Ghosts

// region: Maps and Cornifer
Expand Down Expand Up @@ -3571,18 +3579,18 @@ pub fn continuous_splits(s: &Split, p: &Process, g: &GameManagerFinder, pds: &mu
Split::MaskFragment14 => should_split(g.heart_pieces(p).is_some_and(|s| s == 14 || (g.max_health_base(p).is_some_and(|h| h == 8) && s == 2))),
Split::MaskFragment15 => should_split(g.heart_pieces(p).is_some_and(|s| s == 15 || (g.max_health_base(p).is_some_and(|h| h == 8) && s == 3))),
Split::Mask4 => should_split(g.max_health_base(p).is_some_and(|h| h == 9)),
Split::MaskShardMawlek => should_split(g.get_scene_name(p).is_some_and(|s| s == "Crossroads_09") && pds.obtained_mask_shard(p, g)),
Split::MaskShardGrubfather => should_split(g.get_scene_name(p).is_some_and(|s| s == "Crossroads_38") && pds.obtained_mask_shard(p, g)),
Split::MaskShardBretta => should_split(g.get_scene_name(p).is_some_and(|s| s == "Room_Bretta") && pds.obtained_mask_shard(p, g)),
Split::MaskShardQueensStation => should_split(g.get_scene_name(p).is_some_and(|s| s == "Fungus2_01") && pds.obtained_mask_shard(p, g)),
Split::MaskShardEnragedGuardian => should_split(g.get_scene_name(p).is_some_and(|s| s == "Mines_32") && pds.obtained_mask_shard(p, g)),
Split::MaskShardSeer => should_split(g.get_scene_name(p).is_some_and(|s| s == "RestingGrounds_07") && pds.obtained_mask_shard(p, g)),
Split::MaskShardGoam => should_split(g.get_scene_name(p).is_some_and(|s| s == "Crossroads_13") && pds.obtained_mask_shard(p, g)),
Split::MaskShardStoneSanctuary => should_split(g.get_scene_name(p).is_some_and(|s| s == "Fungus1_36") && pds.obtained_mask_shard(p, g)),
Split::MaskShardWaterways => should_split(g.get_scene_name(p).is_some_and(|s| s == "Waterways_04b") && pds.obtained_mask_shard(p, g)),
Split::MaskShardFungalCore => should_split(g.get_scene_name(p).is_some_and(|s| s == "Fungus2_25") && pds.obtained_mask_shard(p, g)),
Split::MaskShardHive => should_split(g.get_scene_name(p).is_some_and(|s| s == "Hive_04") && pds.obtained_mask_shard(p, g)),
Split::MaskShardFlower => should_split(g.get_scene_name(p).is_some_and(|s| s == "Room_Mansion") && pds.obtained_mask_shard(p, g)),
Split::MaskShardMawlek => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Crossroads_09")),
Split::MaskShardGrubfather => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Crossroads_38")),
Split::MaskShardBretta => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Room_Bretta")),
Split::MaskShardQueensStation => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Fungus2_01")),
Split::MaskShardEnragedGuardian => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Mines_32")),
Split::MaskShardSeer => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "RestingGrounds_07")),
Split::MaskShardGoam => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Crossroads_13")),
Split::MaskShardStoneSanctuary => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Fungus1_36")),
Split::MaskShardWaterways => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Waterways_04b")),
Split::MaskShardFungalCore => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Fungus2_25")),
Split::MaskShardHive => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Hive_04")),
Split::MaskShardFlower => should_split(pds.obtained_mask_shard(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Room_Mansion")),
// endregion: Masks and Mask Shards
// region: Vessels and Vessel Fragments
Split::OnObtainVesselFragment => should_split(pds.obtained_vessel_fragment(p, g)),
Expand All @@ -3595,13 +3603,13 @@ pub fn continuous_splits(s: &Split, p: &Process, g: &GameManagerFinder, pds: &mu
Split::VesselFragment7 => should_split(g.vessel_fragments(p).is_some_and(|f| f == 7 || (g.mp_reserve_max(p).is_some_and(|mp| mp == 66) && f == 1))),
Split::VesselFragment8 => should_split(g.vessel_fragments(p).is_some_and(|f| f == 8 || (g.mp_reserve_max(p).is_some_and(|mp| mp == 66) && f == 2))),
Split::Vessel3 => should_split(g.mp_reserve_max(p).is_some_and(|mp| mp == 99)),
Split::VesselFragGreenpath => should_split(g.get_scene_name(p).is_some_and(|s| s == "Fungus1_13") && pds.obtained_vessel_fragment(p, g)),
Split::VesselFragCrossroadsLift => should_split(g.get_scene_name(p).is_some_and(|s| s == "Crossroads_37") && pds.obtained_vessel_fragment(p, g)),
Split::VesselFragKingsStation => should_split(g.get_scene_name(p).is_some_and(|s| s == "Ruins2_09") && pds.obtained_vessel_fragment(p, g)),
Split::VesselFragGarpedes => should_split(g.get_scene_name(p).is_some_and(|s| s == "Deepnest_38") && pds.obtained_vessel_fragment(p, g)),
Split::VesselFragStagNest => should_split(g.get_scene_name(p).is_some_and(|s| s == "Cliffs_03") && pds.obtained_vessel_fragment(p, g)),
Split::VesselFragSeer => should_split(g.get_scene_name(p).is_some_and(|s| s == "RestingGrounds_07") && pds.obtained_vessel_fragment(p, g)),
Split::VesselFragFountain => should_split(g.get_scene_name(p).is_some_and(|s| s == "Abyss_04") && pds.obtained_vessel_fragment(p, g)),
Split::VesselFragGreenpath => should_split(pds.obtained_vessel_fragment(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Fungus1_13")),
Split::VesselFragCrossroadsLift => should_split(pds.obtained_vessel_fragment(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Crossroads_37")),
Split::VesselFragKingsStation => should_split(pds.obtained_vessel_fragment(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Ruins2_09")),
Split::VesselFragGarpedes => should_split(pds.obtained_vessel_fragment(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Deepnest_38")),
Split::VesselFragStagNest => should_split(pds.obtained_vessel_fragment(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Cliffs_03")),
Split::VesselFragSeer => should_split(pds.obtained_vessel_fragment(p, g) && g.get_scene_name(p).is_some_and(|s| s == "RestingGrounds_07")),
Split::VesselFragFountain => should_split(pds.obtained_vessel_fragment(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Abyss_04")),
// endregion: Vessels and Vessel Fragments
// region: Charm Notches
Split::NotchShrumalOgres => should_split(g.notch_shroom_ogres(p).is_some_and(|n| n)),
Expand Down Expand Up @@ -3857,6 +3865,18 @@ pub fn continuous_splits(s: &Split, p: &Process, g: &GameManagerFinder, pds: &mu
Split::OnObtainGhostGravedigger => should_split(pds.incremented_dream_orbs(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Town")),
Split::OnObtainGhostJoni => should_split(pds.incremented_dream_orbs(p, g) && g.get_scene_name(p).is_some_and(|s| s == "Cliffs_05")),
// TODO: resolve possible confounding essence sources for Cloth, Vespa, and Revek
Split::OnObtainGhostCloth => {
let d = pds.traitor_lord_been_dead_for_a_tick(p, g);
let o = pds.incremented_dream_orbs(p, g);
// make sure both PlayerDataStore methods are evaluated before the `&&` so it doesn't short-circuit
should_split(d && o && g.get_scene_name(p).is_some_and(|s| s == "Fungus3_23"))
}
Split::OnObtainGhostVespa => {
let d = pds.hive_knight_been_dead_for_a_tick(p, g);
let o = pds.incremented_dream_orbs(p, g);
// make sure both PlayerDataStore methods are evaluated before the `&&` so it doesn't short-circuit
should_split(d && o && g.get_scene_name(p).is_some_and(|s| s == "Hive_05"))
}
// endregion: Essence, Trees, and Ghosts

// region: Maps and Cornifer
Expand Down

0 comments on commit faae620

Please sign in to comment.