Upstream merge with released demo build.

pull/57/head
sigonasr2 8 months ago
commit 8b3113fbde
  1. 44
      Adventures in Lestoria/Adventures in Lestoria.tiled-project
  2. 14
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  3. 7
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  4. 122
      Adventures in Lestoria/AdventuresInLestoria.cpp
  5. 3
      Adventures in Lestoria/AdventuresInLestoria.h
  6. 8
      Adventures in Lestoria/Animation.cpp
  7. 14
      Adventures in Lestoria/CharacterMenuWindow.cpp
  8. 21
      Adventures in Lestoria/Class.h
  9. 10
      Adventures in Lestoria/ClassInfo.h
  10. 53
      Adventures in Lestoria/Item.cpp
  11. 2
      Adventures in Lestoria/Item.h
  12. 2
      Adventures in Lestoria/LevelCompleteWindow.cpp
  13. 25
      Adventures in Lestoria/Monster.cpp
  14. 37
      Adventures in Lestoria/Player.cpp
  15. 7
      Adventures in Lestoria/Player.h
  16. 25
      Adventures in Lestoria/SaveFile.cpp
  17. 3
      Adventures in Lestoria/ShermanWindow.cpp
  18. 1
      Adventures in Lestoria/State_GameHub.cpp
  19. 3
      Adventures in Lestoria/State_MainMenu.cpp
  20. 54
      Adventures in Lestoria/SteamStatsReceivedHandler.cpp
  21. 47
      Adventures in Lestoria/SteamStatsReceivedHandler.h
  22. 11
      Adventures in Lestoria/TMXParser.h
  23. 17
      Adventures in Lestoria/TODO.txt
  24. 34
      Adventures in Lestoria/Unlock.cpp
  25. 3
      Adventures in Lestoria/Unlock.h
  26. 2
      Adventures in Lestoria/Version.h
  27. BIN
      Adventures in Lestoria/assets/Achievements/BronzeBorder.xcf
  28. BIN
      Adventures in Lestoria/assets/Achievements/GoldBorder.xcf
  29. BIN
      Adventures in Lestoria/assets/Achievements/SilverBorder.xcf
  30. BIN
      Adventures in Lestoria/assets/Achievements/armor_10.png
  31. BIN
      Adventures in Lestoria/assets/Achievements/armor_10_locked.png
  32. BIN
      Adventures in Lestoria/assets/Achievements/armor_5.png
  33. BIN
      Adventures in Lestoria/assets/Achievements/armor_5_locked.png
  34. BIN
      Adventures in Lestoria/assets/Achievements/blacksmith_unlock.png
  35. BIN
      Adventures in Lestoria/assets/Achievements/blacksmith_unlock_locked.png
  36. BIN
      Adventures in Lestoria/assets/Achievements/camp_unlock.png
  37. BIN
      Adventures in Lestoria/assets/Achievements/camp_unlock_locked.png
  38. BIN
      Adventures in Lestoria/assets/Achievements/chapter1_complete.png
  39. BIN
      Adventures in Lestoria/assets/Achievements/chapter1_complete_locked.png
  40. BIN
      Adventures in Lestoria/assets/Achievements/fully_decked_out.png
  41. BIN
      Adventures in Lestoria/assets/Achievements/fully_decked_out_locked.png
  42. BIN
      Adventures in Lestoria/assets/Achievements/kill_slime_1.png
  43. BIN
      Adventures in Lestoria/assets/Achievements/kill_slime_1_locked.png
  44. BIN
      Adventures in Lestoria/assets/Achievements/kill_slime_2.png
  45. BIN
      Adventures in Lestoria/assets/Achievements/kill_slime_2_locked.png
  46. BIN
      Adventures in Lestoria/assets/Achievements/kill_slime_3.png
  47. BIN
      Adventures in Lestoria/assets/Achievements/kill_slime_3_locked.png
  48. BIN
      Adventures in Lestoria/assets/Achievements/ranger_level5.png
  49. BIN
      Adventures in Lestoria/assets/Achievements/ranger_level5_locked.png
  50. BIN
      Adventures in Lestoria/assets/Achievements/slime_king.png
  51. BIN
      Adventures in Lestoria/assets/Achievements/slime_king_destroyer.png
  52. BIN
      Adventures in Lestoria/assets/Achievements/slime_king_destroyer_locked.png
  53. BIN
      Adventures in Lestoria/assets/Achievements/slime_king_locked.png
  54. BIN
      Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock.png
  55. BIN
      Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock_locked.png
  56. BIN
      Adventures in Lestoria/assets/Achievements/ursule_unlock.png
  57. BIN
      Adventures in Lestoria/assets/Achievements/ursule_unlock_locked.png
  58. BIN
      Adventures in Lestoria/assets/Achievements/warrior_level5.png
  59. BIN
      Adventures in Lestoria/assets/Achievements/warrior_level5_locked.png
  60. BIN
      Adventures in Lestoria/assets/Achievements/weapon_5.png
  61. BIN
      Adventures in Lestoria/assets/Achievements/weapon_5_locked.png
  62. BIN
      Adventures in Lestoria/assets/Achievements/wizard_level5.png
  63. BIN
      Adventures in Lestoria/assets/Achievements/wizard_level5_locked.png
  64. BIN
      Adventures in Lestoria/assets/AdventuresInLestoria_DemoAvailable.png
  65. 6
      Adventures in Lestoria/assets/Campaigns/1_1_v2.tmx
  66. 3
      Adventures in Lestoria/assets/Campaigns/1_4.tmx
  67. 3
      Adventures in Lestoria/assets/Campaigns/1_5.tmx
  68. 3
      Adventures in Lestoria/assets/Campaigns/1_6.tmx
  69. 3
      Adventures in Lestoria/assets/Campaigns/1_7.tmx
  70. 3
      Adventures in Lestoria/assets/Campaigns/1_8.tmx
  71. 3
      Adventures in Lestoria/assets/Campaigns/1_B1.tmx
  72. 2
      Adventures in Lestoria/assets/Campaigns/Boss_1_v2.tmx
  73. 118
      Adventures in Lestoria/assets/config/Achievements.txt
  74. 3
      Adventures in Lestoria/assets/config/Player.txt
  75. 3
      Adventures in Lestoria/assets/config/configuration.txt
  76. 5
      Adventures in Lestoria/assets/config/items/ItemDatabase.txt
  77. BIN
      Adventures in Lestoria/assets/gamepack.pak
  78. BIN
      Adventures in Lestoria/assets/items/Time Medal.png
  79. 43
      Adventures in Lestoria/controller_config/controller_generic.vdf
  80. 43
      Adventures in Lestoria/controller_config/controller_switch_pro.vdf
  81. 41
      Adventures in Lestoria/controller_config/controller_xboxone.vdf
  82. 29
      Adventures in Lestoria/controller_config/game_actions_2895980.vdf
  83. 2
      Adventures in Lestoria/emscripten_compat.h
  84. 10
      Adventures in Lestoria/olcPixelGameEngine.h
  85. 16
      Adventures in Lestoria/olcUTIL_Animate2D.h
  86. 32
      Adventures in Lestoria/util.cpp
  87. 1
      Adventures in Lestoria/util.h
  88. 7
      distribute.sh
  89. 2
      read_debug_log.ps1
  90. BIN
      x64/Release/Adventures in Lestoria.exe

@ -295,6 +295,36 @@
"type": "string",
"value": "None"
},
{
"name": "Dev Completion Time - Ranger (s)",
"type": "float",
"value": 0
},
{
"name": "Dev Completion Time - Thief (s)",
"type": "float",
"value": 0
},
{
"name": "Dev Completion Time - Trapper (s)",
"type": "float",
"value": 0
},
{
"name": "Dev Completion Time - Warrior (s)",
"type": "float",
"value": 0
},
{
"name": "Dev Completion Time - Witch (s)",
"type": "float",
"value": 0
},
{
"name": "Dev Completion Time - Wizard (s)",
"type": "float",
"value": 0
},
{
"name": "Level Type",
"propertyType": "LevelType",
@ -618,6 +648,20 @@
"tileset"
]
},
{
"color": "#fff7ff5d",
"drawFill": true,
"id": 40,
"members": [
],
"name": "TrialClock",
"type": "class",
"useAs": [
"property",
"object",
"project"
]
},
{
"color": "#ffa40aa4",
"drawFill": true,

@ -206,6 +206,10 @@
<Command>
</Command>
</PostBuildEvent>
<CustomBuildStep>
<Command>
</Command>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Emscripten|Win32'">
<Link>
@ -519,6 +523,10 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="SteamStatsReceivedHandler.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="steam\isteamapps.h" />
<ClInclude Include="steam\isteamappticket.h" />
<ClInclude Include="steam\isteamclient.h" />
@ -816,6 +824,10 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="SteamStatsReceivedHandler.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="SwordSlash.cpp">
<SubType>
</SubType>
@ -844,6 +856,7 @@
<ClCompile Include="Wolf.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\read_debug_log.ps1" />
<None Include="ClassDiagram.cd" />
<None Include="ClassDiagram2.cd" />
<None Include="cpp.hint" />
@ -851,6 +864,7 @@
<None Include="steam\steam_api.json" />
</ItemGroup>
<ItemGroup>
<Text Include="assets\config\Achievements.txt" />
<Text Include="assets\config\audio\audio.txt" />
<Text Include="assets\config\audio\bgm.txt" />
<Text Include="assets\config\audio\environmentalaudio.txt" />

@ -998,6 +998,9 @@
<ClCompile Include="SteamKeyboardCallbackHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SteamStatsReceivedHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
@ -1009,6 +1012,7 @@
<None Include="steam\steam_api.json">
<Filter>Header Files\steam</Filter>
</None>
<None Include="..\read_debug_log.ps1" />
</ItemGroup>
<ItemGroup>
<Text Include="InitialConcept.txt">
@ -1159,6 +1163,9 @@
<Text Include="assets\config\credits.txt">
<Filter>Configurations</Filter>
</Text>
<Text Include="assets\config\Achievements.txt">
<Filter>Configurations</Filter>
</Text>
</ItemGroup>
<ItemGroup>
<Image Include="assets\heart.ico">

@ -80,6 +80,7 @@ All rights reserved.
#include "LoadingScreen.h"
#include "Tutorial.h"
#include "SteamKeyboardCallbackHandler.h"
#include "SteamStatsReceivedHandler.h"
INCLUDE_EMITTER_LIST
INCLUDE_ITEM_CATEGORIES
@ -232,6 +233,9 @@ AiL::AiL()
std::string CREDITS_CONFIG = CONFIG_PATH + "credits_config"_S;
utils::datafile::Read(DATA,CREDITS_CONFIG);
std::string ACHIEVEMENT_CONFIG = CONFIG_PATH + "achievement_config"_S;
utils::datafile::Read(DATA,ACHIEVEMENT_CONFIG);
utils::datafile::DEBUG_ACCESS_OPTIONS="debug_access_options"_I;
sAppName = "GAME_NAME"_S;
@ -334,6 +338,9 @@ bool AiL::OnUserCreate(){
if(steamKeyboardCallbackListener==nullptr){
steamKeyboardCallbackListener=new SteamKeyboardCallbackHandler();
}
if(steamStatsReceivedHandlerListener==nullptr){
steamStatsReceivedHandlerListener=new SteamStatsReceivedHandler();
}
#endif
utils::datafile::INITIAL_SETUP_COMPLETE=true;
@ -446,6 +453,7 @@ void AiL::HandleUserInput(float fElapsedTime){
if(KEY_MENU.Released()){
Menu::OpenMenu(MenuType::PAUSE);
}
float animationSpd=0.f;
if(player->GetVelocity().mag()<"Player.Move Allowed Velocity Lower Limit"_F&&player->CanMove()){
auto GetPlayerStaircaseDirection=[&](){
for(LayerTag&layer:MAP_DATA[GetCurrentLevel()].LayerData){
@ -462,20 +470,26 @@ void AiL::HandleUserInput(float fElapsedTime){
std::string staircaseDirection=GetPlayerStaircaseDirection();
vf2d newAimingAngle{};
if(RightHeld()){
player->SetX(player->GetX()+fElapsedTime*"Player.MoveSpd"_F*player->GetMoveSpdMult());
player->movementVelocity.x="Player.MoveSpd"_F;
float moveAmt="Player.MoveSpd"_F;
if(Input::UsingGamepad()&&KEY_SCROLLHORZ_L.Analog()>=0.2f){
float controllerAmt=abs(KEY_SCROLLHORZ_L.Analog());
if(controllerAmt>=0.6f)controllerAmt=1.f; //Edge zone.
if(controllerAmt>animationSpd){
animationSpd=controllerAmt;
}
moveAmt*=controllerAmt;
}else animationSpd=1.f;
player->SetX(player->GetX()+fElapsedTime*moveAmt*player->GetMoveSpdMult());
player->movementVelocity.x=moveAmt*fElapsedTime*player->GetMoveSpdMult();
if(staircaseDirection=="RIGHT"){
player->SetY(player->GetY()-"Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult());
player->movementVelocity.y=-"Player.StaircaseClimbSpd"_F;
player->movementVelocity.y=-"Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult();
} else
if(staircaseDirection=="LEFT"){
player->SetY(player->GetY()+"Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult());
player->movementVelocity.y="Player.StaircaseClimbSpd"_F;
player->movementVelocity.y="Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult();
}
player->SetFacingDirection(RIGHT);
if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){
player->UpdateWalkingAnimation(RIGHT);
}
newAimingAngle+=vf2d{1,0};
@ -483,21 +497,25 @@ void AiL::HandleUserInput(float fElapsedTime){
heldDownMovementKey=true;
}
if(LeftHeld()){
player->SetX(player->GetX()-fElapsedTime*"Player.MoveSpd"_F*player->GetMoveSpdMult());
player->movementVelocity.x=-"Player.MoveSpd"_F;
float moveAmt="Player.MoveSpd"_F;
if(Input::UsingGamepad()&&KEY_SCROLLHORZ_L.Analog()<=-0.2f){
float controllerAmt=abs(KEY_SCROLLHORZ_L.Analog());
controllerAmt+=0.2f;
if(controllerAmt>=0.6f)controllerAmt=1.f; //Edge zone.
if(controllerAmt>animationSpd){
animationSpd=controllerAmt;
}
moveAmt*=controllerAmt;
}else animationSpd=1.f;
player->SetX(player->GetX()-fElapsedTime*moveAmt*player->GetMoveSpdMult());
player->movementVelocity.x=-moveAmt*fElapsedTime*player->GetMoveSpdMult();
if(staircaseDirection=="RIGHT"){
player->SetY(player->GetY()+"Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult());
player->movementVelocity.y="Player.StaircaseClimbSpd"_F;
player->movementVelocity.y="Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult();
} else
if(staircaseDirection=="LEFT"){
player->SetY(player->GetY()-"Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult());
player->movementVelocity.y=-"Player.StaircaseClimbSpd"_F;
}
if(setIdleAnimation){
player->SetFacingDirection(LEFT);
if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){
player->UpdateWalkingAnimation(LEFT);
}
player->movementVelocity.y=-"Player.StaircaseClimbSpd"_F*fElapsedTime*player->GetMoveSpdMult();
}
newAimingAngle-=vf2d{1,0};
@ -506,14 +524,18 @@ void AiL::HandleUserInput(float fElapsedTime){
heldDownMovementKey=true;
}
if(UpHeld()){
player->SetY(player->GetY()-fElapsedTime*"Player.MoveSpd"_F*player->GetMoveSpdMult());
player->movementVelocity.y=-"Player.MoveSpd"_F*fElapsedTime;
if(setIdleAnimation){
player->SetFacingDirection(UP);
if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){
player->UpdateWalkingAnimation(UP);
}
}
float moveAmt="Player.MoveSpd"_F;
if(Input::UsingGamepad()&&KEY_SCROLLVERT_L.Analog()<=-0.2f){
float controllerAmt=abs(KEY_SCROLLVERT_L.Analog());
controllerAmt+=0.2f;
if(controllerAmt>=0.6f)controllerAmt=1.f; //Edge zone.
if(controllerAmt>animationSpd){
animationSpd=controllerAmt;
}
moveAmt*=controllerAmt;
}else animationSpd=1.f;
player->SetY(player->GetY()-fElapsedTime*moveAmt*player->GetMoveSpdMult());
player->movementVelocity.y=-moveAmt*fElapsedTime*player->GetMoveSpdMult();
newAimingAngle-=vf2d{0,1};
@ -521,14 +543,18 @@ void AiL::HandleUserInput(float fElapsedTime){
heldDownMovementKey=true;
}
if(DownHeld()){
player->SetY(player->GetY()+fElapsedTime*"Player.MoveSpd"_F*player->GetMoveSpdMult());
player->movementVelocity.y="Player.MoveSpd"_F*fElapsedTime;
if(setIdleAnimation){
player->SetFacingDirection(DOWN);
if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){
player->UpdateWalkingAnimation(DOWN);
}
}
float moveAmt="Player.MoveSpd"_F;
if(Input::UsingGamepad()&&KEY_SCROLLVERT_L.Analog()>=0.2f){
float controllerAmt=abs(KEY_SCROLLVERT_L.Analog());
controllerAmt+=0.2f;
if(controllerAmt>=0.6f)controllerAmt=1.f; //Edge zone.
if(controllerAmt>animationSpd){
animationSpd=controllerAmt;
}
moveAmt*=controllerAmt;
}else animationSpd=1.f;
player->SetY(player->GetY()+fElapsedTime*moveAmt*player->GetMoveSpdMult());
player->movementVelocity.y=moveAmt*fElapsedTime*player->GetMoveSpdMult();
newAimingAngle+=vf2d{0,1};
@ -539,6 +565,23 @@ void AiL::HandleUserInput(float fElapsedTime){
player->aimingAngle=newAimingAngle.norm().polar();
}
}
if(heldDownMovementKey){
if(abs(player->movementVelocity.x)>abs(player->movementVelocity.y)){ //Greater Horizontal movement.
if(player->movementVelocity.x!=0.f){
player->SetFacingDirection(player->movementVelocity.x>0?RIGHT:LEFT);
if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){
player->UpdateWalkingAnimation(player->GetFacingDirection(),animationSpd);
}
}
}else{ //Greater Vertical movement.
if(player->movementVelocity.y!=0.f){
player->SetFacingDirection(player->movementVelocity.y>0?DOWN:UP);
if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){
player->UpdateWalkingAnimation(player->GetFacingDirection(),animationSpd);
}
}
}
}
if(UpReleased()){
player->SetLastReleasedMovementKey(UP);
player->movementVelocity.y=0;
@ -605,7 +648,7 @@ void AiL::HandleUserInput(float fElapsedTime){
}
if(heldDownMovementKey){
player->footstepTimer+=GetElapsedTime();
player->footstepTimer+=GetElapsedTime()*animationSpd;
if(player->footstepTimer>"Player.Footstep Timer"_F){
player->footstepTimer-="Player.Footstep Timer"_F;
@ -2461,6 +2504,9 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
});
LoadingScreen::AddPhase([&](){
STEAMUSERSTATS(
SteamUserStats()->StoreStats();
)
ClearGarbage();
return true;
});
@ -2693,6 +2739,9 @@ bool Steam_Init(){
LOG(std::format("STEAM[{}]: {}",severity,std::string(message)));
});
}
STEAMUSERSTATS(
SteamUserStats()->RequestCurrentStats();
)
return true;
}
#endif
@ -3705,7 +3754,6 @@ void AiL::ResetGame(bool changeToMainMenu){
for(int i=0;i<GetLoadoutSize();i++){
game->ClearLoadoutItem(i);
}
Unlock::unlocks.clear();
Unlock::Initialize();
State_OverworldMap::SetStageMarker("Story I");
State_OverworldMap::UpdateCurrentConnectionPoint(*State_OverworldMap::currentConnectionPoint);
@ -3886,3 +3934,7 @@ void AiL::ActivateActionSetForAllControllers(InputActionSetHandle_t actionSetHan
}
)
}
const float AiL::GetEncounterDuration()const{
return encounterDuration;
}

@ -59,6 +59,7 @@ All rights reserved.
class SteamKeyboardCallbackHandler;
class SteamStatsReceivedHandler;
#define CreateBullet(type) BULLET_LIST.push_back(std::make_unique<type>(type
#define EndBullet ));
@ -189,6 +190,7 @@ private:
#endif
Audio audioEngine;
SteamKeyboardCallbackHandler*steamKeyboardCallbackListener=nullptr;
SteamStatsReceivedHandler*steamStatsReceivedHandlerListener=nullptr;
public:
AiL();
bool OnUserCreate() override;
@ -316,6 +318,7 @@ public:
rcode LoadResource(Renderable&renderable,std::string_view imgPath,bool filter=false,bool clamp=true);
void UpdateMonsters();
void ActivateActionSetForAllControllers(InputActionSetHandle_t actionSetHandle);
const float GetEncounterDuration()const;
struct TileGroupData{
vi2d tilePos;

@ -61,25 +61,25 @@ void sig::Animation::InitializeAnimations(){
};
auto SetupClassWalkIdleAnimations=[&](Renderable&sheet,std::string className){
Animate2D::FrameSequence pl_walk_s{0.2f};
Animate2D::FrameSequence pl_walk_s{"Player.WalkingFrameSpd"_F};
pl_walk_s.AddFrame({&sheet,{vi2d{0,0}*24,{24,24}}});
pl_walk_s.AddFrame({&sheet,{vi2d{1,0}*24,{24,24}}});
pl_walk_s.AddFrame({&sheet,{vi2d{0,0}*24,{24,24}}});
pl_walk_s.AddFrame({&sheet,{vi2d{2,0}*24,{24,24}}});
ANIMATION_DATA[className+"_WALK_S"]=pl_walk_s;
Animate2D::FrameSequence pl_walk_e{0.2f};
Animate2D::FrameSequence pl_walk_e{"Player.WalkingFrameSpd"_F};
pl_walk_e.AddFrame({&sheet,{vi2d{0,3}*24,{24,24}}});
pl_walk_e.AddFrame({&sheet,{vi2d{1,3}*24,{24,24}}});
pl_walk_e.AddFrame({&sheet,{vi2d{0,3}*24,{24,24}}});
pl_walk_e.AddFrame({&sheet,{vi2d{2,3}*24,{24,24}}});
ANIMATION_DATA[className+"_WALK_E"]=pl_walk_e;
Animate2D::FrameSequence pl_walk_w{0.2f};
Animate2D::FrameSequence pl_walk_w{"Player.WalkingFrameSpd"_F};
pl_walk_w.AddFrame({&sheet,{vi2d{0,2}*24,{24,24}}});
pl_walk_w.AddFrame({&sheet,{vi2d{1,2}*24,{24,24}}});
pl_walk_w.AddFrame({&sheet,{vi2d{0,2}*24,{24,24}}});
pl_walk_w.AddFrame({&sheet,{vi2d{2,2}*24,{24,24}}});
ANIMATION_DATA[className+"_WALK_W"]=pl_walk_w;
Animate2D::FrameSequence pl_walk_n{0.2f};
Animate2D::FrameSequence pl_walk_n{"Player.WalkingFrameSpd"_F};
pl_walk_n.AddFrame({&sheet,{vi2d{0,1}*24,{24,24}}});
pl_walk_n.AddFrame({&sheet,{vi2d{1,1}*24,{24,24}}});
pl_walk_n.AddFrame({&sheet,{vi2d{0,1}*24,{24,24}}});

@ -50,6 +50,9 @@ All rights reserved.
#include "SoundEffect.h"
#include "ProgressBar.h"
#include "MenuItemLabel.h"
#ifndef __EMSCRIPTEN__
#include "steam/isteamuserstats.h"
#endif
INCLUDE_game
INCLUDE_GFX
@ -99,6 +102,17 @@ namespace CharacterMenuWindow{
if(!comp.expired()){
if(SelectedEquipIsDifferent(comp)){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply.
Inventory::EquipItem(comp.lock()->GetItem(),EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE)));
#pragma region Fully Decked Out Achievement
STEAMUSERSTATS(
datafile&unlock=DATA.GetProperty("Achievement.Equip Unlocks.Fully Decked Out");
if(Inventory::EquipsFullyMaxedOut(unlock["Weapon Max Level"].GetInt(),unlock["Armor Max Level"].GetInt())){
SteamUserStats()->SetAchievement(unlock["API Name"].GetString().c_str());
SteamUserStats()->StoreStats();
}
)
#pragma endregion
if(Menu::IsCurrentlyActive(data.menu.GetType())){
SoundEffect::PlaySFX(comp.lock()->GetItem().lock()->UseSound(),SoundEffect::CENTERED);
}

@ -36,12 +36,15 @@ All rights reserved.
*/
#pragma endregion
#pragma once
#include "Ability.h"
#include "Animation.h"
#include <vector>
#include <string>
#include "DEFINES.h"
#include "olcUTIL_DataFile.h"
#undef GetClassInfo
//Classes have bit-wise operator capabilities.
INCLUDE_DATA
enum Class{
ANY=0,
WARRIOR=1,
@ -49,5 +52,15 @@ enum Class{
RANGER=4,
TRAPPER=8,
WIZARD=16,
WITCH=32
WITCH=32,
};
namespace classutils{//Classes have bit-wise operator capabilities.
static inline Class StringToClass(std::string className){
const std::vector<std::string>&classList=DATA["class_list"].GetValues();
auto it=std::find(classList.begin(),classList.end(),className);
if(it==classList.end())ERR(std::format("WARNING! Class {} does not exist!",className));
int element=int(std::distance(classList.begin(),it));
return Class(1<<element); //Yes...It's bitwise flags, who in god's name knows why I did this.
};
};

@ -54,15 +54,7 @@ struct ClassInfo{
Ability*rightClickAbility;
};
class classutils{
public:
static inline Class StringToClass(std::string className){
const std::vector<std::string>&classList=DATA["class_list"].GetValues();
auto it=std::find(classList.begin(),classList.end(),className);
if(it==classList.end())ERR(std::format("WARNING! Class {} does not exist!",className));
int element=int(std::distance(classList.begin(),it));
return Class(1<<element); //Yes...It's bitwise flags, who in god's name knows why I did this.
};
namespace classutils{
static inline ClassInfo GetClassInfo(std::string className){
ClassInfo data{
DATA.GetProperty(className+".ClassName").GetString(),

@ -47,6 +47,9 @@ All rights reserved.
#include "SoundEffect.h"
#include "ClassInfo.h"
#include "RowInventoryScrollableWindowComponent.h"
#ifndef __EMSCRIPTEN__
#include "steam/isteamuserstats.h"
#endif
INCLUDE_game
INCLUDE_DATA
@ -840,8 +843,10 @@ void Inventory::EquipItem(const std::weak_ptr<Item>it,EquipSlot slot){
if(equippedSlot!=EquipSlot::NONE)UnequipItem(equippedSlot);
if(!GetEquip(slot).expired())UnequipItem(slot);
Inventory::equipment[slot]=it;
game->GetPlayer()->RecalculateEquipStats();
};
void Inventory::UnequipItem(EquipSlot slot){
Inventory::equipment[slot]=Item::BLANK;
game->GetPlayer()->RecalculateEquipStats();
@ -924,6 +929,33 @@ void Item::EnhanceItem(uint8_t qty){
enhancementLevel++;
#pragma region Achievements
STEAMUSERSTATS(
// Fully Decked Out Achievement
datafile&unlock=DATA.GetProperty("Achievement.Equip Unlocks.Fully Decked Out");
if(Inventory::EquipsFullyMaxedOut(unlock["Weapon Max Level"].GetInt(),unlock["Armor Max Level"].GetInt())){
SteamUserStats()->SetAchievement(unlock["API Name"].GetString().c_str());
SteamUserStats()->StoreStats();
}
// Equipment achievement unlocks
for(auto&[key,size]:DATA.GetProperty("Achievement.Equip Unlocks")){
datafile&unlock=DATA.GetProperty(std::format("Achievement.Equip Unlocks.{}",key));
if(!(unlock.HasProperty("Upgrade Requirement")&&unlock.HasProperty("Equip Slot")))continue; //Ignore any achievements that don't have an upgrade requirement/equipment slot defined.
if(EnhancementLevel()>=unlock["Upgrade Requirement"].GetInt()){
EquipSlot validSlots=EquipSlot::NONE;
for(const std::string&slot:unlock["Equip Slot"].GetValues()){
validSlots|=ItemInfo::StringToEquipSlot(slot); //Collect all the bits that this equipment can fall under.
}
if(GetEquipSlot()&validSlots){
//This piece of gear matches one of the provided slots.
SteamUserStats()->SetAchievement(unlock["API Name"].GetString().c_str());
SteamUserStats()->StoreStats();
}
}
}
)
#pragma endregion
const CraftingRequirement&consumedResources=GetEnhancementInfo()[EnhancementLevel()].craftingRequirement;
for(const auto&[name,amt]:consumedResources.GetItems()){
@ -1285,3 +1317,24 @@ void Item::Lock(){
void Item::Unlock(){
locked=false;
}
//Specifically for the "Fully Decked Out" achievement.
const bool Inventory::EquipsFullyMaxedOut(int maxWeaponLevel,int maxArmorLevel){
for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){
EquipSlot slot=EquipSlot(i);
if(!ISBLANK(Inventory::GetEquip(slot))){
std::shared_ptr<Item>equip=Inventory::GetEquip(slot).lock();
if(!(equip->IsAccessory()||
(equip->IsWeapon()&&equip->EnhancementLevel()>=maxWeaponLevel)||
(equip->IsArmor()&&equip->EnhancementLevel()>=maxArmorLevel))
){
return false;
}
}else return false;
}
return true;
}
const EquipSlot ItemInfo::StringToEquipSlot(std::string_view slotName){
return nameToEquipSlot[std::string(slotName)];
}

@ -269,6 +269,7 @@ public:
static void AddLoadoutItemUsed(IT item,int slot);
static void ResetLoadoutItemsUsed();
static void GivePlayerLoadoutItemsUsed();
static const bool EquipsFullyMaxedOut(int maxWeaponLevel=10,int maxArmorLevel=10);
static bool SwapItems(ITCategory itemCategory,uint32_t slot1,uint32_t slot2);
//Makes sure this is a valid category. Will error out if it doesn't exist! Use for ERROR HANDLING!
@ -338,6 +339,7 @@ public:
const std::string&Description()const;
const ITCategory Category()const;
const::Decal*const Decal()const;
static const EquipSlot StringToEquipSlot(std::string_view slotName);
/*
For the useFunc, return true if the item can be used, false otherwise.
*/

@ -79,8 +79,10 @@ void Menu::InitializeLevelCompleteWindow(){
};
auto nextButtonAction=[](MenuFuncData data){
if(Component<MenuLabel>(LEVEL_COMPLETE,"Stage Complete Label")->GetLabel()!="Stage Summary"){ //If the label says stage summary, we didn't actually complete the level. Don't unlock anything new for the player.
Unlock::UnlockArea(State_OverworldMap::GetCurrentConnectionPoint().map);
Merchant::RandomizeTravelingMerchant();
}
State_LevelComplete::TurnOffXPSound();
GameState::ChangeState(States::GAME_HUB,0.25f);
return true;

@ -47,6 +47,10 @@ All rights reserved.
#include "MonsterAttribute.h"
#include "ItemDrop.h"
#include "SoundEffect.h"
#include "Unlock.h"
#ifndef __EMSCRIPTEN__
#include "steam/isteamuserstats.h"
#endif
INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_DATA
@ -723,6 +727,27 @@ void Monster::OnDeath(){
}
}
Unlock::IncreaseKillCount();
STEAMUSERSTATS(
for(auto&[key,size]:DATA.GetProperty("Achievement.Kill Unlocks")){
//Monster-specific achievement unlocks.
datafile&unlock=DATA.GetProperty(std::format("Achievement.Kill Unlocks.{}",key));
if(unlock.HasProperty("Monster Name")){
if(unlock["Monster Name"].GetString()!=GetName())continue;
if(unlock.HasProperty("Time Limit")&&isBoss){
if(game->GetEncounterDuration()<=unlock["Time Limit"].GetReal()){
SteamUserStats()->SetAchievement(unlock["API Name"].GetString().c_str());
SteamUserStats()->StoreStats();
}
}else{
SteamUserStats()->SetAchievement(unlock["API Name"].GetString().c_str());
SteamUserStats()->StoreStats();
}
}
}
)
if(hasStrategyDeathFunction){
GameEvent::AddEvent(std::make_unique<MonsterStrategyGameEvent>(strategyDeathFunc,*this,MONSTER_DATA[name].GetAIStrategy()));
}

@ -57,6 +57,9 @@ All rights reserved.
#include "GameSettings.h"
#include "Unlock.h"
#include "Tutorial.h"
#ifndef __EMSCRIPTEN__
#include "steam/isteamuserstats.h"
#endif
INCLUDE_MONSTER_DATA
INCLUDE_MONSTER_LIST
@ -777,9 +780,9 @@ void Player::AddAnimation(std::string state){
animation.AddState(state,ANIMATION_DATA.at(state));
}
void Player::UpdateAnimation(std::string animState,int specificClass){
void Player::UpdateAnimation(std::string animState,int specificClass, const float frameMult){
if(specificClass==ANY||specificClass&GetClass()){
animation.ChangeState(internal_animState,animState);
animation.ChangeState(internal_animState,animState,frameMult);
}
}
@ -868,7 +871,7 @@ void Player::Spin(float duration,float spinSpd){
spin_angle=0;
}
void Player::UpdateWalkingAnimation(Key direction){
void Player::UpdateWalkingAnimation(Key direction, const float frameMult){
std::string anim;
switch(direction){
case UP:anim=GetWalkNAnimation();break;
@ -876,7 +879,7 @@ void Player::UpdateWalkingAnimation(Key direction){
case DOWN:anim=GetWalkSAnimation();break;
case LEFT:anim=GetWalkWAnimation();break;
}
UpdateAnimation(anim);
UpdateAnimation(anim,0,frameMult);
}
void Player::UpdateIdleAnimation(Key direction){
@ -1301,9 +1304,8 @@ void Player::AddXP(const uint32_t xpGain){
currentLevelXP+=xpGain;
totalXPEarned+=xpGain;
if(Level()<LevelCap()){
uint32_t nextLevelXP=NextLevelXPRequired();
while(currentLevelXP>=nextLevelXP){
currentLevelXP-=nextLevelXP;
while(currentLevelXP>=NextLevelXPRequired()){
currentLevelXP-=NextLevelXPRequired();
SetLevel(Level()+1);
OnLevelUp();
}
@ -1324,6 +1326,18 @@ void Player::OnLevelUp(){
stats.SetBaseStat("Health",GetBaseStat("Health")+hpGrowthRate);
stats.SetBaseStat("Attack",GetBaseStat("Attack")+atkGrowthRate);
Heal(GetBaseStat("Health"));
STEAMUSERSTATS(
for(auto&[key,size]:DATA.GetProperty("Achievement.Class Unlocks")){
datafile&unlock=DATA.GetProperty(std::format("Achievement.Class Unlocks.{}",key));
if(classutils::StringToClass(unlock["Class Requirement"].GetString())==GetClass()&&
Level()-1<unlock["Level Requirement"].GetInt()&&
Level()==unlock["Level Requirement"].GetInt()){
SteamUserStats()->SetAchievement(unlock["API Name"].GetString().c_str());
SteamUserStats()->StoreStats();
}
}
)
}
const uint8_t Player::LevelCap()const{
return levelCap;
@ -1429,17 +1443,18 @@ const vf2d Player::GetAimingLocation(bool useWalkDir,bool invert){
vf2d closestPoint={std::numeric_limits<float>::max(),std::numeric_limits<float>::max()};
for(Monster&m:MONSTER_LIST){
if(m.IsAlive()){
float distToMonster=geom2d::line<float>(GetPos(),m.GetPos()).length();
geom2d::line<float>aimingLine=geom2d::line<float>(GetPos(),m.GetPos());
vf2d aimingPoint=aimingLine.rpoint((invert?-1.f:1.f)*operator""_Pixels("Player.Aiming Cursor Max Distance"_F));
float distToMonster=aimingLine.length();
float distToClosestPoint=geom2d::line<float>(GetPos(),closestPoint).length();
if(distToClosestPoint>distToMonster&&distToMonster<=operator""_Pixels("Player.Auto Aim Detection Distance"_F)){
closestPoint=aimingPoint;
closestPoint=m.GetPos();
}
}
}
if(closestPoint!=vf2d{std::numeric_limits<float>::max(),std::numeric_limits<float>::max()}){
return game->GetScreenSize()/2+closestPoint-GetPos();
geom2d::line<float>aimingLine=geom2d::line<float>(GetPos(),closestPoint);
vf2d aimingPoint=aimingLine.rpoint(invert?(-operator""_Pixels("Player.Aiming Cursor Max Distance"_F)):std::min(aimingLine.length()+24.f,float(operator""_Pixels("Player.Aiming Cursor Max Distance"_F))));
return game->GetScreenSize()/2+aimingPoint-GetPos();
}else
return game->GetScreenSize()/2+vf2d{float(operator""_Pixels("Player.Aiming Cursor Max Distance"_F)),aimingAngle.y}.cart();
}

@ -133,7 +133,7 @@ public:
vf2d GetVelocity();
bool HasIframes();
void Update(float fElapsedTime);
void UpdateWalkingAnimation(Key direction);
void UpdateWalkingAnimation(Key direction, const float frameMult=1.f);
void UpdateIdleAnimation(Key direction);
//The range is the search range in tiles.
bool CanPathfindTo(vf2d pos,vf2d targetPos,float range=8);
@ -176,9 +176,8 @@ public:
bool Hurt(int damage,bool onUpperLevel,float z);
//Return false if healing was not possible.
bool Heal(int damage,bool suppressDamageNumber=false);
//specificClass is a bitwise-combination of classes from the Class enum. It makes sure certain animations only play if you are a certain class.
//Set force to true to force the animation to restart evne if the animation were already playing.
void UpdateAnimation(std::string animState,int specificClass=ANY);
//specificClass is a bitwise-combination of classes from the Class enum. It makes sure certain animations only play if you are a certain class.=
void UpdateAnimation(std::string animState,int specificClass=ANY,const float frameMult=1.f);
Animate2D::Frame GetFrame();
Key GetLastReleasedMovementKey();
float GetSwordSwingTimer();

@ -178,7 +178,14 @@ const void SaveFile::SaveGame(){
saveSystemFile["Fullscreen"].SetBool(game->IsFullscreen());
#pragma endregion
saveFile["Hash"].SetString("");
utils::datafile::Write(saveFile,"save_file_path"_S+std::format("save.{:04}",saveFileID));
std::string fileHash=util::GetHash("save_file_path"_S+std::format("save.{:04}",saveFileID));
saveFile["Hash"].SetString(fileHash);
utils::datafile::Write(saveFile,"save_file_path"_S+std::format("save.{:04}",saveFileID)); //Once the hash has been computed and added, save the file a second time.
utils::datafile::Write(saveSystemFile,"save_file_path"_S+"system.conf");
utils::datafile metadata;
if(onlineMode){
@ -279,6 +286,24 @@ void SaveFile::LoadFile(){
if(std::filesystem::exists(loadFilename)){
utils::datafile::Read(loadFile,"save_file_path"_S+std::format("save.{:04}",saveFileID));
if(!loadFile.HasProperty("Hash")){
LOG(std::format("WARNING! Filehash for file {} does not exist!","save_file_path"_S+std::format("save.{:04}",saveFileID)));
return;
}
if(loadFile.HasProperty("Hash")){
std::string expectedFileHash=loadFile["Hash"].GetString();
loadFile["Hash"].SetString("");
utils::datafile::Write(loadFile,"save_file_path"_S+std::format("save.{:04}",saveFileID));
std::string fileHash=util::GetHash("save_file_path"_S+std::format("save.{:04}",saveFileID));
if(expectedFileHash!=fileHash){
LOG(std::format("WARNING! Filehash for file {} was not identified as proper! Will not load this file!","save_file_path"_S+std::format("save.{:04}",saveFileID)));
return;
}
loadFile["Hash"].SetString(expectedFileHash); //Now write the hash back into the file since we tampered with it.
utils::datafile::Write(loadFile,"save_file_path"_S+std::format("save.{:04}",saveFileID));
}
game->ResetGame();
for(auto&[key,data]:loadFile["Items"].GetOrderedKeys()){
std::weak_ptr<Item>newItem=Inventory::AddItem(data["Item Name"].GetString(),data["Amt"].GetInt());

@ -41,6 +41,7 @@ All rights reserved.
#include "GameState.h"
#include "AdventuresInLestoria.h"
#include "Unlock.h"
#include "MenuLabel.h"
INCLUDE_game
@ -48,7 +49,9 @@ void Menu::InitializeShermanWindow(){
Menu*shermanWindow=CreateMenu(SHERMAN,CENTERED,vi2d{144,88});
shermanWindow->ADD("Leave Button",MenuComponent)(geom2d::rect<float>{{0.f,4.f},{144.f,24.f}},"Leave",[](MenuFuncData data){
if(Component<MenuLabel>(LEVEL_COMPLETE,"Stage Complete Label")->GetLabel()!="Stage Summary"){ //If the label says stage summary, we didn't actually complete the level. Don't unlock anything new for the player.
Unlock::UnlockCurrentMap();
}
GameState::ChangeState(States::OVERWORLD_MAP,0.3f);
return true;
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END;

@ -77,6 +77,7 @@ void State_GameHub::OnLevelLoad(){
}
void State_GameHub::OnUserUpdate(AiL*game){
State_GameRun::OnUserUpdate(game);
game->ClearTimedOutGarbage();
}
void State_GameHub::Draw(AiL*game){

@ -42,6 +42,9 @@ All rights reserved.
#include "Key.h"
#include "ItemDrop.h"
#include "util.h"
#ifndef __EMSCRIPTEN__
#include "steam/isteamuserstats.h"
#endif
INCLUDE_game

@ -0,0 +1,54 @@
#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 "SteamStatsReceivedHandler.h"
#include "olcUTIL_DataFile.h"
#include "DEFINES.h"
#include "Unlock.h"
#include "config.h"
#include "Error.h"
INCLUDE_DATA
#ifndef __EMSCRIPTEN__
void SteamStatsReceivedHandler::SteamStatsReceived( UserStatsReceived_t* pCallback ){
if(pCallback->m_eResult==k_EResultOK){
SteamUserStats()->GetStat("Achievement.Kill Unlocks.Total Kill API Name"_S.c_str(),&Unlock::monsterKillCount);
LOG(std::format("Retrieved monster kill count: {}",Unlock::monsterKillCount));
}
}
#endif

@ -0,0 +1,47 @@
#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
#ifndef __EMSCRIPTEN__
#include "steam/steam_api.h"
#endif
#ifndef __EMSCRIPTEN__
class SteamStatsReceivedHandler{
STEAM_CALLBACK(SteamStatsReceivedHandler,SteamStatsReceived,UserStatsReceived_t);
};
#endif

@ -44,6 +44,8 @@ All rights reserved.
#include "DEFINES.h"
#include "safemap.h"
#include "ItemMapData.h"
#include "Class.h"
using MapName=std::string;
using namespace olc;
@ -126,6 +128,7 @@ private:
std::vector<NPCData>npcs;
std::string mapType="";
std::string bgmSongName="";
std::unordered_map<Class,float>devCompletionTrialTime;
BackdropName backdrop="";
std::set<std::string>spawns;
std::map<int,SpawnerTag> SpawnerData; //Spawn groups have IDs, mobs associate which spawner they are tied to via this ID.
@ -138,6 +141,7 @@ public:
const std::vector<ItemMapData>&GetStageLoot()const;
const std::vector<LayerTag>&GetLayers()const;
const std::vector<EnvironmentalAudio>&GetEnvironmentalAudio()const;
const float GetDevCompletionTime(Class cl)const;
const MapName&GetMapName()const;
const std::string_view GetMapDisplayName()const;
std::string FormatLayerData(std::ostream& os, std::vector<LayerTag>tiles);
@ -464,6 +468,13 @@ class TMXParser{
parsedMapInfo.bgmSongName=newTag.data["value"];
}
} else
if (newTag.tag=="property"&&newTag.data["name"].starts_with("Dev Completion Time")) {
if(newTag.data.count("value")){ //None is a default value that we ignore.
size_t classStartPos="Dev Completion Time - "s.length();
std::string className=newTag.data["name"].substr(classStartPos,newTag.data["name"].find(' ',classStartPos)-classStartPos);
parsedMapInfo.devCompletionTrialTime[classutils::StringToClass(className)]=newTag.GetFloat("value");
}
} else
if (newTag.tag=="property"&&newTag.data["name"]=="Backdrop") {
if(newTag.data["value"]!="None"){ //None is a default value that we ignore.
parsedMapInfo.backdrop=newTag.data["value"];

@ -1,3 +1,18 @@
============================================
Time Trial Idea
Upon completing a stage for the first time, the player is shown the completion time and a record time.
On first clears, the player will always obtain a new record. Make it apparent to the player they obtained a new record.
The overworld map will show a new section that says "Completion Time" for any previous completed stages.
Upon the second time entering a stage, the game will spawn a timer that the player can run into to begin a time trial-like mode.
During the Time Trial mode the player can defeat monsters to freeze the timer by 1 second per mob killed.
Upon completion of a stage in time trial mode if the player beat their previous time (which they likely will) the record will update.
For each class and stage combination there will be a "dev time"
Settings menu doesn't scroll back up properly while the scrollbar does?
Merchant descriptions have no newlines.
============================================
Consider a "killed by player" / "marked by player" flag for monsters to determine if a player gets credit for a monster kill (for achievements)
Make another actions config file for the main build (The app # is different)

@ -38,16 +38,33 @@ All rights reserved.
#include "Unlock.h"
#include "State_OverworldMap.h"
#include "config.h"
#include "olcUTIL_DataFile.h"
#include "DEFINES.h"
#include "emscripten_compat.h"
#ifndef __EMSCRIPTEN__
#include "steam/isteamuserstats.h"
#endif
INCLUDE_DATA
std::set<std::string>Unlock::unlocks;
int Unlock::monsterKillCount=0;
void Unlock::Initialize(){
unlocks.clear();
UnlockArea("WORLD_MAP");
}
void Unlock::UnlockArea(std::string mapName){
if(mapName=="NPCs.Sherman.Potion Crafting Unlock Condition"_S&& //When we beat the bonus chapter 1 fight, before sherman's potion crafting is unlocked, if the current map we just unlocked for is the bonus boss stage we will notify the Hub connection point and reset it so the player has a notification to go there again.
!Unlock::IsUnlocked("NPCs.Sherman.Potion Crafting Unlock Condition"_S))State_OverworldMap::ConnectionPointFromString("HUB").value()->ResetVisitedFlag();
STEAMUSERSTATS(
datafile&areaUnlocks=DATA.GetProperty("Achievement.Area Unlocks");
for(auto&[key,size]:areaUnlocks){
datafile&unlock = areaUnlocks[key];
if(mapName==unlock["Unlock Name"].GetString())SteamUserStats()->SetAchievement(unlock["API Name"].GetString().c_str());
}
)
unlocks.insert(mapName);
}
bool Unlock::IsUnlocked(std::string mapName){
@ -61,3 +78,20 @@ bool Unlock::IsUnlocked(ConnectionPoint&cp){
void Unlock::UnlockCurrentMap(){
UnlockArea(State_OverworldMap::GetCurrentConnectionPoint().map);
}
void Unlock::IncreaseKillCount(){
monsterKillCount++;
STEAMUSERSTATS(
SteamUserStats()->SetStat("Achievement.Kill Unlocks.Total Kill API Name"_S.c_str(),Unlock::monsterKillCount);
datafile&killUnlocks=DATA.GetProperty("Achievement.Kill Unlocks");
for(auto&[key,size]:killUnlocks){
if(key.starts_with("Kill Monsters")){
int killRequirement=killUnlocks[key]["Monster Kill Count"].GetInt();
if(monsterKillCount-1<killRequirement&& //These two checks make sure we've just reached the kill requirement, so we can send a more readily live update for the player.
monsterKillCount==killRequirement){
SteamUserStats()->StoreStats();
}
}
}
)
}

@ -43,8 +43,10 @@ All rights reserved.
class Unlock{
friend class AiL;
friend class SaveFile;
friend class SteamStatsReceivedHandler;
static std::set<std::string>unlocks;
static void Initialize();
static int monsterKillCount;
public:
//Provide a map's actual name to trigger unlocks for all connected areas. You can get the current map you are on via State_OverworldMap::GetCurrentConnectionPoint().map
static void UnlockArea(std::string mapName);
@ -52,4 +54,5 @@ public:
static void UnlockCurrentMap();
static bool IsUnlocked(std::string mapName);
static bool IsUnlocked(ConnectionPoint&cp);
static void IncreaseKillCount();
};

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define VERSION_PATCH 0
#define VERSION_BUILD 8477
#define VERSION_BUILD 8598
#define stringify(a) stringify_(a)
#define stringify_(a) #a

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="205" height="205" tilewidth="24" tileheight="24" infinite="0" backgroundcolor="#475500" nextlayerid="11" nextobjectid="179">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="205" height="205" tilewidth="24" tileheight="24" infinite="0" backgroundcolor="#475500" nextlayerid="11" nextobjectid="507">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
<property name="Dev Completion Time - Ranger (s)" type="float" value="120"/>
<property name="Dev Completion Time - Warrior (s)" type="float" value="120"/>
<property name="Dev Completion Time - Wizard (s)" type="float" value="120"/>
<property name="Level Type" propertytype="LevelType" value="Dungeon"/>
</properties>
<tileset firstgid="1" source="../maps/Tilesheet_No_Shadow24x24.tsx"/>
@ -2009,5 +2012,6 @@
</properties>
<point/>
</object>
<object id="180" name="Time Trial Clock" type="TrialClock" x="624" y="4224" width="24" height="24"/>
</objectgroup>
</map>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="200" height="122" tilewidth="24" tileheight="24" infinite="0" nextlayerid="9" nextobjectid="82">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="200" height="122" tilewidth="24" tileheight="24" infinite="0" nextlayerid="9" nextobjectid="83">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -845,5 +845,6 @@
</properties>
<point/>
</object>
<object id="82" name="Time Trial Clock" type="TrialClock" x="3648" y="648" width="24" height="24"/>
</objectgroup>
</map>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="205" height="250" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="97">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="205" height="250" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="98">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -1551,6 +1551,7 @@
<point/>
</object>
<object id="95" name="End Zone" type="EndZone" x="1726" y="1681" width="72" height="72"/>
<object id="97" name="Time Trial Clock" type="TrialClock" x="1656" y="5448" width="24" height="24"/>
</objectgroup>
<objectgroup id="7" name="Environmental Sounds">
<object id="30" name="Waterfall Sound" type="AudioEnvironmentalSound" x="4313" y="2678">

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="250" height="175" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="107">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="250" height="175" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="108">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -1154,5 +1154,6 @@
</properties>
<point/>
</object>
<object id="107" name="Time Trial Clock" type="TrialClock" x="720" y="3456" width="24" height="24"/>
</objectgroup>
</map>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="229" height="155" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="84">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="229" height="155" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="85">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -1118,6 +1118,7 @@
</properties>
<point/>
</object>
<object id="84" name="Time Trial Clock" type="TrialClock" x="1824" y="408" width="24" height="24"/>
</objectgroup>
<objectgroup id="7" name="Environmental Sounds">
<object id="24" name="Waterfall Sound" type="AudioEnvironmentalSound" x="3425" y="264">

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="250" height="127" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="87">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="250" height="127" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="88">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -893,5 +893,6 @@
</properties>
<point/>
</object>
<object id="87" name="Time Trial Clock" type="TrialClock" x="360" y="1488" width="24" height="24"/>
</objectgroup>
</map>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="148" height="131" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="68">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="148" height="131" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="69">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -796,5 +796,6 @@
</properties>
<point/>
</object>
<object id="68" name="Time Trial Clock" type="TrialClock" x="96" y="312" width="24" height="24"/>
</objectgroup>
</map>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="72" height="80" tilewidth="24" tileheight="24" infinite="0" nextlayerid="5" nextobjectid="9">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="72" height="80" tilewidth="24" tileheight="24" infinite="0" nextlayerid="5" nextobjectid="9">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty_boss"/>

@ -0,0 +1,118 @@
Achievement
{
Area Unlocks
{
# Area Unlocks are automatically checked when a new map unlock is achieved.
Camp Unlock
{
API Name = "CAMP_UNLOCK"
Unlock Name = "STORY_1_1"
}
Blacksmith Unlock
{
API Name = "BLACKSMITH_UNLOCK"
Unlock Name = "STORY_1_2"
}
Chapter 2 Unlock
{
API Name = "CHAPTER1_COMPLETE"
Unlock Name = "STORY_1_3"
}
}
Kill Unlocks
{
Total Kill API Name = "MONSTER_KILL_COUNT"
# Achievements that start with "Kill Monsters" will be checked when a monster is killed.
Kill Monsters 1
{
API Name = "KILL_SLIME_1"
Monster Kill Count = 100
}
Kill Monsters 2
{
API Name = "KILL_SLIME_2"
Monster Kill Count = 250
}
Kill Monsters 3
{
API Name = "KILL_SLIME_3"
Monster Kill Count = 1000
}
##################################
# For other Kill Encounter achievements, if they have a Monster Name the game looks for the name when a monster is killed and if it matches, you get awarded that achievement.
# If a Time Limit is included, the encounter must be a boss encounter and
Slime King
{
API Name = "SLIME_KING"
Monster Name = "Slime King"
}
Slime King Destroyer
{
API Name = "SLIME_KING_DESTROYER"
Time Limit = 60s
Monster Name = "Slime King"
}
Ursule
{
API Name = "URSULE"
Monster Name = "Ursule, Mother of Bears"
}
Ursule Destroyer
{
API Name = "URSULE_DESTROYER"
Time Limit = 60s
Monster Name = "Ursule, Mother of Bears"
}
}
Class Unlocks
{
Warrior Lv5
{
API Name = "WARRIOR_LV5"
Class Requirement = Warrior
Level Requirement = 5
}
Ranger Lv5
{
API Name = "RANGER_LV5"
Class Requirement = Ranger
Level Requirement = 5
}
Wizard Lv5
{
API Name = "WIZARD_LV5"
Class Requirement = Wizard
Level Requirement = 5
}
}
Equip Unlocks
{
Weapon Lv5
{
API Name = "WEAPON_LV5"
Equip Slot = Weapon
Upgrade Requirement = 5
}
Armor Lv5
{
API Name = "ARMOR_LV5"
Equip Slot = Helmet,Armor,Gloves,Pants,Shoes
Upgrade Requirement = 5
}
Armor Lv10
{
API Name = "ARMOR_LV10"
Equip Slot = Helmet,Armor,Gloves,Pants,Shoes
Upgrade Requirement = 10
}
Fully Decked Out
{
API Name = "FULLY_DECKED_OUT"
Weapon Max Level = 5
Armor Max Level = 10
}
}
}

@ -20,6 +20,9 @@ Player
# Amount of spd to increase/decrease vertically as you climb staircases
StaircaseClimbSpd = 45
# Walking Default Animation Delay per Frame
WalkingFrameSpd = 0.2s
# How much speed the player loses while no momentum is being added.
Friction = 400

@ -28,6 +28,9 @@ player_config = Player.txt
# Monster Properties Loading Config
monsters_config = Monsters.txt
# Achievement Config
achievement_config = Achievements.txt
# NPC Loading Config
npc_config = NPCs.txt

@ -192,4 +192,9 @@ ItemDatabase
ItemCategory = Materials
SellValue = 180
}
Time Medal
{
Description = A medal provided to top athletes in Lestoria commemorating a huge victory.
ItemCategory = Materials
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

@ -1,18 +1,18 @@
"controller_mappings"
{
"version" "3"
"revision" "82"
"title" "#Title_Config"
"description" "#Description_Config"
"revision" "87"
"title" "Generic Gamepad"
"description" "Default Generic Gamepad controls"
"creator" "76561198025675819"
"progenitor" ""
"url" "usercloud://2895980/generic gamepad_3"
"export_type" "personal_cloud"
"url" "autosave://C:\\Program Files (x86)\\Steam\\steamapps\\common\\Steam Controller Configs\\65410091\\config\\2895980\\controller_generic.vdf"
"export_type" "personal_local"
"controller_type" "controller_generic"
"controller_caps" "1573375"
"major_revision" "0"
"minor_revision" "0"
"Timestamp" "1711341077"
"Timestamp" "1711752971"
"actions"
{
"InGameControls"
@ -21,6 +21,11 @@
"legacy_set" "0"
"StickPadGyro"
{
"Move"
{
"title" "#Move"
"input_mode" "joystick_move"
}
"Scroll"
{
"title" "#Scroll"
@ -66,8 +71,8 @@
{
"english"
{
"Title_Config" "Generic Gamepad"
"Description_Config" "Generic gamepad configuration."
"Title_Config" "Adventures in Lestoria Configuration"
"Description_Config" "The default control set for Adventures in Lestoria."
"Set_Ingame" "Gameplay Controls"
"BasicAttack" "Basic Attack"
"Defensive" "Use Defensive"
@ -84,6 +89,7 @@
"Function1" "Menu Function 1"
"Function2" "Menu Function 2"
"Menu" "Menu/Pause"
"Move" "Move"
"Up" "Up"
"Down" "Down"
"Left" "Left"
@ -614,6 +620,24 @@
}
}
"group"
{
"id" "24"
"mode" "joystick_move"
"name" ""
"description" ""
"inputs"
{
}
"settings"
{
"virtual_mode" "1"
}
"gameactions"
{
"InGameControls" "Move"
}
}
"group"
{
"id" "0"
"mode" "switches"
@ -761,7 +785,8 @@
"2" "left_trigger active"
"3" "right_trigger active"
"15" "joystick inactive"
"23" "joystick active"
"23" "joystick inactive"
"24" "joystick active"
"16" "right_joystick inactive"
"22" "right_joystick active"
"14" "dpad inactive"

@ -1,18 +1,18 @@
"controller_mappings"
{
"version" "3"
"revision" "83"
"title" "#Title_Config"
"description" "#Description_Config"
"revision" "85"
"title" "Switch Pro"
"description" "Default Switch Pro configuration."
"creator" "76561198025675819"
"progenitor" ""
"url" "usercloud://2895980/switch pro configuration_0"
"export_type" "personal_cloud"
"url" "autosave://C:\\Program Files (x86)\\Steam\\steamapps\\common\\Steam Controller Configs\\65410091\\config\\2895980\\controller_switch_pro.vdf"
"export_type" "personal_local"
"controller_type" "controller_switch_pro"
"controller_caps" "613772287"
"major_revision" "0"
"minor_revision" "0"
"Timestamp" "1711340749"
"Timestamp" "1711752483"
"actions"
{
"InGameControls"
@ -21,6 +21,11 @@
"legacy_set" "0"
"StickPadGyro"
{
"Move"
{
"title" "#Move"
"input_mode" "joystick_move"
}
"Scroll"
{
"title" "#Scroll"
@ -66,8 +71,8 @@
{
"english"
{
"Title_Config" "Switch Pro"
"Description_Config" "Nintendo Switch Pro controller configuration."
"Title_Config" "Adventures in Lestoria Configuration"
"Description_Config" "The default control set for Adventures in Lestoria."
"Set_Ingame" "Gameplay Controls"
"BasicAttack" "Basic Attack"
"Defensive" "Use Defensive"
@ -84,6 +89,7 @@
"Function1" "Menu Function 1"
"Function2" "Menu Function 2"
"Menu" "Menu/Pause"
"Move" "Move"
"Up" "Up"
"Down" "Down"
"Left" "Left"
@ -614,6 +620,24 @@
}
}
"group"
{
"id" "25"
"mode" "joystick_move"
"name" ""
"description" ""
"inputs"
{
}
"settings"
{
"virtual_mode" "1"
}
"gameactions"
{
"InGameControls" "Move"
}
}
"group"
{
"id" "0"
"mode" "switches"
@ -761,7 +785,8 @@
"2" "left_trigger active"
"3" "right_trigger active"
"15" "joystick inactive"
"23" "joystick active"
"23" "joystick inactive"
"25" "joystick active"
"16" "right_joystick inactive"
"22" "right_joystick active"
"14" "dpad inactive"

@ -1,18 +1,18 @@
"controller_mappings"
{
"version" "3"
"revision" "76"
"title" "#Title_Config"
"description" "#Description_Config"
"revision" "78"
"title" "Xbox/Playstation"
"description" "Default Xbox/Playstation controls"
"creator" "76561198025675819"
"progenitor" ""
"url" "autosave://C:\\Program Files (x86)\\Steam\\steamapps\\common\\Steam Controller Configs\\65410091\\config\\2895980\\controller_xbox360.vdf"
"export_type" "personal_cloud"
"export_type" "personal_local"
"controller_type" "controller_xbox360"
"controller_caps" "1590271"
"major_revision" "0"
"minor_revision" "0"
"Timestamp" "1711340807"
"Timestamp" "1711752757"
"actions"
{
"InGameControls"
@ -21,6 +21,11 @@
"legacy_set" "0"
"StickPadGyro"
{
"Move"
{
"title" "#Move"
"input_mode" "joystick_move"
}
"Scroll"
{
"title" "#Scroll"
@ -66,8 +71,8 @@
{
"english"
{
"Title_Config" "Xbox/Playstation"
"Description_Config" "Xbox/Playstation controller configuration."
"Title_Config" "Adventures in Lestoria Configuration"
"Description_Config" "The default control set for Adventures in Lestoria."
"Set_Ingame" "Gameplay Controls"
"BasicAttack" "Basic Attack"
"Defensive" "Use Defensive"
@ -84,6 +89,7 @@
"Function1" "Menu Function 1"
"Function2" "Menu Function 2"
"Menu" "Menu/Pause"
"Move" "Move"
"Up" "Up"
"Down" "Down"
"Left" "Left"
@ -614,6 +620,24 @@
}
}
"group"
{
"id" "24"
"mode" "joystick_move"
"name" ""
"description" ""
"inputs"
{
}
"settings"
{
"virtual_mode" "1"
}
"gameactions"
{
"InGameControls" "Move"
}
}
"group"
{
"id" "0"
"mode" "switches"
@ -761,7 +785,8 @@
"2" "left_trigger active"
"3" "right_trigger active"
"15" "joystick inactive"
"23" "joystick active"
"23" "joystick inactive"
"24" "joystick active"
"16" "right_joystick inactive"
"22" "right_joystick active"
"14" "dpad inactive"

@ -7,6 +7,11 @@
"title" "#Set_Ingame"
"StickPadGyro"
{
"Move"
{
"title" "#Move"
"input_mode" "joystick_move"
}
"Scroll"
{
"title" "#Scroll"
@ -64,6 +69,7 @@
"Function1" "Menu Function 1"
"Function2" "Menu Function 2"
"Menu" "Menu/Pause"
"Move" "Move"
"Up" "Up"
"Down" "Down"
"Left" "Left"
@ -79,7 +85,28 @@
{
"0"
{
"path" "controller_xbox360.vdf"
"path" "controller_xboxone.vdf"
}
}
"controller_xboxone"
{
"0"
{
"path" "controller_xboxone.vdf"
}
}
"controller_ps5"
{
"0"
{
"path" "controller_xboxone.vdf"
}
}
"controller_ps4"
{
"0"
{
"path" "controller_xboxone.vdf"
}
}
"controller_generic"

@ -15,7 +15,9 @@ inline void SteamAPI_RunCallbacks(){};
#define STEAMINPUT(statement) if(false){}
#define STEAMUTILS(statement) if(false){}
#define STEAMUSERSTATS(statement) if(false){}
#else
#define STEAMINPUT(statement) if(SteamInput()){statement}
#define STEAMUTILS(statement) if(SteamUtils()){statement}
#define STEAMUSERSTATS(statement) if(SteamUserStats()){statement}
#endif

@ -4690,7 +4690,15 @@ namespace olc
{ bHasMouseFocus = state; }
void PixelGameEngine::olc_UpdateKeyFocus(bool state)
{ bHasInputFocus = state; }
{
if(!state){
for (uint32_t i = 0; i < 256; i++)
{
olc_UpdateKeyState(i,false);
}
}
bHasInputFocus = state;
}
void PixelGameEngine::olc_DropFiles(int32_t x, int32_t y, const std::vector<std::string>& vFiles)
{

@ -104,6 +104,10 @@ namespace olc::utils::Animate2D
class FrameSequence
{
public:
Style m_nStyle;
std::vector<Frame> m_vFrames;
float m_fFrameDuration = 0.1f;
float m_fFrameRate = 10.0f;
// Constructs a sequence of frames with a duration and a traversal style
inline FrameSequence(const float fFrameDuration = 0.1f, const Style nStyle = Style::Repeat)
{
@ -125,10 +129,6 @@ namespace olc::utils::Animate2D
}
private:
Style m_nStyle;
std::vector<Frame> m_vFrames;
float m_fFrameDuration = 0.1f;
float m_fFrameRate = 10.0f;
inline const size_t ConvertTimeToFrame(const float fTime) const
{
@ -170,7 +170,13 @@ namespace olc::utils::Animate2D
{
public:
Animation() = default;
float mult = 1.f;
inline bool ChangeState(AnimationState& state, const StatesEnum& sStateName, const float frameMult)
{
mult=frameMult;
return ChangeState(state,sStateName);
}
// Change an animation state token to a new state
inline bool ChangeState(AnimationState& state, const StatesEnum& sStateName) const
{
@ -188,7 +194,7 @@ namespace olc::utils::Animate2D
// Update an animation state token
inline void UpdateState(AnimationState& state, const float fElapsedTime) const
{
state.fTime = std::fmod(state.fTime+fElapsedTime,1000);
state.fTime = std::fmod(state.fTime+fElapsedTime*mult,1000);
}
public:

@ -148,6 +148,38 @@ std::u32string util::WrapText(PixelGameEngine*pge,std::u32string str,int width,F
return newStr;
}
#pragma region std::string util::GetHash(std::string fileName) //DO NOT MODIFY!
std::string util::GetHash(std::string fileName){
//WARNING! This function is used to save/load files! This means if we ever modify this function all previous save files
//will no longer work! IN OTHER WORDS: DO NOT MODIFY THIS FUNCTION!
std::ifstream file(fileName);
std::string hash="";
uint8_t hashIndex=0;
while(file.good()){
uint8_t hashChar=0;
if(hash.size()==std::numeric_limits<uint8_t>::max()){
hashChar=hash[hashIndex];
}
char newChar=file.get();
if(newChar=='\r'||newChar=='\f'||newChar=='\n')continue;
hashChar+=newChar*21-7;
hashChar^=hashIndex;
if(hashIndex>0)hashChar+=hash[hashIndex-1];
hashChar%=94;
hashChar+=32;
if(hashChar=='"'||hashChar==',')hashChar+=60;
if(hashChar=='|')hashChar++; //FORBIDDEN CHARACTER IN EMSCRIPTEN BUILD!
if(hash.size()<std::numeric_limits<uint8_t>::max()){
hash+=hashChar;
}else{
hash[hashIndex]=hashChar;
}
hashIndex++;
}
return hash;
}
#pragma endregion
long double operator""_Pixels(long double unitDist){
return unitDist/100*24.;
}

@ -58,6 +58,7 @@ namespace olc::util{
std::string WrapText(PixelGameEngine*pge,std::string str,int width,bool proportional,vd2d scale);
std::u32string WrapText(PixelGameEngine*pge,std::u32string str,int width,Font&font,vd2d scale);
float angle_difference(float angle_1, float angle_2);
std::string GetHash(std::string file);
}
template<class TL, class TR>

@ -1,6 +1,7 @@
rm -R release
mkdir release
mkdir release/assets
mkdir "release/Adventures in Lestoria"
cp -R "Adventures in Lestoria/assets/Campaigns" release/assets
cp -R "Adventures in Lestoria/assets/config" release/assets
cp -R "Adventures in Lestoria/assets/maps" release/assets
@ -13,10 +14,10 @@ cp -R "Adventures in Lestoria/assets/npcs" release/assets
cp -R "Adventures in Lestoria/assets/sounds" release/assets
cp -R "Adventures in Lestoria/assets/themes" release/assets
cp -R "Adventures in Lestoria/assets/gamepack.pak" release/assets
cp -R "Adventures in Lestoria/assets/*.ttf" release/assets
cp -R "Adventures in Lestoria/assets/"*.ttf release/assets
cp -R "Adventures in Lestoria/controller_config" release
mkdir "release/Adventures in Lestoria"
cp "Adventures in Lestoria/Adventures in Lestoria/*.so" "release/Adventures in Lestoria"
cp "Adventures in Lestoria/Adventures in Lestoria/"*.so "release/Adventures in Lestoria"
cp -R bin release
rm release/*.pdb

@ -0,0 +1,2 @@
clear
Get-Content "Adventures in Lestoria/debug.log" -Wait
Loading…
Cancel
Save