-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 44f8474
Showing
11 changed files
with
427 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
target/ | ||
Cargo.lock |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
[package] | ||
name = "stage_config" | ||
version = "0.1.0" | ||
authors = ["ThatNintendoNerd"] | ||
edition = "2021" | ||
|
||
[package.metadata.skyline] | ||
titleid = "01006A800016E000" | ||
|
||
[lib] | ||
crate-type = ["cdylib"] | ||
|
||
[dependencies] | ||
skyline = { git = "https://github.com/ultimate-research/skyline-rs.git" } | ||
smash = { git = "https://github.com/blu-dev/smash-rs.git" } | ||
smash_stage = { path = "../smash_stage", features = ["serde"] } | ||
hash40 = "1.1.0" | ||
arcropolis-api = { git = "https://github.com/Raytwo/arcropolis_api.git" } | ||
once_cell = "1.18.0" | ||
serde = { version = "1.0", features = ["derive"] } | ||
toml = "0.8.0" | ||
walkdir = "2" | ||
|
||
[profile.dev] | ||
panic = "abort" | ||
|
||
[profile.release] | ||
panic = "abort" | ||
lto = true |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# stage_config | ||
|
||
A [Skyline](https://github.com/skyline-dev/skyline) plugin for Super Smash Bros. Ultimate that enables the use and modification of stage features that are otherwise hardcoded into the game. | ||
|
||
The latest release can be found [here](https://github.com/ThatNintendoNerd/stage_config/releases/latest). | ||
|
||
## Features | ||
|
||
Through the use of a configuration file, a stage mod can take advantage of the following features: | ||
|
||
- New dynamic ground collisions | ||
- Flatten or unflatten battle objects | ||
- Custom center of gravity or the removal thereof | ||
- Use of `stage_additional_setting` values from spirit battles outside of Spirits | ||
- Discard specialized stage programming | ||
|
||
For more information about these features, please read the [wiki](https://github.com/ThatNintendoNerd/stage_config/wiki). | ||
|
||
## Building | ||
|
||
NOTE: This project cannot be compiled without the smash_stage library. Said library is unreleased due to its incomplete state, but its release is planned. | ||
|
||
With an up-to-date version of the Rust toolchain installed and [cargo-skyline](https://github.com/jam1garner/cargo-skyline) 3.0.0 or newer, run the following command to compile the project in release mode: | ||
|
||
``` | ||
cargo skyline build --release | ||
``` | ||
|
||
The resulting build is found at `./target/aarch64-skyline-switch/release/libstage_config.nro` |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use std::collections::{HashMap, HashSet}; | ||
use std::fs; | ||
|
||
use hash40::Hash40; | ||
use once_cell::sync::Lazy; | ||
use serde::Deserialize; | ||
use smash_stage::app::StageID; | ||
use walkdir::WalkDir; | ||
|
||
use crate::hooks::gravity::GravityParam; | ||
|
||
pub static CONFIG: Lazy<Config> = Lazy::new(|| { | ||
let mut config = Config::new(); | ||
|
||
for entry in WalkDir::new("sd:/ultimate/mods/") | ||
.min_depth(1) | ||
.max_depth(1) | ||
.into_iter() | ||
.filter_map(|e| e.ok()) | ||
{ | ||
let mut entry_path = entry.into_path(); | ||
|
||
if !arcropolis_api::is_mod_enabled(arcropolis_api::hash40( | ||
entry_path.to_str().unwrap_or_default(), | ||
)) { | ||
continue; | ||
} | ||
|
||
entry_path.push("config_stage.toml"); | ||
|
||
if entry_path.is_file() { | ||
if let Ok(string) = fs::read_to_string(&entry_path) { | ||
match toml::from_str(&string) { | ||
Ok(cfg) => config.merge(cfg), | ||
Err(e) => { | ||
eprintln!( | ||
"[stage_config::config] Failed to parse TOML data from file '{}': {}", | ||
entry_path.display(), | ||
e | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
config | ||
}); | ||
|
||
#[derive(Deserialize)] | ||
pub struct Config { | ||
#[serde(default)] | ||
pub new_dynamic_collisions: HashMap<StageID, HashSet<Hash40>>, | ||
|
||
#[serde(default)] | ||
pub is_flat_stage: HashMap<StageID, bool>, | ||
|
||
#[serde(default)] | ||
pub gravity_param: HashMap<StageID, GravityParam>, | ||
|
||
#[serde(default)] | ||
pub stage_additional_settings: HashMap<StageID, i8>, | ||
|
||
#[serde(default)] | ||
pub discard_stage_code: Vec<StageID>, | ||
} | ||
|
||
impl Config { | ||
fn new() -> Self { | ||
Self { | ||
new_dynamic_collisions: HashMap::new(), | ||
is_flat_stage: HashMap::new(), | ||
gravity_param: HashMap::new(), | ||
stage_additional_settings: HashMap::new(), | ||
discard_stage_code: Vec::new(), | ||
} | ||
} | ||
|
||
fn merge(&mut self, other: Self) { | ||
let Self { | ||
new_dynamic_collisions, | ||
is_flat_stage, | ||
gravity_param, | ||
stage_additional_settings, | ||
discard_stage_code, | ||
} = other; | ||
|
||
self.new_dynamic_collisions.extend(new_dynamic_collisions); | ||
self.is_flat_stage.extend(is_flat_stage); | ||
self.gravity_param.extend(gravity_param); | ||
self.stage_additional_settings | ||
.extend(stage_additional_settings); | ||
self.discard_stage_code.extend(discard_stage_code); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub(super) mod gravity; | ||
pub(super) mod ground; | ||
pub(super) mod settings; | ||
pub(super) mod stage; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use serde::Deserialize; | ||
use smash::app; | ||
use smash_stage::app::StageID; | ||
|
||
use crate::config::CONFIG; | ||
|
||
/// Parameters for gravity. | ||
#[derive(Deserialize)] | ||
pub struct GravityParam { | ||
/// Boolean flag determining if the stage assumes a flat gravitational plane. | ||
#[serde(default)] | ||
is_gravity_normal: bool, | ||
|
||
/// Center position of gravity. | ||
#[serde(default)] | ||
pos: Option<GravityCenter>, | ||
} | ||
|
||
/// Center position of gravity. | ||
#[derive(Deserialize)] | ||
struct GravityCenter { | ||
/// Position along the x-axis. | ||
#[serde(default)] | ||
x: f32, | ||
|
||
/// Position along the y-axis. | ||
#[serde(default)] | ||
y: f32, | ||
} | ||
|
||
pub fn set_gravity_param(stage_id: StageID) { | ||
for (stage, param) in CONFIG.gravity_param.iter() { | ||
if *stage == stage_id { | ||
if let Some(instance) = app::BattleObjectWorld::instance_mut() { | ||
if instance.is_gravity_normal != param.is_gravity_normal { | ||
instance.is_gravity_normal = param.is_gravity_normal; | ||
} | ||
|
||
if !instance.is_gravity_normal { | ||
if let Some(pos) = ¶m.pos { | ||
instance.gravity_pos.x = pos.x; | ||
instance.gravity_pos.y = pos.y; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use smash_stage::app::StageBase; | ||
|
||
use crate::config::CONFIG; | ||
|
||
pub fn register_dynamic_collisions(stage_base: &StageBase) { | ||
let stage_id = stage_base.stage_description.stage_id(); | ||
|
||
for (stage, models) in CONFIG.new_dynamic_collisions.iter() { | ||
if stage_id == *stage { | ||
for model_name in models.iter() { | ||
unsafe { | ||
for dynamic_object in | ||
(*(*stage_base.level_data).dynamic_object_collection).iter() | ||
{ | ||
if (**dynamic_object).name_hash == *model_name | ||
&& stage_base.search_draw_model(*model_name).is_some() | ||
{ | ||
stage_base.create_model_related_move_floor(&**dynamic_object); | ||
} | ||
} | ||
} | ||
} | ||
|
||
break; | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
use smash_stage::app::{MeleeMode, SpiritsBattleData, StageDescription}; | ||
|
||
use crate::{config::CONFIG, offsets::OFFSETS}; | ||
|
||
#[skyline::hook(offset = OFFSETS.set_stage_random_settings)] | ||
pub fn set_stage_random_settings(stage_description: &mut StageDescription, seed: u32) { | ||
if !matches!( | ||
MeleeMode::instance(), | ||
MeleeMode::Standard | ||
| MeleeMode::StandardMulti | ||
| MeleeMode::SpiritsBattle | ||
| MeleeMode::SpiritsBattleMulti | ||
) { | ||
let stage_id = stage_description.stage_id(); | ||
|
||
for (stage, setting) in CONFIG.stage_additional_settings.iter() { | ||
if *stage == stage_id && *setting != 0 { | ||
let mut spirits_battle_data = SpiritsBattleData::default(); | ||
|
||
spirits_battle_data.stage_id = stage_id; | ||
spirits_battle_data.stage_additional_setting = *setting; | ||
|
||
unsafe { | ||
set_stage_additional_settings(&spirits_battle_data, stage_description); | ||
} | ||
|
||
break; | ||
} | ||
} | ||
} | ||
|
||
original!()(stage_description, seed); | ||
} | ||
|
||
#[skyline::from_offset(OFFSETS.set_stage_additional_settings)] | ||
fn set_stage_additional_settings( | ||
spirits_battle_data: &SpiritsBattleData, | ||
stage_description: &mut StageDescription, | ||
); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use std::mem; | ||
|
||
use skyline::patching::Patch; | ||
use smash_stage::app::{StageBase, StageID}; | ||
|
||
use crate::{config::CONFIG, hooks, offsets::OFFSETS}; | ||
|
||
#[skyline::hook(offset = OFFSETS.stage_base_pre_setup)] | ||
pub fn stage_base_pre_setup(stage_base: &StageBase) { | ||
original!()(stage_base); | ||
|
||
hooks::ground::register_dynamic_collisions(stage_base); | ||
hooks::gravity::set_gravity_param(stage_base.stage_description.stage_id()); | ||
} | ||
|
||
#[skyline::hook(offset = OFFSETS.is_flat_stage)] | ||
pub fn is_flat_stage(stage_id: StageID) -> bool { | ||
for (stage, value) in CONFIG.is_flat_stage.iter() { | ||
if *stage == stage_id { | ||
return *value; | ||
} | ||
} | ||
|
||
original!()(stage_id) | ||
} | ||
|
||
pub fn patch_create_stage_jump_table() { | ||
for stage in CONFIG.discard_stage_code.iter() { | ||
Patch::in_text( | ||
OFFSETS.create_stage_jump_table + (*stage as usize) * mem::size_of::<StageID>(), | ||
) | ||
.data(0xFE12E38C_u32) | ||
.unwrap(); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use once_cell::sync::Lazy; | ||
|
||
mod config; | ||
mod hooks; | ||
mod offsets; | ||
|
||
use config::CONFIG; | ||
use offsets::OFFSETS; | ||
|
||
#[skyline::main(name = "stage_config")] | ||
fn main() { | ||
Lazy::force(&OFFSETS); | ||
skyline::install_hooks!( | ||
hooks::stage::stage_base_pre_setup, | ||
hooks::stage::is_flat_stage, | ||
hooks::settings::set_stage_random_settings, | ||
); | ||
Lazy::force(&CONFIG); | ||
hooks::stage::patch_create_stage_jump_table(); | ||
} |
Oops, something went wrong.