1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2025-04-25 15:09:18 +00:00
This commit is contained in:
jugeeya 2023-08-14 11:16:15 -07:00
parent 2b241dcb19
commit ddd5f16b87
11 changed files with 248 additions and 313 deletions

3
ryujinx_build.ps1 vendored
View file

@ -1,5 +1,8 @@
$IP=(Test-Connection -ComputerName (hostname) -Count 1 | Select -ExpandProperty IPV4Address).IPAddressToString
cargo skyline build --release --features layout_arc_from_file
if (($lastexitcode -ne 0)) {
exit $lastexitcode
}
# Set up symlinks
$RYUJINX_LAYOUT_ARC_PATH="C:\Users\Josh\AppData\Roaming\Ryujinx\sdcard\ultimate\TrainingModpack\layout.arc"

View file

@ -46,6 +46,113 @@ pub fn button_mapping(
}
}
pub fn name_to_font_glyph(button: ButtonConfig, style: ControllerStyle) -> Option<u16> {
let is_gcc = style == ControllerStyle::GCController;
Some(match button {
ButtonConfig::A => 0xE0E0,
ButtonConfig::B => 0xE0E1,
ButtonConfig::X => {
if is_gcc {
0xE206
} else {
0xE0E2
}
}
ButtonConfig::Y => {
if is_gcc {
0xE207
} else {
0xE0E3
}
}
ButtonConfig::L => {
if is_gcc {
return None;
} else {
0xE0E4
}
}
ButtonConfig::R => {
if is_gcc {
0xE205
} else {
0xE0E5
}
}
ButtonConfig::ZL => {
if is_gcc {
0xE204
} else {
0xE0E6
}
}
ButtonConfig::ZR => {
if is_gcc {
0xE208
} else {
0xE0E7
}
}
ButtonConfig::DPAD_UP => {
if is_gcc {
0xE209
} else {
0xE0EB
}
}
ButtonConfig::DPAD_DOWN => {
if is_gcc {
0xE20A
} else {
0xE0EC
}
}
ButtonConfig::DPAD_LEFT => {
if is_gcc {
0xE20B
} else {
0xE0ED
}
}
ButtonConfig::DPAD_RIGHT => {
if is_gcc {
0xE20C
} else {
0xE0EE
}
}
ButtonConfig::PLUS => {
if is_gcc {
0xE20D
} else {
0xE0EF
}
}
ButtonConfig::MINUS => {
if is_gcc {
return None;
} else {
0xE0F0
}
}
ButtonConfig::LSTICK => {
if is_gcc {
return None;
} else {
0xE104
}
}
ButtonConfig::RSTICK => {
if is_gcc {
return None;
} else {
0xE105
}
}
_ => return None,
})
}
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Copy, Clone)]
pub enum ButtonCombo {
OpenMenu,

View file

@ -41,14 +41,25 @@ pub fn is_emulator() -> bool {
}
pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor {
try_get_module_accessor(fighter_id).unwrap()
}
pub fn try_get_module_accessor(
fighter_id: FighterId,
) -> Option<*mut app::BattleObjectModuleAccessor> {
let entry_id_int = fighter_id as i32;
let entry_id = app::FighterEntryID(entry_id_int);
unsafe {
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
let fighter_entry =
FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry;
if fighter_entry.is_null() {
return None;
}
let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry);
app::sv_battle_object::module_accessor(current_fighter_id as u32)
Some(app::sv_battle_object::module_accessor(
current_fighter_id as u32,
))
}
}

Binary file not shown.

23
src/training/input_log.rs Normal file
View file

@ -0,0 +1,23 @@
use std::collections::VecDeque;
use crate::common::input::*;
use lazy_static::lazy_static;
use parking_lot::Mutex;
lazy_static! {
pub static ref P1_INPUT_MAPPINGS: Mutex<VecDeque<MappedInputs>> = Mutex::new(VecDeque::new());
}
// TODO: how many
const NUM_INPUTS: usize = 120;
pub fn handle_final_input_mapping(player_idx: i32, out: *mut MappedInputs) {
unsafe {
if player_idx == 0 {
let mut mappings = P1_INPUT_MAPPINGS.lock();
mappings.push_front(*out);
mappings.truncate(NUM_INPUTS);
}
}
}

View file

@ -1,7 +1,9 @@
use crate::common::button_config;
use crate::common::consts::{FighterId, HitstunPlayback, OnOff, RecordTrigger};
use crate::common::input::*;
use crate::common::{get_module_accessor, is_in_hitstun, is_in_shieldstun, MENU};
use crate::common::{
get_module_accessor, is_in_hitstun, is_in_shieldstun, try_get_module_accessor, MENU,
};
use crate::training::mash;
use crate::training::ui::notifications::{clear_notifications, color_notification};
use lazy_static::lazy_static;
@ -434,7 +436,14 @@ unsafe fn set_cpu_controls(p_data: *mut *mut u8) {
should_mash_playback();
}
let cpu_module_accessor = get_module_accessor(FighterId::CPU);
let cpu_module_accessor = try_get_module_accessor(FighterId::CPU);
// Sometimes we can try to grab their module accessor before they are valid?
if cpu_module_accessor.is_none() {
return;
}
let cpu_module_accessor = cpu_module_accessor.unwrap();
if INPUT_RECORD == Pause {
match LOCKOUT_FRAME.cmp(&0) {
Ordering::Greater => LOCKOUT_FRAME -= 1,

View file

@ -1,2 +0,0 @@
#[macro_use]
pub mod structures;

View file

@ -1,307 +0,0 @@
#![allow(dead_code)] // TODO: Yeah don't do this
use crate::common::events::smash_version;
use crate::common::release::CURRENT_VERSION;
use crate::training::save_states::SavedState;
use bitflags::bitflags;
use training_mod_consts::TrainingModpackMenu;
use crate::default_save_state;
use crate::training::character_specific::steve;
use crate::training::charge::ChargeState;
use crate::training::save_states::SaveState::NoAction;
// Need to define necesary structures here. Probably should move to consts or something. Realistically, should be in skyline smash prob tho.
// Final final controls used for controlmodule
// can I actually derive these?
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct ControlModuleInternal {
pub vtable: *mut u8,
pub controller_index: i32,
pub buttons: Buttons,
pub stick_x: f32,
pub stick_y: f32,
pub padding: [f32; 2],
pub unk: [u32; 8],
pub clamped_lstick_x: f32,
pub clamped_lstick_y: f32,
pub padding2: [f32; 2],
pub clamped_rstick_x: f32,
pub clamped_rstick_y: f32,
}
impl ControlModuleInternal {
pub fn _clear(&mut self) {
// Try to nullify controls so we can't control player 1 during recording
self.stick_x = 0.0;
self.stick_y = 0.0;
self.buttons = Buttons::empty();
self.clamped_lstick_x = 0.0;
self.clamped_lstick_y = 0.0;
self.clamped_rstick_x = 0.0;
self.clamped_rstick_y = 0.0;
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct ControlModuleStored {
// Custom type for saving only necessary controls/not saving vtable
pub buttons: Buttons,
pub stick_x: f32,
pub stick_y: f32,
pub padding: [f32; 2],
pub unk: [u32; 8],
pub clamped_lstick_x: f32,
pub clamped_lstick_y: f32,
pub padding2: [f32; 2],
pub clamped_rstick_x: f32,
pub clamped_rstick_y: f32,
}
// Re-ordered bitfield the game uses for buttons - TODO: Is this a problem? What's the original order?
pub type ButtonBitfield = i32; // may need to actually implement? Not for now though
/// Controller style declaring what kind of controller is being used
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
#[repr(u32)]
pub enum ControllerStyle {
Handheld = 0x1,
DualJoycon = 0x2,
LeftJoycon = 0x3,
RightJoycon = 0x4,
ProController = 0x5,
DebugPad = 0x6, // probably
GCController = 0x7,
}
#[repr(C)]
pub struct AutorepeatInfo {
field: [u8; 0x18],
}
// Can map any of these over any button - what does this mean?
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum InputKind {
Attack = 0x0,
Special = 0x1,
Jump = 0x2,
Guard = 0x3,
Grab = 0x4,
SmashAttack = 0x5,
AppealHi = 0xA,
AppealS = 0xB,
AppealLw = 0xC,
Unset = 0xD,
}
// 0x50 Byte struct containing the information for controller mappings
#[derive(Debug)]
#[repr(C)]
pub struct ControllerMapping {
pub gc_l: InputKind,
pub gc_r: InputKind,
pub gc_z: InputKind,
pub gc_dup: InputKind,
pub gc_dlr: InputKind,
pub gc_ddown: InputKind,
pub gc_a: InputKind,
pub gc_b: InputKind,
pub gc_cstick: InputKind,
pub gc_y: InputKind,
pub gc_x: InputKind,
pub gc_rumble: bool,
pub gc_absmash: bool,
pub gc_tapjump: bool,
pub gc_sensitivity: u8,
// 0xF
pub pro_l: InputKind,
pub pro_r: InputKind,
pub pro_zl: InputKind,
pub pro_zr: InputKind,
pub pro_dup: InputKind,
pub pro_dlr: InputKind,
pub pro_ddown: InputKind,
pub pro_a: InputKind,
pub pro_b: InputKind,
pub pro_cstick: InputKind,
pub pro_x: InputKind,
pub pro_y: InputKind,
pub pro_rumble: bool,
pub pro_absmash: bool,
pub pro_tapjump: bool,
pub pro_sensitivity: u8,
// 0x1F
pub joy_shoulder: InputKind,
pub joy_zshoulder: InputKind,
pub joy_sl: InputKind,
pub joy_sr: InputKind,
pub joy_up: InputKind,
pub joy_right: InputKind,
pub joy_left: InputKind,
pub joy_down: InputKind,
pub joy_rumble: bool,
pub joy_absmash: bool,
pub joy_tapjump: bool,
pub joy_sensitivity: u8,
// 0x2B
pub _2b: u8,
pub _2c: u8,
pub _2d: u8,
pub _2e: u8,
pub _2f: u8,
pub _30: u8,
pub _31: u8,
pub _32: u8,
pub is_absmash: bool,
pub _34: [u8; 0x1C],
}
//type Buttons = u32; // may need to actually implement (like label and such)? Not for now though
bitflags! {
pub struct Buttons: u32 {
const ATTACK = 0x1;
const SPECIAL = 0x2;
const JUMP = 0x4;
const GUARD = 0x8;
const CATCH = 0x10;
const SMASH = 0x20;
const JUMP_MINI = 0x40;
const CSTICK_ON = 0x80;
const STOCK_SHARE = 0x100;
const ATTACK_RAW = 0x200;
const APPEAL_HI = 0x400;
const SPECIAL_RAW = 0x800;
const APPEAL_LW = 0x1000;
const APPEAL_SL = 0x2000;
const APPEAL_SR = 0x4000;
const FLICK_JUMP = 0x8000;
const GUARD_HOLD = 0x10000;
const SPECIAL_RAW2 = 0x20000;
}
}
// Controller class used internally by the game
#[repr(C)]
pub struct Controller {
pub vtable: *const u64,
pub current_buttons: ButtonBitfield,
pub previous_buttons: ButtonBitfield,
pub left_stick_x: f32,
pub left_stick_y: f32,
pub left_trigger: f32,
pub _left_padding: u32,
pub right_stick_x: f32,
pub right_stick_y: f32,
pub right_trigger: f32,
pub _right_padding: u32,
pub gyro: [f32; 4],
pub button_timespan: AutorepeatInfo,
pub lstick_timespan: AutorepeatInfo,
pub rstick_timespan: AutorepeatInfo,
pub just_down: ButtonBitfield,
pub just_release: ButtonBitfield,
pub autorepeat_keys: u32,
pub autorepeat_threshold: u32,
pub autorepeat_initial_press_threshold: u32,
pub style: ControllerStyle,
pub controller_id: u32,
pub primary_controller_color1: u32,
pub primary_controller_color2: u32,
pub secondary_controller_color1: u32,
pub secondary_controller_color2: u32,
pub led_pattern: u8,
pub button_autorepeat_initial_press: bool,
pub lstick_autorepeat_initial_press: bool,
pub rstick_autorepeat_initial_press: bool,
pub is_valid_controller: bool,
pub _x_b9: [u8; 2],
pub is_connected: bool,
pub is_left_connected: bool,
pub is_right_connected: bool,
pub is_wired: bool,
pub is_left_wired: bool,
pub is_right_wired: bool,
pub _x_c1: [u8; 3],
pub npad_number: u32,
pub _x_c8: [u8; 8],
}
// SomeControllerStruct used in hooked function - need to ask blujay what this is again
#[repr(C)]
pub struct SomeControllerStruct {
padding: [u8; 0x10],
controller: &'static mut Controller,
}
// Define struct used for final controller inputs
#[derive(Copy, Clone)]
#[repr(C)]
pub struct MappedInputs {
pub buttons: Buttons,
pub lstick_x: i8,
pub lstick_y: i8,
pub rstick_x: i8,
pub rstick_y: i8,
}
impl MappedInputs {
// pub needed?
pub fn default() -> MappedInputs {
MappedInputs {
buttons: Buttons::empty(),
lstick_x: 0,
lstick_y: 0,
rstick_x: 0,
rstick_y: 0,
}
}
}
// Final Structure containing all input recording slots, menu options, and save states.
// 5 Input Recording Slots should be fine for now for most mix up scenarios
// When loading a "scenario", we want to load all menu options (with maybe overrides in the config?), load savestate(s), and load input recording slots.
// If we have submenus for input recording slots, we need to get that info as well, and we want to apply saved damage from save states to the menu.
// Damage range seems to be saved in menu for range of damage, so that's taken care of with menu.
#[derive(Clone)]
#[repr(C)]
pub struct Scenario {
pub record_slots: Vec<Vec<MappedInputs>>,
pub starting_statuses: Vec<i32>,
pub menu: TrainingModpackMenu,
pub save_states: Vec<SavedState>,
pub player_char: i32, // fighter_kind
pub cpu_char: i32, // fighter_kind
pub stage: i32, // index of stage, but -1 = random
pub title: String,
pub description: String,
pub mod_version: String,
pub smash_version: String,
// depending on version, we need to modify newly added menu options, so that regardless of their defaults they reflect the previous version to minimize breakage of old scenarios
// we may also add more scenario parts to the struct in the future etc.
// pub screenshot: image????
// datetime?
// author?
// mirroring?
}
impl Scenario {
pub fn default() -> Scenario {
Scenario {
record_slots: vec![vec![MappedInputs::default(); 600]; 5],
starting_statuses: vec![0],
menu: crate::common::consts::DEFAULTS_MENU,
save_states: vec![default_save_state!(); 5],
player_char: 0,
cpu_char: 0,
stage: -1, // index of stage, but -1 = random/any stage
title: "Scenario Title".to_string(),
description: "Description...".to_string(),
mod_version: CURRENT_VERSION.to_string(),
smash_version: smash_version(),
}
}
}

View file

@ -34,6 +34,7 @@ mod character_specific;
mod fast_fall;
mod full_hop;
pub mod input_delay;
mod input_log;
mod input_record;
mod mash;
mod reset;
@ -712,15 +713,34 @@ unsafe fn handle_final_input_mapping(
controller_struct: &mut SomeControllerStruct,
arg: bool,
) {
// go through the original mapping function first
// Order of hooks here REALLY matters. Tread lightly
// Go through the original mapping function first
original!()(mappings, player_idx, out, controller_struct, arg);
if !is_training_mode() {
return;
}
// Grab button input requests from player
// Non-mutable pull
button_config::handle_final_input_mapping(player_idx, controller_struct);
// Grab menu inputs from player
// Non-mutable pull
menu::handle_final_input_mapping(player_idx, controller_struct, out);
// Check if we should apply hot reload configs
// Non-mutable pull
dev_config::handle_final_input_mapping(player_idx, controller_struct);
// Potentially apply input delay
// MUTATES controller state
input_delay::handle_final_input_mapping(player_idx, out);
input_log::handle_final_input_mapping(player_idx, out);
// Potentially apply input recording, thus with delay
// MUTATES controller state
input_record::handle_final_input_mapping(player_idx, out);
}

View file

@ -0,0 +1,69 @@
use skyline::nn::ui2d::*;
use smash::ui2d::{SmashPane, SmashTextBox};
use training_mod_consts::ButtonConfig;
use crate::{
common::{
button_config::name_to_font_glyph,
input::Buttons,
menu::{P1_CONTROLLER_STYLE, QUICK_MENU_ACTIVE},
},
training::input_log::P1_INPUT_MAPPINGS,
};
macro_rules! log_parent_fmt {
($x:ident) => {
format!("TrModInputLog{}", $x).as_str()
};
}
pub unsafe fn draw(root_pane: &Pane) {
let log_number = 0;
let log_pane = root_pane
.find_pane_by_name_recursive(log_parent_fmt!(log_number))
.unwrap();
// TODO: And menu option for input log is on
log_pane.set_visible(!QUICK_MENU_ACTIVE);
let logs_ptr = P1_INPUT_MAPPINGS.data_ptr();
if logs_ptr.is_null() {
return;
}
let logs = &*logs_ptr;
let first_log = logs.front();
if first_log.is_none() {
return;
}
let first_log = first_log.unwrap();
let p1_style_ptr = P1_CONTROLLER_STYLE.data_ptr();
if p1_style_ptr.is_null() {
return;
}
let input_pane = log_pane
.find_pane_by_name_recursive("InputTxt")
.unwrap()
.as_textbox();
input_pane.set_text_string("NONE");
if first_log.buttons.contains(Buttons::ATTACK) {
let potential_font_glyph = name_to_font_glyph(ButtonConfig::A, *p1_style_ptr);
if let Some(font_glyph) = potential_font_glyph {
input_pane.set_text_string("");
let it = input_pane.text_buf as *mut u16;
input_pane.text_len = 1;
*it = font_glyph;
*(it.add(1)) = 0x0;
}
}
log_pane
.find_pane_by_name_recursive("FrameTxt")
.unwrap()
.as_textbox()
.set_text_string(format!("{}", logs.len()).as_str());
}

View file

@ -10,6 +10,7 @@ use crate::consts::LAYOUT_ARC_PATH;
mod damage;
mod display;
mod input_log;
mod menu;
pub mod notifications;
@ -38,6 +39,7 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
damage::draw(root_pane, &layout_name);
if layout_name == "info_training" {
input_log::draw(root_pane);
display::draw(root_pane);
menu::draw(root_pane);
}