diff --git a/Adventures in Lestoria/Adventures in Lestoria.tiled-project b/Adventures in Lestoria/Adventures in Lestoria.tiled-project index 5f23812a..f4460e74 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.tiled-project +++ b/Adventures in Lestoria/Adventures in Lestoria.tiled-project @@ -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, diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 96c4fbf3..e296c594 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -206,6 +206,10 @@ + + + + @@ -519,6 +523,10 @@ + + + + @@ -816,6 +824,10 @@ + + + + @@ -844,6 +856,7 @@ + @@ -851,6 +864,7 @@ + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index 8cf54293..2aa2f719 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -998,6 +998,9 @@ Source Files + + Source Files + @@ -1009,6 +1012,7 @@ Header Files\steam + @@ -1159,6 +1163,9 @@ Configurations + + Configurations + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 81468f41..d5b2958b 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -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;iClearLoadoutItem(i); } - Unlock::unlocks.clear(); Unlock::Initialize(); State_OverworldMap::SetStageMarker("Story I"); State_OverworldMap::UpdateCurrentConnectionPoint(*State_OverworldMap::currentConnectionPoint); @@ -3885,4 +3933,8 @@ void AiL::ActivateActionSetForAllControllers(InputActionSetHandle_t actionSetHan SteamInput()->ActivateActionSet(Input::steamControllers[i],actionSetHandle); } ) +} + +const float AiL::GetEncounterDuration()const{ + return encounterDuration; } \ No newline at end of file diff --git a/Adventures in Lestoria/AdventuresInLestoria.h b/Adventures in Lestoria/AdventuresInLestoria.h index 9ee0fd58..e1ed7e99 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.h +++ b/Adventures in Lestoria/AdventuresInLestoria.h @@ -59,6 +59,7 @@ All rights reserved. class SteamKeyboardCallbackHandler; +class SteamStatsReceivedHandler; #define CreateBullet(type) BULLET_LIST.push_back(std::make_unique(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; diff --git a/Adventures in Lestoria/Animation.cpp b/Adventures in Lestoria/Animation.cpp index 5e8e7bbd..34164081 100644 --- a/Adventures in Lestoria/Animation.cpp +++ b/Adventures in Lestoria/Animation.cpp @@ -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}}}); diff --git a/Adventures in Lestoria/CharacterMenuWindow.cpp b/Adventures in Lestoria/CharacterMenuWindow.cpp index 4c5ca8d6..e26707e3 100644 --- a/Adventures in Lestoria/CharacterMenuWindow.cpp +++ b/Adventures in Lestoria/CharacterMenuWindow.cpp @@ -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); } diff --git a/Adventures in Lestoria/Class.h b/Adventures in Lestoria/Class.h index 39f059aa..680ee6e1 100644 --- a/Adventures in Lestoria/Class.h +++ b/Adventures in Lestoria/Class.h @@ -36,12 +36,15 @@ All rights reserved. */ #pragma endregion #pragma once -#include "Ability.h" -#include "Animation.h" +#include +#include +#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&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<&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<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(); @@ -923,6 +928,33 @@ void Item::EnhanceItem(uint8_t qty){ if(enhancementLevel+1>"Item.Item Max Enhancement Level"_I)ERR("WARNING! Attempted to enhance "<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; @@ -1284,4 +1316,25 @@ 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_ptrequip=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)]; } \ No newline at end of file diff --git a/Adventures in Lestoria/Item.h b/Adventures in Lestoria/Item.h index a6ee1974..66673a37 100644 --- a/Adventures in Lestoria/Item.h +++ b/Adventures in Lestoria/Item.h @@ -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. */ diff --git a/Adventures in Lestoria/LevelCompleteWindow.cpp b/Adventures in Lestoria/LevelCompleteWindow.cpp index 36707f03..ae987e50 100644 --- a/Adventures in Lestoria/LevelCompleteWindow.cpp +++ b/Adventures in Lestoria/LevelCompleteWindow.cpp @@ -79,8 +79,10 @@ void Menu::InitializeLevelCompleteWindow(){ }; auto nextButtonAction=[](MenuFuncData data){ - Unlock::UnlockArea(State_OverworldMap::GetCurrentConnectionPoint().map); - Merchant::RandomizeTravelingMerchant(); + if(Component(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; diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index a008269b..3c63d982 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -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(strategyDeathFunc,*this,MONSTER_DATA[name].GetAIStrategy())); } diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 98170254..3e6cb36f 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -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()=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()-1SetAchievement(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::max(),std::numeric_limits::max()}; for(Monster&m:MONSTER_LIST){ if(m.IsAlive()){ - float distToMonster=geom2d::line(GetPos(),m.GetPos()).length(); geom2d::lineaimingLine=geom2d::line(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(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::max(),std::numeric_limits::max()}){ - return game->GetScreenSize()/2+closestPoint-GetPos(); + geom2d::lineaimingLine=geom2d::line(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(); } diff --git a/Adventures in Lestoria/Player.h b/Adventures in Lestoria/Player.h index 6b2b008e..734fc402 100644 --- a/Adventures in Lestoria/Player.h +++ b/Adventures in Lestoria/Player.h @@ -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(); diff --git a/Adventures in Lestoria/SaveFile.cpp b/Adventures in Lestoria/SaveFile.cpp index a21d5785..708477fd 100644 --- a/Adventures in Lestoria/SaveFile.cpp +++ b/Adventures in Lestoria/SaveFile.cpp @@ -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_ptrnewItem=Inventory::AddItem(data["Item Name"].GetString(),data["Amt"].GetInt()); diff --git a/Adventures in Lestoria/SettingsWindow.cpp b/Adventures in Lestoria/SettingsWindow.cpp index 9911b0c8..428a4efb 100644 --- a/Adventures in Lestoria/SettingsWindow.cpp +++ b/Adventures in Lestoria/SettingsWindow.cpp @@ -249,7 +249,7 @@ void Menu::InitializeSettingsWindow(){ {game->KEY_CONFIRM,{"Select",[](MenuType type){}}}, } ,{ //Button Navigation Rules - {"BGM Slider",{ + {"BGM Slider",{ .up="Go Back", .down="SFX Slider",}}, {"SFX Slider",{ @@ -260,7 +260,7 @@ void Menu::InitializeSettingsWindow(){ .down="Screen Shake Checkbox", .left="Show Max Mana Checkbox", .right="Show Max Mana Checkbox",}}, - {"Show Max Mana Checkbox",{ + {"Show Max Mana Checkbox",{ .up="SFX Slider", .down="Terrain Collision Boxes Checkbox", .left="Show Max HP Checkbox", diff --git a/Adventures in Lestoria/ShermanWindow.cpp b/Adventures in Lestoria/ShermanWindow.cpp index f7425b78..48117038 100644 --- a/Adventures in Lestoria/ShermanWindow.cpp +++ b/Adventures in Lestoria/ShermanWindow.cpp @@ -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{{0.f,4.f},{144.f,24.f}},"Leave",[](MenuFuncData data){ - Unlock::UnlockCurrentMap(); + if(Component(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; diff --git a/Adventures in Lestoria/State_GameHub.cpp b/Adventures in Lestoria/State_GameHub.cpp index f1219ed5..b64029b6 100644 --- a/Adventures in Lestoria/State_GameHub.cpp +++ b/Adventures in Lestoria/State_GameHub.cpp @@ -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){ diff --git a/Adventures in Lestoria/State_MainMenu.cpp b/Adventures in Lestoria/State_MainMenu.cpp index 0fe0480e..1005bff9 100644 --- a/Adventures in Lestoria/State_MainMenu.cpp +++ b/Adventures in Lestoria/State_MainMenu.cpp @@ -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 diff --git a/Adventures in Lestoria/SteamStatsReceivedHandler.cpp b/Adventures in Lestoria/SteamStatsReceivedHandler.cpp new file mode 100644 index 00000000..e2a55c30 --- /dev/null +++ b/Adventures in Lestoria/SteamStatsReceivedHandler.cpp @@ -0,0 +1,54 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +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 \ No newline at end of file diff --git a/Adventures in Lestoria/SteamStatsReceivedHandler.h b/Adventures in Lestoria/SteamStatsReceivedHandler.h new file mode 100644 index 00000000..f5f7f9ee --- /dev/null +++ b/Adventures in Lestoria/SteamStatsReceivedHandler.h @@ -0,0 +1,47 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +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 \ No newline at end of file diff --git a/Adventures in Lestoria/TMXParser.h b/Adventures in Lestoria/TMXParser.h index cae908d5..ccc590ef 100644 --- a/Adventures in Lestoria/TMXParser.h +++ b/Adventures in Lestoria/TMXParser.h @@ -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::vectornpcs; std::string mapType=""; std::string bgmSongName=""; + std::unordered_mapdevCompletionTrialTime; BackdropName backdrop=""; std::setspawns; std::map SpawnerData; //Spawn groups have IDs, mobs associate which spawner they are tied to via this ID. @@ -138,6 +141,7 @@ public: const std::vector&GetStageLoot()const; const std::vector&GetLayers()const; const std::vector&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::vectortiles); @@ -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"]; diff --git a/Adventures in Lestoria/TODO.txt b/Adventures in Lestoria/TODO.txt index 2be30110..c8c3e0d0 100644 --- a/Adventures in Lestoria/TODO.txt +++ b/Adventures in Lestoria/TODO.txt @@ -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) \ No newline at end of file diff --git a/Adventures in Lestoria/Unlock.cpp b/Adventures in Lestoria/Unlock.cpp index eeae723c..cd5a550e 100644 --- a/Adventures in Lestoria/Unlock.cpp +++ b/Adventures in Lestoria/Unlock.cpp @@ -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::setUnlock::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){ @@ -60,4 +77,21 @@ 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-1StoreStats(); + } + } + } + ) } \ No newline at end of file diff --git a/Adventures in Lestoria/Unlock.h b/Adventures in Lestoria/Unlock.h index ced8fafb..0f4f8fee 100644 --- a/Adventures in Lestoria/Unlock.h +++ b/Adventures in Lestoria/Unlock.h @@ -43,8 +43,10 @@ All rights reserved. class Unlock{ friend class AiL; friend class SaveFile; + friend class SteamStatsReceivedHandler; static std::setunlocks; 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(); }; \ No newline at end of file diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index eeaeb521..745b8d2d 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -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 diff --git a/Adventures in Lestoria/assets/Achievements/BronzeBorder.xcf b/Adventures in Lestoria/assets/Achievements/BronzeBorder.xcf new file mode 100644 index 00000000..9a79ef04 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/BronzeBorder.xcf differ diff --git a/Adventures in Lestoria/assets/Achievements/GoldBorder.xcf b/Adventures in Lestoria/assets/Achievements/GoldBorder.xcf new file mode 100644 index 00000000..6a318620 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/GoldBorder.xcf differ diff --git a/Adventures in Lestoria/assets/Achievements/SilverBorder.xcf b/Adventures in Lestoria/assets/Achievements/SilverBorder.xcf new file mode 100644 index 00000000..2bc187b5 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/SilverBorder.xcf differ diff --git a/Adventures in Lestoria/assets/Achievements/armor_10.png b/Adventures in Lestoria/assets/Achievements/armor_10.png new file mode 100644 index 00000000..198e35f6 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/armor_10.png differ diff --git a/Adventures in Lestoria/assets/Achievements/armor_10_locked.png b/Adventures in Lestoria/assets/Achievements/armor_10_locked.png new file mode 100644 index 00000000..4c6c7554 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/armor_10_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/armor_5.png b/Adventures in Lestoria/assets/Achievements/armor_5.png new file mode 100644 index 00000000..d38de0a7 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/armor_5.png differ diff --git a/Adventures in Lestoria/assets/Achievements/armor_5_locked.png b/Adventures in Lestoria/assets/Achievements/armor_5_locked.png new file mode 100644 index 00000000..dd045478 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/armor_5_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/blacksmith_unlock.png b/Adventures in Lestoria/assets/Achievements/blacksmith_unlock.png new file mode 100644 index 00000000..d7c02891 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/blacksmith_unlock.png differ diff --git a/Adventures in Lestoria/assets/Achievements/blacksmith_unlock_locked.png b/Adventures in Lestoria/assets/Achievements/blacksmith_unlock_locked.png new file mode 100644 index 00000000..6a28f3cc Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/blacksmith_unlock_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/camp_unlock.png b/Adventures in Lestoria/assets/Achievements/camp_unlock.png new file mode 100644 index 00000000..37621091 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/camp_unlock.png differ diff --git a/Adventures in Lestoria/assets/Achievements/camp_unlock_locked.png b/Adventures in Lestoria/assets/Achievements/camp_unlock_locked.png new file mode 100644 index 00000000..af5a5f77 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/camp_unlock_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/chapter1_complete.png b/Adventures in Lestoria/assets/Achievements/chapter1_complete.png new file mode 100644 index 00000000..c8a5a5d9 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/chapter1_complete.png differ diff --git a/Adventures in Lestoria/assets/Achievements/chapter1_complete_locked.png b/Adventures in Lestoria/assets/Achievements/chapter1_complete_locked.png new file mode 100644 index 00000000..8d0fa7fb Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/chapter1_complete_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/fully_decked_out.png b/Adventures in Lestoria/assets/Achievements/fully_decked_out.png new file mode 100644 index 00000000..d4ffdb78 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/fully_decked_out.png differ diff --git a/Adventures in Lestoria/assets/Achievements/fully_decked_out_locked.png b/Adventures in Lestoria/assets/Achievements/fully_decked_out_locked.png new file mode 100644 index 00000000..d5c1f33b Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/fully_decked_out_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/kill_slime_1.png b/Adventures in Lestoria/assets/Achievements/kill_slime_1.png new file mode 100644 index 00000000..21d1de3d Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/kill_slime_1.png differ diff --git a/Adventures in Lestoria/assets/Achievements/kill_slime_1_locked.png b/Adventures in Lestoria/assets/Achievements/kill_slime_1_locked.png new file mode 100644 index 00000000..05c0366c Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/kill_slime_1_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/kill_slime_2.png b/Adventures in Lestoria/assets/Achievements/kill_slime_2.png new file mode 100644 index 00000000..b1fdc373 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/kill_slime_2.png differ diff --git a/Adventures in Lestoria/assets/Achievements/kill_slime_2_locked.png b/Adventures in Lestoria/assets/Achievements/kill_slime_2_locked.png new file mode 100644 index 00000000..e75156b5 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/kill_slime_2_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/kill_slime_3.png b/Adventures in Lestoria/assets/Achievements/kill_slime_3.png new file mode 100644 index 00000000..2a4d68b7 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/kill_slime_3.png differ diff --git a/Adventures in Lestoria/assets/Achievements/kill_slime_3_locked.png b/Adventures in Lestoria/assets/Achievements/kill_slime_3_locked.png new file mode 100644 index 00000000..806513c5 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/kill_slime_3_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/ranger_level5.png b/Adventures in Lestoria/assets/Achievements/ranger_level5.png new file mode 100644 index 00000000..dc632a51 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/ranger_level5.png differ diff --git a/Adventures in Lestoria/assets/Achievements/ranger_level5_locked.png b/Adventures in Lestoria/assets/Achievements/ranger_level5_locked.png new file mode 100644 index 00000000..bc23197b Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/ranger_level5_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/slime_king.png b/Adventures in Lestoria/assets/Achievements/slime_king.png new file mode 100644 index 00000000..3f8a277b Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/slime_king.png differ diff --git a/Adventures in Lestoria/assets/Achievements/slime_king_destroyer.png b/Adventures in Lestoria/assets/Achievements/slime_king_destroyer.png new file mode 100644 index 00000000..b9d1ac43 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/slime_king_destroyer.png differ diff --git a/Adventures in Lestoria/assets/Achievements/slime_king_destroyer_locked.png b/Adventures in Lestoria/assets/Achievements/slime_king_destroyer_locked.png new file mode 100644 index 00000000..613e9763 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/slime_king_destroyer_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/slime_king_locked.png b/Adventures in Lestoria/assets/Achievements/slime_king_locked.png new file mode 100644 index 00000000..a9e24e13 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/slime_king_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock.png b/Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock.png new file mode 100644 index 00000000..9e6ad733 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock.png differ diff --git a/Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock_locked.png b/Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock_locked.png new file mode 100644 index 00000000..7fce6f7f Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/ursule_destroyer_unlock_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/ursule_unlock.png b/Adventures in Lestoria/assets/Achievements/ursule_unlock.png new file mode 100644 index 00000000..2a3c863e Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/ursule_unlock.png differ diff --git a/Adventures in Lestoria/assets/Achievements/ursule_unlock_locked.png b/Adventures in Lestoria/assets/Achievements/ursule_unlock_locked.png new file mode 100644 index 00000000..00a95bd5 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/ursule_unlock_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/warrior_level5.png b/Adventures in Lestoria/assets/Achievements/warrior_level5.png new file mode 100644 index 00000000..c808f4da Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/warrior_level5.png differ diff --git a/Adventures in Lestoria/assets/Achievements/warrior_level5_locked.png b/Adventures in Lestoria/assets/Achievements/warrior_level5_locked.png new file mode 100644 index 00000000..46f55fe6 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/warrior_level5_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/weapon_5.png b/Adventures in Lestoria/assets/Achievements/weapon_5.png new file mode 100644 index 00000000..981decdd Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/weapon_5.png differ diff --git a/Adventures in Lestoria/assets/Achievements/weapon_5_locked.png b/Adventures in Lestoria/assets/Achievements/weapon_5_locked.png new file mode 100644 index 00000000..ae832ab3 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/weapon_5_locked.png differ diff --git a/Adventures in Lestoria/assets/Achievements/wizard_level5.png b/Adventures in Lestoria/assets/Achievements/wizard_level5.png new file mode 100644 index 00000000..19cbe917 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/wizard_level5.png differ diff --git a/Adventures in Lestoria/assets/Achievements/wizard_level5_locked.png b/Adventures in Lestoria/assets/Achievements/wizard_level5_locked.png new file mode 100644 index 00000000..a8ec4ff3 Binary files /dev/null and b/Adventures in Lestoria/assets/Achievements/wizard_level5_locked.png differ diff --git a/Adventures in Lestoria/assets/AdventuresInLestoria_DemoAvailable.png b/Adventures in Lestoria/assets/AdventuresInLestoria_DemoAvailable.png new file mode 100644 index 00000000..511d6f49 Binary files /dev/null and b/Adventures in Lestoria/assets/AdventuresInLestoria_DemoAvailable.png differ diff --git a/Adventures in Lestoria/assets/Campaigns/1_1_v2.tmx b/Adventures in Lestoria/assets/Campaigns/1_1_v2.tmx index 6ba387e0..b77c725f 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_1_v2.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_1_v2.tmx @@ -1,8 +1,11 @@ - + + + + @@ -2009,5 +2012,6 @@ + diff --git a/Adventures in Lestoria/assets/Campaigns/1_4.tmx b/Adventures in Lestoria/assets/Campaigns/1_4.tmx index af8b37a3..5282a81f 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_4.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_4.tmx @@ -1,5 +1,5 @@ - + @@ -845,5 +845,6 @@ + diff --git a/Adventures in Lestoria/assets/Campaigns/1_5.tmx b/Adventures in Lestoria/assets/Campaigns/1_5.tmx index ce11fa55..0302e55a 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_5.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_5.tmx @@ -1,5 +1,5 @@ - + @@ -1551,6 +1551,7 @@ + diff --git a/Adventures in Lestoria/assets/Campaigns/1_6.tmx b/Adventures in Lestoria/assets/Campaigns/1_6.tmx index fbacfa68..7f60a95c 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_6.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_6.tmx @@ -1,5 +1,5 @@ - + @@ -1154,5 +1154,6 @@ + diff --git a/Adventures in Lestoria/assets/Campaigns/1_7.tmx b/Adventures in Lestoria/assets/Campaigns/1_7.tmx index e7612f63..d4ff3cde 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_7.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_7.tmx @@ -1,5 +1,5 @@ - + @@ -1118,6 +1118,7 @@ + diff --git a/Adventures in Lestoria/assets/Campaigns/1_8.tmx b/Adventures in Lestoria/assets/Campaigns/1_8.tmx index 162e3fb9..5fc6e73a 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_8.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_8.tmx @@ -1,5 +1,5 @@ - + @@ -893,5 +893,6 @@ + diff --git a/Adventures in Lestoria/assets/Campaigns/1_B1.tmx b/Adventures in Lestoria/assets/Campaigns/1_B1.tmx index 323641bd..fee0cfbf 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_B1.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_B1.tmx @@ -1,5 +1,5 @@ - + @@ -796,5 +796,6 @@ + diff --git a/Adventures in Lestoria/assets/Campaigns/Boss_1_v2.tmx b/Adventures in Lestoria/assets/Campaigns/Boss_1_v2.tmx index f4c4551b..22501419 100644 --- a/Adventures in Lestoria/assets/Campaigns/Boss_1_v2.tmx +++ b/Adventures in Lestoria/assets/Campaigns/Boss_1_v2.tmx @@ -1,5 +1,5 @@ - + diff --git a/Adventures in Lestoria/assets/config/Achievements.txt b/Adventures in Lestoria/assets/config/Achievements.txt new file mode 100644 index 00000000..34451645 --- /dev/null +++ b/Adventures in Lestoria/assets/config/Achievements.txt @@ -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 + } + } +} \ No newline at end of file diff --git a/Adventures in Lestoria/assets/config/Player.txt b/Adventures in Lestoria/assets/config/Player.txt index 87d4f3b1..36975ab5 100644 --- a/Adventures in Lestoria/assets/config/Player.txt +++ b/Adventures in Lestoria/assets/config/Player.txt @@ -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 diff --git a/Adventures in Lestoria/assets/config/configuration.txt b/Adventures in Lestoria/assets/config/configuration.txt index fed894ee..13ed7564 100644 --- a/Adventures in Lestoria/assets/config/configuration.txt +++ b/Adventures in Lestoria/assets/config/configuration.txt @@ -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 diff --git a/Adventures in Lestoria/assets/config/items/ItemDatabase.txt b/Adventures in Lestoria/assets/config/items/ItemDatabase.txt index 3407ef7d..600b1570 100644 --- a/Adventures in Lestoria/assets/config/items/ItemDatabase.txt +++ b/Adventures in Lestoria/assets/config/items/ItemDatabase.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 + } } diff --git a/Adventures in Lestoria/assets/gamepack.pak b/Adventures in Lestoria/assets/gamepack.pak index 79f199fc..32c4edd2 100644 Binary files a/Adventures in Lestoria/assets/gamepack.pak and b/Adventures in Lestoria/assets/gamepack.pak differ diff --git a/Adventures in Lestoria/assets/items/Time Medal.png b/Adventures in Lestoria/assets/items/Time Medal.png new file mode 100644 index 00000000..d66ccdbc Binary files /dev/null and b/Adventures in Lestoria/assets/items/Time Medal.png differ diff --git a/Adventures in Lestoria/controller_config/controller_generic.vdf b/Adventures in Lestoria/controller_config/controller_generic.vdf index 0ee57bea..f4788879 100644 --- a/Adventures in Lestoria/controller_config/controller_generic.vdf +++ b/Adventures in Lestoria/controller_config/controller_generic.vdf @@ -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" diff --git a/Adventures in Lestoria/controller_config/controller_switch_pro.vdf b/Adventures in Lestoria/controller_config/controller_switch_pro.vdf index 8602073d..1f9fa0c5 100644 --- a/Adventures in Lestoria/controller_config/controller_switch_pro.vdf +++ b/Adventures in Lestoria/controller_config/controller_switch_pro.vdf @@ -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" diff --git a/Adventures in Lestoria/controller_config/controller_xbox360.vdf b/Adventures in Lestoria/controller_config/controller_xboxone.vdf similarity index 94% rename from Adventures in Lestoria/controller_config/controller_xbox360.vdf rename to Adventures in Lestoria/controller_config/controller_xboxone.vdf index 35b8aba3..c8d3217d 100644 --- a/Adventures in Lestoria/controller_config/controller_xbox360.vdf +++ b/Adventures in Lestoria/controller_config/controller_xboxone.vdf @@ -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" diff --git a/Adventures in Lestoria/controller_config/game_actions_2895980.vdf b/Adventures in Lestoria/controller_config/game_actions_2895980.vdf index 5aba1291..50f1a57d 100644 --- a/Adventures in Lestoria/controller_config/game_actions_2895980.vdf +++ b/Adventures in Lestoria/controller_config/game_actions_2895980.vdf @@ -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" diff --git a/Adventures in Lestoria/emscripten_compat.h b/Adventures in Lestoria/emscripten_compat.h index 78dd0604..e0b20641 100644 --- a/Adventures in Lestoria/emscripten_compat.h +++ b/Adventures in Lestoria/emscripten_compat.h @@ -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 diff --git a/Adventures in Lestoria/olcPixelGameEngine.h b/Adventures in Lestoria/olcPixelGameEngine.h index b7db8bfb..7647076f 100644 --- a/Adventures in Lestoria/olcPixelGameEngine.h +++ b/Adventures in Lestoria/olcPixelGameEngine.h @@ -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& vFiles) { diff --git a/Adventures in Lestoria/olcUTIL_Animate2D.h b/Adventures in Lestoria/olcUTIL_Animate2D.h index a4438eb7..60df9bb8 100644 --- a/Adventures in Lestoria/olcUTIL_Animate2D.h +++ b/Adventures in Lestoria/olcUTIL_Animate2D.h @@ -104,6 +104,10 @@ namespace olc::utils::Animate2D class FrameSequence { public: + Style m_nStyle; + std::vector 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 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: diff --git a/Adventures in Lestoria/olcUTIL_DataFile.h b/Adventures in Lestoria/olcUTIL_DataFile.h index 89f3a0cb..d80c2af1 100644 --- a/Adventures in Lestoria/olcUTIL_DataFile.h +++ b/Adventures in Lestoria/olcUTIL_DataFile.h @@ -269,7 +269,7 @@ namespace olc::utils } nItems--; } - + // Property written, move to next line file << "\n"; } diff --git a/Adventures in Lestoria/util.cpp b/Adventures in Lestoria/util.cpp index 7098f5d9..e2879cb8 100644 --- a/Adventures in Lestoria/util.cpp +++ b/Adventures in Lestoria/util.cpp @@ -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::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()::max()){ + hash+=hashChar; + }else{ + hash[hashIndex]=hashChar; + } + hashIndex++; + } + return hash; +} +#pragma endregion + long double operator""_Pixels(long double unitDist){ return unitDist/100*24.; } diff --git a/Adventures in Lestoria/util.h b/Adventures in Lestoria/util.h index 71ad4225..f718e47b 100644 --- a/Adventures in Lestoria/util.h +++ b/Adventures in Lestoria/util.h @@ -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 diff --git a/distribute.sh b/distribute.sh index 6f9ca992..f68544aa 100755 --- a/distribute.sh +++ b/distribute.sh @@ -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 diff --git a/read_debug_log.ps1 b/read_debug_log.ps1 new file mode 100644 index 00000000..efef15ae --- /dev/null +++ b/read_debug_log.ps1 @@ -0,0 +1,2 @@ +clear +Get-Content "Adventures in Lestoria/debug.log" -Wait \ No newline at end of file diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 0326ce79..d631d9bb 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ