Skip to content

Commit

Permalink
Dry-coded support for including unarmed in hand cycles.
Browse files Browse the repository at this point in the history
  • Loading branch information
ceejbot committed Jul 20, 2023
1 parent 28cbcbe commit fdccbf7
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 11 deletions.
9 changes: 9 additions & 0 deletions data/mcm/config/SoulsyHUD/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@
"type": "header",
"position": 1
},
{
"id": "bIncludeUnarmed:Options",
"text": "$SoulsyHUD_Options_Unarmed_Text",
"type": "toggle",
"help": "$SoulsyHUD_Options_Unarmed_Help",
"valueOptions": {
"sourceType": "ModSettingInt"
}
},
{
"id": "uMaxCycleLength:Options",
"text": "$SoulsyHUD_Options_Length_Text",
Expand Down
1 change: 1 addition & 0 deletions data/mcm/config/SoulsyHUD/settings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ uRefreshKey = 2
uShowHideKey = 8

[Options]
bIncludeUnarmed = 1
uMaxCycleLength = 10
uEquipDelay = 750
bFade = 0
Expand Down
58 changes: 51 additions & 7 deletions src/controller/control.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::collections::HashMap;
use std::fmt::Display;
use std::sync::Mutex;

use once_cell::sync::Lazy;

use super::cycles::*;
use super::settings::user_settings;
use super::settings::{user_settings, UserSettings};
use crate::hud_layout;
use crate::plugin::*;

Expand Down Expand Up @@ -90,6 +91,28 @@ pub mod public {
let mut ctrl = CONTROLLER.lock().unwrap();
ctrl.cycles.truncate_if_needed(new as usize);
}

pub fn refresh_user_settings() {
if let Some(e) = UserSettings::refresh().err() {
log::warn!("Failed to read user settings! using defaults; {e:?}");
return;
}
let mut ctrl = CONTROLLER.lock().unwrap();
let settings = user_settings();
if settings.include_unarmed() {
let h2h = make_hand_to_hand();
let h2h = *h2h;
ctrl.cycles.include_item(Action::Left, h2h.clone());
ctrl.cycles.include_item(Action::Right, h2h);
} else {
// remove any item with h2h type from cycles
ctrl.cycles
.filter_kind(Action::Left, TesItemKind::HandToHand);
ctrl.cycles
.filter_kind(Action::Right, TesItemKind::HandToHand);
}
showHUD();
}
}

/// What, model/view/controller? In my UI application? oh no
Expand Down Expand Up @@ -173,13 +196,16 @@ impl Controller {
return;
}

// We equip whatever the HUD is showing right now.
let Some(item) = &self.visible.get(&hud) else {
return; // TODO should equipped unarmed?
log::warn!("visible item in hud slot was None, which should not happen; slot={:?};", hud);
unequipSlot(which);
return;
};

// We equip whatever the HUD is showing right now.
let kind = item.kind();
if matches!(kind, TesItemKind::Empty) && which != Action::Utility {
if matches!(kind, TesItemKind::HandToHand) {
log::info!("melee time! unequipping slot {which:?}");
unequipSlot(which);
return;
}
Expand All @@ -201,7 +227,12 @@ impl Controller {
}
let kind = item.kind();
cxx::let_cxx_string!(form_spec = item.form_string());
log::trace!("equip_item: which={:?}; form_spec={}; name='{}'", which, item.form_string(), item.name());
log::trace!(
"equip_item: which={:?}; form_spec={}; name='{}'",
which,
item.form_string(),
item.name()
);

// These are all different because the game API is a bit of an evolved thing.
if kind.is_magic() {
Expand Down Expand Up @@ -343,13 +374,13 @@ impl Controller {
return false;
}

let rightie = if !item.kind().is_weapon() {
let rightie = if !item.kind().is_weapon() {
equippedRightHand()
} else {
boundObjectRightHand()
};

let leftie = if !item.kind().is_weapon() {
let leftie = if !item.kind().is_weapon() {
equippedLeftHand()
} else {
boundObjectLeftHand()
Expand Down Expand Up @@ -651,6 +682,19 @@ impl From<Action> for HudElement {
}
}

impl Display for HudElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
HudElement::Ammo => write!(f, "Ammo"),
HudElement::Left => write!(f, "Left"),
HudElement::Power => write!(f, "Power"),
HudElement::Right => write!(f, "Right"),
HudElement::Utility => write!(f, "Utility"),
_ => write!(f, "unknown"),
}
}
}

impl From<u32> for Action {
/// Turn the key code into an enum for easier processing.
fn from(value: u32) -> Self {
Expand Down
42 changes: 42 additions & 0 deletions src/controller/cycles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ pub fn make_tesitem(
))
}

pub fn make_hand_to_hand() -> Box<TesItemData> {
Box::new(TesItemData::new(
TesItemKind::HandToHand,
false,
false,
1,
"Unarmed",
"",
))
}

/// Construct a default TesItemData struct, which is displayed as
/// an empty spot on the HUD.
pub fn default_tes_item() -> Box<TesItemData> {
Expand Down Expand Up @@ -365,6 +376,37 @@ impl CycleData {
}
false
}

pub fn include_item(&mut self, which: Action, item: TesItemData) {
let cycle = match which {
Action::Power => &mut self.power,
Action::Left => &mut self.left,
Action::Right => &mut self.right,
Action::Utility => &mut self.utility,
_ => {
return;
}
};
if !cycle
.iter()
.any(|xs| xs.kind() == item.kind() || xs.form_string() == item.form_string())
{
cycle.push(item);
}
}

pub fn filter_kind(&mut self, which: Action, kind: TesItemKind) {
let cycle = match which {
Action::Power => &mut self.power,
Action::Left => &mut self.left,
Action::Right => &mut self.right,
Action::Utility => &mut self.utility,
_ => {
return;
}
};
cycle.retain(|xs| xs.kind() != kind);
}
}

impl Display for CycleData {
Expand Down
2 changes: 1 addition & 1 deletion src/controller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::path::PathBuf;
pub use control::public::*;
pub use cycles::{default_tes_item, get_icon_file, make_tesitem, TesItemData};
pub use layout::hud_layout;
pub use settings::{refresh_user_settings, user_settings, UserSettings};
pub use settings::{user_settings, UserSettings};
use simplelog::*;
pub use tesitemkind::{kind_has_count, kind_is_magic}; // hmm, is this for settings? I'm confused...

Expand Down
12 changes: 11 additions & 1 deletion src/controller/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub struct UserSettings {
pub fade_delay: u32,
/// The controller kind to show in the UX. Matches the controller_set enum in key_path.h
pub controller_kind: u32, // 0 = pc, 1 = ps, 2 = xbox
/// Whether to include unarmed as a cycle entry for each hand.
pub include_unarmed: bool,
}

impl Default for UserSettings {
Expand All @@ -83,6 +85,7 @@ impl Default for UserSettings {
fade: true,
fade_delay: 1000, // in milliseconds
controller_kind: 0, // PC
include_unarmed: true,
}
}
}
Expand Down Expand Up @@ -154,6 +157,11 @@ impl UserSettings {
0,
2,
);
self.include_unarmed = if let Some(str_val) = options.get("bIncludeUnarmed") {
str_val != "0"
} else {
self.include_unarmed
};

Ok(())
}
Expand Down Expand Up @@ -206,10 +214,12 @@ impl UserSettings {
pub fn refresh_layout(&self) -> u32 {
self.refresh_layout
}

pub fn controller_kind(&self) -> u32 {
clamp(self.controller_kind, 0, 2)
}
pub fn include_unarmed(&self) -> bool {
self.include_unarmed
}
}

fn clamp(num: u32, min: u32, max: u32) -> u32 {
Expand Down
2 changes: 1 addition & 1 deletion src/controller/tesitemkind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn kind_is_magic(kind: TesItemKind) -> bool {
/// We cannot derive default for shared enums, so we define it here.
impl Default for TesItemKind {
fn default() -> Self {
TesItemKind::Empty
TesItemKind::NotFound
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/game/equippable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace equippable

TesItemKind itemKindFromForm(RE::TESForm*& item_form)
{
if (!item_form) { return TesItemKind::Empty; }
if (!item_form) { return TesItemKind::NotFound; }

if (item_form->IsWeapon())
{
Expand Down

0 comments on commit fdccbf7

Please sign in to comment.