Compare commits

...

2 Commits

Author SHA1 Message Date
InnocentGameDev
b7ecebbc6e
[Localization] Toxic and Flame Orb (en, de, es, fr, it) Localisations (only missing pt_BR, zh) (#1668)
* toxic and flame orb odds tweak and localisation

* Update modifier-type.ts

German name, but English description

* Update modifier-type.ts

Spanish done, in this regard!

* Update src/locales/fr/modifier-type.ts

French translation, thanks @Dakurei

Co-authored-by: Dakurei <maxime.palanchini@gmail.com>

* Update src/locales/fr/modifier-type.ts

French translation, thanks @Dakurei

Co-authored-by: Dakurei <maxime.palanchini@gmail.com>

* Update modifier-type.ts

Missed eliminating the "."

* Update modifier-type.ts

* Update modifier-type.ts

"it" gets proper English description, waiting on an Italian localisation

* Update modifier-type.ts

fixed the english desc.

* Update src/locales/it/modifier-type.ts

it localisation

Co-authored-by: Dakurei <maxime.palanchini@gmail.com>

* Update src/locales/it/modifier-type.ts

GoaTed translator

Co-authored-by: Dakurei <maxime.palanchini@gmail.com>

* Update modifier-type.ts

English in pt

* Update modifier-type.ts

English everywhere zzz

* Update modifier-type.ts

Final English, ready for translation by whoever sees it fit!

* Update src/locales/pt_BR/modifier-type.ts

Fixed a mixup

Co-authored-by: Dakurei <maxime.palanchini@gmail.com>

* Update src/locales/pt_BR/modifier-type.ts

Fixed a mixup

Co-authored-by: Dakurei <maxime.palanchini@gmail.com>

* Update modifier-type.ts

Added DerTapp translations without the "Ein Item zum Tragen." part for consistency throughout the file and with the rest of the locales, and according to what was briefly discussed in the Discord last night.

* Update modifier-type.ts

Missed taking out the "." in the /de, to keep it consistent with all other descriptions

* Delete src/locales/ko/modifier-type.ts

Korean localisation is already done

* Add back ko modifier-type

---------

Co-authored-by: Dakurei <maxime.palanchini@gmail.com>
Co-authored-by: Benjamin Odom <bennybroseph@gmail.com>
Co-authored-by: Temps Ray <temps.ray@gmail.com>
2024-06-01 11:25:46 -04:00
Greenlamp2
060b1b2ccc
Menu - Controls Rebind - Gamepad & Keyboard (Cleaner git log) (#1666)
* squased merge rebind_menu

* azerty to qwerty

* add a check to preven a crash in firefox

* reset navigation menu on quit

* removed dual lock mekanism

* navigation display update icons on new bind

* added submit binding

* removed attribute no longer used

* change protected to abstract

* remove last bind protection since action and cancel are protected + renamed default controller to controller

* removed default alt qwerty keys in config

* fix some errors for doc

* fix tests

* fix some more errors for docs

* fix some more errors for docs final ?

* added alt bind for menu navigation + update icons on delete/home
2024-06-01 13:56:32 +01:00
53 changed files with 5346 additions and 511 deletions

View File

@ -146,7 +146,9 @@ body {
margin-left: 10%;
}
#touchControls:not([data-ui-mode='STARTER_SELECT']) #apad .apadRectBtnContainer > .apadSqBtn, #touchControls:not([data-ui-mode='STARTER_SELECT']) #apad .apadSqBtnContainer > .apadSqBtn {
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadRectBtnContainer > .apadSqBtn,
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadSqBtnContainer
{
display: none;
}
@ -175,4 +177,70 @@ body {
input:-internal-autofill-selected {
-webkit-background-clip: text;
background-clip: text;
}
#banner {
display: none;
position: absolute;
top: 33.2%;
left: 0;
text-align: center;
z-index: 1000; /* Ensures the banner is on top of other elements */
& > img {
opacity: 50%;
}
}
/* Firefox old*/
@-moz-keyframes blink {
0% {
opacity:1;
}
50% {
opacity:0;
}
100% {
opacity:1;
}
}
@-webkit-keyframes blink {
0% {
opacity:1;
}
50% {
opacity:0;
}
100% {
opacity:1;
}
}
/* IE */
@-ms-keyframes blink {
0% {
opacity:1;
}
50% {
opacity:0;
}
100% {
opacity:1;
}
}
/* Opera and prob css3 final iteration */
@keyframes blink {
0% {
opacity:1;
}
50% {
opacity:0;
}
100% {
opacity:1;
}
}
.blink-image {
-moz-animation: blink normal 4s infinite ease-in-out; /* Firefox */
-webkit-animation: blink normal 4s infinite ease-in-out; /* Webkit */
-ms-animation: blink normal 4s infinite ease-in-out; /* IE */
animation: blink normal 4s infinite ease-in-out; /* Opera and prob css3 final iteration */
}

View File

@ -0,0 +1,148 @@
{"frames": [
{
"filename": "CIRCLE.png",
"frame": {"x":0,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "CROSS.png",
"frame": {"x":12,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "DOWN.png",
"frame": {"x":24,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "L1.png",
"frame": {"x":36,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "L2.png",
"frame": {"x":48,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "L3.png",
"frame": {"x":60,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "LEFT.png",
"frame": {"x":72,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "R1.png",
"frame": {"x":84,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "R2.png",
"frame": {"x":96,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "R3.png",
"frame": {"x":108,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "RIGHT.png",
"frame": {"x":120,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "SELECT.png",
"frame": {"x":132,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "SQUARE.png",
"frame": {"x":144,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "START.png",
"frame": {"x":156,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "TOUCH.png",
"frame": {"x":168,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "TRIANGLE.png",
"frame": {"x":180,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "UP.png",
"frame": {"x":192,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
}],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "dualshock.png",
"format": "RGBA8888",
"size": {"w":204,"h":12},
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:47df68ade4299adf7d334f25cb833ece:039b9ac469e3616fb9635a6a19cca50e:adc25708364be3d9e70074e95305c745$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,596 @@
{"frames": [
{
"filename": "0.png",
"frame": {"x":0,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "1.png",
"frame": {"x":12,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "2.png",
"frame": {"x":24,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "3.png",
"frame": {"x":36,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "4.png",
"frame": {"x":48,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "5.png",
"frame": {"x":60,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "6.png",
"frame": {"x":72,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "7.png",
"frame": {"x":84,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "8.png",
"frame": {"x":96,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "9.png",
"frame": {"x":108,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "A.png",
"frame": {"x":120,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "ALT.png",
"frame": {"x":132,"y":0,"w":16,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":16,"h":12},
"sourceSize": {"w":16,"h":12}
},
{
"filename": "B.png",
"frame": {"x":148,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "BACK.png",
"frame": {"x":160,"y":0,"w":24,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":24,"h":12},
"sourceSize": {"w":24,"h":12}
},
{
"filename": "C.png",
"frame": {"x":184,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "CTRL.png",
"frame": {"x":196,"y":0,"w":22,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":22,"h":12},
"sourceSize": {"w":22,"h":12}
},
{
"filename": "D.png",
"frame": {"x":218,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "DEL.png",
"frame": {"x":230,"y":0,"w":17,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":17,"h":12},
"sourceSize": {"w":17,"h":12}
},
{
"filename": "E.png",
"frame": {"x":247,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "END.png",
"frame": {"x":259,"y":0,"w":18,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":18,"h":12},
"sourceSize": {"w":18,"h":12}
},
{
"filename": "ENTER.png",
"frame": {"x":277,"y":0,"w":27,"h":11},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":27,"h":11},
"sourceSize": {"w":27,"h":11}
},
{
"filename": "ESC.png",
"frame": {"x":304,"y":0,"w":17,"h":11},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":17,"h":11},
"sourceSize": {"w":17,"h":11}
},
{
"filename": "F.png",
"frame": {"x":321,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "F1.png",
"frame": {"x":333,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F2.png",
"frame": {"x":346,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F3.png",
"frame": {"x":359,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F4.png",
"frame": {"x":372,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F5.png",
"frame": {"x":385,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F6.png",
"frame": {"x":398,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F7.png",
"frame": {"x":411,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F8.png",
"frame": {"x":424,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F9.png",
"frame": {"x":437,"y":0,"w":13,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":13,"h":12},
"sourceSize": {"w":13,"h":12}
},
{
"filename": "F10.png",
"frame": {"x":450,"y":0,"w":16,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":16,"h":12},
"sourceSize": {"w":16,"h":12}
},
{
"filename": "F11.png",
"frame": {"x":466,"y":0,"w":15,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":15,"h":12},
"sourceSize": {"w":15,"h":12}
},
{
"filename": "F12.png",
"frame": {"x":481,"y":0,"w":16,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":16,"h":12},
"sourceSize": {"w":16,"h":12}
},
{
"filename": "G.png",
"frame": {"x":497,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "H.png",
"frame": {"x":509,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "HOME.png",
"frame": {"x":521,"y":0,"w":23,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":23,"h":12},
"sourceSize": {"w":23,"h":12}
},
{
"filename": "I.png",
"frame": {"x":544,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "INS.png",
"frame": {"x":556,"y":0,"w":16,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":16,"h":12},
"sourceSize": {"w":16,"h":12}
},
{
"filename": "J.png",
"frame": {"x":572,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "K.png",
"frame": {"x":584,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "KEY_ARROW_DOWN.png",
"frame": {"x":596,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "KEY_ARROW_LEFT.png",
"frame": {"x":608,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "KEY_ARROW_RIGHT.png",
"frame": {"x":620,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "KEY_ARROW_UP.png",
"frame": {"x":632,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "L.png",
"frame": {"x":644,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "LEFT_BRACKET.png",
"frame": {"x":656,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "M.png",
"frame": {"x":668,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "MINUS.png",
"frame": {"x":680,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "N.png",
"frame": {"x":692,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "O.png",
"frame": {"x":704,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "P.png",
"frame": {"x":716,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "PAGE_DOWN.png",
"frame": {"x":728,"y":0,"w":20,"h":11},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":20,"h":11},
"sourceSize": {"w":20,"h":11}
},
{
"filename": "PAGE_UP.png",
"frame": {"x":748,"y":0,"w":20,"h":11},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":20,"h":11},
"sourceSize": {"w":20,"h":11}
},
{
"filename": "PLUS.png",
"frame": {"x":768,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "Q.png",
"frame": {"x":780,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "QUOTE.png",
"frame": {"x":792,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "R.png",
"frame": {"x":804,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "RIGHT_BRACKET.png",
"frame": {"x":816,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "S.png",
"frame": {"x":828,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "SEMICOLON.png",
"frame": {"x":840,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "SHIFT.png",
"frame": {"x":852,"y":0,"w":23,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":23,"h":12},
"sourceSize": {"w":23,"h":12}
},
{
"filename": "SPACE.png",
"frame": {"x":875,"y":0,"w":25,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":25,"h":12},
"sourceSize": {"w":25,"h":12}
},
{
"filename": "T.png",
"frame": {"x":900,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "TAB.png",
"frame": {"x":912,"y":0,"w":19,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":19,"h":12},
"sourceSize": {"w":19,"h":12}
},
{
"filename": "TILDE.png",
"frame": {"x":931,"y":0,"w":15,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":15,"h":12},
"sourceSize": {"w":15,"h":12}
},
{
"filename": "U.png",
"frame": {"x":946,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "V.png",
"frame": {"x":958,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "W.png",
"frame": {"x":970,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "X.png",
"frame": {"x":982,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "Y.png",
"frame": {"x":994,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "Z.png",
"frame": {"x":1006,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
}],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "keyboard.png",
"format": "RGBA8888",
"size": {"w":1018,"h":12},
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:085d4353a5c4d18c90f82f8926710d72:45908b22b446cf7f4904d4e0b658b16a:bad03abb89ad027d879c383c13fd51bc$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,140 @@
{"frames": [
{
"filename": "Bumper_L.png",
"frame": {"x":0,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "Bumper_R.png",
"frame": {"x":12,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "DOWN.png",
"frame": {"x":24,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "LEFT.png",
"frame": {"x":36,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "LS.png",
"frame": {"x":48,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "RIGHT.png",
"frame": {"x":60,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "RS.png",
"frame": {"x":72,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "SELECT.png",
"frame": {"x":84,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "START.png",
"frame": {"x":96,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "Trigger_L.png",
"frame": {"x":108,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "Trigger_R.png",
"frame": {"x":120,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "UP.png",
"frame": {"x":132,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "XB_Letter_A_OL.png",
"frame": {"x":144,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "XB_Letter_B_OL.png",
"frame": {"x":156,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "XB_Letter_X_OL.png",
"frame": {"x":168,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
},
{
"filename": "XB_Letter_Y_OL.png",
"frame": {"x":180,"y":0,"w":12,"h":12},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":12},
"sourceSize": {"w":12,"h":12}
}],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "xbox.png",
"format": "RGBA8888",
"size": {"w":192,"h":12},
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:dda9e220b2ea223723253388c465ea25:8ab4a5ecdc22848a8718a1285590a78c:7ad6008cd8fa3f9f4bfb17e0cfcbbb64$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,5 +1,5 @@
import Phaser from "phaser";
import UI from "./ui/ui";
import UI from "./ui/ui";
import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from "./phases";
import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon";
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species";
@ -131,8 +131,6 @@ export default class BattleScene extends SceneBase {
public fusionPaletteSwaps: boolean = true;
public enableTouchControls: boolean = false;
public enableVibration: boolean = false;
public gamepadSupport: boolean = false;
public abSwapped: boolean = false;
public disableMenu: boolean = false;

View File

@ -0,0 +1,293 @@
import {Button} from "#app/enums/buttons";
import {SettingKeyboard} from "#app/system/settings-keyboard";
const cfg_keyboard_qwerty = {
padID: "default",
padType: "keyboard",
deviceMapping: {
KEY_A: Phaser.Input.Keyboard.KeyCodes.A,
KEY_B: Phaser.Input.Keyboard.KeyCodes.B,
KEY_C: Phaser.Input.Keyboard.KeyCodes.C,
KEY_D: Phaser.Input.Keyboard.KeyCodes.D,
KEY_E: Phaser.Input.Keyboard.KeyCodes.E,
KEY_F: Phaser.Input.Keyboard.KeyCodes.F,
KEY_G: Phaser.Input.Keyboard.KeyCodes.G,
KEY_H: Phaser.Input.Keyboard.KeyCodes.H,
KEY_I: Phaser.Input.Keyboard.KeyCodes.I,
KEY_J: Phaser.Input.Keyboard.KeyCodes.J,
KEY_K: Phaser.Input.Keyboard.KeyCodes.K,
KEY_L: Phaser.Input.Keyboard.KeyCodes.L,
KEY_M: Phaser.Input.Keyboard.KeyCodes.M,
KEY_N: Phaser.Input.Keyboard.KeyCodes.N,
KEY_O: Phaser.Input.Keyboard.KeyCodes.O,
KEY_P: Phaser.Input.Keyboard.KeyCodes.P,
KEY_Q: Phaser.Input.Keyboard.KeyCodes.Q,
KEY_R: Phaser.Input.Keyboard.KeyCodes.R,
KEY_S: Phaser.Input.Keyboard.KeyCodes.S,
KEY_T: Phaser.Input.Keyboard.KeyCodes.T,
KEY_U: Phaser.Input.Keyboard.KeyCodes.U,
KEY_V: Phaser.Input.Keyboard.KeyCodes.V,
KEY_W: Phaser.Input.Keyboard.KeyCodes.W,
KEY_X: Phaser.Input.Keyboard.KeyCodes.X,
KEY_Y: Phaser.Input.Keyboard.KeyCodes.Y,
KEY_Z: Phaser.Input.Keyboard.KeyCodes.Z,
KEY_0: Phaser.Input.Keyboard.KeyCodes.ZERO,
KEY_1: Phaser.Input.Keyboard.KeyCodes.ONE,
KEY_2: Phaser.Input.Keyboard.KeyCodes.TWO,
KEY_3: Phaser.Input.Keyboard.KeyCodes.THREE,
KEY_4: Phaser.Input.Keyboard.KeyCodes.FOUR,
KEY_5: Phaser.Input.Keyboard.KeyCodes.FIVE,
KEY_6: Phaser.Input.Keyboard.KeyCodes.SIX,
KEY_7: Phaser.Input.Keyboard.KeyCodes.SEVEN,
KEY_8: Phaser.Input.Keyboard.KeyCodes.EIGHT,
KEY_9: Phaser.Input.Keyboard.KeyCodes.NINE,
KEY_CTRL: Phaser.Input.Keyboard.KeyCodes.CTRL,
KEY_DEL: Phaser.Input.Keyboard.KeyCodes.DELETE,
KEY_END: Phaser.Input.Keyboard.KeyCodes.END,
KEY_ENTER: Phaser.Input.Keyboard.KeyCodes.ENTER,
KEY_ESC: Phaser.Input.Keyboard.KeyCodes.ESC,
KEY_F1: Phaser.Input.Keyboard.KeyCodes.F1,
KEY_F2: Phaser.Input.Keyboard.KeyCodes.F2,
KEY_F3: Phaser.Input.Keyboard.KeyCodes.F3,
KEY_F4: Phaser.Input.Keyboard.KeyCodes.F4,
KEY_F5: Phaser.Input.Keyboard.KeyCodes.F5,
KEY_F6: Phaser.Input.Keyboard.KeyCodes.F6,
KEY_F7: Phaser.Input.Keyboard.KeyCodes.F7,
KEY_F8: Phaser.Input.Keyboard.KeyCodes.F8,
KEY_F9: Phaser.Input.Keyboard.KeyCodes.F9,
KEY_F10: Phaser.Input.Keyboard.KeyCodes.F10,
KEY_F11: Phaser.Input.Keyboard.KeyCodes.F11,
KEY_F12: Phaser.Input.Keyboard.KeyCodes.F12,
KEY_HOME: Phaser.Input.Keyboard.KeyCodes.HOME,
KEY_INSERT: Phaser.Input.Keyboard.KeyCodes.INSERT,
KEY_PAGE_DOWN: Phaser.Input.Keyboard.KeyCodes.PAGE_DOWN,
KEY_PAGE_UP: Phaser.Input.Keyboard.KeyCodes.PAGE_UP,
KEY_PLUS: Phaser.Input.Keyboard.KeyCodes.NUMPAD_ADD, // Assuming numpad plus
KEY_MINUS: Phaser.Input.Keyboard.KeyCodes.NUMPAD_SUBTRACT, // Assuming numpad minus
KEY_QUOTATION: Phaser.Input.Keyboard.KeyCodes.QUOTES,
KEY_SHIFT: Phaser.Input.Keyboard.KeyCodes.SHIFT,
KEY_SPACE: Phaser.Input.Keyboard.KeyCodes.SPACE,
KEY_TAB: Phaser.Input.Keyboard.KeyCodes.TAB,
KEY_TILDE: Phaser.Input.Keyboard.KeyCodes.BACKTICK,
KEY_ARROW_UP: Phaser.Input.Keyboard.KeyCodes.UP,
KEY_ARROW_DOWN: Phaser.Input.Keyboard.KeyCodes.DOWN,
KEY_ARROW_LEFT: Phaser.Input.Keyboard.KeyCodes.LEFT,
KEY_ARROW_RIGHT: Phaser.Input.Keyboard.KeyCodes.RIGHT,
KEY_LEFT_BRACKET: Phaser.Input.Keyboard.KeyCodes.OPEN_BRACKET,
KEY_RIGHT_BRACKET: Phaser.Input.Keyboard.KeyCodes.CLOSED_BRACKET,
KEY_SEMICOLON: Phaser.Input.Keyboard.KeyCodes.SEMICOLON,
KEY_BACKSPACE: Phaser.Input.Keyboard.KeyCodes.BACKSPACE,
KEY_ALT: Phaser.Input.Keyboard.KeyCodes.ALT
},
icons: {
KEY_A: "A.png",
KEY_B: "B.png",
KEY_C: "C.png",
KEY_D: "D.png",
KEY_E: "E.png",
KEY_F: "F.png",
KEY_G: "G.png",
KEY_H: "H.png",
KEY_I: "I.png",
KEY_J: "J.png",
KEY_K: "K.png",
KEY_L: "L.png",
KEY_M: "M.png",
KEY_N: "N.png",
KEY_O: "O.png",
KEY_P: "P.png",
KEY_Q: "Q.png",
KEY_R: "R.png",
KEY_S: "S.png",
KEY_T: "T.png",
KEY_U: "U.png",
KEY_V: "V.png",
KEY_W: "W.png",
KEY_X: "X.png",
KEY_Y: "Y.png",
KEY_Z: "Z.png",
KEY_0: "0.png",
KEY_1: "1.png",
KEY_2: "2.png",
KEY_3: "3.png",
KEY_4: "4.png",
KEY_5: "5.png",
KEY_6: "6.png",
KEY_7: "7.png",
KEY_8: "8.png",
KEY_9: "9.png",
KEY_F1: "F1.png",
KEY_F2: "F2.png",
KEY_F3: "F3.png",
KEY_F4: "F4.png",
KEY_F5: "F5.png",
KEY_F6: "F6.png",
KEY_F7: "F7.png",
KEY_F8: "F8.png",
KEY_F9: "F9.png",
KEY_F10: "F10.png",
KEY_F11: "F11.png",
KEY_F12: "F12.png",
KEY_PAGE_DOWN: "PAGE_DOWN.png",
KEY_PAGE_UP: "PAGE_UP.png",
KEY_CTRL: "CTRL.png",
KEY_DEL: "DEL.png",
KEY_END: "END.png",
KEY_ENTER: "ENTER.png",
KEY_ESC: "ESC.png",
KEY_HOME: "HOME.png",
KEY_INSERT: "INS.png",
KEY_PLUS: "PLUS.png",
KEY_MINUS: "MINUS.png",
KEY_QUOTATION: "QUOTE.png",
KEY_SHIFT: "SHIFT.png",
KEY_SPACE: "SPACE.png",
KEY_TAB: "TAB.png",
KEY_TILDE: "TILDE.png",
KEY_ARROW_UP: "KEY_ARROW_UP.png",
KEY_ARROW_DOWN: "KEY_ARROW_DOWN.png",
KEY_ARROW_LEFT: "KEY_ARROW_LEFT.png",
KEY_ARROW_RIGHT: "KEY_ARROW_RIGHT.png",
KEY_LEFT_BRACKET: "LEFT_BRACKET.png",
KEY_RIGHT_BRACKET: "RIGHT_BRACKET.png",
KEY_SEMICOLON: "SEMICOLON.png",
KEY_BACKSPACE: "BACK.png",
KEY_ALT: "ALT.png"
},
settings: {
[SettingKeyboard.Button_Up]: Button.UP,
[SettingKeyboard.Button_Down]: Button.DOWN,
[SettingKeyboard.Button_Left]: Button.LEFT,
[SettingKeyboard.Button_Right]: Button.RIGHT,
[SettingKeyboard.Button_Submit]: Button.SUBMIT,
[SettingKeyboard.Button_Action]: Button.ACTION,
[SettingKeyboard.Button_Cancel]: Button.CANCEL,
[SettingKeyboard.Button_Menu]: Button.MENU,
[SettingKeyboard.Button_Stats]: Button.STATS,
[SettingKeyboard.Button_Cycle_Shiny]: Button.CYCLE_SHINY,
[SettingKeyboard.Button_Cycle_Form]: Button.CYCLE_FORM,
[SettingKeyboard.Button_Cycle_Gender]: Button.CYCLE_GENDER,
[SettingKeyboard.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
[SettingKeyboard.Button_Cycle_Nature]: Button.CYCLE_NATURE,
[SettingKeyboard.Button_Cycle_Variant]: Button.V,
[SettingKeyboard.Button_Speed_Up]: Button.SPEED_UP,
[SettingKeyboard.Button_Slow_Down]: Button.SLOW_DOWN,
[SettingKeyboard.Alt_Button_Up]: Button.UP,
[SettingKeyboard.Alt_Button_Down]: Button.DOWN,
[SettingKeyboard.Alt_Button_Left]: Button.LEFT,
[SettingKeyboard.Alt_Button_Right]: Button.RIGHT,
[SettingKeyboard.Alt_Button_Submit]: Button.SUBMIT,
[SettingKeyboard.Alt_Button_Action]: Button.ACTION,
[SettingKeyboard.Alt_Button_Cancel]: Button.CANCEL,
[SettingKeyboard.Alt_Button_Menu]: Button.MENU,
[SettingKeyboard.Alt_Button_Stats]: Button.STATS,
[SettingKeyboard.Alt_Button_Cycle_Shiny]: Button.CYCLE_SHINY,
[SettingKeyboard.Alt_Button_Cycle_Form]: Button.CYCLE_FORM,
[SettingKeyboard.Alt_Button_Cycle_Gender]: Button.CYCLE_GENDER,
[SettingKeyboard.Alt_Button_Cycle_Ability]: Button.CYCLE_ABILITY,
[SettingKeyboard.Alt_Button_Cycle_Nature]: Button.CYCLE_NATURE,
[SettingKeyboard.Alt_Button_Cycle_Variant]: Button.V,
[SettingKeyboard.Alt_Button_Speed_Up]: Button.SPEED_UP,
[SettingKeyboard.Alt_Button_Slow_Down]: Button.SLOW_DOWN,
},
default: {
KEY_ARROW_UP: SettingKeyboard.Button_Up,
KEY_ARROW_DOWN: SettingKeyboard.Button_Down,
KEY_ARROW_LEFT: SettingKeyboard.Button_Left,
KEY_ARROW_RIGHT: SettingKeyboard.Button_Right,
KEY_ENTER: SettingKeyboard.Button_Submit,
KEY_SPACE: SettingKeyboard.Button_Action,
KEY_BACKSPACE: SettingKeyboard.Button_Cancel,
KEY_ESC: SettingKeyboard.Button_Menu,
KEY_C: SettingKeyboard.Button_Stats,
KEY_R: SettingKeyboard.Button_Cycle_Shiny,
KEY_F: SettingKeyboard.Button_Cycle_Form,
KEY_G: SettingKeyboard.Button_Cycle_Gender,
KEY_E: SettingKeyboard.Button_Cycle_Ability,
KEY_N: SettingKeyboard.Button_Cycle_Nature,
KEY_V: SettingKeyboard.Button_Cycle_Variant,
KEY_PLUS: -1,
KEY_MINUS: -1,
KEY_A: SettingKeyboard.Alt_Button_Left,
KEY_B: -1,
KEY_D: SettingKeyboard.Alt_Button_Right,
KEY_H: -1,
KEY_I: -1,
KEY_J: -1,
KEY_K: -1,
KEY_L: -1,
KEY_M: SettingKeyboard.Alt_Button_Menu,
KEY_O: -1,
KEY_P: -1,
KEY_Q: -1,
KEY_S: SettingKeyboard.Alt_Button_Down,
KEY_T: SettingKeyboard.Alt_Button_Cycle_Form,
KEY_U: -1,
KEY_W: SettingKeyboard.Alt_Button_Up,
KEY_X: SettingKeyboard.Alt_Button_Cancel,
KEY_Y: SettingKeyboard.Alt_Button_Cycle_Shiny,
KEY_Z: SettingKeyboard.Alt_Button_Action,
KEY_0: -1,
KEY_1: -1,
KEY_2: -1,
KEY_3: -1,
KEY_4: -1,
KEY_5: -1,
KEY_6: -1,
KEY_7: -1,
KEY_8: -1,
KEY_9: -1,
KEY_CTRL: -1,
KEY_DEL: -1,
KEY_END: -1,
KEY_F1: -1,
KEY_F2: -1,
KEY_F3: -1,
KEY_F4: -1,
KEY_F5: -1,
KEY_F6: -1,
KEY_F7: -1,
KEY_F8: -1,
KEY_F9: -1,
KEY_F10: -1,
KEY_F11: -1,
KEY_F12: -1,
KEY_HOME: -1,
KEY_INSERT: -1,
KEY_PAGE_DOWN: SettingKeyboard.Button_Slow_Down,
KEY_PAGE_UP: SettingKeyboard.Button_Speed_Up,
KEY_QUOTATION: -1,
KEY_SHIFT: SettingKeyboard.Alt_Button_Stats,
KEY_TAB: -1,
KEY_TILDE: -1,
KEY_LEFT_BRACKET: -1,
KEY_RIGHT_BRACKET: -1,
KEY_SEMICOLON: -1,
KEY_ALT: -1
},
blacklist: [
"KEY_ENTER",
"KEY_ESC",
"KEY_SPACE",
"KEY_BACKSPACE",
"KEY_ARROW_UP",
"KEY_ARROW_DOWN",
"KEY_ARROW_LEFT",
"KEY_ARROW_RIGHT",
"KEY_DEL",
"KEY_HOME",
]
};
export default cfg_keyboard_qwerty;

View File

@ -0,0 +1,208 @@
import {Device} from "#app/enums/devices";
/**
* Retrieves the key associated with the specified keycode from the mapping.
*
* @param config - The configuration object containing the mapping.
* @param keycode - The keycode to search for.
* @returns The key associated with the specified keycode.
*/
export function getKeyWithKeycode(config, keycode) {
return Object.keys(config.deviceMapping).find(key => config.deviceMapping[key] === keycode);
}
/**
* Retrieves the setting name associated with the specified keycode.
*
* @param config - The configuration object containing custom settings.
* @param keycode - The keycode to search for.
* @returns The setting name associated with the specified keycode.
*/
export function getSettingNameWithKeycode(config, keycode) {
const key = getKeyWithKeycode(config, keycode);
return config.custom[key];
}
/**
* Retrieves the icon associated with the specified keycode.
*
* @param config - The configuration object containing icons.
* @param keycode - The keycode to search for.
* @returns The icon associated with the specified keycode.
*/
export function getIconWithKeycode(config, keycode) {
const key = getKeyWithKeycode(config, keycode);
return config.icons[key];
}
/**
* Retrieves the button associated with the specified keycode.
*
* @param config - The configuration object containing settings.
* @param keycode - The keycode to search for.
* @returns The button associated with the specified keycode.
*/
export function getButtonWithKeycode(config, keycode) {
const settingName = getSettingNameWithKeycode(config, keycode);
return config.settings[settingName];
}
/**
* Retrieves the key associated with the specified setting name.
*
* @param config - The configuration object containing custom settings.
* @param settingName - The setting name to search for.
* @returns The key associated with the specified setting name.
*/
export function getKeyWithSettingName(config, settingName) {
return Object.keys(config.custom).find(key => config.custom[key] === settingName);
}
/**
* Retrieves the setting name associated with the specified key.
*
* @param config - The configuration object containing custom settings.
* @param key - The key to search for.
* @returns The setting name associated with the specified key.
*/
export function getSettingNameWithKey(config, key) {
return config.custom[key];
}
/**
* Retrieves the icon associated with the specified key.
*
* @param config - The configuration object containing icons.
* @param key - The key to search for.
* @returns The icon associated with the specified key.
*/
export function getIconWithKey(config, key) {
return config.icons[key];
}
/**
* Retrieves the icon associated with the specified setting name.
*
* @param config - The configuration object containing icons.
* @param settingName - The setting name to search for.
* @returns The icon associated with the specified setting name.
*/
export function getIconWithSettingName(config, settingName) {
const key = getKeyWithSettingName(config, settingName);
return getIconWithKey(config, key);
}
export function getIconForLatestInput(configs, source, devices, settingName) {
let config;
if (source === "gamepad") {
config = configs[devices[Device.GAMEPAD]];
} else {
config = configs[devices[Device.KEYBOARD]];
}
const icon = getIconWithSettingName(config, settingName);
if (!icon) {
const isAlt = settingName.includes("ALT_");
let altSettingName;
if (isAlt) {
altSettingName = settingName.split("ALT_").splice(1)[0];
} else {
altSettingName = `ALT_${settingName}`;
}
return getIconWithSettingName(config, altSettingName);
}
return icon;
}
export function assign(config, settingNameTarget, keycode): boolean {
// first, we need to check if this keycode is already used on another settingName
if (!canIAssignThisKey(config, getKeyWithKeycode(config, keycode)) || !canIOverrideThisSetting(config, settingNameTarget)) {
return false;
}
const previousSettingName = getSettingNameWithKeycode(config, keycode);
// if it was already bound, we delete the bind
if (previousSettingName) {
const previousKey = getKeyWithSettingName(config, previousSettingName);
config.custom[previousKey] = -1;
}
// then, we need to delete the current key for this settingName
const currentKey = getKeyWithSettingName(config, settingNameTarget);
config.custom[currentKey] = -1;
// then, the new key is assigned to the new settingName
const newKey = getKeyWithKeycode(config, keycode);
config.custom[newKey] = settingNameTarget;
return true;
}
export function swap(config, settingNameTarget, keycode) {
// only for gamepad
if (config.padType === "keyboard") {
return false;
}
const prev_key = getKeyWithSettingName(config, settingNameTarget);
const prev_settingName = getSettingNameWithKey(config, prev_key);
const new_key = getKeyWithKeycode(config, keycode);
const new_settingName = getSettingNameWithKey(config, new_key);
config.custom[prev_key] = new_settingName;
config.custom[new_key] = prev_settingName;
return true;
}
/**
* Deletes the binding of the specified setting name.
*
* @param config - The configuration object containing custom settings.
* @param settingName - The setting name to delete.
*/
export function deleteBind(config, settingName) {
const key = getKeyWithSettingName(config, settingName);
if (config.blacklist.includes(key)) {
return false;
}
config.custom[key] = -1;
return true;
}
export function canIAssignThisKey(config, key) {
const settingName = getSettingNameWithKey(config, key);
if (config.blacklist?.includes(key)) {
return false;
}
if (settingName === -1) {
return true;
}
// if (isTheLatestBind(config, settingName)) {
// return false;
// }
return true;
}
export function canIOverrideThisSetting(config, settingName) {
const key = getKeyWithSettingName(config, settingName);
// || isTheLatestBind(config, settingName) no longer needed since action and cancel are protected
if (config.blacklist?.includes(key)) {
return false;
}
return true;
}
export function canIDeleteThisKey(config, key) {
return canIAssignThisKey(config, key);
}
// export function isTheLatestBind(config, settingName) {
// if (config.padType !== "keyboard") {
// return false;
// }
// const isAlt = settingName.includes("ALT_");
// let altSettingName;
// if (isAlt) {
// altSettingName = settingName.split("ALT_").splice(1)[0];
// } else {
// altSettingName = `ALT_${settingName}`;
// }
// const secondButton = getKeyWithSettingName(config, altSettingName);
// return secondButton === undefined;
// }

View File

@ -0,0 +1,88 @@
import {SettingGamepad} from "../../system/settings-gamepad";
import {Button} from "../../enums/buttons";
/**
* Dualshock mapping
*/
const pad_dualshock = {
padID: "Dualshock",
padType: "dualshock",
deviceMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9, // Options
SELECT: 8, // Share
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
TOUCH: 17
},
icons: {
RC_S: "CROSS.png",
RC_E: "CIRCLE.png",
RC_W: "SQUARE.png",
RC_N: "TRIANGLE.png",
START: "START.png",
SELECT: "SELECT.png",
LB: "L1.png",
RB: "R1.png",
LT: "L2.png",
RT: "R2.png",
LS: "L3.png",
RS: "R3.png",
LC_N: "UP.png",
LC_S: "DOWN.png",
LC_W: "LEFT.png",
LC_E: "RIGHT.png",
TOUCH: "TOUCH.png"
},
settings: {
[SettingGamepad.Button_Up]: Button.UP,
[SettingGamepad.Button_Down]: Button.DOWN,
[SettingGamepad.Button_Left]: Button.LEFT,
[SettingGamepad.Button_Right]: Button.RIGHT,
[SettingGamepad.Button_Action]: Button.ACTION,
[SettingGamepad.Button_Cancel]: Button.CANCEL,
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
[SettingGamepad.Button_Cycle_Variant]: Button.V,
[SettingGamepad.Button_Menu]: Button.MENU,
[SettingGamepad.Button_Stats]: Button.STATS,
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
[SettingGamepad.Button_Cycle_Shiny]: Button.CYCLE_SHINY,
[SettingGamepad.Button_Cycle_Gender]: Button.CYCLE_GENDER,
[SettingGamepad.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
[SettingGamepad.Button_Speed_Up]: Button.SPEED_UP,
[SettingGamepad.Button_Slow_Down]: Button.SLOW_DOWN,
[SettingGamepad.Button_Submit]: Button.SUBMIT
},
default: {
LC_N: SettingGamepad.Button_Up,
LC_S: SettingGamepad.Button_Down,
LC_W: SettingGamepad.Button_Left,
LC_E: SettingGamepad.Button_Right,
RC_S: SettingGamepad.Button_Action,
RC_E: SettingGamepad.Button_Cancel,
RC_W: SettingGamepad.Button_Cycle_Nature,
RC_N: SettingGamepad.Button_Cycle_Variant,
START: SettingGamepad.Button_Menu,
SELECT: SettingGamepad.Button_Stats,
LB: SettingGamepad.Button_Cycle_Form,
RB: SettingGamepad.Button_Cycle_Shiny,
LT: SettingGamepad.Button_Cycle_Gender,
RT: SettingGamepad.Button_Cycle_Ability,
LS: SettingGamepad.Button_Speed_Up,
RS: SettingGamepad.Button_Slow_Down,
TOUCH: SettingGamepad.Button_Submit,
},
};
export default pad_dualshock;

View File

@ -0,0 +1,90 @@
import {SettingGamepad} from "../../system/settings-gamepad";
import {Button} from "../../enums/buttons";
/**
* Generic pad mapping
*/
const pad_generic = {
padID: "Generic",
padType: "xbox",
deviceMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15
},
icons: {
RC_S: "XB_Letter_A_OL.png",
RC_E: "XB_Letter_B_OL.png",
RC_W: "XB_Letter_X_OL.png",
RC_N: "XB_Letter_Y_OL.png",
START: "START.png",
SELECT: "SELECT.png",
LB: "Bumper_L.png",
RB: "Bumper_R.png",
LT: "Trigger_L.png",
RT: "Trigger_R.png",
LS: "LS.png",
RS: "RS.png",
LC_N: "UP.png",
LC_S: "DOWN.png",
LC_W: "LEFT.png",
LC_E: "RIGHT.png",
},
settings: {
[SettingGamepad.Button_Up]: Button.UP,
[SettingGamepad.Button_Down]: Button.DOWN,
[SettingGamepad.Button_Left]: Button.LEFT,
[SettingGamepad.Button_Right]: Button.RIGHT,
[SettingGamepad.Button_Action]: Button.ACTION,
[SettingGamepad.Button_Cancel]: Button.CANCEL,
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
[SettingGamepad.Button_Cycle_Variant]: Button.V,
[SettingGamepad.Button_Menu]: Button.MENU,
[SettingGamepad.Button_Stats]: Button.STATS,
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
[SettingGamepad.Button_Cycle_Shiny]: Button.CYCLE_SHINY,
[SettingGamepad.Button_Cycle_Gender]: Button.CYCLE_GENDER,
[SettingGamepad.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
[SettingGamepad.Button_Speed_Up]: Button.SPEED_UP,
[SettingGamepad.Button_Slow_Down]: Button.SLOW_DOWN
},
default: {
LC_N: SettingGamepad.Button_Up,
LC_S: SettingGamepad.Button_Down,
LC_W: SettingGamepad.Button_Left,
LC_E: SettingGamepad.Button_Right,
RC_S: SettingGamepad.Button_Action,
RC_E: SettingGamepad.Button_Cancel,
RC_W: SettingGamepad.Button_Cycle_Nature,
RC_N: SettingGamepad.Button_Cycle_Variant,
START: SettingGamepad.Button_Menu,
SELECT: SettingGamepad.Button_Stats,
LB: SettingGamepad.Button_Cycle_Form,
RB: SettingGamepad.Button_Cycle_Shiny,
LT: SettingGamepad.Button_Cycle_Gender,
RT: SettingGamepad.Button_Cycle_Ability,
LS: SettingGamepad.Button_Speed_Up,
RS: SettingGamepad.Button_Slow_Down
},
blacklist: [
"LC_N",
"LC_S",
"LC_W",
"LC_E",
]
};
export default pad_generic;

View File

@ -0,0 +1,85 @@
import {SettingGamepad} from "#app/system/settings-gamepad";
import {Button} from "#app/enums/buttons";
/**
* Nintendo Pro Controller mapping
*/
const pad_procon = {
padID: "Pro Controller",
padType: "xbox",
deviceMapping: {
RC_S: 1,
RC_E: 0,
RC_W: 3,
RC_N: 2,
START: 9, // +
SELECT: 8, // -
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16, // Home
},
icons: {
RC_S: "XB_Letter_B_OL.png",
RC_E: "XB_Letter_A_OL.png",
RC_W: "XB_Letter_Y_OL.png",
RC_N: "XB_Letter_X_OL.png",
START: "START.png",
SELECT: "SELECT.png",
LB: "Bumper_L.png",
RB: "Bumper_R.png",
LT: "Trigger_L.png",
RT: "Trigger_R.png",
LS: "LS.png",
RS: "RS.png",
LC_N: "UP.png",
LC_S: "DOWN.png",
LC_W: "LEFT.png",
LC_E: "RIGHT.png",
},
settings: {
[SettingGamepad.Button_Up]: Button.UP,
[SettingGamepad.Button_Down]: Button.DOWN,
[SettingGamepad.Button_Left]: Button.LEFT,
[SettingGamepad.Button_Right]: Button.RIGHT,
[SettingGamepad.Button_Action]: Button.ACTION,
[SettingGamepad.Button_Cancel]: Button.CANCEL,
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
[SettingGamepad.Button_Cycle_Variant]: Button.V,
[SettingGamepad.Button_Menu]: Button.MENU,
[SettingGamepad.Button_Stats]: Button.STATS,
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
[SettingGamepad.Button_Cycle_Shiny]: Button.CYCLE_SHINY,
[SettingGamepad.Button_Cycle_Gender]: Button.CYCLE_GENDER,
[SettingGamepad.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
[SettingGamepad.Button_Speed_Up]: Button.SPEED_UP,
[SettingGamepad.Button_Slow_Down]: Button.SLOW_DOWN
},
default: {
LC_N: SettingGamepad.Button_Up,
LC_S: SettingGamepad.Button_Down,
LC_W: SettingGamepad.Button_Left,
LC_E: SettingGamepad.Button_Right,
RC_S: SettingGamepad.Button_Action,
RC_E: SettingGamepad.Button_Cancel,
RC_W: SettingGamepad.Button_Cycle_Nature,
RC_N: SettingGamepad.Button_Cycle_Variant,
START: SettingGamepad.Button_Menu,
SELECT: SettingGamepad.Button_Stats,
LB: SettingGamepad.Button_Cycle_Form,
RB: SettingGamepad.Button_Cycle_Shiny,
LT: SettingGamepad.Button_Cycle_Gender,
RT: SettingGamepad.Button_Cycle_Ability,
LS: SettingGamepad.Button_Speed_Up,
RS: SettingGamepad.Button_Slow_Down
},
};
export default pad_procon;

View File

@ -0,0 +1,76 @@
import {SettingGamepad} from "../../system/settings-gamepad";
import {Button} from "../../enums/buttons";
/**
* 081f-e401 - UnlicensedSNES
*/
const pad_unlicensedSNES = {
padID: "081f-e401",
padType: "xbox",
deviceMapping : {
RC_S: 2,
RC_E: 1,
RC_W: 3,
RC_N: 0,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15
},
icons: {
RC_S: "XB_Letter_A_OL.png",
RC_E: "XB_Letter_B_OL.png",
RC_W: "XB_Letter_X_OL.png",
RC_N: "XB_Letter_Y_OL.png",
START: "START.png",
SELECT: "SELECT.png",
LB: "Bumper_L.png",
RB: "Bumper_R.png",
LC_N: "UP.png",
LC_S: "DOWN.png",
LC_W: "LEFT.png",
LC_E: "RIGHT.png",
},
settings: {
[SettingGamepad.Button_Up]: Button.UP,
[SettingGamepad.Button_Down]: Button.DOWN,
[SettingGamepad.Button_Left]: Button.LEFT,
[SettingGamepad.Button_Right]: Button.RIGHT,
[SettingGamepad.Button_Action]: Button.ACTION,
[SettingGamepad.Button_Cancel]: Button.CANCEL,
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
[SettingGamepad.Button_Cycle_Variant]: Button.V,
[SettingGamepad.Button_Menu]: Button.MENU,
[SettingGamepad.Button_Stats]: Button.STATS,
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
[SettingGamepad.Button_Cycle_Shiny]: Button.CYCLE_SHINY,
[SettingGamepad.Button_Cycle_Gender]: Button.CYCLE_GENDER,
[SettingGamepad.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
[SettingGamepad.Button_Speed_Up]: Button.SPEED_UP,
[SettingGamepad.Button_Slow_Down]: Button.SLOW_DOWN
},
default: {
LC_N: SettingGamepad.Button_Up,
LC_S: SettingGamepad.Button_Down,
LC_W: SettingGamepad.Button_Left,
LC_E: SettingGamepad.Button_Right,
RC_S: SettingGamepad.Button_Action,
RC_E: SettingGamepad.Button_Cancel,
RC_W: SettingGamepad.Button_Cycle_Nature,
RC_N: SettingGamepad.Button_Cycle_Variant,
START: SettingGamepad.Button_Menu,
SELECT: SettingGamepad.Button_Stats,
LB: SettingGamepad.Button_Cycle_Form,
RB: SettingGamepad.Button_Cycle_Shiny,
LT: -1,
RT: -1,
LS: -1,
RS: -1
},
};
export default pad_unlicensedSNES;

View File

@ -0,0 +1,84 @@
import {SettingGamepad} from "../../system/settings-gamepad";
import {Button} from "#app/enums/buttons";
/**
* Generic pad mapping
*/
const pad_xbox360 = {
padID: "Xbox 360 controller (XInput STANDARD GAMEPAD)",
padType: "xbox",
deviceMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15
},
icons: {
RC_S: "XB_Letter_A_OL.png",
RC_E: "XB_Letter_B_OL.png",
RC_W: "XB_Letter_X_OL.png",
RC_N: "XB_Letter_Y_OL.png",
START: "START.png",
SELECT: "SELECT.png",
LB: "Bumper_L.png",
RB: "Bumper_R.png",
LT: "Trigger_L.png",
RT: "Trigger_R.png",
LS: "LS.png",
RS: "RS.png",
LC_N: "UP.png",
LC_S: "DOWN.png",
LC_W: "LEFT.png",
LC_E: "RIGHT.png",
},
settings: {
[SettingGamepad.Button_Up]: Button.UP,
[SettingGamepad.Button_Down]: Button.DOWN,
[SettingGamepad.Button_Left]: Button.LEFT,
[SettingGamepad.Button_Right]: Button.RIGHT,
[SettingGamepad.Button_Action]: Button.ACTION,
[SettingGamepad.Button_Cancel]: Button.CANCEL,
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
[SettingGamepad.Button_Cycle_Variant]: Button.V,
[SettingGamepad.Button_Menu]: Button.MENU,
[SettingGamepad.Button_Stats]: Button.STATS,
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
[SettingGamepad.Button_Cycle_Shiny]: Button.CYCLE_SHINY,
[SettingGamepad.Button_Cycle_Gender]: Button.CYCLE_GENDER,
[SettingGamepad.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
[SettingGamepad.Button_Speed_Up]: Button.SPEED_UP,
[SettingGamepad.Button_Slow_Down]: Button.SLOW_DOWN
},
default: {
LC_N: SettingGamepad.Button_Up,
LC_S: SettingGamepad.Button_Down,
LC_W: SettingGamepad.Button_Left,
LC_E: SettingGamepad.Button_Right,
RC_S: SettingGamepad.Button_Action,
RC_E: SettingGamepad.Button_Cancel,
RC_W: SettingGamepad.Button_Cycle_Nature,
RC_N: SettingGamepad.Button_Cycle_Variant,
START: SettingGamepad.Button_Menu,
SELECT: SettingGamepad.Button_Stats,
LB: SettingGamepad.Button_Cycle_Form,
RB: SettingGamepad.Button_Cycle_Shiny,
LT: SettingGamepad.Button_Cycle_Gender,
RT: SettingGamepad.Button_Cycle_Ability,
LS: SettingGamepad.Button_Speed_Up,
RS: SettingGamepad.Button_Slow_Down
},
};
export default pad_xbox360;

View File

@ -1,29 +0,0 @@
/**
* Dualshock mapping
*/
const pad_dualshock = {
padID: "Dualshock",
padType: "Sony",
gamepadMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9, // Options
SELECT: 8, // Share
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16,
TOUCH: 17
},
};
export default pad_dualshock;

View File

@ -1,27 +0,0 @@
/**
* Generic pad mapping
*/
const pad_generic = {
padID: "Generic",
padType: "generic",
gamepadMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15
},
};
export default pad_generic;

View File

@ -1,28 +0,0 @@
/**
* Nintendo Pro Controller mapping
*/
const pad_procon = {
padID: "Pro Controller",
padType: "Nintendo",
gamepadMapping: {
RC_S: 1,
RC_E: 0,
RC_W: 3,
RC_N: 2,
START: 9, // +
SELECT: 8, // -
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16, // Home
},
};
export default pad_procon;

View File

@ -1,23 +0,0 @@
/**
* 081f-e401 - UnlicensedSNES
*/
const pad_unlicensedSNES = {
padID: "081f-e401",
padType: "snes",
gamepadMapping : {
RC_S: 2,
RC_E: 1,
RC_W: 3,
RC_N: 0,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15
}
};
export default pad_unlicensedSNES;

View File

@ -1,28 +0,0 @@
/**
* Generic pad mapping
*/
const pad_xbox360 = {
padID: "Xbox 360 controller (XInput STANDARD GAMEPAD)",
padType: "xbox",
gamepadMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16
},
};
export default pad_xbox360;

4
src/enums/devices.ts Normal file
View File

@ -0,0 +1,4 @@
export enum Device {
GAMEPAD,
KEYBOARD,
}

View File

@ -1,30 +1,70 @@
import Phaser from "phaser";
import * as Utils from "./utils";
import {ButtonKey, initTouchControls} from "./touch-controls";
import pad_generic from "./configs/pad_generic";
import pad_unlicensedSNES from "./configs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/pad_xbox360";
import pad_dualshock from "./configs/pad_dualshock";
import pad_procon from "./configs/pad_procon";
import {deepCopy} from "./utils";
import {initTouchControls} from "./touch-controls";
import pad_generic from "./configs/inputs/pad_generic";
import pad_unlicensedSNES from "./configs/inputs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/inputs/pad_xbox360";
import pad_dualshock from "./configs/inputs/pad_dualshock";
import pad_procon from "./configs/inputs/pad_procon";
import {Button} from "./enums/buttons";
import {Mode} from "./ui/ui";
import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler";
import SettingsKeyboardUiHandler from "./ui/settings/settings-keyboard-ui-handler";
import cfg_keyboard_qwerty from "./configs/inputs/cfg_keyboard_qwerty";
import {Device} from "#app/enums/devices";
import {
assign,
getButtonWithKeycode,
getIconForLatestInput, swap,
} from "#app/configs/inputs/configHandler";
import BattleScene from "./battle-scene";
import {SettingGamepad} from "#app/system/settings-gamepad";
import {SettingKeyboard} from "#app/system/settings-keyboard";
export interface GamepadMapping {
export interface DeviceMapping {
[key: string]: number;
}
export interface GamepadConfig {
padID: string;
padType: string;
gamepadMapping: GamepadMapping;
export interface IconsMapping {
[key: string]: string;
}
export interface ActionGamepadMapping {
export interface SettingMapping {
[key: string]: Button;
}
export interface MappingLayout {
[key: string]: SettingGamepad | SettingKeyboard | number;
}
export interface InterfaceConfig {
padID: string;
padType: string;
deviceMapping: DeviceMapping;
icons: IconsMapping;
settings: SettingMapping;
default: MappingLayout;
custom?: MappingLayout;
}
const repeatInputDelayMillis = 250;
// Phaser.Input.Gamepad.GamepadPlugin#refreshPads
declare module "phaser" {
namespace Input {
namespace Gamepad {
interface GamepadPlugin {
/**
* Refreshes the list of connected Gamepads.
* This is called automatically when a gamepad is connected or disconnected, and during the update loop.
*/
refreshPads(): void;
}
}
}
}
/**
* Manages and handles all input controls for the game, including keyboard and gamepad interactions.
*
@ -49,17 +89,24 @@ const repeatInputDelayMillis = 250;
*/
export class InputsController {
private buttonKeys: Phaser.Input.Keyboard.Key[][];
private gamepads: Phaser.Input.Gamepad.Gamepad[] = new Array();
private gamepads: Array<Phaser.Input.Gamepad.Gamepad> = new Array();
private scene: BattleScene;
public events: Phaser.Events.EventEmitter;
private buttonLock: Button;
private buttonLock2: Button;
private interactions: Map<Button, Map<string, boolean>> = new Map();
private time: Phaser.Time.Clock;
private player: GamepadMapping;
private configs: Map<string, InterfaceConfig> = new Map();
private gamepadSupport: boolean = true;
public events: Phaser.Events.EventEmitter;
public gamepadSupport: boolean = true;
public selectedDevice;
private disconnectedGamepads: Array<String> = new Array();
private pauseUpdate: boolean = false;
public lastSource: string = "keyboard";
private keys: Array<number> = [];
/**
* Initializes a new instance of the game control system, setting up initial state and configurations.
@ -72,10 +119,15 @@ export class InputsController {
* Specific buttons like MENU and STATS are set not to repeat their actions.
* It concludes by calling the `init` method to complete the setup.
*/
constructor(scene: BattleScene) {
this.scene = scene;
this.time = this.scene.time;
this.buttonKeys = [];
this.selectedDevice = {
[Device.GAMEPAD]: null,
[Device.KEYBOARD]: "default"
};
for (const b of Utils.getEnumValues(Button)) {
this.interactions[b] = {
@ -99,18 +151,28 @@ export class InputsController {
* Additionally, it manages the game's behavior when it loses focus to prevent unwanted game actions during this state.
*/
init(): void {
this.events = new Phaser.Events.EventEmitter();
this.events = this.scene.game.events;
this.scene.game.events.on(Phaser.Core.Events.BLUR, () => {
this.loseFocus();
});
if (typeof this.scene.input.gamepad !== "undefined") {
this.scene.input.gamepad.on("connected", function (thisGamepad) {
if (!thisGamepad) {
return;
}
this.refreshGamepads();
this.setupGamepad(thisGamepad);
this.onReconnect(thisGamepad);
}, this);
this.scene.input.gamepad.on("disconnected", function (thisGamepad) {
this.onDisconnect(thisGamepad); // when a gamepad is disconnected
}, this);
// Check to see if the gamepad has already been setup by the browser
this.scene.input.gamepad.refreshPads();
if (this.scene.input.gamepad.total) {
this.refreshGamepads();
for (const thisGamepad of this.gamepads) {
@ -120,10 +182,10 @@ export class InputsController {
this.scene.input.gamepad.on("down", this.gamepadButtonDown, this);
this.scene.input.gamepad.on("up", this.gamepadButtonUp, this);
this.scene.input.keyboard.on("keydown", this.keyboardKeyDown, this);
this.scene.input.keyboard.on("keyup", this.keyboardKeyUp, this);
}
// Keyboard
this.setupKeyboardControls();
initTouchControls(this.events);
}
/**
@ -149,35 +211,58 @@ export class InputsController {
this.gamepadSupport = true;
} else {
this.gamepadSupport = false;
// if we disable the gamepad, we want to release every key pressed
this.deactivatePressedKey();
}
}
/**
* Sets the currently chosen gamepad and initializes related settings.
* This method first deactivates any active key presses and then initializes the gamepad settings.
*
* @param gamepad - The identifier of the gamepad to set as chosen.
*/
setChosenGamepad(gamepad: String): void {
this.deactivatePressedKey();
this.initChosenGamepad(gamepad);
}
/**
* Sets the currently chosen keyboard layout and initializes related settings.
*
* @param layoutKeyboard - The identifier of the keyboard layout to set as chosen.
*/
setChosenKeyboardLayout(layoutKeyboard: String): void {
this.deactivatePressedKey();
this.initChosenLayoutKeyboard(layoutKeyboard);
}
/**
* Updates the interaction handling by processing input states.
* This method gives priority to certain buttons by reversing the order in which they are checked.
* This method loops through all button values, checks for valid and timely interactions, and conditionally processes
* or ignores them based on the current state of gamepad support and other criteria.
*
* @remarks
* The method iterates over all possible buttons, checking for specific conditions such as:
* - If the button is registered in the `interactions` dictionary.
* - If the button has been held down long enough.
* - If the button is currently pressed.
* It handles special conditions such as the absence of gamepad support or mismatches between the source of the input and
* the currently chosen gamepad. It also respects the paused state of updates to prevent unwanted input processing.
*
* Special handling is applied if gamepad support is disabled but a gamepad source is still triggering inputs,
* preventing potential infinite loops by removing the last processed movement time for the button.
* If an interaction is valid and should be processed, it emits an 'input_down' event with details of the interaction.
*/
update(): void {
for (const b of Utils.getEnumValues(Button).reverse()) {
if (
this.interactions.hasOwnProperty(b) &&
this.repeatInputDurationJustPassed(b) &&
this.repeatInputDurationJustPassed(b as Button) &&
this.interactions[b].isPressed
) {
// Prevents repeating button interactions when gamepad support is disabled.
if (!this.gamepadSupport && this.interactions[b].source === "gamepad") {
if (
(!this.gamepadSupport && this.interactions[b].source === "gamepad") ||
(this.interactions[b].source === "gamepad" && this.interactions[b].sourceName && this.interactions[b].sourceName !== this.selectedDevice[Device.GAMEPAD]) ||
(this.interactions[b].source === "keyboard" && this.interactions[b].sourceName && this.interactions[b].sourceName !== this.selectedDevice[Device.KEYBOARD]) ||
this.pauseUpdate
) {
// Deletes the last interaction for a button if gamepad is disabled.
this.delLastProcessedMovementTime(b);
this.delLastProcessedMovementTime(b as Button);
return;
}
// Emits an event for the button press.
@ -185,25 +270,104 @@ export class InputsController {
controller_type: this.interactions[b].source,
button: b,
});
this.setLastProcessedMovementTime(b, this.interactions[b].source);
this.setLastProcessedMovementTime(b as Button, this.interactions[b].source, this.interactions[b].sourceName);
}
}
}
/**
* Configures a gamepad for use based on its device ID.
* Retrieves the identifiers of all connected gamepads, excluding any that are currently marked as disconnected.
* @returns Array<String> An array of strings representing the IDs of the connected gamepads.
*/
getGamepadsName(): Array<String> {
return this.gamepads.filter(g => !this.disconnectedGamepads.includes(g.id)).map(g => g.id);
}
/**
* Initializes the chosen gamepad by setting its identifier in the local storage and updating the UI to reflect the chosen gamepad.
* If a gamepad name is provided, it uses that as the chosen gamepad; otherwise, it defaults to the currently chosen gamepad.
* @param gamepadName Optional parameter to specify the name of the gamepad to initialize as chosen.
*/
initChosenGamepad(gamepadName?: String): void {
if (gamepadName) {
this.selectedDevice[Device.GAMEPAD] = gamepadName.toLowerCase();
}
const handler = this.scene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler;
handler && handler.updateChosenGamepadDisplay();
}
/**
* Initializes the chosen keyboard layout by setting its identifier in the local storage and updating the UI to reflect the chosen layout.
* If a layout name is provided, it uses that as the chosen layout; otherwise, it defaults to the currently chosen layout.
* @param layoutKeyboard Optional parameter to specify the name of the keyboard layout to initialize as chosen.
*/
initChosenLayoutKeyboard(layoutKeyboard?: String): void {
if (layoutKeyboard) {
this.selectedDevice[Device.KEYBOARD] = layoutKeyboard.toLowerCase();
}
const handler = this.scene.ui?.handlers[Mode.SETTINGS_KEYBOARD] as SettingsKeyboardUiHandler;
handler && handler.updateChosenKeyboardDisplay();
}
/**
* Handles the disconnection of a gamepad by adding its identifier to a list of disconnected gamepads.
* This is necessary because Phaser retains memory of previously connected gamepads, and without tracking
* disconnections, it would be impossible to determine the connection status of gamepads. This method ensures
* that disconnected gamepads are recognized and can be appropriately hidden in the gamepad selection menu.
*
* @param thisGamepad - The gamepad to set up.
* @param thisGamepad The gamepad that has been disconnected.
*/
onDisconnect(thisGamepad: Phaser.Input.Gamepad.Gamepad): void {
this.disconnectedGamepads.push(thisGamepad.id);
}
/**
* Updates the tracking of disconnected gamepads when a gamepad is reconnected.
* It removes the reconnected gamepad's identifier from the `disconnectedGamepads` array,
* effectively updating its status to connected.
*
* @remarks
* This method initializes a gamepad by mapping its ID to a predefined configuration.
* It updates the player's gamepad mapping based on the identified configuration, ensuring
* that the gamepad controls are correctly mapped to in-game actions.
* @param thisGamepad The gamepad that has been reconnected.
*/
onReconnect(thisGamepad: Phaser.Input.Gamepad.Gamepad): void {
this.disconnectedGamepads = this.disconnectedGamepads.filter(g => g !== thisGamepad.id);
}
/**
* Initializes or updates configurations for connected gamepads.
* It retrieves the names of all connected gamepads, sets up their configurations according to stored or default settings,
* and ensures these configurations are saved. If the connected gamepad is the currently chosen one,
* it reinitializes the chosen gamepad settings.
*
* @param thisGamepad The gamepad that is being set up.
*/
setupGamepad(thisGamepad: Phaser.Input.Gamepad.Gamepad): void {
const gamepadID = thisGamepad.id.toLowerCase();
const mappedPad = this.mapGamepad(gamepadID);
this.player = mappedPad.gamepadMapping;
const allGamepads = this.getGamepadsName();
for (const gamepad of allGamepads) {
const gamepadID = gamepad.toLowerCase();
if (!this.selectedDevice[Device.GAMEPAD]) {
this.setChosenGamepad(gamepadID);
}
const config = deepCopy(this.getConfig(gamepadID)) as InterfaceConfig;
config.custom = this.configs[gamepadID]?.custom || {...config.default};
this.configs[gamepadID] = config;
this.scene.gameData?.saveMappingConfigs(gamepadID, this.configs[gamepadID]);
}
this.lastSource = "gamepad";
const handler = this.scene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler;
handler && handler.updateChosenGamepadDisplay();
}
/**
* Initializes or updates configurations for connected keyboards.
*/
setupKeyboard(): void {
for (const layout of ["default"]) {
const config = deepCopy(this.getConfigKeyboard(layout)) as InterfaceConfig;
config.custom = this.configs[layout]?.custom || {...config.default};
this.configs[layout] = config;
this.scene.gameData?.saveMappingConfigs(this.selectedDevice[Device.KEYBOARD], this.configs[layout]);
}
this.initChosenLayoutKeyboard(this.selectedDevice[Device.KEYBOARD]);
}
/**
@ -226,89 +390,110 @@ export class InputsController {
}
/**
* Retrieves the current gamepad mapping for in-game actions.
*
* @returns An object mapping gamepad buttons to in-game actions based on the player's current gamepad configuration.
*
* @remarks
* This method constructs a mapping of gamepad buttons to in-game action buttons according to the player's
* current gamepad configuration. If no configuration is available, it returns an empty mapping.
* The mapping includes directional controls, action buttons, and system commands among others,
* adjusted for any custom settings such as swapped action buttons.
* Ensures the keyboard is initialized by checking if there is an active configuration for the keyboard.
* If not, it sets up the keyboard with default configurations.
*/
getActionGamepadMapping(): ActionGamepadMapping {
const gamepadMapping = {};
if (!this?.player) {
return gamepadMapping;
ensureKeyboardIsInit(): void {
if (!this.getActiveConfig(Device.KEYBOARD)?.padID) {
this.setupKeyboard();
}
gamepadMapping[this.player.LC_N] = Button.UP;
gamepadMapping[this.player.LC_S] = Button.DOWN;
gamepadMapping[this.player.LC_W] = Button.LEFT;
gamepadMapping[this.player.LC_E] = Button.RIGHT;
gamepadMapping[this.player.TOUCH] = Button.SUBMIT;
gamepadMapping[this.player.RC_S] = this.scene.abSwapped ? Button.CANCEL : Button.ACTION;
gamepadMapping[this.player.RC_E] = this.scene.abSwapped ? Button.ACTION : Button.CANCEL;
gamepadMapping[this.player.SELECT] = Button.STATS;
gamepadMapping[this.player.START] = Button.MENU;
gamepadMapping[this.player.RB] = Button.CYCLE_SHINY;
gamepadMapping[this.player.LB] = Button.CYCLE_FORM;
gamepadMapping[this.player.LT] = Button.CYCLE_GENDER;
gamepadMapping[this.player.RT] = Button.CYCLE_ABILITY;
gamepadMapping[this.player.RC_W] = Button.CYCLE_NATURE;
gamepadMapping[this.player.RC_N] = Button.V;
gamepadMapping[this.player.LS] = Button.SPEED_UP;
gamepadMapping[this.player.RS] = Button.SLOW_DOWN;
return gamepadMapping;
}
/**
* Handles the 'down' event for gamepad buttons, emitting appropriate events and updating the interaction state.
* Handles the keydown event for the keyboard.
*
* @param pad - The gamepad on which the button press occurred.
* @param button - The button that was pressed.
* @param value - The value associated with the button press, typically indicating pressure or degree of activation.
*
* @remarks
* This method is triggered when a gamepad button is pressed. If gamepad support is enabled, it:
* - Retrieves the current gamepad action mapping.
* - Checks if the pressed button is mapped to a game action.
* - If mapped, emits an 'input_down' event with the controller type and button action, and updates the interaction of this button.
* @param event The keyboard event.
*/
gamepadButtonDown(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void {
if (!this.gamepadSupport) {
keyboardKeyDown(event): void {
this.lastSource = "keyboard";
const keyDown = event.keyCode;
this.ensureKeyboardIsInit();
if (this.keys.includes(keyDown)) {
return;
}
const actionMapping = this.getActionGamepadMapping();
const buttonDown = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index];
this.keys.push(keyDown);
const buttonDown = getButtonWithKeycode(this.getActiveConfig(Device.KEYBOARD), keyDown);
if (buttonDown !== undefined) {
this.events.emit("input_down", {
controller_type: "keyboard",
button: buttonDown,
});
this.setLastProcessedMovementTime(buttonDown, "keyboard", this.selectedDevice[Device.KEYBOARD]);
}
}
/**
* Handles the keyup event for the keyboard.
*
* @param event The keyboard event.
*/
keyboardKeyUp(event): void {
this.lastSource = "keyboard";
const keyDown = event.keyCode;
this.keys = this.keys.filter(k => k !== keyDown);
this.ensureKeyboardIsInit();
const buttonUp = getButtonWithKeycode(this.getActiveConfig(Device.KEYBOARD), keyDown);
if (buttonUp !== undefined) {
this.events.emit("input_up", {
controller_type: "keyboard",
button: buttonUp,
});
this.delLastProcessedMovementTime(buttonUp);
}
}
/**
* Handles button press events on a gamepad. This method sets the gamepad as chosen on the first input if no gamepad is currently chosen.
* It checks if gamepad support is enabled and if the event comes from the chosen gamepad. If so, it maps the button press to a specific
* action using a custom configuration, emits an event for the button press, and records the time of the action.
*
* @param pad The gamepad on which the button was pressed.
* @param button The specific button that was pressed.
* @param value The intensity or value of the button press, if applicable.
*/
gamepadButtonDown(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void {
if (!this.configs[this.selectedDevice[Device.KEYBOARD]]?.padID) {
this.setupKeyboard();
}
if (!pad) {
return;
}
this.lastSource = "gamepad";
if (!this.selectedDevice[Device.GAMEPAD] || (this.scene.ui.getMode() !== Mode.GAMEPAD_BINDING && this.selectedDevice[Device.GAMEPAD] !== pad.id.toLowerCase())) {
this.setChosenGamepad(pad.id);
}
if (!this.gamepadSupport || pad.id.toLowerCase() !== this.selectedDevice[Device.GAMEPAD].toLowerCase()) {
return;
}
const activeConfig = this.getActiveConfig(Device.GAMEPAD);
const buttonDown = activeConfig && getButtonWithKeycode(activeConfig, button.index);
if (buttonDown !== undefined) {
this.events.emit("input_down", {
controller_type: "gamepad",
button: buttonDown,
});
this.setLastProcessedMovementTime(buttonDown, "gamepad");
this.setLastProcessedMovementTime(buttonDown, "gamepad", pad.id);
}
}
/**
* Handles the 'up' event for gamepad buttons, emitting appropriate events and clearing the interaction state.
* Responds to a button release event on a gamepad by checking if the gamepad is supported and currently chosen.
* If conditions are met, it identifies the configured action for the button, emits an event signaling the button release,
* and clears the record of the button.
*
* @param pad - The gamepad on which the button release occurred.
* @param button - The button that was released.
* @param value - The value associated with the button release, typically indicating pressure or degree of deactivation.
*
* @remarks
* This method is triggered when a gamepad button is released. If gamepad support is enabled, it:
* - Retrieves the current gamepad action mapping.
* - Checks if the released button is mapped to a game action.
* - If mapped, emits an 'input_up' event with the controller type and button action, and clears the interaction for this button.
* @param pad The gamepad from which the button was released.
* @param button The specific button that was released.
* @param value The intensity or value of the button release, if applicable.
*/
gamepadButtonUp(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void {
if (!this.gamepadSupport) {
if (!pad) {
return;
}
const actionMapping = this.getActionGamepadMapping();
const buttonUp = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index];
this.lastSource = "gamepad";
if (!this.gamepadSupport || pad.id.toLowerCase() !== this.selectedDevice[Device.GAMEPAD]) {
return;
}
const buttonUp = getButtonWithKeycode(this.getActiveConfig(Device.GAMEPAD), button.index);
if (buttonUp !== undefined) {
this.events.emit("input_up", {
controller_type: "gamepad",
@ -319,114 +504,14 @@ export class InputsController {
}
/**
* Configures keyboard controls for the game, mapping physical keys to game actions.
* Retrieves the configuration object for a gamepad based on its identifier. The method identifies specific gamepad models
* based on substrings in the identifier and returns predefined configurations for recognized models.
* If no specific configuration matches, it defaults to a generic gamepad configuration.
*
* @remarks
* This method sets up keyboard bindings for game controls using Phaser's `KeyCodes`. Each game action, represented
* by a button in the `Button` enum, is associated with one or more physical keys. For example, movement actions
* (up, down, left, right) are mapped to both arrow keys and WASD keys. Actions such as submit, cancel, and other
* game-specific functions are mapped to appropriate keys like Enter, Space, etc.
*
* The method does the following:
* - Defines a `keyConfig` object that associates each `Button` enum value with an array of `KeyCodes`.
* - Iterates over all values of the `Button` enum to set up these key bindings within the Phaser game scene.
* - For each button, it adds the respective keys to the game's input system and stores them in `this.buttonKeys`.
* - Additional configurations for mobile or alternative input schemes are stored in `mobileKeyConfig`.
*
* Post-setup, it initializes touch controls (if applicable) and starts listening for keyboard inputs using
* `listenInputKeyboard`, ensuring that all configured keys are actively monitored for player interactions.
* @param id The identifier string of the gamepad.
* @returns InterfaceConfig The configuration object corresponding to the identified gamepad type.
*/
setupKeyboardControls(): void {
const keyCodes = Phaser.Input.Keyboard.KeyCodes;
const keyConfig = {
[Button.UP]: [keyCodes.UP, keyCodes.W],
[Button.DOWN]: [keyCodes.DOWN, keyCodes.S],
[Button.LEFT]: [keyCodes.LEFT, keyCodes.A],
[Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D],
[Button.SUBMIT]: [keyCodes.ENTER],
[Button.ACTION]: [keyCodes.SPACE, keyCodes.Z],
[Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X],
[Button.MENU]: [keyCodes.ESC, keyCodes.M],
[Button.STATS]: [keyCodes.SHIFT, keyCodes.C],
[Button.CYCLE_SHINY]: [keyCodes.R],
[Button.CYCLE_FORM]: [keyCodes.F],
[Button.CYCLE_GENDER]: [keyCodes.G],
[Button.CYCLE_ABILITY]: [keyCodes.E],
[Button.CYCLE_NATURE]: [keyCodes.N],
[Button.V]: [keyCodes.V],
[Button.SPEED_UP]: [keyCodes.PLUS],
[Button.SLOW_DOWN]: [keyCodes.MINUS]
};
const mobileKeyConfig = new Map<string, ButtonKey>();
for (const b of Utils.getEnumValues(Button)) {
const keys: Phaser.Input.Keyboard.Key[] = [];
if (keyConfig.hasOwnProperty(b)) {
for (const k of keyConfig[b]) {
keys.push(this.scene.input.keyboard.addKey(k, false));
}
mobileKeyConfig[Button[b]] = keys[0];
}
this.buttonKeys[b] = keys;
}
initTouchControls(mobileKeyConfig);
this.listenInputKeyboard();
}
/**
* Sets up event listeners for keyboard inputs on all registered keys.
*
* @remarks
* This method iterates over an array of keyboard button rows (`this.buttonKeys`), adding 'down' and 'up'
* event listeners for each key. These listeners handle key press and release actions respectively.
*
* - **Key Down Event**: When a key is pressed down, the method emits an 'input_down' event with the button
* and the source ('keyboard'). It also records the time and state of the key press by calling
* `setLastProcessedMovementTime`.
*
* - **Key Up Event**: When a key is released, the method emits an 'input_up' event similarly, specifying the button
* and source. It then clears the recorded press time and state by calling
* `delLastProcessedMovementTime`.
*
* This setup ensures that each key on the keyboard is monitored for press and release events,
* and that these events are properly communicated within the system.
*/
listenInputKeyboard(): void {
this.buttonKeys.forEach((row, index) => {
for (const key of row) {
key.on("down", () => {
this.events.emit("input_down", {
controller_type: "keyboard",
button: index,
});
this.setLastProcessedMovementTime(index, "keyboard");
});
key.on("up", () => {
this.events.emit("input_up", {
controller_type: "keyboard",
button: index,
});
this.delLastProcessedMovementTime(index);
});
}
});
}
/**
* Maps a gamepad ID to a specific gamepad configuration based on the ID's characteristics.
*
* @param id - The gamepad ID string, typically representing a unique identifier for a gamepad model or make.
* @returns A `GamepadConfig` object corresponding to the identified gamepad model.
*
* @remarks
* This function analyzes the provided gamepad ID and matches it to a predefined configuration based on known identifiers:
* - If the ID includes both '081f' and 'e401', it is identified as an unlicensed SNES gamepad.
* - If the ID contains 'xbox' and '360', it is identified as an Xbox 360 gamepad.
* - If the ID contains '054c', it is identified as a DualShock gamepad.
* - If the ID includes both '057e' and '2009', it is identified as a Pro controller gamepad.
* If no specific identifiers are recognized, a generic gamepad configuration is returned.
*/
mapGamepad(id: string): GamepadConfig {
getConfig(id: string): InterfaceConfig {
id = id.toLowerCase();
if (id.includes("081f") && id.includes("e401")) {
@ -442,6 +527,20 @@ export class InputsController {
return pad_generic;
}
/**
* Retrieves the configuration object for a keyboard layout based on its identifier.
*
* @param id The identifier string of the keyboard layout.
* @returns InterfaceConfig The configuration object corresponding to the identified keyboard layout.
*/
getConfigKeyboard(id: string): InterfaceConfig {
if (id === "default") {
return cfg_keyboard_qwerty;
}
return cfg_keyboard_qwerty;
}
/**
* repeatInputDurationJustPassed returns true if @param button has been held down long
* enough to fire a repeated input. A button must claim the buttonLock before
@ -471,7 +570,7 @@ export class InputsController {
*
* Additionally, this method locks the button (by calling `setButtonLock`) to prevent it from being re-processed until it is released, ensuring that each press is handled distinctly.
*/
setLastProcessedMovementTime(button: Button, source: String = "keyboard"): void {
setLastProcessedMovementTime(button: Button, source: String = "keyboard", sourceName?: String): void {
if (!this.interactions.hasOwnProperty(button)) {
return;
}
@ -479,6 +578,7 @@ export class InputsController {
this.interactions[button].pressTime = this.time.now;
this.interactions[button].isPressed = true;
this.interactions[button].source = source;
this.interactions[button].sourceName = sourceName.toLowerCase();
}
/**
@ -503,6 +603,7 @@ export class InputsController {
this.interactions[button].pressTime = null;
this.interactions[button].isPressed = false;
this.interactions[button].source = null;
this.interactions[button].sourceName = null;
}
/**
@ -512,7 +613,7 @@ export class InputsController {
* This method is used to reset the state of all buttons within the `interactions` dictionary,
* effectively deactivating any currently pressed keys. It performs the following actions:
*
* - Releases button locks for predefined buttons (`buttonLock` and `buttonLock2`), allowing them
* - Releases button lock for predefined buttons, allowing them
* to be pressed again or properly re-initialized in future interactions.
* - Iterates over all possible button values obtained via `Utils.getEnumValues(Button)`, and for
* each button:
@ -524,55 +625,44 @@ export class InputsController {
* This method is typically called when needing to ensure that all inputs are neutralized.
*/
deactivatePressedKey(): void {
this.pauseUpdate = true;
this.releaseButtonLock(this.buttonLock);
this.releaseButtonLock(this.buttonLock2);
for (const b of Utils.getEnumValues(Button)) {
if (this.interactions.hasOwnProperty(b)) {
this.interactions[b].pressTime = null;
this.interactions[b].isPressed = false;
this.interactions[b].source = null;
this.interactions[b].sourceName = null;
}
}
setTimeout(() => this.pauseUpdate = false, 500);
}
/**
* Checks if a specific button is currently locked.
*
* @param button - The button to check for a lock status.
* @returns `true` if the button is either of the two potentially locked buttons (`buttonLock` or `buttonLock2`), otherwise `false`.
* @returns `true` if the button is locked, otherwise `false`.
*
* @remarks
* This method is used to determine if a given button is currently prevented from being processed due to a lock.
* It checks against two separate lock variables, allowing for up to two buttons to be locked simultaneously.
*/
isButtonLocked(button: Button): boolean {
return (this.buttonLock === button || this.buttonLock2 === button);
return this.buttonLock === button;
}
/**
* Sets a lock on a given button if it is not already locked.
* Sets a lock on a given button.
*
* @param button - The button to lock.
*
* @remarks
* This method ensures that a button is not processed multiple times inadvertently.
* It checks if the button is already locked by either of the two lock variables (`buttonLock` or `buttonLock2`).
* If not, it locks the button using the first available lock variable.
* This mechanism allows for up to two buttons to be locked at the same time.
* It checks if the button is already locked.
*/
setButtonLock(button: Button): void {
if (this.buttonLock === button || this.buttonLock2 === button) {
return;
}
if (this.buttonLock === button) {
this.buttonLock2 = button;
} else if (this.buttonLock2 === button) {
this.buttonLock = button;
} else if (!!this.buttonLock) {
this.buttonLock2 = button;
} else {
this.buttonLock = button;
}
this.buttonLock = button;
}
/**
@ -581,15 +671,93 @@ export class InputsController {
* @param button - The button whose lock is to be released.
*
* @remarks
* This method checks both lock variables (`buttonLock` and `buttonLock2`).
* This method checks lock variable.
* If either lock matches the specified button, that lock is cleared.
* This action frees the button to be processed again, ensuring it can respond to new inputs.
*/
releaseButtonLock(button: Button): void {
if (this.buttonLock === button) {
this.buttonLock = null;
} else if (this.buttonLock2 === button) {
this.buttonLock2 = null;
}
}
/**
* Retrieves the active configuration for the currently chosen device.
* It checks if a specific device ID is stored in configurations and returns it.
*
* @returns InterfaceConfig The configuration object for the active gamepad, or null if not set.
*/
getActiveConfig(device: Device) {
if (this.configs[this.selectedDevice[device]]?.padID) {
return this.configs[this.selectedDevice[device]];
}
return null;
}
getIconForLatestInputRecorded(settingName) {
if (this.lastSource === "keyboard") {
this.ensureKeyboardIsInit();
}
return getIconForLatestInput(this.configs, this.lastSource, this.selectedDevice, settingName);
}
getLastSourceDevice(): Device {
if (this.lastSource === "gamepad") {
return Device.GAMEPAD;
} else {
return Device.KEYBOARD;
}
}
getLastSourceConfig() {
const sourceDevice = this.getLastSourceDevice();
if (sourceDevice === Device.KEYBOARD) {
this.ensureKeyboardIsInit();
}
return this.getActiveConfig(sourceDevice);
}
getLastSourceType() {
const config = this.getLastSourceConfig();
return config?.padType;
}
/**
* Injects a custom mapping configuration into the configuration for a specific gamepad.
* If the device does not have an existing configuration, it initializes one first.
*
* @param selectedDevice The identifier of the device to configure.
* @param mappingConfigs The mapping configuration to apply to the device.
*/
injectConfig(selectedDevice: string, mappingConfigs): void {
if (!this.configs[selectedDevice]) {
this.configs[selectedDevice] = {};
}
this.configs[selectedDevice].custom = mappingConfigs.custom;
}
resetConfigs(): void {
this.configs = new Map();
if (this.getGamepadsName()?.length) {
this.setupGamepad(this.selectedDevice[Device.GAMEPAD]);
}
this.setupKeyboard();
}
/**
* Swaps a binding in the configuration.
*
* @param config The configuration object.
* @param settingName The name of the setting to swap.
* @param pressedButton The button that was pressed.
*/
assignBinding(config, settingName, pressedButton): boolean {
this.pauseUpdate = true;
setTimeout(() => this.pauseUpdate = false, 500);
if (config.padType === "keyboard") {
return assign(config, settingName, pressedButton);
} else {
return swap(config, settingName, pressedButton);
}
}
}

View File

@ -251,6 +251,10 @@ export class LoadingScene extends SceneBase {
}
}
this.loadAtlas("dualshock", "inputs");
this.loadAtlas("xbox", "inputs");
this.loadAtlas("keyboard", "inputs");
this.loadSe("select");
this.loadSe("menu_open");
this.loadSe("hit");

View File

@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "Überreste", description: "Heilt 1/16 der maximalen KP eines Pokémon pro Runde" },
"SHELL_BELL": { name: "Muschelglocke", description: "Heilt den Anwender um 1/8 des von ihm zugefügten Schadens" },
"TOXIC_ORB": { name: "Toxik-Orb", description: "Dieser bizarre Orb vergiftet seinen Träger im Kampf schwer" },
"FLAME_ORB": { name: "Heiß-Orb", description: "Dieser bizarre Orb fügt seinem Träger im Kampf Verbrennungen zu" },
"BATON": { name: "Stab", description: "Ermöglicht das Weitergeben von Effekten beim Wechseln von Pokémon, wodurch auch Fallen umgangen werden." },
"SHINY_CHARM": { name: "Schillerpin", description: "Erhöht die Chance deutlich, dass ein wildes Pokémon ein schillernd ist" },

View File

@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "Restos", description: "Cura 1/16 de los PS máximo de un Pokémon cada turno" },
"SHELL_BELL": { name: "Camp Concha", description: "Cura 1/8 del daño infligido por un Pokémon" },
"TOXIC_ORB": { name: "Toxiesfera", description: "Extraña esfera que envenena gravemente a quien la usa en combate" },
"FLAME_ORB": { name: "Llamasfera", description: "Extraña esfera que causa quemaduras a quien la usa en combate" },
"BATON": { name: "Baton", description: "Permite pasar los efectos al cambiar de Pokémon, también evita las trampas" },
"SHINY_CHARM": { name: "Amuleto Iris", description: "Aumenta drásticamente la posibilidad de que un Pokémon salvaje sea Shiny" },

View File

@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "Restes", description: "Soigne à chaque tour 1/16 des PV max dun Pokémon" },
"SHELL_BELL": { name: "Grelot Coque", description: "Soigne 1/8 des dégâts infligés par un Pokémon" },
"TOXIC_ORB": { name: "Orbe Toxique", description: "Un orbe bizarre qui empoisonne gravement son porteur durant le combat" },
"FLAME_ORB": { name: "Orbe Flamme", description: "Un orbe bizarre qui brûle son porteur durant le combat" },
"BATON": { name: "Bâton", description: "Permet de transmettre les effets en cas de changement de Pokémon. Ignore les pièges." },
"SHINY_CHARM": { name: "Charme Chroma", description: "Augmente énormément les chances de rencontrer un Pokémon sauvage chromatique" },

View File

@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "Avanzi", description: "Ripristina 1/16 dei PS massimi di un Pokémon ogni turno" },
"SHELL_BELL": { name: "Conchinella", description: "Guarisce 1/8 del danno inflitto a un Pokémon" },
"TOXIC_ORB": { name: "Tossicsfera", description: "Sfera bizzarra che iperavvelena chi lha con sé in una lotta" },
"FLAME_ORB": { name: "Fiammosfera", description: "Sfera bizzarra che procura una scottatura a chi lha con sé in una lotta" },
"BATON": { name: "Staffetta", description: "Permette di trasmettere gli effetti quando si cambia Pokémon, aggirando anche le trappole" },
"SHINY_CHARM": { name: "Cromamuleto", description: "Misterioso amuleto luminoso che aumenta la probabilità di incontrare Pokémon cromatici" },

View File

@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "Sobras", description: "Cura 1/16 dos PS máximos de um Pokémon a cada turno" },
"SHELL_BELL": { name: "Concha-Sino", description: "Cura 1/8 do dano causado por um Pokémon" },
"TOXIC_ORB": { name: "Toxic Orb", description: "It's a bizarre orb that exudes toxins when touched and will badly poison the holder during battle" },
"FLAME_ORB": { name: "Flame Orb", description: "It's a bizarre orb that gives off heat when touched and will affect the holder with a burn during battle" },
"BATON": { name: "Bastão", description: "Permite passar mudanças de atributo ao trocar Pokémon, ignorando armadilhas" },
"SHINY_CHARM": { name: "Amuleto Brilhante", description: "Aumenta drasticamente a chance de um Pokémon selvagem ser Shiny" },

View File

@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "吃剩的东西", description: "携带该道具的宝可梦在每个回合结束时恢复\n最大HP的1/16" },
"SHELL_BELL": { name: "贝壳之铃", description: "携带该道具的宝可梦在攻击对方成功造成伤\n害时携带者的HP会恢复其所造成伤害\n的1/8" },
"TOXIC_ORB": { name: "Toxic Orb", description: "It's a bizarre orb that exudes toxins when touched and will badly poison the holder during battle" },
"FLAME_ORB": { name: "Flame Orb", description: "It's a bizarre orb that gives off heat when touched and will affect the holder with a burn during battle" },
"BATON": { name: "接力棒", description: "允许在切换宝可梦时保留能力变化, 对陷阱\n同样生效" },
"SHINY_CHARM": { name: "闪耀护符", description: "显著增加野生宝可梦的闪光概率" },

View File

@ -238,6 +238,16 @@ export const modifierType: ModifierTypeTranslationEntries = {
description:
"攜帶該道具的寶可夢在攻擊對方成功造成傷\n害時攜帶者的HP會恢復其所造成傷害\n的1/8",
},
TOXIC_ORB: {
name: "Toxic Orb",
description:
"It's a bizarre orb that exudes toxins when touched and will badly poison the holder during battle"
},
FLAME_ORB: {
name: "Flame Orb",
description:
"It's a bizarre orb that gives off heat when touched and will affect the holder with a burn during battle"
},
BATON: {
name: "接力棒",
description: "允許在切換寶可夢時保留能力變化, 對陷阱\n同樣生效",

View File

@ -30,6 +30,8 @@ import { allMoves } from "../data/move";
import { TrainerVariant } from "../field/trainer";
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
import { Variant, variantData } from "#app/data/variant";
import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings-gamepad";
import {setSettingKeyboard, SettingKeyboard, settingKeyboardDefaults} from "#app/system/settings-keyboard";
import { TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
@ -243,6 +245,8 @@ export class GameData {
constructor(scene: BattleScene) {
this.scene = scene;
this.loadSettings();
this.loadGamepadSettings();
this.loadMappingConfigs();
this.trainerId = Utils.randInt(65536);
this.secretId = Utils.randInt(65536);
this.starterData = {};
@ -565,6 +569,125 @@ export class GameData {
return true;
}
/**
* Saves the mapping configurations for a specified device.
*
* @param deviceName - The name of the device for which the configurations are being saved.
* @param config - The configuration object containing custom mapping details.
* @returns `true` if the configurations are successfully saved.
*/
public saveMappingConfigs(deviceName: string, config): boolean {
const key = deviceName.toLowerCase(); // Convert the gamepad name to lowercase to use as a key
let mappingConfigs: object = {}; // Initialize an empty object to hold the mapping configurations
if (localStorage.hasOwnProperty("mappingConfigs")) {// Check if 'mappingConfigs' exists in localStorage
mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs"));
} // Parse the existing 'mappingConfigs' from localStorage
if (!mappingConfigs[key]) {
mappingConfigs[key] = {};
} // If there is no configuration for the given key, create an empty object for it
mappingConfigs[key].custom = config.custom; // Assign the custom configuration to the mapping configuration for the given key
localStorage.setItem("mappingConfigs", JSON.stringify(mappingConfigs)); // Save the updated mapping configurations back to localStorage
return true; // Return true to indicate the operation was successful
}
/**
* Loads the mapping configurations from localStorage and injects them into the input controller.
*
* @returns `true` if the configurations are successfully loaded and injected; `false` if no configurations are found in localStorage.
*
* @remarks
* This method checks if the 'mappingConfigs' entry exists in localStorage. If it does not exist, the method returns `false`.
* If 'mappingConfigs' exists, it parses the configurations and injects each configuration into the input controller
* for the corresponding gamepad or device key. The method then returns `true` to indicate success.
*/
public loadMappingConfigs(): boolean {
if (!localStorage.hasOwnProperty("mappingConfigs")) {// Check if 'mappingConfigs' exists in localStorage
return false;
} // If 'mappingConfigs' does not exist, return false
const mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")); // Parse the existing 'mappingConfigs' from localStorage
for (const key of Object.keys(mappingConfigs)) {// Iterate over the keys of the mapping configurations
this.scene.inputController.injectConfig(key, mappingConfigs[key]);
} // Inject each configuration into the input controller for the corresponding key
return true; // Return true to indicate the operation was successful
}
public resetMappingToFactory(): boolean {
if (!localStorage.hasOwnProperty("mappingConfigs")) {// Check if 'mappingConfigs' exists in localStorage
return false;
} // If 'mappingConfigs' does not exist, return false
localStorage.removeItem("mappingConfigs");
this.scene.inputController.resetConfigs();
}
/**
* Saves a gamepad setting to localStorage.
*
* @param setting - The gamepad setting to save.
* @param valueIndex - The index of the value to set for the gamepad setting.
* @returns `true` if the setting is successfully saved.
*
* @remarks
* This method initializes an empty object for gamepad settings if none exist in localStorage.
* It then updates the setting in the current scene and iterates over the default gamepad settings
* to update the specified setting with the new value. Finally, it saves the updated settings back
* to localStorage and returns `true` to indicate success.
*/
public saveGamepadSetting(setting: SettingGamepad, valueIndex: integer): boolean {
let settingsGamepad: object = {}; // Initialize an empty object to hold the gamepad settings
if (localStorage.hasOwnProperty("settingsGamepad")) { // Check if 'settingsGamepad' exists in localStorage
settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")); // Parse the existing 'settingsGamepad' from localStorage
}
setSettingGamepad(this.scene, setting as SettingGamepad, valueIndex); // Set the gamepad setting in the current scene
Object.keys(settingGamepadDefaults).forEach(s => { // Iterate over the default gamepad settings
if (s === setting) {// If the current setting matches, update its value
settingsGamepad[s] = valueIndex;
}
});
localStorage.setItem("settingsGamepad", JSON.stringify(settingsGamepad)); // Save the updated gamepad settings back to localStorage
return true; // Return true to indicate the operation was successful
}
/**
* Saves a keyboard setting to localStorage.
*
* @param setting - The keyboard setting to save.
* @param valueIndex - The index of the value to set for the keyboard setting.
* @returns `true` if the setting is successfully saved.
*
* @remarks
* This method initializes an empty object for keyboard settings if none exist in localStorage.
* It then updates the setting in the current scene and iterates over the default keyboard settings
* to update the specified setting with the new value. Finally, it saves the updated settings back
* to localStorage and returns `true` to indicate success.
*/
public saveKeyboardSetting(setting: SettingKeyboard, valueIndex: integer): boolean {
let settingsKeyboard: object = {}; // Initialize an empty object to hold the keyboard settings
if (localStorage.hasOwnProperty("settingsKeyboard")) { // Check if 'settingsKeyboard' exists in localStorage
settingsKeyboard = JSON.parse(localStorage.getItem("settingsKeyboard")); // Parse the existing 'settingsKeyboard' from localStorage
}
setSettingKeyboard(this.scene, setting as SettingKeyboard, valueIndex); // Set the keyboard setting in the current scene
Object.keys(settingKeyboardDefaults).forEach(s => { // Iterate over the default keyboard settings
if (s === setting) {// If the current setting matches, update its value
settingsKeyboard[s] = valueIndex;
}
});
localStorage.setItem("settingsKeyboard", JSON.stringify(settingsKeyboard)); // Save the updated keyboard settings back to localStorage
return true; // Return true to indicate the operation was successful
}
private loadSettings(): boolean {
Object.values(Setting).map(setting => setting as Setting).forEach(setting => setSetting(this.scene, setting, settingDefaults[setting]));
@ -579,6 +702,19 @@ export class GameData {
}
}
private loadGamepadSettings(): boolean {
Object.values(SettingGamepad).map(setting => setting as SettingGamepad).forEach(setting => setSettingGamepad(this.scene, setting, settingGamepadDefaults[setting]));
if (!localStorage.hasOwnProperty("settingsGamepad")) {
return false;
}
const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad"));
for (const setting of Object.keys(settingsGamepad)) {
setSettingGamepad(this.scene, setting as SettingGamepad, settingsGamepad[setting]);
}
}
public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean {
let tutorials: object = {};
if (localStorage.hasOwnProperty("tutorials")) {

View File

@ -0,0 +1,147 @@
import BattleScene from "../battle-scene";
import {SettingDefaults, SettingOptions} from "./settings";
import SettingsGamepadUiHandler from "../ui/settings/settings-gamepad-ui-handler";
import {Mode} from "../ui/ui";
import {truncateString} from "../utils";
import {Button} from "../enums/buttons";
import {SettingKeyboard} from "#app/system/settings-keyboard";
export enum SettingGamepad {
Controller = "CONTROLLER",
Gamepad_Support = "GAMEPAD_SUPPORT",
Button_Up = "BUTTON_UP",
Button_Down = "BUTTON_DOWN",
Button_Left = "BUTTON_LEFT",
Button_Right = "BUTTON_RIGHT",
Button_Action = "BUTTON_ACTION",
Button_Cancel = "BUTTON_CANCEL",
Button_Menu = "BUTTON_MENU",
Button_Stats = "BUTTON_STATS",
Button_Cycle_Form = "BUTTON_CYCLE_FORM",
Button_Cycle_Shiny = "BUTTON_CYCLE_SHINY",
Button_Cycle_Gender = "BUTTON_CYCLE_GENDER",
Button_Cycle_Ability = "BUTTON_CYCLE_ABILITY",
Button_Cycle_Nature = "BUTTON_CYCLE_NATURE",
Button_Cycle_Variant = "BUTTON_CYCLE_VARIANT",
Button_Speed_Up = "BUTTON_SPEED_UP",
Button_Slow_Down = "BUTTON_SLOW_DOWN",
Button_Submit = "BUTTON_SUBMIT",
}
export const settingGamepadOptions: SettingOptions = {
[SettingGamepad.Controller]: ["Default", "Change"],
[SettingGamepad.Gamepad_Support]: ["Auto", "Disabled"],
[SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
[SettingGamepad.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
};
export const settingGamepadDefaults: SettingDefaults = {
[SettingGamepad.Controller]: 0,
[SettingGamepad.Gamepad_Support]: 0,
[SettingGamepad.Button_Up]: 0,
[SettingGamepad.Button_Down]: 0,
[SettingGamepad.Button_Left]: 0,
[SettingGamepad.Button_Right]: 0,
[SettingGamepad.Button_Action]: 0,
[SettingGamepad.Button_Cancel]: 0,
[SettingGamepad.Button_Menu]: 0,
[SettingGamepad.Button_Stats]: 0,
[SettingGamepad.Button_Cycle_Form]: 0,
[SettingGamepad.Button_Cycle_Shiny]: 0,
[SettingGamepad.Button_Cycle_Gender]: 0,
[SettingGamepad.Button_Cycle_Ability]: 0,
[SettingGamepad.Button_Cycle_Nature]: 0,
[SettingGamepad.Button_Cycle_Variant]: 0,
[SettingGamepad.Button_Speed_Up]: 0,
[SettingGamepad.Button_Slow_Down]: 0,
[SettingGamepad.Button_Submit]: 0,
};
export const settingGamepadBlackList = [
SettingKeyboard.Button_Up,
SettingKeyboard.Button_Down,
SettingKeyboard.Button_Left,
SettingKeyboard.Button_Right,
];
export function setSettingGamepad(scene: BattleScene, setting: SettingGamepad, value: integer): boolean {
switch (setting) {
case SettingGamepad.Gamepad_Support:
// if we change the value of the gamepad support, we call a method in the inputController to
// activate or deactivate the controller listener
scene.inputController.setGamepadSupport(settingGamepadOptions[setting][value] !== "Disabled");
break;
case SettingGamepad.Button_Action:
case SettingGamepad.Button_Cancel:
case SettingGamepad.Button_Menu:
case SettingGamepad.Button_Stats:
case SettingGamepad.Button_Cycle_Shiny:
case SettingGamepad.Button_Cycle_Form:
case SettingGamepad.Button_Cycle_Gender:
case SettingGamepad.Button_Cycle_Ability:
case SettingGamepad.Button_Cycle_Nature:
case SettingGamepad.Button_Cycle_Variant:
case SettingGamepad.Button_Speed_Up:
case SettingGamepad.Button_Slow_Down:
case SettingGamepad.Button_Submit:
if (value) {
if (scene.ui) {
const cancelHandler = (success: boolean = false) : boolean => {
scene.ui.revertMode();
(scene.ui.getHandler() as SettingsGamepadUiHandler).updateBindings();
return success;
};
scene.ui.setOverlayMode(Mode.GAMEPAD_BINDING, {
target: setting,
cancelHandler: cancelHandler,
});
}
}
break;
case SettingGamepad.Controller:
if (value) {
const gp = scene.inputController.getGamepadsName();
if (scene.ui && gp) {
const cancelHandler = () => {
scene.ui.revertMode();
(scene.ui.getHandler() as SettingsGamepadUiHandler).setOptionCursor(Object.values(SettingGamepad).indexOf(SettingGamepad.Controller), 0, true);
(scene.ui.getHandler() as SettingsGamepadUiHandler).updateBindings();
return false;
};
const changeGamepadHandler = (gamepad: string) => {
scene.inputController.setChosenGamepad(gamepad);
cancelHandler();
return true;
};
scene.ui.setOverlayMode(Mode.OPTION_SELECT, {
options: [...gp.map((g: string) => ({
label: truncateString(g, 30), // Truncate the gamepad name for display
handler: () => changeGamepadHandler(g)
})), {
label: "Cancel",
handler: cancelHandler,
}]
});
return false;
}
}
break;
}
return true;
}

View File

@ -0,0 +1,208 @@
import {SettingDefaults, SettingOptions} from "#app/system/settings";
import {Button} from "#app/enums/buttons";
import BattleScene from "#app/battle-scene";
import {Mode} from "#app/ui/ui";
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
export enum SettingKeyboard {
// Default_Layout = "DEFAULT_LAYOUT",
Button_Up = "BUTTON_UP",
Alt_Button_Up = "ALT_BUTTON_UP",
Button_Down = "BUTTON_DOWN",
Alt_Button_Down = "ALT_BUTTON_DOWN",
Button_Left = "BUTTON_LEFT",
Alt_Button_Left = "ALT_BUTTON_LEFT",
Button_Right = "BUTTON_RIGHT",
Alt_Button_Right = "ALT_BUTTON_RIGHT",
Button_Action = "BUTTON_ACTION",
Alt_Button_Action = "ALT_BUTTON_ACTION",
Button_Cancel = "BUTTON_CANCEL",
Alt_Button_Cancel = "ALT_BUTTON_CANCEL",
Button_Menu = "BUTTON_MENU",
Alt_Button_Menu = "ALT_BUTTON_MENU",
Button_Stats = "BUTTON_STATS",
Alt_Button_Stats = "ALT_BUTTON_STATS",
Button_Cycle_Form = "BUTTON_CYCLE_FORM",
Alt_Button_Cycle_Form = "ALT_BUTTON_CYCLE_FORM",
Button_Cycle_Shiny = "BUTTON_CYCLE_SHINY",
Alt_Button_Cycle_Shiny = "ALT_BUTTON_CYCLE_SHINY",
Button_Cycle_Gender = "BUTTON_CYCLE_GENDER",
Alt_Button_Cycle_Gender = "ALT_BUTTON_CYCLE_GENDER",
Button_Cycle_Ability = "BUTTON_CYCLE_ABILITY",
Alt_Button_Cycle_Ability = "ALT_BUTTON_CYCLE_ABILITY",
Button_Cycle_Nature = "BUTTON_CYCLE_NATURE",
Alt_Button_Cycle_Nature = "ALT_BUTTON_CYCLE_NATURE",
Button_Cycle_Variant = "BUTTON_CYCLE_VARIANT",
Alt_Button_Cycle_Variant = "ALT_BUTTON_CYCLE_VARIANT",
Button_Speed_Up = "BUTTON_SPEED_UP",
Alt_Button_Speed_Up = "ALT_BUTTON_SPEED_UP",
Button_Slow_Down = "BUTTON_SLOW_DOWN",
Alt_Button_Slow_Down = "ALT_BUTTON_SLOW_DOWN",
Button_Submit = "BUTTON_SUBMIT",
Alt_Button_Submit = "ALT_BUTTON_SUBMIT",
}
export const settingKeyboardOptions: SettingOptions = {
// [SettingKeyboard.Default_Layout]: ['Default'],
[SettingKeyboard.Button_Up]: [`KEY ${Button.UP.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Up]: [`KEY ${Button.UP.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
[SettingKeyboard.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
[SettingKeyboard.Alt_Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
};
export const settingKeyboardDefaults: SettingDefaults = {
// [SettingKeyboard.Default_Layout]: 0,
[SettingKeyboard.Button_Up]: 0,
[SettingKeyboard.Button_Down]: 0,
[SettingKeyboard.Button_Left]: 0,
[SettingKeyboard.Button_Right]: 0,
[SettingKeyboard.Button_Action]: 0,
[SettingKeyboard.Button_Menu]: 0,
[SettingKeyboard.Button_Submit]: 0,
[SettingKeyboard.Alt_Button_Up]: 0,
[SettingKeyboard.Alt_Button_Down]: 0,
[SettingKeyboard.Alt_Button_Left]: 0,
[SettingKeyboard.Alt_Button_Right]: 0,
[SettingKeyboard.Alt_Button_Action]: 0,
[SettingKeyboard.Button_Cancel]: 0,
[SettingKeyboard.Alt_Button_Cancel]: 0,
[SettingKeyboard.Alt_Button_Menu]: 0,
[SettingKeyboard.Button_Stats]: 0,
[SettingKeyboard.Alt_Button_Stats]: 0,
[SettingKeyboard.Button_Cycle_Form]: 0,
[SettingKeyboard.Alt_Button_Cycle_Form]: 0,
[SettingKeyboard.Button_Cycle_Shiny]: 0,
[SettingKeyboard.Alt_Button_Cycle_Shiny]: 0,
[SettingKeyboard.Button_Cycle_Gender]: 0,
[SettingKeyboard.Alt_Button_Cycle_Gender]: 0,
[SettingKeyboard.Button_Cycle_Ability]: 0,
[SettingKeyboard.Alt_Button_Cycle_Ability]: 0,
[SettingKeyboard.Button_Cycle_Nature]: 0,
[SettingKeyboard.Alt_Button_Cycle_Nature]: 0,
[SettingKeyboard.Button_Cycle_Variant]: 0,
[SettingKeyboard.Alt_Button_Cycle_Variant]: 0,
[SettingKeyboard.Button_Speed_Up]: 0,
[SettingKeyboard.Alt_Button_Speed_Up]: 0,
[SettingKeyboard.Button_Slow_Down]: 0,
[SettingKeyboard.Alt_Button_Slow_Down]: 0,
[SettingKeyboard.Alt_Button_Submit]: 0,
};
export const settingKeyboardBlackList = [
SettingKeyboard.Button_Submit,
SettingKeyboard.Button_Menu,
SettingKeyboard.Button_Action,
SettingKeyboard.Button_Cancel,
SettingKeyboard.Button_Up,
SettingKeyboard.Button_Down,
SettingKeyboard.Button_Left,
SettingKeyboard.Button_Right,
];
export function setSettingKeyboard(scene: BattleScene, setting: SettingKeyboard, value: integer): boolean {
switch (setting) {
case SettingKeyboard.Button_Up:
case SettingKeyboard.Button_Down:
case SettingKeyboard.Button_Left:
case SettingKeyboard.Button_Right:
case SettingKeyboard.Button_Action:
case SettingKeyboard.Button_Cancel:
case SettingKeyboard.Button_Menu:
case SettingKeyboard.Button_Stats:
case SettingKeyboard.Button_Cycle_Shiny:
case SettingKeyboard.Button_Cycle_Form:
case SettingKeyboard.Button_Cycle_Gender:
case SettingKeyboard.Button_Cycle_Ability:
case SettingKeyboard.Button_Cycle_Nature:
case SettingKeyboard.Button_Cycle_Variant:
case SettingKeyboard.Button_Speed_Up:
case SettingKeyboard.Button_Slow_Down:
case SettingKeyboard.Alt_Button_Up:
case SettingKeyboard.Alt_Button_Down:
case SettingKeyboard.Alt_Button_Left:
case SettingKeyboard.Alt_Button_Right:
case SettingKeyboard.Alt_Button_Action:
case SettingKeyboard.Alt_Button_Cancel:
case SettingKeyboard.Alt_Button_Menu:
case SettingKeyboard.Alt_Button_Stats:
case SettingKeyboard.Alt_Button_Cycle_Shiny:
case SettingKeyboard.Alt_Button_Cycle_Form:
case SettingKeyboard.Alt_Button_Cycle_Gender:
case SettingKeyboard.Alt_Button_Cycle_Ability:
case SettingKeyboard.Alt_Button_Cycle_Nature:
case SettingKeyboard.Alt_Button_Cycle_Variant:
case SettingKeyboard.Alt_Button_Speed_Up:
case SettingKeyboard.Alt_Button_Slow_Down:
case SettingKeyboard.Alt_Button_Submit:
if (value) {
if (scene.ui) {
const cancelHandler = (success: boolean = false) : boolean => {
scene.ui.revertMode();
(scene.ui.getHandler() as SettingsKeyboardUiHandler).updateBindings();
return success;
};
scene.ui.setOverlayMode(Mode.KEYBOARD_BINDING, {
target: setting,
cancelHandler: cancelHandler,
});
}
}
break;
// case SettingKeyboard.Default_Layout:
// if (value && scene.ui) {
// const cancelHandler = () => {
// scene.ui.revertMode();
// (scene.ui.getHandler() as SettingsKeyboardUiHandler).setOptionCursor(Object.values(SettingKeyboard).indexOf(SettingKeyboard.Default_Layout), 0, true);
// (scene.ui.getHandler() as SettingsKeyboardUiHandler).updateBindings();
// return false;
// };
// const changeKeyboardHandler = (keyboardLayout: string) => {
// scene.inputController.setChosenKeyboardLayout(keyboardLayout);
// cancelHandler();
// return true;
// };
// scene.ui.setOverlayMode(Mode.OPTION_SELECT, {
// options: [{
// label: 'Default',
// handler: changeKeyboardHandler,
// }]
// });
// return false;
// }
}
return true;
}

View File

@ -1,4 +1,3 @@
import SettingsUiHandler from "#app/ui/settings-ui-handler";
import { Mode } from "#app/ui/ui";
import i18next from "i18next";
import BattleScene from "../battle-scene";
@ -7,6 +6,7 @@ import { updateWindowType } from "../ui/ui-theme";
import { PlayerGender } from "./game-data";
import { CandyUpgradeNotificationChangedEvent } from "#app/battle-scene-events.js";
import { MoneyFormat } from "../enums/money-format";
import SettingsUiHandler from "#app/ui/settings/settings-ui-handler";
export enum Setting {
Game_Speed = "GAME_SPEED",
@ -31,8 +31,6 @@ export enum Setting {
HP_Bar_Speed = "HP_BAR_SPEED",
Fusion_Palette_Swaps = "FUSION_PALETTE_SWAPS",
Player_Gender = "PLAYER_GENDER",
Gamepad_Support = "GAMEPAD_SUPPORT",
Swap_A_and_B = "SWAP_A_B", // Swaps which gamepad button handles ACTION and CANCEL
Touch_Controls = "TOUCH_CONTROLS",
Vibration = "VIBRATION"
}
@ -68,8 +66,6 @@ export const settingOptions: SettingOptions = {
[Setting.HP_Bar_Speed]: ["Normal", "Fast", "Faster", "Instant"],
[Setting.Fusion_Palette_Swaps]: ["Off", "On"],
[Setting.Player_Gender]: ["Boy", "Girl"],
[Setting.Gamepad_Support]: ["Auto", "Disabled"],
[Setting.Swap_A_and_B]: ["Enabled", "Disabled"],
[Setting.Touch_Controls]: ["Auto", "Disabled"],
[Setting.Vibration]: ["Auto", "Disabled"]
};
@ -97,8 +93,6 @@ export const settingDefaults: SettingDefaults = {
[Setting.HP_Bar_Speed]: 0,
[Setting.Fusion_Palette_Swaps]: 1,
[Setting.Player_Gender]: 0,
[Setting.Gamepad_Support]: 0,
[Setting.Swap_A_and_B]: 1, // Set to 'Disabled' by default
[Setting.Touch_Controls]: 0,
[Setting.Vibration]: 0
};
@ -194,14 +188,6 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer)
return false;
}
break;
case Setting.Gamepad_Support:
// if we change the value of the gamepad support, we call a method in the inputController to
// activate or deactivate the controller listener
scene.inputController.setGamepadSupport(settingOptions[setting][value] !== "Disabled");
break;
case Setting.Swap_A_and_B:
scene.abSwapped = settingOptions[setting][value] !== "Disabled";
break;
case Setting.Touch_Controls:
scene.enableTouchControls = settingOptions[setting][value] !== "Disabled" && hasTouchscreen();
const touchControls = document.getElementById("touchControls");

View File

@ -0,0 +1,79 @@
import {
getIconForLatestInput,
getSettingNameWithKeycode
} from "#app/configs/inputs/configHandler";
import {expect} from "vitest";
import {SettingKeyboard} from "#app/system/settings-keyboard";
export class InGameManip {
private config;
private keycode;
private settingName;
private icon;
private configs;
private latestSource;
private selectedDevice;
constructor(configs, config, selectedDevice) {
this.config = config;
this.configs = configs;
this.selectedDevice = selectedDevice;
this.keycode = null;
this.settingName = null;
this.icon = null;
this.latestSource = null;
}
whenWePressOnKeyboard(keycode) {
this.keycode = Phaser.Input.Keyboard.KeyCodes[keycode.toUpperCase()];
return this;
}
nothingShouldHappen() {
const settingName = getSettingNameWithKeycode(this.config, this.keycode);
expect(settingName).toEqual(-1);
return this;
}
forTheWantedBind(settingName) {
if (!settingName.includes("Button_")) {
settingName = "Button_" + settingName;
}
this.settingName = SettingKeyboard[settingName];
return this;
}
weShouldSeeTheIcon(icon) {
if (!icon.includes("KEY_")) {
icon = "KEY_" + icon;
}
this.icon = this.config.icons[icon];
expect(getIconForLatestInput(this.configs, this.latestSource, this.selectedDevice, this.settingName)).toEqual(this.icon);
return this;
}
forTheSource(source) {
this.latestSource = source;
return this;
}
normalizeSettingNameString(input) {
// Convert the input string to lower case
const lowerCasedInput = input.toLowerCase();
// Replace underscores with spaces, capitalize the first letter of each word, and join them back with underscores
const words = lowerCasedInput.split("_").map(word => word.charAt(0).toUpperCase() + word.slice(1));
const result = words.join("_");
return result;
}
weShouldTriggerTheButton(settingName) {
if (!settingName.includes("Button_")) {
settingName = "Button_" + settingName;
}
this.settingName = SettingKeyboard[this.normalizeSettingNameString(settingName)];
expect(getSettingNameWithKeycode(this.config, this.keycode)).toEqual(this.settingName);
return this;
}
}

View File

@ -0,0 +1,131 @@
import {expect} from "vitest";
import {
deleteBind,
getIconWithKeycode,
getIconWithSettingName,
getKeyWithKeycode,
getKeyWithSettingName,
assign,
getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting
} from "#app/configs/inputs/configHandler";
import {SettingKeyboard} from "#app/system/settings-keyboard";
export class MenuManip {
private config;
private settingName;
private keycode;
private icon;
private iconDisplayed;
private specialCaseIcon;
constructor(config) {
this.config = config;
this.settingName = null;
this.icon = null;
this.iconDisplayed = null;
this.specialCaseIcon = null;
}
convertNameToButtonString(input) {
// Check if the input starts with "Alt_Button"
if (input.startsWith("Alt_Button")) {
// Return the last part in uppercase
return input.split("_").pop().toUpperCase();
}
// Split the input string by underscore
const parts = input.split("_");
// Skip the first part and join the rest with an underscore
const result = parts.slice(1).map(part => part.toUpperCase()).join("_");
return result;
}
whenCursorIsOnSetting(settingName) {
if (!settingName.includes("Button_")) {
settingName = "Button_" + settingName;
}
this.settingName = SettingKeyboard[settingName];
return this;
}
iconDisplayedIs(icon) {
if (!(icon.toUpperCase().includes("KEY_"))) {
icon = "KEY_" + icon.toUpperCase();
}
this.iconDisplayed = this.config.icons[icon];
expect(getIconWithSettingName(this.config, this.settingName)).toEqual(this.iconDisplayed);
return this;
}
thereShouldBeNoIconAnymore() {
const icon = getIconWithSettingName(this.config, this.settingName);
expect(icon === undefined).toEqual(true);
return this;
}
thereShouldBeNoIcon() {
return this.thereShouldBeNoIconAnymore();
}
nothingShouldHappen() {
const settingName = getSettingNameWithKeycode(this.config, this.keycode);
expect(settingName).toEqual(-1);
return this;
}
weWantThisBindInstead(keycode) {
this.keycode = Phaser.Input.Keyboard.KeyCodes[keycode];
const icon = getIconWithKeycode(this.config, this.keycode);
const key = getKeyWithKeycode(this.config, this.keycode);
const _keys = key.toLowerCase().split("_");
const iconIdentifier = _keys[_keys.length-1];
expect(icon.toLowerCase().includes(iconIdentifier)).toEqual(true);
return this;
}
whenWeDelete(settingName?: string) {
this.settingName = SettingKeyboard[settingName] || this.settingName;
// const key = getKeyWithSettingName(this.config, this.settingName);
deleteBind(this.config, this.settingName);
// expect(this.config.custom[key]).toEqual(-1);
return this;
}
whenWeTryToDelete(settingName?: string) {
this.settingName = SettingKeyboard[settingName] || this.settingName;
deleteBind(this.config, this.settingName);
return this;
}
confirmAssignment() {
assign(this.config, this.settingName, this.keycode);
}
butLetsForceIt() {
this.confirm();
}
confirm() {
assign(this.config, this.settingName, this.keycode);
}
weCantAssignThisKey() {
const key = getKeyWithKeycode(this.config, this.keycode);
expect(canIAssignThisKey(this.config, key)).toEqual(false);
return this;
}
weCantOverrideThisBind() {
expect(canIOverrideThisSetting(this.config, this.settingName)).toEqual(false);
return this;
}
weCantDelete() {
const key = getKeyWithSettingName(this.config, this.settingName);
expect(canIDeleteThisKey(this.config, key)).toEqual(false);
return this;
}
}

View File

@ -0,0 +1,417 @@
import {beforeEach, describe, expect, it} from "vitest";
import {Button} from "#app/enums/buttons";
import {deepCopy} from "#app/utils";
import {
getKeyWithKeycode,
getKeyWithSettingName,
} from "#app/configs/inputs/configHandler";
import {MenuManip} from "#app/test/helpers/menuManip";
import {InGameManip} from "#app/test/helpers/inGameManip";
import {Device} from "#app/enums/devices";
import {InterfaceConfig} from "#app/inputs-controller";
import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty";
import {SettingKeyboard} from "#app/system/settings-keyboard";
describe("Test Rebinding", () => {
let config;
let inGame;
let inTheSettingMenu;
const configs: Map<string, InterfaceConfig> = new Map();
const selectedDevice = {
[Device.GAMEPAD]: null,
[Device.KEYBOARD]: "default",
};
beforeEach(() => {
config = deepCopy(cfg_keyboard_qwerty);
config.custom = {...config.default};
configs["default"] = config;
inGame = new InGameManip(configs, config, selectedDevice);
inTheSettingMenu = new MenuManip(config);
});
it("Check if config is loaded", () => {
expect(config).not.toBeNull();
});
it("Check button for setting name", () => {
const settingName = SettingKeyboard.Button_Left;
const button = config.settings[settingName];
expect(button).toEqual(Button.LEFT);
});
it("Check key for Keyboard KeyCode", () => {
const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.LEFT);
const settingName = config.custom[key];
const button = config.settings[settingName];
expect(button).toEqual(Button.LEFT);
});
it("Check key for currenly Assigned to action not alt", () => {
const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.A);
const settingName = config.custom[key];
const button = config.settings[settingName];
expect(button).toEqual(Button.LEFT);
});
it("Check key for currenly Assigned to setting name", () => {
const settingName = SettingKeyboard.Button_Left;
const key = getKeyWithSettingName(config, settingName);
expect(key).toEqual("KEY_ARROW_LEFT");
});
it("Check key for currenly Assigned to setting name alt", () => {
const settingName = SettingKeyboard.Alt_Button_Left;
const key = getKeyWithSettingName(config, settingName);
expect(key).toEqual("KEY_A");
});
it("Check key from key code", () => {
const keycode = Phaser.Input.Keyboard.KeyCodes.LEFT;
const key = getKeyWithKeycode(config, keycode);
expect(key).toEqual("KEY_ARROW_LEFT");
});
it("Check icon for currenly Assigned to key code", () => {
const keycode = Phaser.Input.Keyboard.KeyCodes.LEFT;
const key = getKeyWithKeycode(config, keycode);
const icon = config.icons[key];
expect(icon).toEqual("KEY_ARROW_LEFT.png");
});
it("Check icon for currenly Assigned to key code", () => {
const keycode = Phaser.Input.Keyboard.KeyCodes.A;
const key = getKeyWithKeycode(config, keycode);
const icon = config.icons[key];
expect(icon).toEqual("A.png");
});
it("Check icon for currenly Assigned to setting name", () => {
const settingName = SettingKeyboard.Button_Left;
const key = getKeyWithSettingName(config, settingName);
const icon = config.icons[key];
expect(icon).toEqual("KEY_ARROW_LEFT.png");
});
it("Check icon for currenly Assigned to setting name alt", () => {
const settingName = SettingKeyboard.Alt_Button_Left;
const key = getKeyWithSettingName(config, settingName);
const icon = config.icons[key];
expect(icon).toEqual("A.png");
});
it("Check if is working", () => {
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("A");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Right").iconDisplayedIs("D");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("A").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Left");
});
it("Check prevent rebind indirectly the d-pad buttons", () => {
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("A");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Right").iconDisplayedIs("D");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("A").weWantThisBindInstead("LEFT").weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Left");
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
});
it("Swap alt with a d-pad main", () => {
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Button_Up").iconDisplayedIs("KEY_ARROW_UP").weWantThisBindInstead("W").weCantOverrideThisBind().butLetsForceIt();
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
});
it("Check if double assign d-pad is blocked", () => {
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Button_Left").iconDisplayedIs("KEY_ARROW_LEFT").weWantThisBindInstead("RIGHT").weCantOverrideThisBind().weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Button_Left").iconDisplayedIs("KEY_ARROW_LEFT").weWantThisBindInstead("UP").weCantOverrideThisBind().weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Button_Left").iconDisplayedIs("KEY_ARROW_LEFT").weWantThisBindInstead("RIGHT").weCantOverrideThisBind().weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
});
it("Check if double assign is working", () => {
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("KEY_A").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("KEY_D").weWantThisBindInstead("W").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").nothingShouldHappen();
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Left");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("KEY_W").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("W").nothingShouldHappen();
});
it("Check if triple swap d-pad is prevented", () => {
inTheSettingMenu.whenCursorIsOnSetting("Button_Left").iconDisplayedIs("KEY_ARROW_LEFT").weWantThisBindInstead("RIGHT").weCantOverrideThisBind().weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Button_Right").iconDisplayedIs("KEY_ARROW_RIGHT").weWantThisBindInstead("UP").weCantOverrideThisBind().weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Button_Left").iconDisplayedIs("KEY_ARROW_LEFT").weWantThisBindInstead("LEFT").weCantOverrideThisBind().weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
});
it("Check if triple swap is working", () => {
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("KEY_A").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Right").thereShouldBeNoIcon().weWantThisBindInstead("W").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Right");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("KEY_D").weWantThisBindInstead("A").confirm();
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("D").nothingShouldHappen();
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Right");
});
it("Swap alt with a main", () => {
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inGame.whenWePressOnKeyboard("R").weShouldTriggerTheButton("Cycle_Shiny");
inTheSettingMenu.whenCursorIsOnSetting("Cycle_Shiny").iconDisplayedIs("KEY_R").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Cycle_Shiny");
inGame.whenWePressOnKeyboard("R").nothingShouldHappen();
});
it("multiple Swap alt with another main", () => {
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inGame.whenWePressOnKeyboard("R").weShouldTriggerTheButton("Button_Cycle_Shiny");
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("F").weShouldTriggerTheButton("Button_Cycle_Form");
inTheSettingMenu.whenCursorIsOnSetting("Button_Cycle_Shiny").iconDisplayedIs("KEY_R").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Button_Cycle_Shiny");
inGame.whenWePressOnKeyboard("R").nothingShouldHappen();
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("F").weShouldTriggerTheButton("Button_Cycle_Form");
inTheSettingMenu.whenCursorIsOnSetting("Button_Cycle_Form").iconDisplayedIs("KEY_F").weWantThisBindInstead("R").confirm();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Button_Cycle_Shiny");
inGame.whenWePressOnKeyboard("R").weShouldTriggerTheButton("Button_Cycle_Form");
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("F").nothingShouldHappen();
});
it("Swap alt with a key not binded yet", () => {
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inGame.whenWePressOnKeyboard("B").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Up").iconDisplayedIs("KEY_W").weWantThisBindInstead("B").confirm();
inGame.whenWePressOnKeyboard("W").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").weShouldTriggerTheButton("Alt_Button_Up");
});
it("Delete blacklisted bind", () => {
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inTheSettingMenu.whenWeDelete("Button_Left").weCantDelete().iconDisplayedIs("KEY_ARROW_LEFT");
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
});
it("Delete bind", () => {
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inTheSettingMenu.whenWeDelete("Alt_Button_Left").thereShouldBeNoIconAnymore();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
});
it("Delete bind then assign a not yet binded button", () => {
inTheSettingMenu.whenWeDelete("Alt_Button_Left").thereShouldBeNoIconAnymore();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").thereShouldBeNoIcon().weWantThisBindInstead("B").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").weShouldTriggerTheButton("Alt_Button_Left");
});
it("swap 2 bind, than delete 1 bind than assign another bind", () => {
inGame.whenWePressOnKeyboard("R").weShouldTriggerTheButton("Button_Cycle_Shiny");
inGame.whenWePressOnKeyboard("F").weShouldTriggerTheButton("Button_Cycle_Form");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inTheSettingMenu.whenCursorIsOnSetting("Button_Cycle_Shiny").iconDisplayedIs("KEY_R").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("R").nothingShouldHappen();
inGame.whenWePressOnKeyboard("F").weShouldTriggerTheButton("Button_Cycle_Form");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Button_Cycle_Shiny");
inTheSettingMenu.whenCursorIsOnSetting("Button_Cycle_Form").iconDisplayedIs("KEY_F").weWantThisBindInstead("W").confirm();
inGame.whenWePressOnKeyboard("R").nothingShouldHappen();
inGame.whenWePressOnKeyboard("F").nothingShouldHappen();
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Button_Cycle_Form");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Button_Cycle_Shiny");
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inTheSettingMenu.whenWeDelete("Alt_Button_Left").thereShouldBeNoIconAnymore();
inGame.whenWePressOnKeyboard("R").nothingShouldHappen();
inGame.whenWePressOnKeyboard("F").nothingShouldHappen();
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Button_Cycle_Form");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Button_Cycle_Shiny");
inGame.whenWePressOnKeyboard("S").weShouldTriggerTheButton("Alt_Button_Down");
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Down").iconDisplayedIs("KEY_S").weWantThisBindInstead("B").confirm();
inGame.whenWePressOnKeyboard("R").nothingShouldHappen();
inGame.whenWePressOnKeyboard("F").nothingShouldHappen();
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Button_Cycle_Form");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Button_Cycle_Shiny");
inGame.whenWePressOnKeyboard("S").nothingShouldHappen();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").weShouldTriggerTheButton("Alt_Button_Down");
});
it("Delete bind then assign not already existing button", () => {
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("B").nothingShouldHappen();
inTheSettingMenu.whenWeDelete("Alt_Button_Left").thereShouldBeNoIconAnymore();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").thereShouldBeNoIcon().weWantThisBindInstead("B").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").weShouldTriggerTheButton("Alt_Button_Left");
});
it("change alt bind to not already existing button, than another one alt bind with another not already existing button", () => {
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inGame.whenWePressOnKeyboard("B").nothingShouldHappen();
inGame.whenWePressOnKeyboard("U").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").iconDisplayedIs("KEY_A").weWantThisBindInstead("B").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inGame.whenWePressOnKeyboard("B").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("U").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Right").iconDisplayedIs("KEY_D").weWantThisBindInstead("U").confirm();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").nothingShouldHappen();
inGame.whenWePressOnKeyboard("B").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("U").weShouldTriggerTheButton("Alt_Button_Right");
});
it("Swap multiple touch alt and main", () => {
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inTheSettingMenu.whenCursorIsOnSetting("Button_Up").iconDisplayedIs("KEY_ARROW_UP").weWantThisBindInstead("RIGHT").weCantOverrideThisBind().weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Up").iconDisplayedIs("KEY_W").weWantThisBindInstead("D").confirm();
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("W").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Up");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Up").iconDisplayedIs("KEY_D").weWantThisBindInstead("W").confirm();
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("W").weShouldTriggerTheButton("Alt_Button_Up");
inGame.whenWePressOnKeyboard("D").nothingShouldHappen();
});
it("Delete 2 bind then reassign one of them", () => {
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inTheSettingMenu.whenWeDelete("Alt_Button_Left").thereShouldBeNoIconAnymore();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").weShouldTriggerTheButton("Alt_Button_Right");
inTheSettingMenu.whenWeDelete("Alt_Button_Right").thereShouldBeNoIconAnymore();
inGame.whenWePressOnKeyboard("A").nothingShouldHappen();
inGame.whenWePressOnKeyboard("D").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Left").thereShouldBeNoIcon().weWantThisBindInstead("A").confirm();
inGame.whenWePressOnKeyboard("A").weShouldTriggerTheButton("Alt_Button_Left");
inGame.whenWePressOnKeyboard("D").nothingShouldHappen();
});
it("test keyboard listener", () => {
const keyDown = Phaser.Input.Keyboard.KeyCodes.S;
const key = getKeyWithKeycode(config, keyDown);
const settingName = config.custom[key];
const buttonDown = config.settings[settingName];
expect(buttonDown).toEqual(Button.DOWN);
});
it("retrieve the correct icon for a given source", () => {
inTheSettingMenu.whenCursorIsOnSetting("Cycle_Shiny").iconDisplayedIs("KEY_R");
inTheSettingMenu.whenCursorIsOnSetting("Cycle_Form").iconDisplayedIs("KEY_F");
inGame.forTheSource("keyboard").forTheWantedBind("Cycle_Shiny").weShouldSeeTheIcon("R");
inGame.forTheSource("keyboard").forTheWantedBind("Cycle_Form").weShouldSeeTheIcon("F");
});
it("check the key displayed on confirm", () => {
inGame.whenWePressOnKeyboard("ENTER").weShouldTriggerTheButton("Button_Submit");
inGame.whenWePressOnKeyboard("UP").weShouldTriggerTheButton("Button_Up");
inGame.whenWePressOnKeyboard("DOWN").weShouldTriggerTheButton("Button_Down");
inGame.whenWePressOnKeyboard("LEFT").weShouldTriggerTheButton("Button_Left");
inGame.whenWePressOnKeyboard("RIGHT").weShouldTriggerTheButton("Button_Right");
inGame.whenWePressOnKeyboard("ESC").weShouldTriggerTheButton("Button_Menu");
inGame.whenWePressOnKeyboard("HOME").nothingShouldHappen();
inGame.whenWePressOnKeyboard("DELETE").nothingShouldHappen();
inTheSettingMenu.whenCursorIsOnSetting("Button_Submit").iconDisplayedIs("KEY_ENTER").whenWeDelete().iconDisplayedIs("KEY_ENTER");
inTheSettingMenu.whenCursorIsOnSetting("Button_Up").iconDisplayedIs("KEY_ARROW_UP").whenWeDelete().iconDisplayedIs("KEY_ARROW_UP");
inTheSettingMenu.whenCursorIsOnSetting("Button_Down").iconDisplayedIs("KEY_ARROW_DOWN").whenWeDelete().iconDisplayedIs("KEY_ARROW_DOWN");
inTheSettingMenu.whenCursorIsOnSetting("Button_Left").iconDisplayedIs("KEY_ARROW_LEFT").whenWeDelete().iconDisplayedIs("KEY_ARROW_LEFT");
inTheSettingMenu.whenCursorIsOnSetting("Button_Right").iconDisplayedIs("KEY_ARROW_RIGHT").whenWeDelete().iconDisplayedIs("KEY_ARROW_RIGHT");
inTheSettingMenu.whenCursorIsOnSetting("Button_Menu").iconDisplayedIs("KEY_ESC").whenWeDelete().iconDisplayedIs("KEY_ESC");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Up").iconDisplayedIs("KEY_W").whenWeDelete().thereShouldBeNoIconAnymore();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Up").thereShouldBeNoIcon().weWantThisBindInstead("DELETE").weCantAssignThisKey().butLetsForceIt();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Up").thereShouldBeNoIcon().weWantThisBindInstead("HOME").weCantAssignThisKey().butLetsForceIt();
inGame.whenWePressOnKeyboard("DELETE").nothingShouldHappen();
inGame.whenWePressOnKeyboard("HOME").nothingShouldHappen();
inGame.whenWePressOnKeyboard("W").nothingShouldHappen();
});
it("check to delete all the binds of an action", () => {
inGame.whenWePressOnKeyboard("V").weShouldTriggerTheButton("Button_Cycle_Variant");
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Cycle_Variant").thereShouldBeNoIcon().weWantThisBindInstead("K").confirm();
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Cycle_Variant").iconDisplayedIs("KEY_K").whenWeDelete().thereShouldBeNoIconAnymore();
inTheSettingMenu.whenCursorIsOnSetting("Button_Cycle_Variant").iconDisplayedIs("KEY_V").whenWeDelete().thereShouldBeNoIconAnymore();
});
});

View File

@ -1,153 +1,137 @@
export interface ButtonKey {
onDown: (opt: object) => void;
onUp: (opt: object) => void;
}
import {Button} from "./enums/buttons";
import EventEmitter = Phaser.Events.EventEmitter;
export type ButtonMap = Map<string, ButtonKey>;
export const keys = new Map();
export const keysDown = new Map();
// Create a map to store key bindings
export const keys = new Map<string, string>();
// Create a map to store keys that are currently pressed
export const keysDown = new Map<string, string>();
// Variable to store the ID of the last touched element
let lastTouchedId: string;
/**
* Initializes all touch controls
* Initialize touch controls by binding keys to buttons.
*
* @param buttonMap Map of buttons to key objects
* @param events - The event emitter for handling input events.
*/
export function initTouchControls(buttonMap: ButtonMap) {
export function initTouchControls(events: EventEmitter): void {
preventElementZoom(document.querySelector("#dpad"));
preventElementZoom(document.querySelector("#apad"));
for (const button of document.querySelectorAll<HTMLElement>("[data-key]")) {
bindKey(button, button.dataset.key, buttonMap);
// Select all elements with the 'data-key' attribute and bind keys to them
for (const button of document.querySelectorAll("[data-key]")) {
// @ts-ignore - Bind the key to the button using the dataset key
bindKey(button, button.dataset.key, events);
}
}
/**
* Check if the device has a touchscreen
* Check if the device has a touchscreen.
*
* @returns `true` if the device has a touchscreen, otherwise `false`.
*/
export function hasTouchscreen() {
export function hasTouchscreen(): boolean {
return window.matchMedia("(hover: none), (pointer: coarse)").matches;
}
/**
* Check if it's a mobile device through the user-agent
* Check if the device is a mobile device.
*
* @returns `true` if the device is a mobile device, otherwise `false`.
*/
export function isMobile() {
const userAgent = navigator.userAgent || navigator.vendor || window["opera"];
return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(userAgent)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(userAgent.substr(0, 4)));
export function isMobile(): boolean {
let ret = false;
(function (a) {
// Check the user agent string against a regex for mobile devices
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
ret = true;
}
})(navigator.userAgent || navigator.vendor || window["opera"]);
return ret;
}
/**
* Simulate a keyboard event on the canvas
* Simulates a keyboard event on the canvas.
*
* @param eventType Type of the keyboard event
* @param button Button to simulate
* @param buttonMap Map of buttons to key objects
* @param eventType - The type of the keyboard event ('keydown' or 'keyup').
* @param key - The key to simulate.
* @param events - The event emitter for handling input events.
*
* @remarks
* This function checks if the key exists in the Button enum. If it does, it retrieves the corresponding button
* and emits the appropriate event ('input_down' or 'input_up') based on the event type.
*/
function simulateKeyboardEvent(eventType: string, button: string, buttonMap: ButtonMap) {
const key = buttonMap[button];
function simulateKeyboardEvent(eventType: string, key: string, events: EventEmitter) {
if (!Button.hasOwnProperty(key)) {
return;
}
const button = Button[key];
switch (eventType) {
case "keydown":
key.onDown({});
events.emit("input_down", {
controller_type: "keyboard",
button: button,
});
break;
case "keyup":
key.onUp({});
events.emit("input_up", {
controller_type: "keyboard",
button: button,
});
break;
}
}
/**
* Simulate a keyboard input from 'keydown' to 'keyup'
* Binds a node to a specific key to simulate keyboard events on touch.
*
* @param {string} key Key to simulate
* @param {object} buttonMap Map of buttons to key objects
*/
// function simulateKeyboardInput(key, buttonMap) {
// simulateKeyboardEvent('keydown', key, buttonMap);
// window.setTimeout(() => {
// simulateKeyboardEvent('keyup', key, buttonMap);
// }, 100);
// }
/**
* Bind a node by a specific key to simulate on touch
* @param node - The DOM element to bind the key to.
* @param key - The key to simulate.
* @param events - The event emitter for handling input events.
*
* @param node The node to bind a key to
* @param key Key to simulate
* @param buttonMap Map of buttons to key objects
* @remarks
* This function binds touch events to a node to simulate 'keydown' and 'keyup' keyboard events.
* It adds the key to the keys map and tracks the keydown state. When a touch starts, it simulates
* a 'keydown' event and adds an 'active' class to the node. When the touch ends, it simulates a 'keyup'
* event, removes the keydown state, and removes the 'active' class from the node and the last touched element.
*/
function bindKey(node: Element, key: string, buttonMap: ButtonMap) {
function bindKey(node: HTMLElement, key: string, events) {
keys.set(node.id, key);
node.addEventListener("touchstart", (event: TouchEvent) => {
node.addEventListener("touchstart", event => {
event.preventDefault();
simulateKeyboardEvent("keydown", key, buttonMap);
if (!(event.target instanceof Element)) {
return;
}
keysDown.set(event.target.id, node.id);
simulateKeyboardEvent("keydown", key, events);
keysDown.set(event.target["id"], node.id);
node.classList.add("active");
});
node.addEventListener("touchend", (event: TouchEvent) => {
node.addEventListener("touchend", event => {
event.preventDefault();
if (!(event.target instanceof Element)) {
return;
}
const pressedKey = keysDown.get(event.target.id);
const pressedKey = keysDown.get(event.target["id"]);
if (pressedKey && keys.has(pressedKey)) {
const key = keys.get(pressedKey);
simulateKeyboardEvent("keyup", key, buttonMap);
simulateKeyboardEvent("keyup", key, events);
}
keysDown.delete(event.target.id);
keysDown.delete(event.target["id"]);
node.classList.remove("active");
if (lastTouchedId) {
document.getElementById(lastTouchedId).classList.remove("active");
}
});
// Inspired by https://github.com/pulsejet/mkxp-web/blob/262a2254b684567311c9f0e135ee29f6e8c3613e/extra/js/dpad.js
node.addEventListener("touchmove", (event : TouchEvent) => {
if (!(event.changedTouches[0].target instanceof Element)) {
return;
}
const { target, clientX, clientY } = event.changedTouches[0];
const origTargetId = keysDown.get(target.id);
const nextTargetId = document.elementFromPoint(clientX, clientY).id;
if (origTargetId === nextTargetId) {
return;
}
if (origTargetId) {
const key = keys.get(origTargetId);
simulateKeyboardEvent("keyup", key, buttonMap);
keysDown.delete(target.id);
document.getElementById(origTargetId).classList.remove("active");
}
if (keys.has(nextTargetId)) {
const key = keys.get(nextTargetId);
simulateKeyboardEvent("keydown", key, buttonMap);
keysDown.set(target.id, nextTargetId);
lastTouchedId = nextTargetId;
document.getElementById(nextTargetId).classList.add("active");
}
});
}
/**
* Prevent zoom on specified element
*
* {@link https://stackoverflow.com/a/39778831/4622620|Source}
*
* @param element The element to prevent zoom on
* Prevent zoom on specified element
* @param {HTMLElement} element
*/
function preventElementZoom(element: HTMLElement) {
function preventElementZoom(element: HTMLElement): void {
if (!element) {
return;
}
element.addEventListener("touchstart", (event: TouchEvent) => {
if (!(event.currentTarget instanceof HTMLElement)) {

View File

@ -4,8 +4,10 @@ import {InputsController} from "./inputs-controller";
import MessageUiHandler from "./ui/message-ui-handler";
import StarterSelectUiHandler from "./ui/starter-select-ui-handler";
import {Setting, settingOptions} from "./system/settings";
import SettingsUiHandler from "./ui/settings-ui-handler";
import SettingsUiHandler from "./ui/settings/settings-ui-handler";
import {Button} from "./enums/buttons";
import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler";
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
import BattleScene from "./battle-scene";
type ActionKeys = Record<Button, () => void>;
@ -159,7 +161,9 @@ export class UiInputs {
}
buttonCycleOption(button: Button): void {
if (this.scene.ui?.getHandler() instanceof StarterSelectUiHandler) {
const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler];
const uiHandler = this.scene.ui?.getHandler();
if (whitelist.some(handler => uiHandler instanceof handler)) {
this.scene.ui.processInput(button);
} else if (button === Button.V) {
this.buttonInfo(true);

View File

@ -0,0 +1,259 @@
import UiHandler from "../ui-handler";
import BattleScene from "../../battle-scene";
import {Mode} from "../ui";
import {addWindow} from "../ui-theme";
import {addTextObject, TextStyle} from "../text";
import {Button} from "../../enums/buttons";
import {NavigationManager} from "#app/ui/settings/navigationMenu";
/**
* Abstract class for handling UI elements related to button bindings.
*/
export default abstract class AbstractBindingUiHandler extends UiHandler {
// Containers for different segments of the UI.
protected optionSelectContainer: Phaser.GameObjects.Container;
protected actionsContainer: Phaser.GameObjects.Container;
// Background elements for titles and action areas.
protected titleBg: Phaser.GameObjects.NineSlice;
protected actionBg: Phaser.GameObjects.NineSlice;
protected optionSelectBg: Phaser.GameObjects.NineSlice;
// Text elements for displaying instructions and actions.
protected unlockText: Phaser.GameObjects.Text;
protected timerText: Phaser.GameObjects.Text;
protected swapText: Phaser.GameObjects.Text;
protected actionLabel: Phaser.GameObjects.Text;
protected cancelLabel: Phaser.GameObjects.Text;
protected listening: boolean = false;
protected buttonPressed: number | null = null;
// Icons for displaying current and new button assignments.
protected newButtonIcon: Phaser.GameObjects.Sprite;
protected targetButtonIcon: Phaser.GameObjects.Sprite;
// Function to call on cancel or completion of binding.
protected cancelFn: (boolean?) => boolean;
abstract swapAction(): boolean;
protected timeLeftAutoClose: number = 5;
protected countdownTimer;
// The specific setting being modified.
protected target;
/**
* Constructor for the AbstractBindingUiHandler.
*
* @param scene - The BattleScene instance.
* @param mode - The UI mode.
*/
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
}
/**
* Setup UI elements.
*/
setup() {
const ui = this.getUi();
this.optionSelectContainer = this.scene.add.container(0, 0);
this.actionsContainer = this.scene.add.container(0, 0);
// Initially, containers are not visible.
this.optionSelectContainer.setVisible(false);
this.actionsContainer.setVisible(false);
// Add containers to the UI.
ui.add(this.optionSelectContainer);
ui.add(this.actionsContainer);
// Setup backgrounds and text objects for UI.
this.titleBg = addWindow(this.scene, (this.scene.game.canvas.width / 6) - this.getWindowWidth(), -(this.scene.game.canvas.height / 6) + 28 + 21, this.getWindowWidth(), 24);
this.titleBg.setOrigin(0.5);
this.optionSelectContainer.add(this.titleBg);
this.actionBg = addWindow(this.scene, (this.scene.game.canvas.width / 6) - this.getWindowWidth(), -(this.scene.game.canvas.height / 6) + this.getWindowHeight() + 28 + 21 + 21, this.getWindowWidth(), 24);
this.actionBg.setOrigin(0.5);
this.actionsContainer.add(this.actionBg);
// Text prompts and instructions for the user.
this.unlockText = addTextObject(this.scene, 0, 0, "Press a button...", TextStyle.WINDOW);
this.unlockText.setOrigin(0, 0);
this.unlockText.setPositionRelative(this.titleBg, 36, 4);
this.optionSelectContainer.add(this.unlockText);
this.timerText = addTextObject(this.scene, 0, 0, "(5)", TextStyle.WINDOW);
this.timerText.setOrigin(0, 0);
this.timerText.setPositionRelative(this.unlockText, (this.unlockText.width/6) + 5, 0);
this.optionSelectContainer.add(this.timerText);
this.optionSelectBg = addWindow(this.scene, (this.scene.game.canvas.width / 6) - this.getWindowWidth(), -(this.scene.game.canvas.height / 6) + this.getWindowHeight() + 28, this.getWindowWidth(), this.getWindowHeight());
this.optionSelectBg.setOrigin(0.5);
this.optionSelectContainer.add(this.optionSelectBg);
this.cancelLabel = addTextObject(this.scene, 0, 0, "Cancel", TextStyle.SETTINGS_LABEL);
this.cancelLabel.setOrigin(0, 0.5);
this.cancelLabel.setPositionRelative(this.actionBg, 10, this.actionBg.height / 2);
this.actionsContainer.add(this.cancelLabel);
}
manageAutoCloseTimer() {
clearTimeout(this.countdownTimer);
this.countdownTimer = setTimeout(() => {
this.timeLeftAutoClose -= 1;
this.timerText.setText(`(${this.timeLeftAutoClose})`);
if (this.timeLeftAutoClose >= 0) {
this.manageAutoCloseTimer();
} else {
this.cancelFn();
}
}, 1000);
}
/**
* Show the UI with the provided arguments.
*
* @param args - Arguments to be passed to the show method.
* @returns `true` if successful.
*/
show(args: any[]): boolean {
super.show(args);
this.buttonPressed = null;
this.timeLeftAutoClose = 5;
this.cancelFn = args[0].cancelHandler;
this.target = args[0].target;
// Bring the option and action containers to the front of the UI.
this.getUi().bringToTop(this.optionSelectContainer);
this.getUi().bringToTop(this.actionsContainer);
this.optionSelectContainer.setVisible(true);
setTimeout(() => {
this.listening = true;
this.manageAutoCloseTimer();
}, 100);
return true;
}
/**
* Get the width of the window.
*
* @returns The window width.
*/
getWindowWidth(): number {
return 160;
}
/**
* Get the height of the window.
*
* @returns The window height.
*/
getWindowHeight(): number {
return 64;
}
/**
* Process the input for the given button.
*
* @param button - The button to process.
* @returns `true` if the input was processed successfully.
*/
processInput(button: Button): boolean {
if (this.buttonPressed === null) {
return;
}
const ui = this.getUi();
let success = false;
switch (button) {
case Button.LEFT:
case Button.RIGHT:
// Toggle between action and cancel options.
const cursor = this.cursor ? 0 : 1;
success = this.setCursor(cursor);
break;
case Button.ACTION:
// Process actions based on current cursor position.
if (this.cursor === 0) {
this.cancelFn();
} else {
success = this.swapAction();
NavigationManager.getInstance().updateIcons();
this.cancelFn(success);
}
break;
}
// Plays a select sound effect if an action was successfully processed.
if (success) {
ui.playSelect();
} else {
ui.playError();
}
return success;
}
/**
* Set the cursor to the specified position.
*
* @param cursor - The cursor position to set.
* @returns `true` if the cursor was set successfully.
*/
setCursor(cursor: integer): boolean {
this.cursor = cursor;
if (cursor === 1) {
this.actionLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
this.actionLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
this.cancelLabel.setColor(this.getTextColor(TextStyle.WINDOW));
this.cancelLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
return true;
}
this.actionLabel.setColor(this.getTextColor(TextStyle.WINDOW));
this.actionLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
this.cancelLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
this.cancelLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
return true;
}
/**
* Clear the UI elements and state.
*/
clear() {
super.clear();
clearTimeout(this.countdownTimer);
this.timerText.setText("(5)");
this.timeLeftAutoClose = 5;
this.listening = false;
this.target = null;
this.cancelFn = null;
this.optionSelectContainer.setVisible(false);
this.actionsContainer.setVisible(false);
this.newButtonIcon.setVisible(false);
this.buttonPressed = null;
}
/**
* Handle input down events.
*
* @param buttonIcon - The icon of the button that was pressed.
* @param assignedButtonIcon - The icon of the button that is assigned.
* @param type - The type of button press.
*/
onInputDown(buttonIcon: string, assignedButtonIcon: string, type: string): void {
clearTimeout(this.countdownTimer);
this.timerText.setText("");
this.newButtonIcon.setTexture(type);
this.newButtonIcon.setFrame(buttonIcon);
if (assignedButtonIcon) {
this.targetButtonIcon.setTexture(type);
this.targetButtonIcon.setFrame(assignedButtonIcon);
this.targetButtonIcon.setVisible(true);
this.swapText.setVisible(true);
}
this.newButtonIcon.setVisible(true);
this.setCursor(0);
this.actionsContainer.setVisible(true);
}
}

View File

@ -0,0 +1,647 @@
import UiHandler from "../ui-handler";
import BattleScene from "../../battle-scene";
import {Mode} from "../ui";
import {InterfaceConfig} from "../../inputs-controller";
import {addWindow} from "../ui-theme";
import {addTextObject, TextStyle} from "../text";
import {Button} from "../../enums/buttons";
import {getIconWithSettingName} from "#app/configs/inputs/configHandler";
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
export interface InputsIcons {
[key: string]: Phaser.GameObjects.Sprite;
}
export interface LayoutConfig {
optionsContainer: Phaser.GameObjects.Container;
inputsIcons: InputsIcons;
settingLabels: Phaser.GameObjects.Text[];
optionValueLabels: Phaser.GameObjects.Text[][];
optionCursors: integer[];
keys: string[];
bindingSettings: Array<String>;
}
/**
* Abstract class for handling UI elements related to settings.
*/
export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
protected settingsContainer: Phaser.GameObjects.Container;
protected optionsContainer: Phaser.GameObjects.Container;
protected navigationContainer: NavigationMenu;
protected scrollCursor: integer;
protected optionCursors: integer[];
protected cursorObj: Phaser.GameObjects.NineSlice;
protected optionsBg: Phaser.GameObjects.NineSlice;
protected actionsBg: Phaser.GameObjects.NineSlice;
protected settingLabels: Phaser.GameObjects.Text[];
protected optionValueLabels: Phaser.GameObjects.Text[][];
// layout will contain the 3 Gamepad tab for each config - dualshock, xbox, snes
protected layout: Map<string, LayoutConfig> = new Map<string, LayoutConfig>();
// Will contain the input icons from the selected layout
protected inputsIcons: InputsIcons;
protected navigationIcons: InputsIcons;
// list all the setting keys used in the selected layout (because dualshock has more buttons than xbox)
protected keys: Array<String>;
// Store the specific settings related to key bindings for the current gamepad configuration.
protected bindingSettings: Array<String>;
protected settingDevice;
protected settingBlacklisted;
protected settingDeviceDefaults;
protected settingDeviceOptions;
protected configs;
protected commonSettingsCount;
protected textureOverride;
protected titleSelected;
protected localStoragePropertyName;
protected rowsToDisplay: number;
abstract getLocalStorageSetting(): object;
abstract navigateMenuLeft(): boolean;
abstract navigateMenuRight(): boolean;
abstract saveSettingToLocalStorage(setting, cursor): void;
abstract getActiveConfig(): InterfaceConfig;
abstract setSetting(scene: BattleScene, setting, value: integer): boolean;
/**
* Constructor for the AbstractSettingsUiUiHandler.
*
* @param scene - The BattleScene instance.
* @param mode - The UI mode.
*/
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
this.rowsToDisplay = 8;
}
/**
* Setup UI elements.
*/
setup() {
const ui = this.getUi();
this.navigationIcons = {};
this.settingsContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1);
this.settingsContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6), Phaser.Geom.Rectangle.Contains);
this.navigationContainer = new NavigationMenu(this.scene, 0, 0);
this.optionsBg = addWindow(this.scene, 0, this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2);
this.optionsBg.setOrigin(0, 0);
this.actionsBg = addWindow(this.scene, 0, (this.scene.game.canvas.height / 6) - this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, 22);
this.actionsBg.setOrigin(0, 0);
const iconAction = this.scene.add.sprite(0, 0, "keyboard");
iconAction.setOrigin(0, -0.1);
iconAction.setPositionRelative(this.actionsBg, this.navigationContainer.width - 32, 4);
this.navigationIcons["BUTTON_ACTION"] = iconAction;
const actionText = addTextObject(this.scene, 0, 0, "Action", TextStyle.SETTINGS_LABEL);
actionText.setOrigin(0, 0.15);
actionText.setPositionRelative(iconAction, -actionText.width/6-2, 0);
const iconCancel = this.scene.add.sprite(0, 0, "keyboard");
iconCancel.setOrigin(0, -0.1);
iconCancel.setPositionRelative(this.actionsBg, this.navigationContainer.width - 100, 4);
this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
const cancelText = addTextObject(this.scene, 0, 0, "Cancel", TextStyle.SETTINGS_LABEL);
cancelText.setOrigin(0, 0.15);
cancelText.setPositionRelative(iconCancel, -cancelText.width/6-2, 0);
const iconReset = this.scene.add.sprite(0, 0, "keyboard");
iconReset.setOrigin(0, -0.1);
iconReset.setPositionRelative(this.actionsBg, this.navigationContainer.width - 180, 4);
this.navigationIcons["BUTTON_HOME"] = iconReset;
const resetText = addTextObject(this.scene, 0, 0, "Reset all", TextStyle.SETTINGS_LABEL);
resetText.setOrigin(0, 0.15);
resetText.setPositionRelative(iconReset, -resetText.width/6-2, 0);
this.settingsContainer.add(this.optionsBg);
this.settingsContainer.add(this.actionsBg);
this.settingsContainer.add(this.navigationContainer);
this.settingsContainer.add(iconAction);
this.settingsContainer.add(iconCancel);
this.settingsContainer.add(iconReset);
this.settingsContainer.add(actionText);
this.settingsContainer.add(cancelText);
this.settingsContainer.add(resetText);
/// Initialize a new configuration "screen" for each type of gamepad.
for (const config of this.configs) {
// Create a map to store layout settings based on the pad type.
this.layout[config.padType] = new Map();
// Create a container for gamepad options in the scene, initially hidden.
const optionsContainer = this.scene.add.container(0, 0);
optionsContainer.setVisible(false);
// Gather all binding settings from the configuration.
const bindingSettings = Object.keys(config.settings);
// Array to hold labels for different settings such as 'Controller', 'Gamepad Support', etc.
const settingLabels: Phaser.GameObjects.Text[] = [];
// Array to hold options for each setting, e.g., 'Auto', 'Disabled'.
const optionValueLabels: Phaser.GameObjects.GameObject[][] = [];
// Object to store sprites for each button configuration.
const inputsIcons: InputsIcons = {};
// Fetch common setting keys such as 'Controller' and 'Gamepad Support' from gamepad settings.
const commonSettingKeys = Object.keys(this.settingDevice).slice(0, this.commonSettingsCount).map(key => this.settingDevice[key]);
// Combine common and specific bindings into a single array.
const specificBindingKeys = [...commonSettingKeys, ...Object.keys(config.settings)];
// Fetch default values for these settings and prepare to highlight selected options.
const optionCursors = Object.values(Object.keys(this.settingDeviceDefaults).filter(s => specificBindingKeys.includes(s)).map(k => this.settingDeviceDefaults[k]));
// Filter out settings that are not relevant to the current gamepad configuration.
const settingFiltered = Object.keys(this.settingDevice).filter(_key => specificBindingKeys.includes(this.settingDevice[_key]));
// Loop through the filtered settings to manage display and options.
settingFiltered.forEach((setting, s) => {
// Convert the setting key from format 'Key_Name' to 'Key name' for display.
const settingName = setting.replace(/\_/g, " ");
// Create and add a text object for the setting name to the scene.
const isLock = this.settingBlacklisted.includes(this.settingDevice[setting]);
const labelStyle = isLock ? TextStyle.SETTINGS_LOCKED : TextStyle.SETTINGS_LABEL;
settingLabels[s] = addTextObject(this.scene, 8, 28 + s * 16, settingName, labelStyle);
settingLabels[s].setOrigin(0, 0);
optionsContainer.add(settingLabels[s]);
// Initialize an array to store the option labels for this setting.
const valueLabels: Phaser.GameObjects.GameObject[] = [];
// Process each option for the current setting.
for (const [o, option] of this.settingDeviceOptions[this.settingDevice[setting]].entries()) {
// Check if the current setting is for binding keys.
if (bindingSettings.includes(this.settingDevice[setting])) {
// Create a label for non-null options, typically indicating actionable options like 'change'.
if (o) {
const valueLabel = addTextObject(this.scene, 0, 0, isLock ? "" : option, TextStyle.WINDOW);
valueLabel.setOrigin(0, 0);
optionsContainer.add(valueLabel);
valueLabels.push(valueLabel);
continue;
}
// For null options, add an icon for the key.
const icon = this.scene.add.sprite(0, 0, this.textureOverride ? this.textureOverride : config.padType);
icon.setOrigin(0, -0.15);
inputsIcons[this.settingDevice[setting]] = icon;
optionsContainer.add(icon);
valueLabels.push(icon);
continue;
}
// For regular settings like 'Gamepad support', create a label and determine if it is selected.
const valueLabel = addTextObject(this.scene, 0, 0, option, this.settingDeviceDefaults[this.settingDevice[setting]] === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW);
valueLabel.setOrigin(0, 0);
optionsContainer.add(valueLabel);
//if a setting has 2 options, valueLabels will be an array of 2 elements
valueLabels.push(valueLabel);
}
// Collect all option labels for this setting into the main array.
optionValueLabels.push(valueLabels);
// Calculate the total width of all option labels within a specific setting
// This is achieved by summing the width of each option label
const totalWidth = optionValueLabels[s].map((o) => (o as Phaser.GameObjects.Text).width).reduce((total, width) => total += width, 0);
// Define the minimum width for a label, ensuring it's at least 78 pixels wide or the width of the setting label plus some padding
const labelWidth = Math.max(130, settingLabels[s].displayWidth + 8);
// Calculate the total available space for placing option labels next to their setting label
// We reserve space for the setting label and then distribute the remaining space evenly
const totalSpace = (300 - labelWidth) - totalWidth / 6;
// Calculate the spacing between options based on the available space divided by the number of gaps between labels
const optionSpacing = Math.floor(totalSpace / (optionValueLabels[s].length - 1));
// Initialize xOffset to zero, which will be used to position each option label horizontally
let xOffset = 0;
// Start positioning each option label one by one
for (const value of optionValueLabels[s]) {
// Set the option label's position right next to the setting label, adjusted by xOffset
(value as Phaser.GameObjects.Text).setPositionRelative(settingLabels[s], labelWidth + xOffset, 0);
// Move the xOffset to the right for the next label, ensuring each label is spaced evenly
xOffset += (value as Phaser.GameObjects.Text).width / 6 + optionSpacing;
}
});
// Assigning the newly created components to the layout map under the specific gamepad type.
this.layout[config.padType].optionsContainer = optionsContainer; // Container for this pad's options.
this.layout[config.padType].inputsIcons = inputsIcons; // Icons for each input specific to this pad.
this.layout[config.padType].settingLabels = settingLabels; // Text labels for each setting available on this pad.
this.layout[config.padType].optionValueLabels = optionValueLabels; // Labels for values corresponding to each setting.
this.layout[config.padType].optionCursors = optionCursors; // Cursors to navigate through the options.
this.layout[config.padType].keys = specificBindingKeys; // Keys that identify each setting specifically bound to this pad.
this.layout[config.padType].bindingSettings = bindingSettings; // Settings that define how the keys are bound.
// Add the options container to the overall settings container to be displayed in the UI.
this.settingsContainer.add(optionsContainer);
}
// Add the settings container to the UI.
ui.add(this.settingsContainer);
// Initially hide the settings container until needed (e.g., when a gamepad is connected or a button is pressed).
this.settingsContainer.setVisible(false);
}
/**
* Update the bindings for the current active device configuration.
*/
updateBindings(): void {
// Hide the options container for all layouts to reset the UI visibility.
Object.keys(this.layout).forEach((key) => this.layout[key].optionsContainer.setVisible(false));
// Fetch the active gamepad configuration from the input controller.
const activeConfig = this.getActiveConfig();
// Set the UI layout for the active configuration. If unsuccessful, exit the function early.
if (!this.setLayout(activeConfig)) {
return;
}
// Retrieve the gamepad settings from local storage or use an empty object if none exist.
const settings: object = this.getLocalStorageSetting();
// Update the cursor for each key based on the stored settings or default cursors.
this.keys.forEach((key, index) => {
this.setOptionCursor(index, settings.hasOwnProperty(key as string) ? settings[key as string] : this.optionCursors[index]);
});
// If the active configuration has no custom bindings set, exit the function early.
// by default, if custom does not exists, a default is assigned to it
// it only means the gamepad is not yet initalized
if (!activeConfig.custom) {
return;
}
// For each element in the binding settings, update the icon according to the current assignment.
for (const elm of this.bindingSettings) {
const icon = getIconWithSettingName(activeConfig, elm);
if (icon) {
this.inputsIcons[elm as string].setFrame(icon);
this.inputsIcons[elm as string].alpha = 1;
} else {
this.inputsIcons[elm as string].alpha = 0;
}
}
// Set the cursor and scroll cursor to their initial positions.
this.setCursor(this.cursor);
this.setScrollCursor(this.scrollCursor);
}
updateNavigationDisplay() {
const specialIcons = {
"BUTTON_HOME": "HOME.png",
"BUTTON_DELETE": "DEL.png",
};
for (const settingName of Object.keys(this.navigationIcons)) {
if (Object.keys(specialIcons).includes(settingName)) {
this.navigationIcons[settingName].setTexture("keyboard");
this.navigationIcons[settingName].setFrame(specialIcons[settingName]);
this.navigationIcons[settingName].alpha = 1;
continue;
}
const icon = this.scene.inputController?.getIconForLatestInputRecorded(settingName);
if (icon) {
const type = this.scene.inputController?.getLastSourceType();
this.navigationIcons[settingName].setTexture(type);
this.navigationIcons[settingName].setFrame(icon);
this.navigationIcons[settingName].alpha = 1;
} else {
this.navigationIcons[settingName].alpha = 0;
}
}
}
/**
* Show the UI with the provided arguments.
*
* @param args - Arguments to be passed to the show method.
* @returns `true` if successful.
*/
show(args: any[]): boolean {
super.show(args);
this.updateNavigationDisplay();
NavigationManager.getInstance().updateIcons();
// Update the bindings for the current active gamepad configuration.
this.updateBindings();
// Make the settings container visible to the user.
this.settingsContainer.setVisible(true);
// Reset the scroll cursor to the top of the settings container.
this.resetScroll();
// Move the settings container to the end of the UI stack to ensure it is displayed on top.
this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1);
// Hide any tooltips that might be visible before showing the settings container.
this.getUi().hideTooltip();
// Return true to indicate the UI was successfully shown.
return true;
}
/**
* Set the UI layout for the active device configuration.
*
* @param activeConfig - The active device configuration.
* @returns `true` if the layout was successfully applied, otherwise `false`.
*/
setLayout(activeConfig: InterfaceConfig): boolean {
// Check if there is no active configuration (e.g., no gamepad connected).
if (!activeConfig) {
// Retrieve the layout for when no gamepads are connected.
const layout = this.layout["noGamepads"];
// Make the options container visible to show message.
layout.optionsContainer.setVisible(true);
// Return false indicating the layout application was not successful due to lack of gamepad.
return false;
}
// Extract the type of the gamepad from the active configuration.
const configType = activeConfig.padType;
// Retrieve the layout settings based on the type of the gamepad.
const layout = this.layout[configType];
// Update the main controller with configuration details from the selected layout.
this.keys = layout.keys;
this.optionsContainer = layout.optionsContainer;
this.optionsContainer.setVisible(true);
this.settingLabels = layout.settingLabels;
this.optionValueLabels = layout.optionValueLabels;
this.optionCursors = layout.optionCursors;
this.inputsIcons = layout.inputsIcons;
this.bindingSettings = layout.bindingSettings;
// Return true indicating the layout was successfully applied.
return true;
}
/**
* Process the input for the given button.
*
* @param button - The button to process.
* @returns `true` if the input was processed successfully.
*/
processInput(button: Button): boolean {
const ui = this.getUi();
// Defines the maximum number of rows that can be displayed on the screen.
let success = false;
this.updateNavigationDisplay();
// Handle the input based on the button pressed.
if (button === Button.CANCEL) {
// Handle cancel button press, reverting UI mode to previous state.
success = true;
NavigationManager.getInstance().reset();
this.scene.ui.revertMode();
} else {
const cursor = this.cursor + this.scrollCursor; // Calculate the absolute cursor position.
const setting = this.settingDevice[Object.keys(this.settingDevice)[cursor]];
switch (button) {
case Button.ACTION:
if (!this.optionCursors || !this.optionValueLabels) {
return;
}
if (this.settingBlacklisted.includes(setting) || !setting.includes("BUTTON_")) {
success = false;
} else {
success = this.setSetting(this.scene, setting, 1);
}
break;
case Button.UP: // Move up in the menu.
if (!this.optionValueLabels) {
return false;
}
if (cursor) { // If not at the top, move the cursor up.
if (this.cursor) {
success = this.setCursor(this.cursor - 1);
} else {// If at the top of the visible items, scroll up.
success = this.setScrollCursor(this.scrollCursor - 1);
}
} else {
// When at the top of the menu and pressing UP, move to the bottommost item.
// First, set the cursor to the last visible element, preparing for the scroll to the end.
const successA = this.setCursor(this.rowsToDisplay - 1);
// Then, adjust the scroll to display the bottommost elements of the menu.
const successB = this.setScrollCursor(this.optionValueLabels.length - this.rowsToDisplay);
success = successA && successB; // success is just there to play the little validation sound effect
}
break;
case Button.DOWN: // Move down in the menu.
if (!this.optionValueLabels) {
return false;
}
if (cursor < this.optionValueLabels.length - 1) {
if (this.cursor < this.rowsToDisplay - 1) {
success = this.setCursor(this.cursor + 1);
} else if (this.scrollCursor < this.optionValueLabels.length - this.rowsToDisplay) {
success = this.setScrollCursor(this.scrollCursor + 1);
}
} else {
// When at the bottom of the menu and pressing DOWN, move to the topmost item.
// First, set the cursor to the first visible element, resetting the scroll to the top.
const successA = this.setCursor(0);
// Then, reset the scroll to start from the first element of the menu.
const successB = this.setScrollCursor(0);
success = successA && successB; // Indicates a successful cursor and scroll adjustment.
}
break;
case Button.LEFT: // Move selection left within the current option set.
if (!this.optionCursors || !this.optionValueLabels) {
return;
}
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
success = false;
} else if (this.optionCursors[cursor]) {
success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
}
break;
case Button.RIGHT: // Move selection right within the current option set.
if (!this.optionCursors || !this.optionValueLabels) {
return;
}
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
success = false;
} else if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) {
success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
}
break;
case Button.CYCLE_FORM:
case Button.CYCLE_SHINY:
success = this.navigationContainer.navigate(button);
break;
}
}
// If a change occurred, play the selection sound.
if (success) {
ui.playSelect();
}
return success; // Return whether the input resulted in a successful action.
}
resetScroll() {
this.cursorObj?.destroy();
this.cursorObj = null;
this.cursor = null;
this.setCursor(0);
this.setScrollCursor(0);
this.updateSettingsScroll();
}
/**
* Set the cursor to the specified position.
*
* @param cursor - The cursor position to set.
* @returns `true` if the cursor was set successfully.
*/
setCursor(cursor: integer): boolean {
const ret = super.setCursor(cursor);
// If the optionsContainer is not initialized, return the result from the parent class directly.
if (!this.optionsContainer) {
return ret;
}
// Check if the cursor object exists, if not, create it.
if (!this.cursorObj) {
this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1);
this.cursorObj.setOrigin(0, 0); // Set the origin to the top-left corner.
this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container.
}
// Update the position of the cursor object relative to the options background based on the current cursor and scroll positions.
this.cursorObj.setPositionRelative(this.optionsBg, 4, 4 + (this.cursor + this.scrollCursor) * 16);
return ret; // Return the result from the parent class's setCursor method.
}
/**
* Set the scroll cursor to the specified position.
*
* @param scrollCursor - The scroll cursor position to set.
* @returns `true` if the scroll cursor was set successfully.
*/
setScrollCursor(scrollCursor: integer): boolean {
// Check if the new scroll position is the same as the current one; if so, do not update.
if (scrollCursor === this.scrollCursor) {
return false;
}
// Update the internal scroll cursor state
this.scrollCursor = scrollCursor;
// Apply the new scroll position to the settings UI.
this.updateSettingsScroll();
// Reset the cursor to its current position to adjust its visibility after scrolling.
this.setCursor(this.cursor);
return true; // Return true to indicate the scroll cursor was successfully updated.
}
/**
* Set the option cursor to the specified position.
*
* @param settingIndex - The index of the setting.
* @param cursor - The cursor position to set.
* @param save - Whether to save the setting to local storage.
* @returns `true` if the option cursor was set successfully.
*/
setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean {
// Retrieve the specific setting using the settingIndex from the settingDevice enumeration.
const setting = this.settingDevice[Object.keys(this.settingDevice)[settingIndex]];
// Get the current cursor position for this setting.
const lastCursor = this.optionCursors[settingIndex];
// Check if the setting is not part of the bindings (i.e., it's a regular setting).
if (!this.bindingSettings.includes(setting) && !setting.includes("BUTTON_")) {
// Get the label of the last selected option and revert its color to the default.
const lastValueLabel = this.optionValueLabels[settingIndex][lastCursor];
lastValueLabel.setColor(this.getTextColor(TextStyle.WINDOW));
lastValueLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
// Update the cursor for the setting to the new position.
this.optionCursors[settingIndex] = cursor;
// Change the color of the new selected option to indicate it's selected.
const newValueLabel = this.optionValueLabels[settingIndex][cursor];
newValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
newValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
}
// If the save flag is set, save the setting to local storage
if (save) {
this.saveSettingToLocalStorage(setting, cursor);
}
return true; // Return true to indicate the cursor was successfully updated.
}
/**
* Update the scroll position of the settings UI.
*/
updateSettingsScroll(): void {
// Return immediately if the options container is not initialized.
if (!this.optionsContainer) {
return;
}
// Set the vertical position of the options container based on the current scroll cursor, multiplying by the item height.
this.optionsContainer.setY(-16 * this.scrollCursor);
// Iterate over all setting labels to update their visibility.
for (let s = 0; s < this.settingLabels.length; s++) {
// Determine if the current setting should be visible based on the scroll position.
const visible = s >= this.scrollCursor && s < this.scrollCursor + this.rowsToDisplay;
// Set the visibility of the setting label and its corresponding options.
this.settingLabels[s].setVisible(visible);
for (const option of this.optionValueLabels[s]) {
option.setVisible(visible);
}
}
}
/**
* Clear the UI elements and state.
*/
clear(): void {
super.clear();
// Hide the settings container to remove it from the view.
this.settingsContainer.setVisible(false);
// Remove the cursor from the UI.
this.eraseCursor();
}
/**
* Erase the cursor from the UI.
*/
eraseCursor(): void {
// Check if a cursor object exists.
if (this.cursorObj) {
this.cursorObj.destroy();
} // Destroy the cursor object to clean up resources.
// Set the cursor object reference to null to fully dereference it.
this.cursorObj = null;
}
}

View File

@ -0,0 +1,83 @@
import BattleScene from "../../battle-scene";
import AbstractBindingUiHandler from "./abstract-binding-ui-handler";
import {Mode} from "../ui";
import {Device} from "#app/enums/devices";
import {getIconWithSettingName, getKeyWithKeycode} from "#app/configs/inputs/configHandler";
import {addTextObject, TextStyle} from "#app/ui/text";
export default class GamepadBindingUiHandler extends AbstractBindingUiHandler {
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
this.scene.input.gamepad.on("down", this.gamepadButtonDown, this);
}
setup() {
super.setup();
// New button icon setup.
this.newButtonIcon = this.scene.add.sprite(0, 0, "xbox");
this.newButtonIcon.setPositionRelative(this.optionSelectBg, 78, 16);
this.newButtonIcon.setOrigin(0.5);
this.newButtonIcon.setVisible(false);
this.swapText = addTextObject(this.scene, 0, 0, "will swap with", TextStyle.WINDOW);
this.swapText.setOrigin(0.5);
this.swapText.setPositionRelative(this.optionSelectBg, this.optionSelectBg.width / 2 - 2, this.optionSelectBg.height / 2 - 2);
this.swapText.setVisible(false);
this.targetButtonIcon = this.scene.add.sprite(0, 0, "xbox");
this.targetButtonIcon.setPositionRelative(this.optionSelectBg, 78, 48);
this.targetButtonIcon.setOrigin(0.5);
this.targetButtonIcon.setVisible(false);
this.actionLabel = addTextObject(this.scene, 0, 0, "Confirm swap", TextStyle.SETTINGS_LABEL);
this.actionLabel.setOrigin(0, 0.5);
this.actionLabel.setPositionRelative(this.actionBg, this.actionBg.width - 75, this.actionBg.height / 2);
this.actionsContainer.add(this.actionLabel);
this.optionSelectContainer.add(this.newButtonIcon);
this.optionSelectContainer.add(this.swapText);
this.optionSelectContainer.add(this.targetButtonIcon);
}
getSelectedDevice() {
return this.scene.inputController?.selectedDevice[Device.GAMEPAD];
}
gamepadButtonDown(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void {
const blacklist = [12, 13, 14, 15]; // d-pad buttons are blacklisted.
// Check conditions before processing the button press.
if (!this.listening || pad.id.toLowerCase() !== this.getSelectedDevice() || blacklist.includes(button.index) || this.buttonPressed !== null) {
return;
}
const activeConfig = this.scene.inputController.getActiveConfig(Device.GAMEPAD);
const type = activeConfig.padType;
const key = getKeyWithKeycode(activeConfig, button.index);
const buttonIcon = activeConfig.icons[key];
if (!buttonIcon) {
return;
}
this.buttonPressed = button.index;
const assignedButtonIcon = getIconWithSettingName(activeConfig, this.target);
this.onInputDown(buttonIcon, assignedButtonIcon, type);
}
swapAction(): boolean {
const activeConfig = this.scene.inputController.getActiveConfig(Device.GAMEPAD);
if (this.scene.inputController.assignBinding(activeConfig, this.target, this.buttonPressed)) {
this.scene.gameData.saveMappingConfigs(this.getSelectedDevice(), activeConfig);
return true;
}
return false;
}
/**
* Clear the UI elements and state.
*/
clear() {
super.clear();
this.targetButtonIcon.setVisible(false);
this.swapText.setVisible(false);
}
}

View File

@ -0,0 +1,73 @@
import BattleScene from "../../battle-scene";
import AbstractBindingUiHandler from "./abstract-binding-ui-handler";
import {Mode} from "../ui";
import { getKeyWithKeycode} from "#app/configs/inputs/configHandler";
import {Device} from "#app/enums/devices";
import {addTextObject, TextStyle} from "#app/ui/text";
export default class KeyboardBindingUiHandler extends AbstractBindingUiHandler {
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
// Listen to gamepad button down events to initiate binding.
scene.input.keyboard.on("keydown", this.onKeyDown, this);
}
setup() {
super.setup();
// New button icon setup.
this.newButtonIcon = this.scene.add.sprite(0, 0, "keyboard");
this.newButtonIcon.setPositionRelative(this.optionSelectBg, 78, 32);
this.newButtonIcon.setOrigin(0.5);
this.newButtonIcon.setVisible(false);
this.actionLabel = addTextObject(this.scene, 0, 0, "Assign button", TextStyle.SETTINGS_LABEL);
this.actionLabel.setOrigin(0, 0.5);
this.actionLabel.setPositionRelative(this.actionBg, this.actionBg.width - 80, this.actionBg.height / 2);
this.actionsContainer.add(this.actionLabel);
this.optionSelectContainer.add(this.newButtonIcon);
}
getSelectedDevice() {
return this.scene.inputController?.selectedDevice[Device.KEYBOARD];
}
onKeyDown(event): void {
const blacklist = [
Phaser.Input.Keyboard.KeyCodes.UP,
Phaser.Input.Keyboard.KeyCodes.DOWN,
Phaser.Input.Keyboard.KeyCodes.LEFT,
Phaser.Input.Keyboard.KeyCodes.RIGHT,
Phaser.Input.Keyboard.KeyCodes.HOME,
Phaser.Input.Keyboard.KeyCodes.ENTER,
Phaser.Input.Keyboard.KeyCodes.ESC,
Phaser.Input.Keyboard.KeyCodes.DELETE,
];
const key = event.keyCode;
// // Check conditions before processing the button press.
if (!this.listening || this.buttonPressed !== null || blacklist.includes(key)) {
return;
}
const activeConfig = this.scene.inputController.getActiveConfig(Device.KEYBOARD);
const _key = getKeyWithKeycode(activeConfig, key);
const buttonIcon = activeConfig.icons[_key];
if (!buttonIcon) {
return;
}
this.buttonPressed = key;
// const assignedButtonIcon = getIconWithSettingName(activeConfig, this.target);
this.onInputDown(buttonIcon, null, "keyboard");
}
swapAction(): boolean {
const activeConfig = this.scene.inputController.getActiveConfig(Device.KEYBOARD);
if (this.scene.inputController.assignBinding(activeConfig, this.target, this.buttonPressed)) {
this.scene.gameData.saveMappingConfigs(this.getSelectedDevice(), activeConfig);
return true;
}
return false;
}
}

View File

@ -0,0 +1,217 @@
import BattleScene from "#app/battle-scene";
import {Mode} from "#app/ui/ui";
import {InputsIcons} from "#app/ui/settings/abstract-settings-ui-handler";
import {addTextObject, setTextStyle, TextStyle} from "#app/ui/text";
import {addWindow} from "#app/ui/ui-theme";
import {Button} from "#app/enums/buttons";
/**
* Manages navigation and menus tabs within the setting menu.
*/
export class NavigationManager {
private static instance: NavigationManager;
public modes: Mode[];
public selectedMode: Mode = Mode.SETTINGS;
public navigationMenus: NavigationMenu[] = new Array<NavigationMenu>();
public labels: string[];
/**
* Creates an instance of NavigationManager.
* To create a new tab in the menu, add the mode to the modes array and the label to the labels array.
* and instantiate a new NavigationMenu instance in your handler
* like: this.navigationContainer = new NavigationMenu(this.scene, 0, 0);
*/
constructor() {
this.modes = [
Mode.SETTINGS,
Mode.SETTINGS_GAMEPAD,
Mode.SETTINGS_KEYBOARD,
];
this.labels = ["General", "Gamepad", "Keyboard"];
}
public reset() {
this.selectedMode = Mode.SETTINGS;
}
/**
* Gets the singleton instance of the NavigationManager.
* @returns The singleton instance of NavigationManager.
*/
public static getInstance(): NavigationManager {
if (!NavigationManager.instance) {
NavigationManager.instance = new NavigationManager();
}
return NavigationManager.instance;
}
/**
* Navigates to the previous mode in the modes array.
* @param scene The current BattleScene instance.
*/
public navigateLeft(scene) {
const pos = this.modes.indexOf(this.selectedMode);
const maxPos = this.modes.length - 1;
if (pos === 0) {
this.selectedMode = this.modes[maxPos];
} else {
this.selectedMode = this.modes[pos - 1];
}
scene.ui.setMode(this.selectedMode);
this.updateNavigationMenus();
}
/**
* Navigates to the next mode in the modes array.
* @param scene The current BattleScene instance.
*/
public navigateRight(scene) {
const pos = this.modes.indexOf(this.selectedMode);
const maxPos = this.modes.length - 1;
if (pos === maxPos) {
this.selectedMode = this.modes[0];
} else {
this.selectedMode = this.modes[pos + 1];
}
scene.ui.setMode(this.selectedMode);
this.updateNavigationMenus();
}
/**
* Updates all navigation menus.
*/
public updateNavigationMenus() {
for (const instance of this.navigationMenus) {
instance.update();
}
}
/**
* Updates icons for all navigation menus.
*/
public updateIcons() {
for (const instance of this.navigationMenus) {
instance.updateIcons();
}
}
}
export default class NavigationMenu extends Phaser.GameObjects.Container {
private navigationIcons: InputsIcons;
public scene: BattleScene;
protected headerTitles: Phaser.GameObjects.Text[] = new Array<Phaser.GameObjects.Text>();
/**
* Creates an instance of NavigationMenu.
* @param scene The current BattleScene instance.
* @param x The x position of the NavigationMenu.
* @param y The y position of the NavigationMenu.
*/
constructor(scene: BattleScene, x: number, y: number) {
super(scene, x, y);
this.scene = scene;
this.setup();
}
/**
* Sets up the NavigationMenu by adding windows, icons, and labels.
*/
setup() {
const navigationManager = NavigationManager.getInstance();
const headerBg = addWindow(this.scene, 0, 0, (this.scene.game.canvas.width / 6) - 2, 24);
headerBg.setOrigin(0, 0);
this.add(headerBg);
this.width = headerBg.width;
this.height = headerBg.height;
this.navigationIcons = {};
const iconPreviousTab = this.scene.add.sprite(8, 4, "keyboard");
iconPreviousTab.setOrigin(0, -0.1);
iconPreviousTab.setPositionRelative(headerBg, 8, 4);
this.navigationIcons["BUTTON_CYCLE_FORM"] = iconPreviousTab;
const iconNextTab = this.scene.add.sprite(0, 0, "keyboard");
iconNextTab.setOrigin(0, -0.1);
iconNextTab.setPositionRelative(headerBg, headerBg.width - 20, 4);
this.navigationIcons["BUTTON_CYCLE_SHINY"] = iconNextTab;
let relative: Phaser.GameObjects.Sprite | Phaser.GameObjects.Text = iconPreviousTab;
let relativeWidth: number = iconPreviousTab.width*6;
for (const label of navigationManager.labels) {
const labelText = addTextObject(this.scene, 0, 0, label, TextStyle.SETTINGS_LABEL);
labelText.setOrigin(0, 0);
labelText.setPositionRelative(relative, 6 + relativeWidth/6, 0);
this.add(labelText);
this.headerTitles.push(labelText);
relative = labelText;
relativeWidth = labelText.width;
}
this.add(iconPreviousTab);
this.add(iconNextTab);
navigationManager.navigationMenus.push(this);
navigationManager.updateNavigationMenus();
}
/**
* Updates the NavigationMenu's header titles based on the selected mode.
*/
update() {
const navigationManager = NavigationManager.getInstance();
const posSelected = navigationManager.modes.indexOf(navigationManager.selectedMode);
for (const [index, title] of this.headerTitles.entries()) {
setTextStyle(title, this.scene, index === posSelected ? TextStyle.SETTINGS_SELECTED : TextStyle.SETTINGS_LABEL);
}
}
/**
* Updates the icons in the NavigationMenu based on the latest input recorded.
*/
updateIcons() {
const specialIcons = {
"BUTTON_HOME": "HOME.png",
"BUTTON_DELETE": "DEL.png",
};
for (const settingName of Object.keys(this.navigationIcons)) {
if (Object.keys(specialIcons).includes(settingName)) {
this.navigationIcons[settingName].setTexture("keyboard");
this.navigationIcons[settingName].setFrame(specialIcons[settingName]);
this.navigationIcons[settingName].alpha = 1;
continue;
}
const icon = this.scene.inputController?.getIconForLatestInputRecorded(settingName);
if (icon) {
const type = this.scene.inputController?.getLastSourceType();
this.navigationIcons[settingName].setTexture(type);
this.navigationIcons[settingName].setFrame(icon);
this.navigationIcons[settingName].alpha = 1;
} else {
this.navigationIcons[settingName].alpha = 0;
}
}
}
/**
* Handles navigation based on the button pressed.
* @param button The button pressed for navigation.
* @returns A boolean indicating if the navigation was handled.
*/
navigate(button: Button): boolean {
const navigationManager = NavigationManager.getInstance();
switch (button) {
case Button.CYCLE_FORM:
navigationManager.navigateLeft(this.scene);
return true;
break;
case Button.CYCLE_SHINY:
navigationManager.navigateRight(this.scene);
return true;
break;
}
return false;
}
}

View File

@ -1,6 +1,6 @@
import BattleScene from "../battle-scene";
import AbstractOptionSelectUiHandler from "./abstact-option-select-ui-handler";
import { Mode } from "./ui";
import BattleScene from "../../battle-scene";
import AbstractOptionSelectUiHandler from "../abstact-option-select-ui-handler";
import { Mode } from "../ui";
export default class OptionSelectUiHandler extends AbstractOptionSelectUiHandler {
constructor(scene: BattleScene, mode: Mode = Mode.OPTION_SELECT) {

View File

@ -0,0 +1,168 @@
import BattleScene from "../../battle-scene";
import {addTextObject, TextStyle} from "../text";
import {Mode} from "../ui";
import {
setSettingGamepad,
SettingGamepad,
settingGamepadBlackList,
settingGamepadDefaults,
settingGamepadOptions
} from "../../system/settings-gamepad";
import pad_xbox360 from "#app/configs/inputs/pad_xbox360";
import pad_dualshock from "#app/configs/inputs/pad_dualshock";
import pad_unlicensedSNES from "#app/configs/inputs/pad_unlicensedSNES";
import {InterfaceConfig} from "#app/inputs-controller";
import AbstractSettingsUiUiHandler from "#app/ui/settings/abstract-settings-ui-handler";
import {Device} from "#app/enums/devices";
import {truncateString} from "#app/utils";
/**
* Class representing the settings UI handler for gamepads.
*
* @extends AbstractSettingsUiUiHandler
*/
export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandler {
/**
* Creates an instance of SettingsGamepadUiHandler.
*
* @param scene - The BattleScene instance.
* @param mode - The UI mode, optional.
*/
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
this.titleSelected = "Gamepad";
this.settingDevice = SettingGamepad;
this.settingDeviceDefaults = settingGamepadDefaults;
this.settingDeviceOptions = settingGamepadOptions;
this.configs = [pad_xbox360, pad_dualshock, pad_unlicensedSNES];
this.commonSettingsCount = 2;
this.localStoragePropertyName = "settingsGamepad";
this.settingBlacklisted = settingGamepadBlackList;
}
setSetting(scene: BattleScene, setting, value: integer): boolean {
return setSettingGamepad(scene, setting, value);
}
/**
* Setup UI elements.
*/
setup() {
super.setup();
// If no gamepads are detected, set up a default UI prompt in the settings container.
this.layout["noGamepads"] = new Map();
const optionsContainer = this.scene.add.container(0, 0);
optionsContainer.setVisible(false); // Initially hide the container as no gamepads are connected.
const label = addTextObject(this.scene, 8, 28, "Please plug a controller or press a button", TextStyle.SETTINGS_LABEL);
label.setOrigin(0, 0);
optionsContainer.add(label);
this.settingsContainer.add(optionsContainer);
// Map the 'noGamepads' layout options for easy access.
this.layout["noGamepads"].optionsContainer = optionsContainer;
this.layout["noGamepads"].label = label;
}
/**
* Get the active configuration.
*
* @returns The active gamepad configuration.
*/
getActiveConfig(): InterfaceConfig {
return this.scene.inputController.getActiveConfig(Device.GAMEPAD);
}
/**
* Get the gamepad settings from local storage.
*
* @returns The gamepad settings from local storage.
*/
getLocalStorageSetting(): object {
// Retrieve the gamepad settings from local storage or use an empty object if none exist.
const settings: object = localStorage.hasOwnProperty("settingsGamepad") ? JSON.parse(localStorage.getItem("settingsGamepad")) : {};
return settings;
}
/**
* Set the layout for the active configuration.
*
* @param activeConfig - The active gamepad configuration.
* @returns `true` if the layout was successfully applied, otherwise `false`.
*/
setLayout(activeConfig: InterfaceConfig): boolean {
// Check if there is no active configuration (e.g., no gamepad connected).
if (!activeConfig) {
// Retrieve the layout for when no gamepads are connected.
const layout = this.layout["noGamepads"];
// Make the options container visible to show message.
layout.optionsContainer.setVisible(true);
// Return false indicating the layout application was not successful due to lack of gamepad.
return false;
}
return super.setLayout(activeConfig);
}
/**
* Navigate to the left menu tab.
*
* @returns `true` indicating the navigation was successful.
*/
navigateMenuLeft(): boolean {
this.scene.ui.setMode(Mode.SETTINGS);
return true;
}
/**
* Navigate to the right menu tab.
*
* @returns `true` indicating the navigation was successful.
*/
navigateMenuRight(): boolean {
this.scene.ui.setMode(Mode.SETTINGS_KEYBOARD);
return true;
}
/**
* Update the display of the chosen gamepad.
*/
updateChosenGamepadDisplay(): void {
// Update any bindings that might have changed since the last update.
this.updateBindings();
this.resetScroll();
// Iterate over the keys in the settingDevice enumeration.
for (const [index, key] of Object.keys(this.settingDevice).entries()) {
const setting = this.settingDevice[key]; // Get the actual setting value using the key.
// Check if the current setting corresponds to the controller setting.
if (setting === this.settingDevice.Controller) {
// Iterate over all layouts excluding the 'noGamepads' special case.
for (const _key of Object.keys(this.layout)) {
if (_key === "noGamepads") {
continue;
} // Skip updating the no gamepad layout.
// Update the text of the first option label under the current setting to the name of the chosen gamepad,
// truncating the name to 30 characters if necessary.
this.layout[_key].optionValueLabels[index][0].setText(truncateString(this.scene.inputController.selectedDevice[Device.GAMEPAD], 20));
}
}
}
}
/**
* Save the setting to local storage.
*
* @param setting - The setting to save.
* @param cursor - The cursor position to save.
*/
saveSettingToLocalStorage(setting, cursor): void {
if (this.settingDevice[setting] !== this.settingDevice.Controller) {
this.scene.gameData.saveGamepadSetting(setting, cursor);
}
}
}

View File

@ -0,0 +1,224 @@
import BattleScene from "../../battle-scene";
import {Mode} from "../ui";
import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty";
import {
setSettingKeyboard,
SettingKeyboard,
settingKeyboardBlackList,
settingKeyboardDefaults,
settingKeyboardOptions
} from "#app/system/settings-keyboard";
import {reverseValueToKeySetting, truncateString} from "#app/utils";
import AbstractSettingsUiUiHandler from "#app/ui/settings/abstract-settings-ui-handler";
import {InterfaceConfig} from "#app/inputs-controller";
import {addTextObject, TextStyle} from "#app/ui/text";
import {deleteBind} from "#app/configs/inputs/configHandler";
import {Device} from "#app/enums/devices";
import {NavigationManager} from "#app/ui/settings/navigationMenu";
/**
* Class representing the settings UI handler for keyboards.
*
* @extends AbstractSettingsUiUiHandler
*/
export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandler {
/**
* Creates an instance of SettingsKeyboardUiHandler.
*
* @param scene - The BattleScene instance.
* @param mode - The UI mode, optional.
*/
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
this.titleSelected = "Keyboard";
this.settingDevice = SettingKeyboard;
this.settingDeviceDefaults = settingKeyboardDefaults;
this.settingDeviceOptions = settingKeyboardOptions;
this.configs = [cfg_keyboard_qwerty];
this.commonSettingsCount = 0;
this.textureOverride = "keyboard";
this.localStoragePropertyName = "settingsKeyboard";
this.settingBlacklisted = settingKeyboardBlackList;
const deleteEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE);
const restoreDefaultEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.HOME);
deleteEvent.on("up", this.onDeleteDown, this);
restoreDefaultEvent.on("up", this.onHomeDown, this);
}
setSetting(scene: BattleScene, setting, value: integer): boolean {
return setSettingKeyboard(scene, setting, value);
}
/**
* Setup UI elements.
*/
setup() {
super.setup();
// If no gamepads are detected, set up a default UI prompt in the settings container.
this.layout["noKeyboard"] = new Map();
const optionsContainer = this.scene.add.container(0, 0);
optionsContainer.setVisible(false); // Initially hide the container as no gamepads are connected.
const label = addTextObject(this.scene, 8, 28, "Please press a key on your keyboard", TextStyle.SETTINGS_LABEL);
label.setOrigin(0, 0);
optionsContainer.add(label);
this.settingsContainer.add(optionsContainer);
const iconDelete = this.scene.add.sprite(0, 0, "keyboard");
iconDelete.setOrigin(0, -0.1);
iconDelete.setPositionRelative(this.actionsBg, this.navigationContainer.width - 260, 4);
this.navigationIcons["BUTTON_DELETE"] = iconDelete;
const deleteText = addTextObject(this.scene, 0, 0, "Delete", TextStyle.SETTINGS_LABEL);
deleteText.setOrigin(0, 0.15);
deleteText.setPositionRelative(iconDelete, -deleteText.width/6-2, 0);
this.settingsContainer.add(iconDelete);
this.settingsContainer.add(deleteText);
// Map the 'noKeyboard' layout options for easy access.
this.layout["noKeyboard"].optionsContainer = optionsContainer;
this.layout["noKeyboard"].label = label;
}
/**
* Handle the home key press event.
*/
onHomeDown(): void {
if (![Mode.SETTINGS_KEYBOARD, Mode.SETTINGS_GAMEPAD].includes(this.scene.ui.getMode())) {
return;
}
this.scene.gameData.resetMappingToFactory();
NavigationManager.getInstance().updateIcons();
}
/**
* Handle the delete key press event.
*/
onDeleteDown(): void {
if (this.scene.ui.getMode() !== Mode.SETTINGS_KEYBOARD) {
return;
}
const cursor = this.cursor + this.scrollCursor; // Calculate the absolute cursor position.
const selection = this.settingLabels[cursor].text;
const key = reverseValueToKeySetting(selection);
const settingName = SettingKeyboard[key];
const activeConfig = this.getActiveConfig();
const success = deleteBind(this.getActiveConfig(), settingName);
if (success) {
this.saveCustomKeyboardMappingToLocalStorage(activeConfig);
this.updateBindings();
NavigationManager.getInstance().updateIcons();
}
}
/**
* Get the active configuration.
*
* @returns The active keyboard configuration.
*/
getActiveConfig(): InterfaceConfig {
return this.scene.inputController.getActiveConfig(Device.KEYBOARD);
}
/**
* Get the keyboard settings from local storage.
*
* @returns The keyboard settings from local storage.
*/
getLocalStorageSetting(): object {
// Retrieve the gamepad settings from local storage or use an empty object if none exist.
const settings: object = localStorage.hasOwnProperty("settingsKeyboard") ? JSON.parse(localStorage.getItem("settingsKeyboard")) : {};
return settings;
}
/**
* Set the layout for the active configuration.
*
* @param activeConfig - The active keyboard configuration.
* @returns `true` if the layout was successfully applied, otherwise `false`.
*/
setLayout(activeConfig: InterfaceConfig): boolean {
// Check if there is no active configuration (e.g., no gamepad connected).
if (!activeConfig) {
// Retrieve the layout for when no gamepads are connected.
const layout = this.layout["noKeyboard"];
// Make the options container visible to show message.
layout.optionsContainer.setVisible(true);
// Return false indicating the layout application was not successful due to lack of gamepad.
return false;
}
return super.setLayout(activeConfig);
}
/**
* Navigate to the left menu tab.
*
* @returns `true` indicating the navigation was successful.
*/
navigateMenuLeft(): boolean {
this.scene.ui.setMode(Mode.SETTINGS_GAMEPAD);
return true;
}
/**
* Navigate to the right menu tab.
*
* @returns `true` indicating the navigation was successful.
*/
navigateMenuRight(): boolean {
this.scene.ui.setMode(Mode.SETTINGS);
return true;
}
/**
* Update the display of the chosen keyboard layout.
*/
updateChosenKeyboardDisplay(): void {
// Update any bindings that might have changed since the last update.
this.updateBindings();
// Iterate over the keys in the settingDevice enumeration.
for (const [index, key] of Object.keys(this.settingDevice).entries()) {
const setting = this.settingDevice[key]; // Get the actual setting value using the key.
// Check if the current setting corresponds to the layout setting.
if (setting === this.settingDevice.Default_Layout) {
// Iterate over all layouts excluding the 'noGamepads' special case.
for (const _key of Object.keys(this.layout)) {
if (_key === "noKeyboard") {
continue;
} // Skip updating the no gamepad layout.
// Update the text of the first option label under the current setting to the name of the chosen gamepad,
// truncating the name to 30 characters if necessary.
this.layout[_key].optionValueLabels[index][0].setText(truncateString(this.scene.inputController.selectedDevice[Device.KEYBOARD], 22));
}
}
}
}
/**
* Save the custom keyboard mapping to local storage.
*
* @param config - The configuration to save.
*/
saveCustomKeyboardMappingToLocalStorage(config): void {
this.scene.gameData.saveMappingConfigs(this.scene.inputController?.selectedDevice[Device.KEYBOARD], config);
}
/**
* Save the setting to local storage.
*
* @param settingName - The name of the setting to save.
* @param cursor - The cursor position to save.
*/
saveSettingToLocalStorage(settingName, cursor): void {
if (this.settingDevice[settingName] !== this.settingDevice.Default_Layout) {
this.scene.gameData.saveKeyboardSetting(settingName, cursor);
}
}
}

View File

@ -1,15 +1,18 @@
import BattleScene from "../battle-scene";
import { Setting, reloadSettings, settingDefaults, settingOptions } from "../system/settings";
import { hasTouchscreen, isMobile } from "../touch-controls";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
import BattleScene from "../../battle-scene";
import {Setting, reloadSettings, settingDefaults, settingOptions} from "../../system/settings";
import { hasTouchscreen, isMobile } from "../../touch-controls";
import { TextStyle, addTextObject } from "../text";
import { Mode } from "../ui";
import UiHandler from "../ui-handler";
import { addWindow } from "../ui-theme";
import {Button} from "../../enums/buttons";
import {InputsIcons} from "#app/ui/settings/abstract-settings-ui-handler";
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
export default class SettingsUiHandler extends UiHandler {
private settingsContainer: Phaser.GameObjects.Container;
private optionsContainer: Phaser.GameObjects.Container;
private navigationContainer: NavigationMenu;
private scrollCursor: integer;
@ -20,16 +23,20 @@ export default class SettingsUiHandler extends UiHandler {
private settingLabels: Phaser.GameObjects.Text[];
private optionValueLabels: Phaser.GameObjects.Text[][];
protected navigationIcons: InputsIcons;
private cursorObj: Phaser.GameObjects.NineSlice;
private reloadRequired: boolean;
private reloadI18n: boolean;
private rowsToDisplay: number;
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
this.reloadRequired = false;
this.reloadI18n = false;
this.rowsToDisplay = 8;
}
setup() {
@ -37,18 +44,36 @@ export default class SettingsUiHandler extends UiHandler {
this.settingsContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1);
this.settingsContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6), Phaser.Geom.Rectangle.Contains);
this.settingsContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6 - 20), Phaser.Geom.Rectangle.Contains);
const headerBg = addWindow(this.scene, 0, 0, (this.scene.game.canvas.width / 6) - 2, 24);
headerBg.setOrigin(0, 0);
this.navigationIcons = {};
const headerText = addTextObject(this.scene, 0, 0, "Options", TextStyle.SETTINGS_LABEL);
headerText.setOrigin(0, 0);
headerText.setPositionRelative(headerBg, 8, 4);
this.navigationContainer = new NavigationMenu(this.scene, 0, 0);
this.optionsBg = addWindow(this.scene, 0, headerBg.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - headerBg.height - 2);
this.optionsBg = addWindow(this.scene, 0, this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2);
this.optionsBg.setOrigin(0, 0);
const actionsBg = addWindow(this.scene, 0, (this.scene.game.canvas.height / 6) - this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, 22);
actionsBg.setOrigin(0, 0);
const iconAction = this.scene.add.sprite(0, 0, "keyboard");
iconAction.setOrigin(0, -0.1);
iconAction.setPositionRelative(actionsBg, this.navigationContainer.width - 32, 4);
this.navigationIcons["BUTTON_ACTION"] = iconAction;
const actionText = addTextObject(this.scene, 0, 0, "Action", TextStyle.SETTINGS_LABEL);
actionText.setOrigin(0, 0.15);
actionText.setPositionRelative(iconAction, -actionText.width/6-2, 0);
const iconCancel = this.scene.add.sprite(0, 0, "keyboard");
iconCancel.setOrigin(0, -0.1);
iconCancel.setPositionRelative(actionsBg, this.navigationContainer.width - 100, 4);
this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
const cancelText = addTextObject(this.scene, 0, 0, "Cancel", TextStyle.SETTINGS_LABEL);
cancelText.setOrigin(0, 0.15);
cancelText.setPositionRelative(iconCancel, -cancelText.width/6-2, 0);
this.optionsContainer = this.scene.add.container(0, 0);
this.settingLabels = [];
@ -91,10 +116,14 @@ export default class SettingsUiHandler extends UiHandler {
this.optionCursors = Object.values(settingDefaults);
this.settingsContainer.add(headerBg);
this.settingsContainer.add(headerText);
this.settingsContainer.add(this.optionsBg);
this.settingsContainer.add(this.navigationContainer);
this.settingsContainer.add(actionsBg);
this.settingsContainer.add(this.optionsContainer);
this.settingsContainer.add(iconAction);
this.settingsContainer.add(iconCancel);
this.settingsContainer.add(actionText);
this.settingsContainer.add(cancelText);
ui.add(this.settingsContainer);
@ -104,8 +133,30 @@ export default class SettingsUiHandler extends UiHandler {
this.settingsContainer.setVisible(false);
}
updateBindings(): void {
for (const settingName of Object.keys(this.navigationIcons)) {
if (settingName === "BUTTON_HOME") {
this.navigationIcons[settingName].setTexture("keyboard");
this.navigationIcons[settingName].setFrame("HOME.png");
this.navigationIcons[settingName].alpha = 1;
continue;
}
const icon = this.scene.inputController?.getIconForLatestInputRecorded(settingName);
if (icon) {
const type = this.scene.inputController?.getLastSourceType();
this.navigationIcons[settingName].setTexture(type);
this.navigationIcons[settingName].setFrame(icon);
this.navigationIcons[settingName].alpha = 1;
} else {
this.navigationIcons[settingName].alpha = 0;
}
}
NavigationManager.getInstance().updateIcons();
}
show(args: any[]): boolean {
super.show(args);
this.updateBindings();
const settings: object = localStorage.hasOwnProperty("settings") ? JSON.parse(localStorage.getItem("settings")) : {};
@ -133,12 +184,12 @@ export default class SettingsUiHandler extends UiHandler {
processInput(button: Button): boolean {
const ui = this.getUi();
// Defines the maximum number of rows that can be displayed on the screen.
const rowsToDisplay = 9;
let success = false;
if (button === Button.CANCEL) {
success = true;
NavigationManager.getInstance().reset();
// Reverts UI to its previous state on cancel.
this.scene.ui.revertMode();
} else {
@ -154,17 +205,17 @@ export default class SettingsUiHandler extends UiHandler {
} else {
// When at the top of the menu and pressing UP, move to the bottommost item.
// First, set the cursor to the last visible element, preparing for the scroll to the end.
const successA = this.setCursor(rowsToDisplay - 1);
const successA = this.setCursor(this.rowsToDisplay - 1);
// Then, adjust the scroll to display the bottommost elements of the menu.
const successB = this.setScrollCursor(this.optionValueLabels.length - rowsToDisplay);
const successB = this.setScrollCursor(this.optionValueLabels.length - this.rowsToDisplay);
success = successA && successB; // success is just there to play the little validation sound effect
}
break;
case Button.DOWN:
if (cursor < this.optionValueLabels.length - 1) {
if (this.cursor < rowsToDisplay - 1) { // if the visual cursor is in the frame of 0 to 8
if (this.cursor < this.rowsToDisplay - 1) {// if the visual cursor is in the frame of 0 to 8
success = this.setCursor(this.cursor + 1);
} else if (this.scrollCursor < this.optionValueLabels.length - rowsToDisplay) {
} else if (this.scrollCursor < this.optionValueLabels.length - this.rowsToDisplay) {
success = this.setScrollCursor(this.scrollCursor + 1);
}
} else {
@ -177,7 +228,7 @@ export default class SettingsUiHandler extends UiHandler {
}
break;
case Button.LEFT:
if (this.optionCursors[cursor]) { // Moves the option cursor left, if possible.
if (this.optionCursors[cursor]) {// Moves the option cursor left, if possible.
success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
}
break;
@ -187,6 +238,10 @@ export default class SettingsUiHandler extends UiHandler {
success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
}
break;
case Button.CYCLE_FORM:
case Button.CYCLE_SHINY:
success = this.navigationContainer.navigate(button);
break;
}
}
@ -263,7 +318,7 @@ export default class SettingsUiHandler extends UiHandler {
this.optionsContainer.setY(-16 * this.scrollCursor);
for (let s = 0; s < this.settingLabels.length; s++) {
const visible = s >= this.scrollCursor && s < this.scrollCursor + 9;
const visible = s >= this.scrollCursor && s < this.scrollCursor + this.rowsToDisplay;
this.settingLabels[s].setVisible(visible);
for (const option of this.optionValueLabels[s]) {
option.setVisible(visible);

View File

@ -742,7 +742,7 @@ export default class SummaryUiHandler extends UiHandler {
allAbilityInfo.push(this.passiveContainer);
// Sets up the pixel button prompt image
this.abilityPrompt = this.scene.add.image(0, 0, !this.scene.gamepadSupport ? "summary_profile_prompt_z" : "summary_profile_prompt_a");
this.abilityPrompt = this.scene.add.image(0, 0, !this.scene.inputController?.gamepadSupport ? "summary_profile_prompt_z" : "summary_profile_prompt_a");
this.abilityPrompt.setPosition(8, 43);
this.abilityPrompt.setVisible(true);
this.abilityPrompt.setOrigin(0, 0);

View File

@ -26,6 +26,7 @@ export enum TextStyle {
STATS_VALUE,
SETTINGS_LABEL,
SETTINGS_SELECTED,
SETTINGS_LOCKED,
TOOLTIP_TITLE,
TOOLTIP_CONTENT,
MOVE_INFO_CONTENT
@ -63,6 +64,15 @@ export function addTextObject(scene: Phaser.Scene, x: number, y: number, content
return ret;
}
export function setTextStyle(obj: Phaser.GameObjects.Text, scene: Phaser.Scene, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle) {
const [ styleOptions, shadowColor, shadowXpos, shadowYpos ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions);
obj.setScale(0.1666666667);
obj.setShadow(shadowXpos, shadowYpos, shadowColor);
if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) {
obj.setLineSpacing(5);
}
}
export function addBBCodeTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): BBCodeText {
const [ styleOptions, shadowColor, shadowXpos, shadowYpos ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions);
@ -143,6 +153,7 @@ function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptio
break;
case TextStyle.MESSAGE:
case TextStyle.SETTINGS_LABEL:
case TextStyle.SETTINGS_LOCKED:
case TextStyle.SETTINGS_SELECTED:
styleOptions.fontSize = languageSettings[lang]?.summaryFontSize || "96px";
break;
@ -226,6 +237,7 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui
case TextStyle.SUMMARY_GOLD:
case TextStyle.MONEY:
return !shadow ? "#e8e8a8" : "#a0a060";
case TextStyle.SETTINGS_LOCKED:
case TextStyle.SUMMARY_GRAY:
return !shadow ? "#a0a0a0" : "#636363";
case TextStyle.STATS_LABEL:

View File

@ -1,6 +1,6 @@
import BattleScene from "../battle-scene";
import { DailyRunScoreboard } from "./daily-run-scoreboard";
import OptionSelectUiHandler from "./option-select-ui-handler";
import OptionSelectUiHandler from "./settings/option-select-ui-handler";
import { Mode } from "./ui";
import * as Utils from "../utils";
import { TextStyle, addTextObject } from "./text";

View File

@ -12,12 +12,13 @@ import SummaryUiHandler from "./summary-ui-handler";
import StarterSelectUiHandler from "./starter-select-ui-handler";
import EvolutionSceneHandler from "./evolution-scene-handler";
import TargetSelectUiHandler from "./target-select-ui-handler";
import SettingsUiHandler from "./settings-ui-handler";
import {addTextObject, TextStyle} from "./text";
import SettingsUiHandler from "./settings/settings-ui-handler";
import SettingsGamepadUiHandler from "./settings/settings-gamepad-ui-handler";
import { TextStyle, addTextObject } from "./text";
import AchvBar from "./achv-bar";
import MenuUiHandler from "./menu-ui-handler";
import AchvsUiHandler from "./achvs-ui-handler";
import OptionSelectUiHandler from "./option-select-ui-handler";
import OptionSelectUiHandler from "./settings/option-select-ui-handler";
import EggHatchSceneHandler from "./egg-hatch-scene-handler";
import EggListUiHandler from "./egg-list-ui-handler";
import EggGachaUiHandler from "./egg-gacha-ui-handler";
@ -38,6 +39,9 @@ import SessionReloadModalUiHandler from "./session-reload-modal-ui-handler";
import {Button} from "../enums/buttons";
import i18next, {ParseKeys} from "i18next";
import {PlayerGender} from "#app/system/game-data";
import GamepadBindingUiHandler from "./settings/gamepad-binding-ui-handler";
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
import KeyboardBindingUiHandler from "#app/ui/settings/keyboard-binding-ui-handler";
export enum Mode {
MESSAGE,
@ -58,6 +62,10 @@ export enum Mode {
MENU,
MENU_OPTION_SELECT,
SETTINGS,
SETTINGS_GAMEPAD,
GAMEPAD_BINDING,
SETTINGS_KEYBOARD,
KEYBOARD_BINDING,
ACHIEVEMENTS,
GAME_STATS,
VOUCHERS,
@ -88,7 +96,11 @@ const noTransitionModes = [
Mode.OPTION_SELECT,
Mode.MENU,
Mode.MENU_OPTION_SELECT,
Mode.GAMEPAD_BINDING,
Mode.KEYBOARD_BINDING,
Mode.SETTINGS,
Mode.SETTINGS_GAMEPAD,
Mode.SETTINGS_KEYBOARD,
Mode.ACHIEVEMENTS,
Mode.GAME_STATS,
Mode.VOUCHERS,
@ -103,7 +115,7 @@ const noTransitionModes = [
export default class UI extends Phaser.GameObjects.Container {
private mode: Mode;
private modeChain: Mode[];
private handlers: UiHandler[];
public handlers: UiHandler[];
private overlay: Phaser.GameObjects.Rectangle;
public achvBar: AchvBar;
public savingIcon: SavingIconHandler;
@ -139,6 +151,10 @@ export default class UI extends Phaser.GameObjects.Container {
new MenuUiHandler(scene),
new OptionSelectUiHandler(scene, Mode.MENU_OPTION_SELECT),
new SettingsUiHandler(scene),
new SettingsGamepadUiHandler(scene),
new GamepadBindingUiHandler(scene),
new SettingsKeyboardUiHandler(scene),
new KeyboardBindingUiHandler(scene),
new AchvsUiHandler(scene),
new GameStatsUiHandler(scene),
new VouchersUiHandler(scene),

View File

@ -423,3 +423,49 @@ export function printContainerList(container: Phaser.GameObjects.Container): voi
return {type: go.type, name: go.name};
}));
}
/**
* Truncate a string to a specified maximum length and add an ellipsis if it exceeds that length.
*
* @param str - The string to be truncated.
* @param maxLength - The maximum length of the truncated string, defaults to 10.
* @returns The truncated string with an ellipsis if it was longer than maxLength.
*/
export function truncateString(str: String, maxLength: number = 10) {
// Check if the string length exceeds the maximum length
if (str.length > maxLength) {
// Truncate the string and add an ellipsis
return str.slice(0, maxLength - 3) + "..."; // Subtract 3 to accommodate the ellipsis
}
// Return the original string if it does not exceed the maximum length
return str;
}
/**
* Perform a deep copy of an object.
*
* @param values - The object to be deep copied.
* @returns A new object that is a deep copy of the input.
*/
export function deepCopy(values: object): object {
// Convert the object to a JSON string and parse it back to an object to perform a deep copy
return JSON.parse(JSON.stringify(values));
}
/**
* Convert a space-separated string into a capitalized and underscored string.
*
* @param input - The string to be converted.
* @returns The converted string with words capitalized and separated by underscores.
*/
export function reverseValueToKeySetting(input) {
// Split the input string into an array of words
const words = input.split(" ");
// Capitalize the first letter of each word and convert the rest to lowercase
const capitalizedWords = words.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
// Join the capitalized words with underscores and return the result
return capitalizedWords.join("_");
}