# 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
# pragma once
# include "MenuComponent.h"
# include "LoadingScreen.h"
# include "Key.h"
# include "Menu.h"
# undef KEY_LEFT
# undef KEY_UP
# undef KEY_RIGHT
# undef KEY_DOWN
# undef KEY_MENU
# include "AdventuresInLestoria.h"
using A = Attribute ;
INCLUDE_game
# undef BLACKSMITH
enum class TutorialTaskName {
SET_LOADOUT_ITEM ,
MOVE_AROUND ,
USE_ATTACK ,
USE_ABILITIES ,
USE_DEFENSIVE ,
USE_RECOVERY_ITEMS ,
BLACKSMITH ,
EQUIP_GEAR ,
ARTIFICER_INTRO ,
NONE ,
} ;
class TutorialTask : public IAttributable {
friend class Tutorial ;
public :
TutorialTask ( ) ;
protected :
bool completed = false ;
bool systemInitializeCalled = false ;
virtual void Initialize ( ) ;
private :
void _Initialize ( ) ;
//Actions that occur when this task is reset/locked. DO NOT CALL DIRECTLY, USE _Initialize()!
virtual void Update ( ) ;
virtual void OnActivate ( ) ;
virtual void Draw ( ) const ;
//Returns true when the task has detected it is completed.
virtual bool CompleteCondition ( ) = 0 ;
//Action occurs when the task completes.
virtual void OnComplete ( ) = 0 ;
const bool IsComplete ( ) const ;
} ;
class Tutorial {
friend class SaveFile ;
public :
static void Initialize ( ) ;
static void ResetTasks ( ) ;
static void Update ( ) ;
static void Draw ( ) ;
static const bool TaskIsComplete ( TutorialTaskName task ) ;
static void SetNextTask ( TutorialTaskName task ) ;
static TutorialTask & GetTask ( TutorialTaskName task ) ;
static void CompleteTask ( TutorialTaskName task ) ;
static void GiveUpCurrentTask ( ) ;
private :
static TutorialTaskName currentTaskState ;
static std : : map < TutorialTaskName , std : : unique_ptr < TutorialTask > > taskList ;
} ;
class SetLoadoutItemTask : public TutorialTask {
public :
inline SetLoadoutItemTask ( ) : TutorialTask ( ) { } ;
private :
virtual inline void Initialize ( ) override final {
Component < MenuComponent > ( ITEM_LOADOUT , " Start Level Button " ) - > SetGrayedOut ( true ) ;
}
virtual inline bool CompleteCondition ( ) override final {
for ( int i = 0 ; i < game - > GetLoadoutSize ( ) ; i + + ) {
if ( ! ISBLANK ( game - > GetLoadoutItem ( i ) ) ) {
return true ;
}
}
return false ;
}
virtual inline void OnComplete ( ) override final {
Component < MenuComponent > ( ITEM_LOADOUT , " Start Level Button " ) - > SetGrayedOut ( false ) ;
}
} ;
class MoveAroundTask : public TutorialTask {
public :
inline MoveAroundTask ( ) : TutorialTask ( ) { } ;
private :
InputGroup moveGroup ;
vf2d initialPlayerPos = vf2d { } ;
virtual inline void Initialize ( ) override final {
initialPlayerPos = vf2d { } ;
moveGroup . ClearAllKeybinds ( ) ;
moveGroup . AddKeybind ( game - > KEY_LEFT . GetPrimaryKey ( InputType : : CONTROLLER ) . value ( ) ) ;
moveGroup . AddKeybind ( game - > KEY_DOWN . GetPrimaryKey ( InputType : : CONTROLLER ) . value ( ) ) ;
moveGroup . AddKeybind ( game - > KEY_UP . GetPrimaryKey ( InputType : : CONTROLLER ) . value ( ) ) ;
moveGroup . AddKeybind ( game - > KEY_RIGHT . GetPrimaryKey ( InputType : : CONTROLLER ) . value ( ) ) ;
moveGroup . AddKeybind ( game - > KEY_LEFT . GetPrimaryKey ( InputType : : KEY ) . value ( ) ) ;
moveGroup . AddKeybind ( game - > KEY_DOWN . GetPrimaryKey ( InputType : : KEY ) . value ( ) ) ;
moveGroup . AddKeybind ( game - > KEY_UP . GetPrimaryKey ( InputType : : KEY ) . value ( ) ) ;
moveGroup . AddKeybind ( game - > KEY_RIGHT . GetPrimaryKey ( InputType : : KEY ) . value ( ) ) ;
}
virtual inline void Update ( ) override final {
if ( ! LoadingScreen : : loading & & initialPlayerPos = = vf2d { } ) {
initialPlayerPos = game - > GetPlayer ( ) - > GetPos ( ) ;
}
}
virtual inline bool CompleteCondition ( ) override final {
return initialPlayerPos ! = vf2d { } & & geom2d : : line < float > ( initialPlayerPos , game - > GetPlayer ( ) - > GetPos ( ) ) . length ( ) > 60 ;
}
virtual inline void OnComplete ( ) override final {
initialPlayerPos = vf2d { } ;
}
virtual inline void Draw ( ) const override final {
if ( Input : : UsingGamepad ( ) ) {
moveGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Move " , 180 , InputType : : CONTROLLER ) ;
} else {
moveGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Move " , 180 , InputType : : KEY ) ;
}
}
} ;
# define ADDKEYBIND(group,key,type) \
group . AddKeybind ( key . GetPrimaryKey ( InputType : : type ) . value ( ) ) ; \
if ( type = = InputType : : KEY & & key . GetPrimaryKey ( InputType : : MOUSE ) . has_value ( ) ) { \
group . AddKeybind ( key . GetPrimaryKey ( InputType : : MOUSE ) . value ( ) ) ; \
}
class UseAttackTask : public TutorialTask {
public :
inline UseAttackTask ( ) : TutorialTask ( ) { } ;
private :
InputGroup attackGroup ;
virtual inline void OnActivate ( ) override final {
attackGroup . ClearAllKeybinds ( ) ;
ADDKEYBIND ( attackGroup , game - > KEY_ATTACK , CONTROLLER ) ;
ADDKEYBIND ( attackGroup , game - > KEY_ATTACK , KEY ) ;
ADDKEYBIND ( attackGroup , game - > KEY_ATTACK , STEAM ) ;
}
virtual inline bool CompleteCondition ( ) override final {
return I ( A : : ATTACK_COUNT ) > = 10 ;
}
virtual inline void OnComplete ( ) override final { }
virtual inline void Draw ( ) const override final {
if ( Input : : UsingGamepad ( ) ) {
STEAMINPUT (
attackGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Attack " , 180 , InputType : : STEAM ) ;
) else {
attackGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Attack " , 180 , InputType : : CONTROLLER ) ;
}
} else {
attackGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Attack " , 180 , InputType : : KEY ) ;
}
}
} ;
class UseAbilitiesTask : public TutorialTask {
public :
inline UseAbilitiesTask ( ) : TutorialTask ( ) { } ;
private :
InputGroup abilityGroup ;
virtual inline void OnActivate ( ) override final {
abilityGroup . ClearAllKeybinds ( ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY1 , CONTROLLER ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY1 , KEY ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY1 , STEAM ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY2 , CONTROLLER ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY2 , KEY ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY2 , STEAM ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY3 , CONTROLLER ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY3 , KEY ) ;
ADDKEYBIND ( abilityGroup , game - > GetPlayer ( ) - > KEY_ABILITY3 , STEAM ) ;
}
virtual inline bool CompleteCondition ( ) override final {
return I ( A : : ABILITY_COUNT ) > = 5 ;
}
virtual inline void OnComplete ( ) override final { }
virtual inline void Draw ( ) const override final {
if ( Input : : UsingGamepad ( ) ) {
STEAMINPUT (
abilityGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Use Abilities " , 180 , InputType : : STEAM ) ;
) else {
abilityGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Use Abilities " , 180 , InputType : : CONTROLLER ) ;
}
} else {
abilityGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Use Abilities " , 180 , InputType : : KEY ) ;
}
}
} ;
class UseDefensiveTask : public TutorialTask {
public :
inline UseDefensiveTask ( ) : TutorialTask ( ) { } ;
private :
InputGroup defensiveGroup ;
virtual inline void OnActivate ( ) override final {
defensiveGroup . ClearAllKeybinds ( ) ;
ADDKEYBIND ( defensiveGroup , game - > GetPlayer ( ) - > KEY_DEFENSIVE , CONTROLLER ) ;
ADDKEYBIND ( defensiveGroup , game - > GetPlayer ( ) - > KEY_DEFENSIVE , KEY ) ;
ADDKEYBIND ( defensiveGroup , game - > GetPlayer ( ) - > KEY_DEFENSIVE , STEAM ) ;
}
virtual inline bool CompleteCondition ( ) override final {
return I ( A : : DEFENSIVE_COUNT ) > = 2 ;
}
virtual inline void OnComplete ( ) override final { }
virtual inline void Draw ( ) const override final {
if ( Input : : UsingGamepad ( ) ) {
STEAMINPUT (
defensiveGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Defensive Ability " , 180 , InputType : : STEAM ) ;
) else {
defensiveGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Defensive Ability " , 180 , InputType : : CONTROLLER ) ;
}
} else {
defensiveGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Defensive Ability " , 180 , InputType : : KEY ) ;
}
}
} ;
class UseRecoveryItemsTask : public TutorialTask {
public :
inline UseRecoveryItemsTask ( ) : TutorialTask ( ) { } ;
private :
InputGroup itemsGroup ;
virtual inline void OnActivate ( ) override final {
itemsGroup . ClearAllKeybinds ( ) ;
if ( ! ISBLANK ( game - > GetLoadoutItem ( 0 ) ) ) {
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM1 , CONTROLLER ) ;
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM1 , KEY ) ;
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM1 , STEAM ) ;
}
if ( ! ISBLANK ( game - > GetLoadoutItem ( 1 ) ) ) {
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM2 , CONTROLLER ) ;
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM2 , KEY ) ;
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM2 , STEAM ) ;
}
if ( ! ISBLANK ( game - > GetLoadoutItem ( 2 ) ) ) {
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM3 , CONTROLLER ) ;
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM3 , KEY ) ;
ADDKEYBIND ( itemsGroup , game - > GetPlayer ( ) - > KEY_ITEM3 , STEAM ) ;
}
}
virtual inline bool CompleteCondition ( ) override final {
return I ( A : : ITEM_USE_COUNT ) > = 1 ;
}
virtual inline void OnComplete ( ) override final { }
virtual inline void Draw ( ) const override final {
if ( Input : : UsingGamepad ( ) ) {
STEAMINPUT (
itemsGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Use Consumables " , 180 , InputType : : STEAM ) ;
) else {
itemsGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Use Consumables " , 180 , InputType : : CONTROLLER ) ;
}
} else {
itemsGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " Use Consumables " , 180 , InputType : : KEY ) ;
}
}
} ;
class BlacksmithTask : public TutorialTask {
public :
inline BlacksmithTask ( ) : TutorialTask ( ) { } ;
private :
virtual inline void OnActivate ( ) override final {
Component < MenuComponent > ( SHERMAN , " Leave Button " ) - > SetGrayedOut ( true ) ;
Component < MenuComponent > ( MenuType : : PAUSE , " Return to Camp Button " ) - > SetGrayedOut ( true ) ;
}
virtual inline bool CompleteCondition ( ) override final {
return Menu : : IsMenuOpen ( ) & & Menu : : stack . back ( ) = = Menu : : menus [ MenuType : : BLACKSMITH ] ;
}
virtual inline void OnComplete ( ) override final {
Component < MenuComponent > ( SHERMAN , " Leave Button " ) - > SetGrayedOut ( false ) ;
Component < MenuComponent > ( MenuType : : PAUSE , " Return to Camp Button " ) - > SetGrayedOut ( false ) ;
}
virtual inline void Draw ( ) const override final {
std : : string helpText = " Visit #00FFD0 \" Greg \" the Blacksmith#FFFFFF to browse craftable gear. " ;
float textWidth = game - > GetTextSizeProp ( helpText ) . x ;
game - > DrawShadowStringPropDecal ( { game - > ScreenWidth ( ) / 2.f - textWidth / 2.f , 48.f } , helpText ) ;
}
} ;
class EquipGearTask : public TutorialTask {
public :
inline EquipGearTask ( ) : TutorialTask ( ) { } ;
private :
InputGroup startGroup ;
virtual inline void OnActivate ( ) override final {
startGroup . ClearAllKeybinds ( ) ;
ADDKEYBIND ( startGroup , game - > KEY_MENU , CONTROLLER ) ;
ADDKEYBIND ( startGroup , game - > KEY_MENU , KEY ) ;
ADDKEYBIND ( startGroup , game - > KEY_MENU , STEAM ) ;
Component < MenuComponent > ( SHERMAN , " Leave Button " ) - > SetGrayedOut ( true ) ;
Component < MenuComponent > ( MenuType : : PAUSE , " Return to Camp Button " ) - > SetGrayedOut ( true ) ;
}
virtual inline bool CompleteCondition ( ) override final {
for ( int i = int ( EquipSlot : : HELMET ) ; i < = int ( EquipSlot : : RING2 ) ; i < < = 1 ) {
EquipSlot slot = EquipSlot ( i ) ;
if ( ! ISBLANK ( Inventory : : GetEquip ( slot ) ) ) return true ;
}
return false ;
}
virtual inline void OnComplete ( ) override final {
Component < MenuComponent > ( SHERMAN , " Leave Button " ) - > SetGrayedOut ( false ) ;
Component < MenuComponent > ( MenuType : : PAUSE , " Return to Camp Button " ) - > SetGrayedOut ( false ) ;
}
virtual inline void Draw ( ) const override final {
if ( Input : : UsingGamepad ( ) ) {
STEAMINPUT (
startGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " to equip your new gear. " , 255 , InputType : : STEAM , { 0.85f , 1.f } , " Open the #FFCF0CCharacter#FFFFFF menu with " ) ;
) else {
startGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " to equip your new gear. " , 255 , InputType : : CONTROLLER , { 0.85f , 1.f } , " Open the #FFCF0CCharacter#FFFFFF menu with " ) ;
}
} else {
startGroup . DrawInput ( game , { game - > ScreenWidth ( ) / 2.f , 48.f } , " to equip your new gear. " , 255 , InputType : : KEY , { 0.85f , 1.f } , " Open the #FFCF0CCharacter#FFFFFF menu with " ) ;
}
}
} ;
class ArtificerIntroductionTask : public TutorialTask {
public :
inline ArtificerIntroductionTask ( ) : TutorialTask ( ) { } ;
private :
virtual inline void OnActivate ( ) override final {
Component < MenuComponent > ( SHERMAN , " Leave Button " ) - > SetGrayedOut ( true ) ;
Component < MenuComponent > ( MenuType : : PAUSE , " Return to Camp Button " ) - > SetGrayedOut ( true ) ;
}
virtual inline bool CompleteCondition ( ) override final {
return false ;
}
virtual inline void OnComplete ( ) override final {
Component < MenuComponent > ( SHERMAN , " Leave Button " ) - > SetGrayedOut ( false ) ;
Component < MenuComponent > ( MenuType : : PAUSE , " Return to Camp Button " ) - > SetGrayedOut ( false ) ;
}
virtual inline void Draw ( ) const override final {
std : : string helpText = " Visit the #00FFD0Artificer#FFFFFF to unlock new accessory upgrades! " ;
float textWidth = game - > GetTextSizeProp ( helpText ) . x ;
game - > DrawShadowStringPropDecal ( { game - > ScreenWidth ( ) / 2.f - textWidth / 2.f , 48.f } , helpText ) ;
}
} ;