1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-11-27 20:34:03 +00:00

Randomized percent on load (#394)

* Create save states tab

* Initial work - crashes on boot

* Change usizes to u32's.
Refactor toggle "checked" logic.
Add blujay's panic tracker.

* Rename tui mins/maxes

* Fix misc. TUI bugs

* Fix panic caused by prematurely setting the submenu state to GaugeState::None

Set submenu state to GaugeState::MinHover when opening a slider menu so that the slider is immediately loaded

When changing from GaugeState::Min/MaxSelected to Min/MaxHover, commit changes from App.current_sub_menu_slider to SubMenu.slider so that it can be exported to JSON

* Merge save_damage and save_state_pct_rand_enable settings

* Add comments to training_mod_tui::lib.rs

* Add icon

* Initial work on web slider

Todo:
Styling polish
Bugfix for initial settings load
Handle dragging using gamepad

* Style and fix web slider

* Add separate settings for player random damage

* TUI styling fixes

* Paginate TUI tabs

* Address CR comments
This commit is contained in:
asimon-1 2022-11-01 13:52:38 -07:00 committed by GitHub
parent aea5011a89
commit fa451986e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 3063 additions and 2004 deletions

View file

@ -96,7 +96,7 @@ pub unsafe fn set_menu_from_json(message: &str) {
skyline::error::show_error( skyline::error::show_error(
0x70, 0x70,
"Could not parse the menu response!\nPlease send a screenshot of the details page to the developers.\n\0", "Could not parse the menu response!\nPlease send a screenshot of the details page to the developers.\n\0",
&*format!("{:#?}\0", web_response.err().unwrap()) &*format!("{:#?}\0", message)
); );
}; };
if MENU.quick_menu == OnOff::Off { if MENU.quick_menu == OnOff::Off {

View file

@ -54,6 +54,27 @@ macro_rules! c_str {
#[skyline::main(name = "training_modpack")] #[skyline::main(name = "training_modpack")]
pub fn main() { pub fn main() {
std::panic::set_hook(Box::new(|info| {
let location = info.location().unwrap();
let msg = match info.payload().downcast_ref::<&'static str>() {
Some(s) => *s,
None => {
match info.payload().downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
}
},
};
let err_msg = format!("thread has panicked at '{}', {}", msg, location);
skyline::error::show_error(
69,
"Skyline plugin has panicked! Please open the details and send a screenshot to the developer, then close the game.\n",
err_msg.as_str(),
);
}));
macro_rules! log { macro_rules! log {
($($arg:tt)*) => { ($($arg:tt)*) => {
println!("{}{}", "[Training Modpack] ".green(), format!($($arg)*)); println!("{}{}", "[Training Modpack] ".green(), format!($($arg)*));

1
src/static/css/nouislider.min.css vendored Normal file
View file

@ -0,0 +1 @@
.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;right:0;height:100%;width:100%;-ms-transform-origin:0 0;-webkit-transform-origin:0 0;-webkit-transform-style:preserve-3d;transform-origin:0 0;transform-style:flat}.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin{left:0;right:auto}.noUi-vertical .noUi-origin{top:-100%;width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute}.noUi-touch-area{height:100%;width:100%}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;right:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;right:-6px;bottom:-17px}.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle{left:-17px;right:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-sub{background:#AAA}.noUi-marker-large{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%}.noUi-horizontal .noUi-origin>.noUi-tooltip{-webkit-transform:translate(50%,0);transform:translate(50%,0);left:auto;bottom:10px}.noUi-vertical .noUi-origin>.noUi-tooltip{-webkit-transform:translate(0,-18px);transform:translate(0,-18px);top:auto;right:28px}

View file

@ -185,7 +185,10 @@ body {
visibility: hidden; visibility: hidden;
} }
:focus { :focus:not(.noUi-handle),
.handleSelected,
.noUi-connect
{
background: rgb(255, 70, 2); background: rgb(255, 70, 2);
background: linear-gradient( background: linear-gradient(
45deg, 45deg,
@ -302,3 +305,38 @@ body {
text-align: center; text-align: center;
font-size: large; font-size: large;
} }
.modal-slider {
width: 70%;
margin-bottom: 170px;
}
.modal-slider-label {
width: 100%;
display: flex;
justify-content: center;
}
.modal-slider-label > p {
font-size: 26px;
padding: 0.5rem;
background: #ccc;
border-color: black;
border-radius: 0.5rem;
border-style: solid;
border-width: 0.25rem;
}
.noUi-value, .noUi-pips {
margin-top: 12px;
color: #000;
}
.noUi-marker, .noUi-marker-large {
background: #000;
}
.noUi-tooltip {
padding: 12px;
font-size: 1.5rem;
}

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80.0px"
height="80.0px"
viewBox="0 0 80.0 80.0"
version="1.1"
id="SVGRoot"
sodipodi:docname="save_damage_limits.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs5493" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="7.9195959"
inkscape:cx="43.373425"
inkscape:cy="38.322662"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="true"
inkscape:window-width="1904"
inkscape:window-height="1001"
inkscape:window-x="192"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="1">
<inkscape:grid
type="xygrid"
id="grid6063" />
</sodipodi:namedview>
<metadata
id="metadata5496">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#000000;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 15,65 65,15"
id="path6070" />
<circle
style="opacity:1;fill:none;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
id="path6072"
cx="22.5"
cy="22.5"
r="7.5" />
<circle
style="opacity:1;fill:none;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
id="path6072-8"
cx="60.051399"
cy="55.43021"
r="7.5" />
<path
style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 10,5 H 70"
id="path993" />
<path
style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,75 H 70"
id="path993-8" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80.0px"
height="80.0px"
viewBox="0 0 80.0 80.0"
version="1.1"
id="SVGRoot"
sodipodi:docname="save_damage_limits.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs5493" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="7.9195959"
inkscape:cx="43.373425"
inkscape:cy="38.322662"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="true"
inkscape:window-width="1904"
inkscape:window-height="1001"
inkscape:window-x="192"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="1">
<inkscape:grid
type="xygrid"
id="grid6063" />
</sodipodi:namedview>
<metadata
id="metadata5496">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#000000;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 15,65 65,15"
id="path6070" />
<circle
style="opacity:1;fill:none;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
id="path6072"
cx="22.5"
cy="22.5"
r="7.5" />
<circle
style="opacity:1;fill:none;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
id="path6072-8"
cx="60.051399"
cy="55.43021"
r="7.5" />
<path
style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 10,5 H 70"
id="path993" />
<path
style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,75 H 70"
id="path993-8" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="80.0px"
height="80.0px"
viewBox="0 0 80.0 80.0"
version="1.1"
id="SVGRoot"
sodipodi:docname="save_damage.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<defs
id="defs5493" />
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="7.9195959"
inkscape:cx="43.461799"
inkscape:cy="28.245216"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="true"
inkscape:window-width="1431"
inkscape:window-height="1150"
inkscape:window-x="192"
inkscape:window-y="0"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid6063" />
</sodipodi:namedview>
<metadata
id="metadata5496">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#000000;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 15,65 65,15"
id="path6070" />
<circle
style="opacity:1;fill:none;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
id="path6072"
cx="22.5"
cy="22.5"
r="7.5" />
<circle
style="opacity:1;fill:none;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
id="path6072-8"
cx="60.051399"
cy="55.43021"
r="7.5" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
src/static/js/nouislider.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -23,17 +23,31 @@ var DEFAULTS_PREFIX = '__';
// Set input handlers // Set input handlers
if (isNx) { if (isNx) {
window.nx.footer.setAssign('A', '', function () { select(document.activeElement) }, { se: '' });
window.nx.footer.setAssign('B', '', closeOrExit, { se: '' }); window.nx.footer.setAssign('B', '', closeOrExit, { se: '' });
window.nx.footer.setAssign('X', '', resetCurrentMenu, { se: '' }); window.nx.footer.setAssign('X', '', resetCurrentMenu, { se: '' });
window.nx.footer.setAssign('L', '', resetAllMenus, { se: '' }); window.nx.footer.setAssign('L', '', resetAllMenus, { se: '' });
window.nx.footer.setAssign('R', '', saveDefaults, { se: '' }); window.nx.footer.setAssign('R', '', saveDefaults, { se: '' });
window.nx.footer.setAssign('ZR', '', cycleNextTab, { se: '' }); window.nx.footer.setAssign('ZR', '', cycleNextTab, { se: '' });
window.nx.footer.setAssign('ZL', '', cyclePrevTab, { se: '' }); window.nx.footer.setAssign('ZL', '', cyclePrevTab, { se: '' });
window.nx.addEventListener("message", function(msg) { setSettingsFromJSON(msg)}); window.nx.addEventListener("message", function (msg) { setSettingsFromJSON(msg.data) });
window.nx.sendMessage("loaded"); document.addEventListener('keydown', (event) => {
switch (event.keyCode) {
case 37: // Control stick left
decreaseSelectedHandle();
break;
case 39: // Control stick right
increaseSelectedHandle();
break;
}
})
} else { } else {
document.addEventListener('keypress', (event) => { document.addEventListener('keydown', (event) => {
switch (event.key) { switch (event.key) {
case 'a':
console.log('a');
select(document.activeElement);
break;
case 'b': case 'b':
console.log('b'); console.log('b');
closeOrExit(); closeOrExit();
@ -58,6 +72,14 @@ if (isNx) {
console.log('o'); console.log('o');
cyclePrevTab(); cyclePrevTab();
break; break;
case 'ArrowLeft':
console.log('ArrowLeft');
decreaseSelectedHandle();
break;
case 'ArrowRight':
console.log('ArrowRight');
increaseSelectedHandle();
break;
} }
}); });
} }
@ -65,6 +87,13 @@ if (isNx) {
const onLoad = () => { const onLoad = () => {
// Activate the first tab // Activate the first tab
openTab(document.querySelector('button.tab-button')); openTab(document.querySelector('button.tab-button'));
initializeAllSliders();
if (isNx) {
window.nx.sendMessage("loaded");
} else {
settings = {};
setSettingsFromJSON("{\"menu\":{\"aerial_delay\":0,\"air_dodge_dir\":0,\"attack_angle\":0,\"buff_state\":0,\"character_item\":0,\"clatter_strength\":0,\"crouch\":0,\"di_state\":0,\"falling_aerials\":0,\"fast_fall_delay\":0,\"fast_fall\":0,\"follow_up\":0,\"frame_advantage\":0,\"full_hop\":0,\"hitbox_vis\":1,\"input_delay\":1,\"ledge_delay\":0,\"ledge_state\":31,\"mash_state\":0,\"mash_triggers\":131,\"miss_tech_state\":15,\"oos_offset\":0,\"pummel_delay\":0,\"quick_menu\":0,\"reaction_time\":0,\"save_damage\":4,\"save_damage_limits\":[63,106],\"save_state_autoload\":1,\"save_state_enable\":1,\"save_state_mirroring\":1,\"sdi_state\":0,\"sdi_strength\":0,\"shield_state\":0,\"shield_tilt\":0,\"stage_hazards\":0,\"tech_state\":15,\"throw_delay\":0,\"throw_state\":1},\"defaults_menu\":{\"aerial_delay\":0,\"air_dodge_dir\":0,\"attack_angle\":0,\"buff_state\":0,\"character_item\":0,\"clatter_strength\":0,\"crouch\":0,\"di_state\":0,\"falling_aerials\":0,\"fast_fall_delay\":0,\"fast_fall\":0,\"follow_up\":0,\"frame_advantage\":0,\"full_hop\":0,\"hitbox_vis\":1,\"input_delay\":1,\"ledge_delay\":0,\"ledge_state\":31,\"mash_state\":0,\"mash_triggers\":131,\"miss_tech_state\":15,\"oos_offset\":0,\"pummel_delay\":0,\"quick_menu\":0,\"reaction_time\":0,\"save_damage\":4,\"save_damage_limits\":[41,118],\"save_state_autoload\":1,\"save_state_enable\":1,\"save_state_mirroring\":1,\"sdi_state\":0,\"sdi_strength\":0,\"shield_state\":0,\"shield_tilt\":0,\"stage_hazards\":0,\"tech_state\":15,\"throw_delay\":0,\"throw_state\":1}}");
}
}; };
window.onload = onLoad; window.onload = onLoad;
@ -73,6 +102,8 @@ var settings;
var defaultSettings; var defaultSettings;
var lastFocusedItem = document.querySelector('.menu-item > button'); var lastFocusedItem = document.querySelector('.menu-item > button');
var selectedSliderHandle = -1;
const currentTabContent = () => { const currentTabContent = () => {
const currentActiveTab = document.querySelector('.tab-button.active'); const currentActiveTab = document.querySelector('.tab-button.active');
@ -114,7 +145,11 @@ const openMenuItem = (eventTarget) => {
currentTabContent().classList.toggle('hide'); currentTabContent().classList.toggle('hide');
modal.classList.toggle('hide'); modal.classList.toggle('hide');
modal.querySelector('button').focus(); elem = modal.querySelector('button');
if (!elem) {
elem = modal.querySelector('.noUi-handle-lower')
}
elem.focus();
lastFocusedItem = eventTarget; lastFocusedItem = eventTarget;
}; };
@ -195,6 +230,19 @@ const exit = () => {
}; };
function closeOrExit() { function closeOrExit() {
// Deselect any sliders
handlesWereSelected = deselectSliderHandles();
if (handlesWereSelected) {return}
selectedHandles = document.querySelectorAll(".handleSelected");
if (selectedHandles.length) {
console.log("Found selected handles");
selectedHandles.forEach((handle) => {
handle.classList.remove("handleSelected");
});
return;
}
// Close any open menus // Close any open menus
if (document.querySelector('.modal:not(.hide)')) { if (document.querySelector('.modal:not(.hide)')) {
console.log('Closing Items'); console.log('Closing Items');
@ -210,52 +258,12 @@ function closeOrExit() {
function setSettingsFromJSON(msg) { function setSettingsFromJSON(msg) {
// Receive a menu message and set settings // Receive a menu message and set settings
var msg_json = JSON.parse(msg.data); var msg_json = JSON.parse(msg);
settings = msg_json["menu"]; settings = msg_json["menu"];
defaultSettings = msg_json["defaults_menu"]; defaultSettings = msg_json["defaults_menu"];
populateMenuFromSettings(); populateMenuFromSettings();
} }
function setSettingsFromURL() {
var { search } = window.location;
// Actual settings
const settingsFromSearch = search
.replace('?', '')
.split('&')
.reduce((accumulator, currentValue) => {
var [key, value] = currentValue.split('=');
if (!key.startsWith('__')) {
accumulator[key] = parseInt(value);
}
return accumulator;
}, {});
settings = settingsFromSearch;
// Default settings
const defaultSettingsFromSearch = search
.replace('?', '')
.split('&')
.reduce((accumulator, currentValue) => {
var [key, value] = currentValue.split('=');
if (key.startsWith('__')) {
accumulator[key.replace('__','')] = parseInt(value);
}
return accumulator;
}, {});
defaultSettings = defaultSettingsFromSearch;
populateMenuFromSettings()
}
function buildURLFromSettings() {
const url = 'http://localhost/?';
const urlParams = Object.entries(settings).map((setting) => {
return `${setting[0]}=${setting[1]}`;
});
return url + urlParams.join('&');
}
function selectSingleOption(eventTarget) { function selectSingleOption(eventTarget) {
// Deselect all options in the submenu // Deselect all options in the submenu
const parent = eventTarget.parentElement; const parent = eventTarget.parentElement;
@ -277,19 +285,26 @@ const isValueInBitmask = (value, mask) => (mask & value) != 0;
const setOptionsForMenu = (menuId) => { const setOptionsForMenu = (menuId) => {
const modal = document.querySelector(`.modal[data-id="${menuId}"]`); const modal = document.querySelector(`.modal[data-id="${menuId}"]`);
modal.querySelectorAll('.menu-icon').forEach(function (toggle) { if (modal.querySelector('.modal-button')) {
if (isValueInBitmask(toggle.dataset.val, settings[menuId])) { // Toggle menu
toggle.classList.remove('hidden'); modal.querySelectorAll('.menu-icon').forEach(function (toggle) {
} else { if (isValueInBitmask(toggle.dataset.val, settings[menuId])) {
toggle.classList.add('hidden'); toggle.classList.remove('hidden');
} } else {
}); toggle.classList.add('hidden');
}
});
if (modal.classList.contains('single-option')) { if (modal.classList.contains('single-option')) {
// If no option is selected default to the first option // If no option is selected default to the first option
if (modal.querySelectorAll('.menu-icon:not(.hidden)').length === 0) { if (modal.querySelectorAll('.menu-icon:not(.hidden)').length === 0) {
selectSingleOption(modal.querySelector('button')); selectSingleOption(modal.querySelector('button'));
}
} }
} else {
// Slider menu
slider = modal.querySelector('.modal-slider');
setSliderVals(slider, settings[menuId]);
} }
}; };
@ -297,17 +312,25 @@ function populateMenuFromSettings() {
document.querySelectorAll('.menu-item').forEach((item) => setOptionsForMenu(item.id)); document.querySelectorAll('.menu-item').forEach((item) => setOptionsForMenu(item.id));
} }
function getMaskFromMenuID(id) { function getSettingsValFromMenuID(id) {
var value = 0;
const modal = document.querySelector(`.modal[data-id='${id}']`); const modal = document.querySelector(`.modal[data-id='${id}']`);
const options = modal.querySelectorAll('img:not(.hidden)'); if (modal.querySelector('.modal-button')) {
// Toggle menu
// Return value is a bitmask
var value = 0;
const options = modal.querySelectorAll('img:not(.hidden)');
options.forEach(function (toggle) { options.forEach(function (toggle) {
value += parseInt(toggle.dataset.val); value += parseInt(toggle.dataset.val);
}); });
return value;
return value; } else {
// Slider menu
// Return value is a [lower,upper] array
slider = modal.querySelector('.modal-slider');
return getSliderVals(slider);
}
} }
function resetCurrentMenu() { function resetCurrentMenu() {
@ -315,10 +338,11 @@ function resetCurrentMenu() {
const menu = document.querySelector('.modal:not(.hide)'); const menu = document.querySelector('.modal:not(.hide)');
const menuId = menu.dataset.id; const menuId = menu.dataset.id;
const defaultSectionMask = defaultSettings[menuId]; const defaultSubmenuSetting = defaultSettings[menuId];
settings[menuId] = defaultSectionMask; settings[menuId] = defaultSubmenuSetting;
deselectSliderHandles();
populateMenuFromSettings(); populateMenuFromSettings();
} }
@ -327,10 +351,11 @@ function resetAllMenus() {
if (confirm('Are you sure that you want to reset all menu settings to the default?')) { if (confirm('Are you sure that you want to reset all menu settings to the default?')) {
document.querySelectorAll('.menu-item').forEach(function (item) { document.querySelectorAll('.menu-item').forEach(function (item) {
const defaultMenuId = item.id; const defaultMenuId = item.id;
const defaultMask = defaultSettings[defaultMenuId]; const defaultSubmenuSetting = defaultSettings[defaultMenuId];
settings[item.id] = defaultMask; settings[item.id] = defaultSubmenuSetting;
deselectSliderHandles();
populateMenuFromSettings(); populateMenuFromSettings();
}); });
} }
@ -344,13 +369,13 @@ function saveDefaults() {
if (confirm('Are you sure that you want to change the default menu settings to the current selections?')) { if (confirm('Are you sure that you want to change the default menu settings to the current selections?')) {
document.querySelectorAll('.menu-item').forEach((item) => { document.querySelectorAll('.menu-item').forEach((item) => {
const menu = item.id; const menu = item.id;
defaultSettings[menu] = getSettingsValFromMenuID(item.id);
defaultSettings[menu] = getMaskFromMenuID(item.id);
}); });
} }
} }
function cycleNextTab() { function cycleNextTab() {
deselectSliderHandles();
// Cycle to the next tab // Cycle to the next tab
const activeTab = document.querySelector('.tab-button.active'); const activeTab = document.querySelector('.tab-button.active');
var nextTab = activeTab.nextElementSibling; var nextTab = activeTab.nextElementSibling;
@ -362,6 +387,7 @@ function cycleNextTab() {
} }
function cyclePrevTab() { function cyclePrevTab() {
deselectSliderHandles();
// Cycle to the previous tab // Cycle to the previous tab
const activeTab = document.querySelector('.tab-button.active'); const activeTab = document.querySelector('.tab-button.active');
var previousTab = activeTab.previousElementSibling; var previousTab = activeTab.previousElementSibling;
@ -372,3 +398,124 @@ function cyclePrevTab() {
} }
openTab(previousTab); openTab(previousTab);
} }
function getSliderVals(slider) {
var arr = slider.noUiSlider.get();
return [parseFloat(arr[0]), parseFloat(arr[1])]
}
function setSliderVals(slider, vals) {
slider.noUiSlider.set(vals);
}
function setSettingsFromSlider(slider) {
menuId = closestClass(slider, "modal").dataset.id;
settings[menuId] = getSliderVals(slider)
}
function initializeSlider(slider) {
noUiSlider.create(
slider,
{
start: [
parseFloat(slider.dataset.selectedMin),
parseFloat(slider.dataset.selectedMax),
],
connect: true,
range: {
'min': parseFloat(slider.dataset.absMin),
'max': parseFloat(slider.dataset.absMax),
},
step: 1,
tooltips: [
{ to: function (value) { return value.toFixed(0) + '%'; } },
{ to: function (value) { return value.toFixed(0) + '%'; } },
],
pips: {
mode: 'range',
density: 10,
},
keyboardMultiplier: 0, // Prevents doublestepping with custom implementation
}
);
slider.noUiSlider.on('set', function () { setSettingsFromSlider(slider) });
}
function initializeAllSliders() {
document.querySelectorAll(".modal-slider").forEach((item) => {
initializeSlider(item);
});
}
function select(element) {
if (element.classList.contains("noUi-handle")) {
element.classList.toggle("handleSelected");
}
element.click();
}
function increaseSelectedHandle() {
// Increments the selected slider handle, if one is selected
// Won't go past the slider limit
handle = document.querySelector(".noUi-handle.handleSelected");
if (handle) {
slider = closestClass(handle, "modal-slider");
isLowerHandle = handle.classList.contains("noUi-handle-lower");
step = slider.noUiSlider.options.step;
currentVals = getSliderVals(slider);
if (isLowerHandle) {
setSliderVals(
slider,
[currentVals[0] + step, null]
);
} else {
setSliderVals(
slider,
[null, currentVals[1] + step]
);
}
// Refocus the handle, since the native navigation might focus the other handle
// TODO: Is there a more elegant way to do this?
setTimeout( function() {handle.focus() }, 15);
}
}
function decreaseSelectedHandle() {
// Decrements the selected slider handle, if one is selected
// Won't go past the slider limit
handle = document.querySelector(".noUi-handle.handleSelected");
if (handle) {
slider = closestClass(handle, "modal-slider");
isLowerHandle = handle.classList.contains("noUi-handle-lower");
step = slider.noUiSlider.options.step;
currentVals = getSliderVals(slider);
if (isLowerHandle) {
setSliderVals(
slider,
[currentVals[0] - step, null]
);
} else {
setSliderVals(
slider,
[null, currentVals[1] - step]
);
}
// Refocus the handle, since the native navigation might focus the other handle
// TODO: Is there a more elegant way to do this?
setTimeout( function() {handle.focus() }, 15);
}
}
function deselectSliderHandles() {
/// Returns true if any slider handles were changed from selected -> deselected
/// Returns false if there were no selected slider handles to begin with
selectedHandles = document.querySelectorAll(".handleSelected");
if (selectedHandles.length) {
selectedHandles.forEach((handle) => {
handle.classList.remove("handleSelected");
});
return true;
} else {
return false;
}
}

View file

@ -5,10 +5,12 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Modpack Menu</title> <title>Modpack Menu</title>
<link rel="stylesheet" href="./css/nouislider.min.css">
<link rel="stylesheet" href="./css/training_modpack.css" /> <link rel="stylesheet" href="./css/training_modpack.css" />
</head> </head>
<body> <body>
<script defer src="./js/nouislider.min.js"></script>
<script defer src="./js/training_modpack.js"></script> <script defer src="./js/training_modpack.js"></script>
<div class="header"> <div class="header">
<a id="ret-button" tabindex="-1" class="return-icon-container" onclick="closeOrExit()"> <a id="ret-button" tabindex="-1" class="return-icon-container" onclick="closeOrExit()">
@ -42,6 +44,20 @@
</div> </div>
</button> </button>
{{/toggles}} {{/toggles}}
{{#slider}}
<div class="modal-slider-label">
<p>{{submenu_title}}</p>
</div>
<div
id="{{submenu_id}}-slider"
data-selected-min="{{selected_min}}"
data-selected-max="{{selected_max}}"
data-abs-min="{{abs_min}}"
data-abs-max="{{abs_max}}"
class="modal-slider"
>
</div>
{{/slider}}
</div> </div>
{{/tab_submenus}} {{/tab_submenus}}
<div id="{{tab_id}}_tab" class="tab-content hide"> <div id="{{tab_id}}_tab" class="tab-content hide">

View file

@ -1,4 +1,5 @@
use crate::common::button_config; use crate::common::button_config;
use crate::common::consts::get_random_float;
use crate::common::consts::get_random_int; use crate::common::consts::get_random_int;
use crate::common::consts::FighterId; use crate::common::consts::FighterId;
use crate::common::consts::OnOff; use crate::common::consts::OnOff;
@ -15,7 +16,7 @@ use smash::app::{self, lua_bind::*, Item};
use smash::hash40; use smash::hash40;
use smash::lib::lua_const::*; use smash::lib::lua_const::*;
use smash::phx::{Hash40, Vector3f}; use smash::phx::{Hash40, Vector3f};
use training_mod_consts::CharacterItem; use training_mod_consts::{CharacterItem, SaveDamage};
#[derive(PartialEq)] #[derive(PartialEq)]
enum SaveState { enum SaveState {
@ -149,16 +150,6 @@ pub unsafe fn get_param_int(
} }
fn set_damage(module_accessor: &mut app::BattleObjectModuleAccessor, damage: f32) { fn set_damage(module_accessor: &mut app::BattleObjectModuleAccessor, damage: f32) {
let overwrite_damage;
unsafe {
overwrite_damage = MENU.save_damage == OnOff::On;
}
if !overwrite_damage {
return;
}
unsafe { unsafe {
DamageModule::heal( DamageModule::heal(
module_accessor, module_accessor,
@ -365,7 +356,41 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
// If we're done moving, reset percent, handle charges, and apply buffs // If we're done moving, reset percent, handle charges, and apply buffs
if save_state.state == NoAction { if save_state.state == NoAction {
set_damage(module_accessor, save_state.percent); // Set damage of the save state
if !is_cpu {
match MENU.save_damage_player {
SaveDamage::SAVED => {
set_damage(module_accessor, save_state.percent);
}
SaveDamage::RANDOM => {
// Gen random value
let pct: f32 = get_random_float(
MENU.save_damage_limits_player.0 as f32,
MENU.save_damage_limits_player.1 as f32,
);
set_damage(module_accessor, pct);
}
SaveDamage::DEFAULT => {}
_ => {}
}
} else {
match MENU.save_damage_cpu {
SaveDamage::SAVED => {
set_damage(module_accessor, save_state.percent);
}
SaveDamage::RANDOM => {
// Gen random value
let pct: f32 = get_random_float(
MENU.save_damage_limits_cpu.0 as f32,
MENU.save_damage_limits_cpu.1 as f32,
);
set_damage(module_accessor, pct);
}
SaveDamage::DEFAULT => {}
_ => {}
}
}
// Set to held item // Set to held item
if !is_cpu && !fighter_is_nana && MENU.character_item != CharacterItem::None { if !is_cpu && !fighter_is_nana && MENU.character_item != CharacterItem::None {
apply_item(MENU.character_item); apply_item(MENU.character_item);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
pub enum GaugeState {
MinHover,
MaxHover,
MinSelected,
MaxSelected,
None,
}
pub struct DoubleEndedGauge {
pub state: GaugeState,
pub selected_min: u32,
pub selected_max: u32,
pub abs_min: u32,
pub abs_max: u32,
}
impl DoubleEndedGauge {
pub fn new() -> DoubleEndedGauge {
DoubleEndedGauge {
state: GaugeState::None,
selected_min: 0,
selected_max: 150,
abs_min: 0,
abs_max: 150,
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,6 @@ impl<T: Clone> MultiStatefulList<T> {
(self.total_len as f32 / self.lists.len() as f32).ceil() as usize * (list_section + 1), (self.total_len as f32 / self.lists.len() as f32).ceil() as usize * (list_section + 1),
self.total_len); self.total_len);
if (list_section_min_idx..list_section_max_idx).contains(&idx) { if (list_section_min_idx..list_section_max_idx).contains(&idx) {
// println!("\n{}: ({}, {})", idx, list_section_min_idx, list_section_max_idx);
return (list_section, idx - list_section_min_idx) return (list_section, idx - list_section_min_idx)
} }
} }

View file

@ -54,6 +54,8 @@ fn ensure_menu_retains_selections() -> Result<(), Box<dyn Error>> {
} }
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = std::env::args().collect();
let inputs = args.get(1);
let menu; let menu;
unsafe { unsafe {
menu = get_menu(); menu = get_menu();
@ -61,6 +63,21 @@ fn main() -> Result<(), Box<dyn Error>> {
#[cfg(not(feature = "has_terminal"))] { #[cfg(not(feature = "has_terminal"))] {
let (mut terminal, mut app) = test_backend_setup(menu)?; let (mut terminal, mut app) = test_backend_setup(menu)?;
if inputs.is_some() {
inputs.unwrap().split(",").for_each(|input| {
match input.to_uppercase().as_str() {
"L" => app.on_l(),
"R" => app.on_r(),
"A" => app.on_a(),
"B" => app.on_b(),
"UP" => app.on_up(),
"DOWN" => app.on_down(),
"LEFT" => app.on_left(),
"RIGHT" => app.on_right(),
_ => {}
}
})
}
let mut json_response = String::new(); let mut json_response = String::new();
let frame_res = terminal.draw(|f| json_response = training_mod_tui::ui(f, &mut app))?; let frame_res = terminal.draw(|f| json_response = training_mod_tui::ui(f, &mut app))?;