# pragma region License
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Copyright 2024 Joshua Sigona < sigonasr2 @ gmail . com >
Redistribution and use in source and binary forms , with or without modification ,
are permitted provided that the following conditions are met :
1. Redistributions or derivations of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice . This list of conditions and the following disclaimer must be
reproduced in the documentation and / or other materials provided with the distribution .
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED
TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
Portions of this software are copyright © 2024 The FreeType
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
*/
# pragma endregion
# include "AdventuresInLestoria.h"
# include "DEFINES.h"
# include "Menu.h"
# include "MenuLabel.h"
# include "MenuAnimatedIconToggleButton.h"
# include "GameState.h"
# include "ClassInfo.h"
# include "SaveFileNameButton.h"
# include "TextEntryLabel.h"
# include "Checkbox.h"
# ifndef __EMSCRIPTEN__
# include "steam/isteamutils.h"
# endif
INCLUDE_game
using A = Attribute ;
void Menu : : InitializeClassSelectionWindow ( ) {
vf2d windowSize = game - > GetScreenSize ( ) - vi2d { 24 , 8 } ;
Menu * classSelectionWindow = CreateMenu ( CLASS_SELECTION , CENTERED , windowSize ) ;
classSelectionWindow - > S ( A : : CLASS_SELECTION ) = " Warrior " ; //Default selected class.
vf2d outlineSize = classSelectionWindow - > size - vf2d { 8 , 29 } ;
classSelectionWindow - > ADD ( " Class Selection Title Label " , MenuLabel ) ( geom2d : : rect < float > { { 4 , 12 } , { outlineSize . x , 32 } } , " Choose a Character Class " , 2 , ComponentAttr : : SHADOW | ComponentAttr : : OUTLINE | ComponentAttr : : BACKGROUND ) END ;
auto outline = classSelectionWindow - > ADD ( " Outline Border " , MenuLabel ) ( geom2d : : rect < float > { { 4 , 4 } , outlineSize + vf2d { 0 , 25 } } , " " , 1 , ComponentAttr : : OUTLINE ) END ;
classSelectionWindow - > ADD ( " Character Name Label " , MenuLabel ) ( geom2d : : rect < float > { { windowSize . x / 2 - 144.f , 60.f } , { 96.f , 24.f } } , " Name: " , 2.0f , ComponentAttr : : BACKGROUND | ComponentAttr : : OUTLINE | ComponentAttr : : SHADOW ) END ;
classSelectionWindow - > ADD ( " Character Name Box " , SaveFileNameButton ) ( geom2d : : rect < float > { { windowSize . x / 2 - 48.f , 60.f } , { 192.f , 24 } } , [ & ] ( MenuFuncData data ) {
Component < TextEntryLabel > ( SAVE_FILE_NAME , " Save File Name Text Entry " ) - > SetInput ( std : : string ( SaveFile : : GetSaveFileName ( ) ) ) ;
game - > TextEntryEnable ( true ) ;
Menu : : OpenMenu ( SAVE_FILE_NAME ) ;
vf2d textboxWindowPos = data . component . lock ( ) - > rect . pos * game - > GetPixelSize ( ) ;
vf2d textboxWindowSize = data . component . lock ( ) - > rect . size * game - > GetPixelSize ( ) ;
STEAMUTILS (
SteamUtils ( ) - > ShowGamepadTextInput ( k_EGamepadTextInputModeNormal , k_EGamepadTextInputLineModeSingleLine , " Character Name: " , 16U , std : : string ( SaveFile : : GetSaveFileName ( ) ) . c_str ( ) ) ;
)
return true ;
} , vf2d { 2.f , 2.f } ) END ;
vf2d navigationButtonSize = { 24 * 2.5f , 16 } ;
classSelectionWindow - > ADD ( " Back " , MenuComponent ) ( geom2d : : rect < float > { { 6 , outlineSize . y + 29 - navigationButtonSize . y - 14 } , navigationButtonSize } , " Back " , [ ] ( MenuFuncData ) { Menu : : CloseMenu ( ) ; return true ; } ) END ;
classSelectionWindow - > ADD ( " Confirm " , MenuComponent ) ( geom2d : : rect < float > { { outlineSize . x + 4 - navigationButtonSize . x - 2 , outlineSize . y + 29 - navigationButtonSize . y - 14 } , navigationButtonSize } , " Confirm " , [ ] ( MenuFuncData data ) {
std : : string selectedClass = data . component . lock ( ) - > S ( A : : CLASS_SELECTION ) ;
//Player starting items.
Inventory : : AddItem ( " Minor Health Potion " s , 3 ) ;
Inventory : : AddItem ( " Bandages " s , 10 ) ;
data . game - > ChangePlayerClass ( classutils : : StringToClass ( selectedClass ) ) ;
# ifdef __EMSCRIPTEN__
if ( SaveFile : : IsOnline ( ) ) {
SaveFile : : SetSaveFileID ( SaveFile : : GetOnlineSaveFileCount ( ) ) ;
SaveFile : : UpdateSaveGameData ( [ ] ( ) { GameState : : ChangeState ( States : : OVERWORLD_MAP ) ; } ) ;
} else {
SaveFile : : SetSaveFileOfflineID_TransitionToOverworldMap ( ) ;
}
# else
SaveFile : : SetSaveFileID ( SaveFile : : GetSaveFileCount ( ) ) ;
SaveFile : : SaveGame ( ) ;
GameState : : ChangeState ( States : : OVERWORLD_MAP ) ;
# endif
return true ;
} ) END
- > Disable ( ) ;
vf2d buttonPadding = { 2 , 2 } ;
vf2d buttonSize = { floor ( outlineSize . y / 3 - buttonPadding . y * 3 ) , outlineSize . y / 9 - buttonPadding . y * 3 } ; //The floor is for fixing a small pixel rounding bug.
float buttonTotalWidth = ( buttonSize . x + buttonPadding . x ) * 3 ;
vf2d buttonStartPos = outline - > GetPos ( ) + vf2d { outlineSize . x / 2 , outlineSize . y / 3 } - vf2d { buttonTotalWidth / 2 , 0 } + vf2d { 0 , 24 } ;
std : : array < std : : string , 6 > classNames = {
Warrior : : name ,
Ranger : : name ,
Wizard : : name ,
Thief : : name ,
Trapper : : name ,
Witch : : name ,
} ;
std : : array < std : : string , 6 > classAnimationNames = {
Warrior : : walk_s ,
Ranger : : walk_s ,
Wizard : : walk_s ,
Thief : : walk_s ,
Trapper : : walk_s ,
Witch : : walk_s ,
} ;
std : : vector < std : : weak_ptr < IToggleable > > toggleGroup ;
for ( int i = 0 ; i < 6 ; i + + ) {
std : : string className = classNames [ i ] ;
std : : string classAnimationName = classAnimationNames [ i ] ;
vf2d offsetPos = {
buttonStartPos . x + ( buttonSize . x + buttonPadding . x ) * float ( i % 3 ) ,
buttonStartPos . y + ( buttonSize . y + buttonPadding . y + 2 * outlineSize . y / 9 ) * float ( i / 3 ) + 2 * outlineSize . y / 9 ,
} ;
vf2d backgroundOffsetPos = {
buttonStartPos . x + ( buttonSize . x + buttonPadding . x ) * float ( i % 3 ) ,
buttonStartPos . y + ( buttonSize . y + buttonPadding . y + 2 * outlineSize . y / 9 ) * float ( i / 3 ) ,
} ;
vf2d backgroundSize = { floor ( outlineSize . y / 3 - buttonPadding . y * 3 ) , outlineSize . y / 3 - buttonPadding . y * 3 } ; //The floor is for fixing a small pixel rounding bug.
auto classLabel = classSelectionWindow - > ADD ( className + " Background " , MenuLabel ) ( geom2d : : rect < float > { backgroundOffsetPos , backgroundSize } , " " , 1 , ComponentAttr : : OUTLINE | ComponentAttr : : BACKGROUND ) END ;
auto classButton = classSelectionWindow - > ADD ( className + " Button " , MenuComponent ) ( geom2d : : rect < float > { offsetPos , buttonSize } , " Info " ,
[ ] ( MenuFuncData data ) {
data . menu . S ( A : : CLASS_SELECTION ) = data . component . lock ( ) - > S ( A : : CLASS_SELECTION ) ;
delete Menu : : menus [ CLASS_INFO ] ;
Menu : : InitializeClassInfoWindow ( ) ;
Menu : : OpenMenu ( CLASS_INFO ) ;
return true ;
} ) END ;
classButton - > S ( A : : CLASS_SELECTION ) = className ;
classSelectionWindow - > ADD ( className + " Label " , MenuLabel ) ( geom2d : : rect < float > { backgroundOffsetPos , buttonSize } , className , 1 , ComponentAttr : : SHADOW ) END ;
auto classSprite = classSelectionWindow - > ADD ( className + " Icon " , MenuAnimatedIconToggleButton ) ( geom2d : : rect < float > { backgroundOffsetPos + vf2d { 0 , 12 } , backgroundSize + vf2d { 0 , - buttonSize . y - 12 } } , classAnimationName , [ ] ( MenuFuncData data ) {
data . menu . components [ " Confirm " ] - > Enable ( ) ;
data . menu . components [ " Confirm " ] - > S ( A : : CLASS_SELECTION ) = data . component . lock ( ) - > S ( A : : CLASS_SELECTION ) ;
return true ;
} ) END ;
classSprite - > S ( A : : CLASS_SELECTION ) = className ;
toggleGroup . push_back ( classSprite ) ;
}
# ifdef __EMSCRIPTEN__
classSelectionWindow - > ADD ( " Online Character Checkbox " , Checkbox ) ( geom2d : : rect < float > { classSelectionWindow - > pos + classSelectionWindow - > size - vf2d { 84 , 72 } , { 12 , 12 } } , [ ] ( ToggleFuncData data ) {
SaveFile : : SetOnlineMode ( data . checked ) ;
if ( SaveFile : : IsOnline ( ) & & SaveFile : : GetUserID ( ) . length ( ) = = 0 ) {
//Present the user with the user ID form.
game - > TextEntryEnable ( true ) ;
Menu : : OpenMenu ( USER_ID ) ;
}
return true ;
} , SaveFile : : IsOnline ( ) ) END ;
classSelectionWindow - > ADD ( " Online Character Text Label " , MenuLabel ) ( geom2d : : rect < float > { classSelectionWindow - > pos + classSelectionWindow - > size - vf2d { 69 , 70 } , { 54 , 12 } } , " Online Character " , 0.75f , ComponentAttr : : SHADOW ) END ;
# endif
for ( std : : weak_ptr < IToggleable > item : toggleGroup ) {
item . lock ( ) - > SetToggleGroup ( toggleGroup ) ;
}
classSelectionWindow - > SetupKeyboardNavigation (
[ ] ( MenuType type , Data & returnData ) { //On Open
returnData = Warrior : : name + " Button " ;
} ,
{ //Button Key
{ game - > KEY_BACK , { " Back " , [ ] ( MenuType type ) {
Component < MenuComponent > ( type , " Back " ) - > Click ( ) ;
} } } ,
{ game - > KEY_START , { [ ] ( MenuFuncData data ) {
if ( Component < MenuComponent > ( data . menu . GetType ( ) , " Confirm " ) - > IsEnabled ( ) ) {
return " Confirm " ;
} else {
return " " ;
}
} , [ ] ( MenuType type ) {
Component < MenuComponent > ( type , " Confirm " ) - > Click ( ) ;
} } } ,
{ game - > KEY_FACEUP , { " Change Player Name " , [ ] ( MenuType type ) {
Component < SaveFileNameButton > ( type , " Character Name Box " ) - > Click ( ) ;
} } } ,
# ifdef __EMSCRIPTEN__
{ game - > KEY_FACELEFT , { " Toggle Online Character " , [ ] ( MenuType type ) {
Component < Checkbox > ( type , " Online Character Checkbox " ) - > Click ( ) ;
} } } ,
# endif
}
, { //Button Navigation Rules
{ Warrior : : name + " Button " , {
. up = Warrior : : name + " Icon " ,
. down = Thief : : name + " Icon " ,
. left = " Back " ,
. right = Ranger : : name + " Button " ,
} } ,
{ Warrior : : name + " Icon " , {
. up = " Character Name Box " ,
. down = Warrior : : name + " Button " ,
. left = " Back " ,
. right = Ranger : : name + " Icon " ,
} } ,
{ Ranger : : name + " Button " , {
. up = Ranger : : name + " Icon " ,
. down = Trapper : : name + " Icon " ,
. left = Warrior : : name + " Button " ,
. right = Wizard : : name + " Button " ,
} } ,
{ Ranger : : name + " Icon " , {
. up = " Character Name Box " ,
. down = Ranger : : name + " Button " ,
. left = Warrior : : name + " Icon " ,
. right = Wizard : : name + " Icon " ,
} } ,
{ Wizard : : name + " Button " , {
. up = Wizard : : name + " Icon " ,
. down = Witch : : name + " Icon " ,
. left = Ranger : : name + " Button " ,
# ifdef __EMSCRIPTEN__
. right = " Online Character Checkbox " ,
# else
. right = " Confirm " ,
# endif
} } ,
{ Wizard : : name + " Icon " , {
. up = " Character Name Box " ,
. down = Wizard : : name + " Button " ,
. left = Ranger : : name + " Icon " ,
# ifdef __EMSCRIPTEN__
. right = " Online Character Checkbox " ,
# else
. right = " Confirm " ,
# endif
} } ,
{ Thief : : name + " Button " , {
. up = Thief : : name + " Icon " ,
. down = " Character Name Box " ,
. left = " Back " ,
. right = Trapper : : name + " Button " ,
} } ,
{ Thief : : name + " Icon " , {
. up = Warrior : : name + " Button " ,
. down = Thief : : name + " Button " ,
. left = " Back " ,
. right = Trapper : : name + " Icon " ,
} } ,
{ Trapper : : name + " Button " , {
. up = Trapper : : name + " Icon " ,
. down = " Character Name Box " ,
. left = Thief : : name + " Button " ,
. right = Witch : : name + " Button " ,
} } ,
{ Trapper : : name + " Icon " , {
. up = Ranger : : name + " Button " ,
. down = Trapper : : name + " Button " ,
. left = Thief : : name + " Icon " ,
. right = Witch : : name + " Icon " ,
} } ,
{ Witch : : name + " Button " , {
. up = Witch : : name + " Icon " ,
. down = " Character Name Box " ,
. left = Trapper : : name + " Button " ,
# ifdef __EMSCRIPTEN__
. right = " Online Character Checkbox " ,
# else
. right = " Confirm " ,
# endif
} } ,
{ Witch : : name + " Icon " , {
. up = Wizard : : name + " Button " ,
. down = Witch : : name + " Button " ,
. left = Trapper : : name + " Icon " ,
# ifdef __EMSCRIPTEN__
. right = " Online Character Checkbox " ,
# else
. right = " Confirm " ,
# endif
} } ,
{ " Character Name Box " , {
. up = Witch : : name + " Button " ,
. down = Wizard : : name + " Icon " ,
} } ,
{ " Confirm " , {
# ifdef __EMSCRIPTEN__
. up = " Online Character Checkbox " ,
# else
. up = Wizard : : name + " Button " ,
# endif
. down = Wizard : : name + " Button " ,
. left = Wizard : : name + " Button " ,
. right = " Back " ,
} } ,
{ " Online Character Checkbox " , {
. up = " Confirm " ,
. down = " Confirm " ,
. left = Wizard : : name + " Button " ,
. right = Warrior : : name + " Button " ,
} } ,
{ " Back " , {
. up = Warrior : : name + " Button " ,
. down = Warrior : : name + " Button " ,
. left = " Confirm " ,
. right = Warrior : : name + " Button " ,
} } ,
} ) ;
}