Saving and loading saves items in proper sorted order. Fixed bug with get inventory slot function. Connection point data is now static.

pull/28/head
sigonasr2 11 months ago
parent 0d6ab94fda
commit ad1e2260cf
  1. 1
      .gitignore
  2. 2
      Crawler/ConnectionPoint.h
  3. 6
      Crawler/Crawler.cpp
  4. 8
      Crawler/Crawler.vcxproj
  5. 6
      Crawler/Crawler.vcxproj.filters
  6. 27
      Crawler/Item.cpp
  7. 64
      Crawler/LoadFileButton.h
  8. 46
      Crawler/LoadGameWindow.cpp
  9. 12
      Crawler/MainMenuWindow.cpp
  10. 1
      Crawler/Menu.cpp
  11. 2
      Crawler/Menu.h
  12. 4
      Crawler/MenuComponent.cpp
  13. 1
      Crawler/MenuComponent.h
  14. 6
      Crawler/OverworldMenuWindow.cpp
  15. 82
      Crawler/SaveFile.cpp
  16. 2
      Crawler/SaveFile.h
  17. 6
      Crawler/ScrollableWindowComponent.h
  18. 17
      Crawler/State_OverworldMap.cpp
  19. 5
      Crawler/State_OverworldMap.h
  20. 10
      Crawler/TODO.txt
  21. 2
      Crawler/Version.h
  22. 354
      Crawler/assets/saves/save.0000
  23. 5
      Crawler/olcPixelGameEngine.h
  24. 8
      Crawler/olcUTIL_DataFile.h

1
.gitignore vendored

@ -28,6 +28,7 @@ memoryleak.txt
[Dd]ebugPublic/ [Dd]ebugPublic/
*.tlog/ *.tlog/
Crawler/x64/ Crawler/x64/
Crawler/assets/saves/
[Rr]eleases/ [Rr]eleases/
x86/ x86/
[Ww][Ii][Nn]32/ [Ww][Ii][Nn]32/

@ -39,6 +39,7 @@ All rights reserved.
#include "olcUTIL_Geometry2D.h" #include "olcUTIL_Geometry2D.h"
struct ConnectionPoint{ struct ConnectionPoint{
friend class State_OverworldMap;
geom2d::rect<float>rect; geom2d::rect<float>rect;
std::string type; std::string type;
std::string name; std::string name;
@ -52,4 +53,5 @@ struct ConnectionPoint{
neighbors.fill(-1); neighbors.fill(-1);
} }
bool IsNeighbor(ConnectionPoint&testPoint); bool IsNeighbor(ConnectionPoint&testPoint);
static void UpdateCurrentConnectionPoint(const ConnectionPoint&connection);
}; };

@ -2540,7 +2540,7 @@ void Crawler::InitializePlayerLevelCap(){
} }
void Crawler::ResetGame(){ void Crawler::ResetGame(){
GameState::ChangeState(States::OVERWORLD_MAP,0.5f); GameState::ChangeState(States::MAIN_MENU,0.5f);
for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){ for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){
Inventory::UnequipItem(EquipSlot(i)); Inventory::UnequipItem(EquipSlot(i));
} }
@ -2551,9 +2551,11 @@ void Crawler::ResetGame(){
player->stats.Reset(); player->stats.Reset();
player->ResetAccumulatedXP(); player->ResetAccumulatedXP();
player->totalXPEarned=0; player->totalXPEarned=0;
player->SetMoney(100U);
Unlock::unlocks.clear(); Unlock::unlocks.clear();
Unlock::Initialize(); Unlock::Initialize();
DYNAMIC_CAST<State_OverworldMap*>(GameState::STATE)->SetStageMarker("Stage I-I"); State_OverworldMap::SetStageMarker("Stage I-I");
State_OverworldMap::UpdateCurrentConnectionPoint(*State_OverworldMap::currentConnectionPoint);
SetChapter(1); SetChapter(1);
SaveFile::SetSaveFileName(""); SaveFile::SetSaveFileName("");
} }

@ -355,6 +355,10 @@
<SubType> <SubType>
</SubType> </SubType>
</ClInclude> </ClInclude>
<ClInclude Include="LoadFileButton.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="PlayerMoneyLabel.h"> <ClInclude Include="PlayerMoneyLabel.h">
<SubType> <SubType>
</SubType> </SubType>
@ -535,6 +539,10 @@
<ClCompile Include="LevelCompleteWindow.cpp" /> <ClCompile Include="LevelCompleteWindow.cpp" />
<ClCompile Include="LightningBolt.cpp" /> <ClCompile Include="LightningBolt.cpp" />
<ClCompile Include="LightningBoltEmitter.cpp" /> <ClCompile Include="LightningBoltEmitter.cpp" />
<ClCompile Include="LoadGameWindow.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="MainMenuWindow.cpp" /> <ClCompile Include="MainMenuWindow.cpp" />
<ClCompile Include="Map.cpp" /> <ClCompile Include="Map.cpp" />
<ClCompile Include="Menu.cpp" /> <ClCompile Include="Menu.cpp" />

@ -396,6 +396,9 @@
<ClInclude Include="SaveFile.h"> <ClInclude Include="SaveFile.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="LoadFileButton.h">
<Filter>Header Files\Interface</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Player.cpp"> <ClCompile Include="Player.cpp">
@ -665,6 +668,9 @@
<ClCompile Include="SaveFile.cpp"> <ClCompile Include="SaveFile.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="LoadGameWindow.cpp">
<Filter>Source Files\Interface</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="cpp.hint" /> <None Include="cpp.hint" />

@ -488,7 +488,14 @@ bool Inventory::RemoveItem(std::weak_ptr<Item>itemRef,ITCategory inventory,uint3
return true; return true;
}else{ }else{
if(itemRef.lock()->IsEquippable()){ //Since equipment doesn't stack, if we have more than one piece we have to still remove that piece. if(itemRef.lock()->IsEquippable()){ //Since equipment doesn't stack, if we have more than one piece we have to still remove that piece.
size_t erased=std::erase_if(_inventory,[&](const std::pair<const IT,std::shared_ptr<Item>>data){return data.second==itemRef;}); bool found=false;
size_t erased=std::erase_if(_inventory,[&](const std::pair<const IT,std::shared_ptr<Item>>data){
if(!found&&data.second==itemRef){
found=true;
return true;
}
return false;
});
if(erased!=1)ERR(std::format("Did not erase a single element, instead erased {} elements.",erased)); if(erased!=1)ERR(std::format("Did not erase a single element, instead erased {} elements.",erased));
inv.erase(inv.begin()+count); //Clears it from the detected sorted inventory as well! inv.erase(inv.begin()+count); //Clears it from the detected sorted inventory as well!
Menu::InventorySlotsUpdated(inventory); Menu::InventorySlotsUpdated(inventory);
@ -664,11 +671,7 @@ const bool Item::IsBlank()const{
void Inventory::Clear(ITCategory itemCategory){ void Inventory::Clear(ITCategory itemCategory){
std::vector<std::shared_ptr<Item>>itemList=get(itemCategory); //We have to make a copy here because RemoveItem() will modify the list provided by get() inline. std::vector<std::shared_ptr<Item>>itemList=get(itemCategory); //We have to make a copy here because RemoveItem() will modify the list provided by get() inline.
for(std::shared_ptr<Item>&item:itemList){ for(std::shared_ptr<Item>&item:itemList){
size_t itemQuantity=GetItemCount(item->ActualName());//Normally we want to clear all the items that are actually in our inventory...But... RemoveItem(item,itemCategory,item->Amt());
if(itemCategory=="Monster Loot"||itemCategory=="Stage Loot"){//These do not affect the actual inventory, we just clear the lists.
itemQuantity=item->Amt();
}
RemoveItem(item,itemCategory,uint32_t(itemQuantity));
} }
} }
@ -930,17 +933,7 @@ void Item::SetAmt(uint32_t newAmt){
} }
const std::weak_ptr<Item>Inventory::GetInventorySlot(ITCategory itemCategory,size_t slot){ const std::weak_ptr<Item>Inventory::GetInventorySlot(ITCategory itemCategory,size_t slot){
auto GetFirstIndex = [](ITCategory itemCategory,size_t slot){ return get(itemCategory).at(slot);
auto firstIter=std::find_if(get(itemCategory).begin(),get(itemCategory).end(),[&](std::shared_ptr<Item>item){
if(item->ActualName()==get(itemCategory).at(slot)->ActualName())return true;
return false;
});
size_t firstIndex=firstIter-get(itemCategory).begin();
if(firstIter==get(itemCategory).end())ERR(std::format("Invalid slot {} for category {} specified!",slot,itemCategory));
return firstIndex;
};
return GetItem(get(itemCategory).at(slot)->ActualName())[slot-GetFirstIndex(itemCategory,slot)];
} }
bool Item::IsBlank(std::shared_ptr<Item>item){ bool Item::IsBlank(std::shared_ptr<Item>item){

@ -0,0 +1,64 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018 - 2022 OneLoneCoder.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 © 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
#include "MenuComponent.h"
class LoadFileButton:public MenuComponent{
double playTime;
int chapter;
int level;
std::string className;
std::string saveFileName;
int saveFileID;
public:
inline LoadFileButton(geom2d::rect<float>rect,const utils::datafile&metadata,const int saveFileID,MenuFunc onClick,ButtonAttr attributes)
:MenuComponent(rect,"",onClick,attributes),playTime(metadata.GetReal(0U)),chapter(metadata.GetInt(1U)),level(metadata.GetInt(2U)),className(metadata.GetString(3U)),saveFileName(metadata.GetString(4U)),saveFileID(saveFileID){
showDefaultLabel=false;
}
inline void DrawDecal(ViewPort&window,bool focused){
MenuComponent::DrawDecal(window,focused);
}
inline const int&getSaveFileID()const{
return saveFileID;
}
};

@ -0,0 +1,46 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018 - 2022 OneLoneCoder.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 © 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "Menu.h"
#include "ScrollableWindowComponent.h"
void Menu::InitializeLoadGameWindow(){
Menu*loadGameWindow=CreateMenu(LOAD_GAME,CENTERED,vi2d{96,96});
loadGameWindow->ADD("Game Files List",ScrollableWindowComponent)({{-8,0},{112,104}})END;
}

@ -39,6 +39,7 @@ All rights reserved.
#include "DEFINES.h" #include "DEFINES.h"
#include "Menu.h" #include "Menu.h"
#include "MenuComponent.h" #include "MenuComponent.h"
#include "SaveFile.h"
INCLUDE_game INCLUDE_game
using A=Attribute; using A=Attribute;
@ -51,6 +52,13 @@ void Menu::InitializeMainMenuWindow(){
Menu::OpenMenu(SAVE_FILE_NAME); Menu::OpenMenu(SAVE_FILE_NAME);
return true; return true;
})END; })END;
mainMenuWindow->ADD("Load Game Button",MenuComponent)({{12,36},{72,24}},"Load Game",DO_NOTHING)END; mainMenuWindow->ADD("Load Game Button",MenuComponent)({{12,36},{72,24}},"Load Game",[](MenuFuncData data){
mainMenuWindow->ADD("Quit Game Button",MenuComponent)({{12,68},{72,24}},"Quit Game",DO_NOTHING)END; SaveFile::UpdateSaveGameData();
Menu::OpenMenu(LOAD_GAME);
return true;
})END;
mainMenuWindow->ADD("Quit Game Button",MenuComponent)({{12,68},{72,24}},"Quit Game",[](MenuFuncData data){
game->EndGame();
return true;
})END;
} }

@ -112,6 +112,7 @@ void Menu::InitializeMenus(){
InitializeConsumableCraftingWindow(); InitializeConsumableCraftingWindow();
InitializeConsumableCraftItemWindow(); InitializeConsumableCraftItemWindow();
InitializeSaveFileWindow(); InitializeSaveFileWindow();
InitializeLoadGameWindow();
for(MenuType type=MenuType(int(MenuType::ENUM_START)+1);type<MenuType::ENUM_END;type=MenuType(int(type+1))){ for(MenuType type=MenuType(int(MenuType::ENUM_START)+1);type<MenuType::ENUM_END;type=MenuType(int(type+1))){
if(menus.count(type)==0){ if(menus.count(type)==0){

@ -80,6 +80,7 @@ enum MenuType{
CRAFT_CONSUMABLE, CRAFT_CONSUMABLE,
CONSUMABLE_CRAFT_ITEM, CONSUMABLE_CRAFT_ITEM,
SAVE_FILE_NAME, SAVE_FILE_NAME,
LOAD_GAME,
#pragma region Enum End //DO NOT REMOVE #pragma region Enum End //DO NOT REMOVE
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
/*DO NOT REMOVE!!*/ENUM_END//////////////////////////////// /*DO NOT REMOVE!!*/ENUM_END////////////////////////////////
@ -106,6 +107,7 @@ class Menu:public IAttributable{
static void InitializeConsumableCraftingWindow(); static void InitializeConsumableCraftingWindow();
static void InitializeConsumableCraftItemWindow(); static void InitializeConsumableCraftItemWindow();
static void InitializeSaveFileWindow(); static void InitializeSaveFileWindow();
static void InitializeLoadGameWindow();
friend class Crawler; friend class Crawler;
friend struct Player; friend struct Player;

@ -191,6 +191,10 @@ vf2d MenuComponent::GetPos(){
return rect.pos; return rect.pos;
} }
const vf2d&MenuComponent::GetSize()const{
return rect.size;
}
std::string MenuComponent::GetLabel(){ std::string MenuComponent::GetLabel(){
return label; return label;
} }

@ -132,6 +132,7 @@ public:
MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,vf2d labelScaling,ButtonAttr attributes=ButtonAttr::NONE); MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,vf2d labelScaling,ButtonAttr attributes=ButtonAttr::NONE);
virtual ~MenuComponent(); virtual ~MenuComponent();
vf2d GetPos(); vf2d GetPos();
const vf2d&GetSize()const;
//We picked up a draggable component, we should make a copy and return it here. If a nullptr is returned here, the pickup is not allowed. //We picked up a draggable component, we should make a copy and return it here. If a nullptr is returned here, the pickup is not allowed.
//WARNING!!! This allocates a brand new component when successful!!! Be prepared to clear it! //WARNING!!! This allocates a brand new component when successful!!! Be prepared to clear it!
virtual MenuComponent*PickUpDraggableItem(); virtual MenuComponent*PickUpDraggableItem();

@ -56,5 +56,9 @@ void Menu::InitializeOverworldMenuWindow(){
})END; })END;
overworldMenuWindow->ADD("Inventory Button",MenuComponent)({{4,12+28*2},{88,24}},"Inventory",[](MenuFuncData data){Menu::OpenMenu(INVENTORY);return true;})END; overworldMenuWindow->ADD("Inventory Button",MenuComponent)({{4,12+28*2},{88,24}},"Inventory",[](MenuFuncData data){Menu::OpenMenu(INVENTORY);return true;})END;
overworldMenuWindow->ADD("Settings Button",MenuComponent)({{4,12+28*3},{88,24}},"Settings",[](MenuFuncData data){/*Menu::OpenMenu(SETTINGS_MENU);*/return true;})END; overworldMenuWindow->ADD("Settings Button",MenuComponent)({{4,12+28*3},{88,24}},"Settings",[](MenuFuncData data){/*Menu::OpenMenu(SETTINGS_MENU);*/return true;})END;
overworldMenuWindow->ADD("Quit Button",MenuComponent)({{4,12+28*4},{88,24}},"Quit Game",[](MenuFuncData data){game->EndGame();return true;})END; overworldMenuWindow->ADD("Quit Button",MenuComponent)({{4,12+28*4},{88,24}},"Quit Game",[](MenuFuncData data){
Menu::CloseAllMenus();
game->ResetGame();
return true;
})END;
} }

@ -43,6 +43,8 @@ All rights reserved.
#include "State_OverworldMap.h" #include "State_OverworldMap.h"
#include "SaveFile.h" #include "SaveFile.h"
#include "ClassInfo.h" #include "ClassInfo.h"
#include "ScrollableWindowComponent.h"
#include "LoadFileButton.h"
INCLUDE_game INCLUDE_game
@ -62,18 +64,21 @@ const size_t SaveFile::GetSaveFileCount(){
const void SaveFile::SaveGame(){ const void SaveFile::SaveGame(){
utils::datafile saveFile; utils::datafile saveFile;
utils::datafile::INITIAL_SETUP_COMPLETE=false; utils::datafile::INITIAL_SETUP_COMPLETE=false;
for(size_t itemCount=0;auto&[name,item]:Inventory::_inventory){ for(size_t itemCount=0;auto&[cat,items]:Inventory::sortedInv){
saveFile["Items"][std::format("Item[{}]",itemCount)]["Amt"].SetInt(item->Amt()); for(std::shared_ptr<Item>&item:items){
saveFile["Items"][std::format("Item[{}]",itemCount)]["Enhancement Level"].SetInt(item->EnhancementLevel()); saveFile["Items"][std::format("Item[{}]",itemCount)]["Amt"].SetInt(item->Amt());
saveFile["Items"][std::format("Item[{}]",itemCount)]["Item Name"].SetString(item->ActualName()); saveFile["Items"][std::format("Item[{}]",itemCount)]["Enhancement Level"].SetInt(item->EnhancementLevel());
saveFile["Items"][std::format("Item[{}]",itemCount)]["Equip Slot"].SetInt(int(Inventory::GetSlotEquippedIn(item))); saveFile["Items"][std::format("Item[{}]",itemCount)]["Item Name"].SetString(item->ActualName());
for(const auto&[attr,val]:item->RandomStats()){ saveFile["Items"][std::format("Item[{}]",itemCount)]["Equip Slot"].SetInt(int(Inventory::GetSlotEquippedIn(item)));
saveFile["Items"][std::format("Item[{}]",itemCount)]["Attributes"][std::string(attr.ActualName())].SetReal(val); for(const auto&[attr,val]:item->RandomStats()){
saveFile["Items"][std::format("Item[{}]",itemCount)]["Attributes"][std::string(attr.ActualName())].SetReal(val);
}
itemCount++;
} }
itemCount++;
} }
saveFile["Player"]["Class"].SetString(game->GetPlayer()->GetClassName()); saveFile["Player"]["Class"].SetString(game->GetPlayer()->GetClassName());
saveFile["Player"]["Level"].SetInt(game->GetPlayer()->Level()); saveFile["Player"]["Level"].SetInt(game->GetPlayer()->Level());
saveFile["Player"]["Money"].SetInt(game->GetPlayer()->GetMoney());
saveFile["Player"]["Current EXP"].SetInt(game->GetPlayer()->CurrentXP()); saveFile["Player"]["Current EXP"].SetInt(game->GetPlayer()->CurrentXP());
saveFile["Player"]["Total EXP"].SetInt(game->GetPlayer()->TotalXP()); saveFile["Player"]["Total EXP"].SetInt(game->GetPlayer()->TotalXP());
for(const auto&[attr,val]:game->GetPlayer()->GetBaseStats()){ for(const auto&[attr,val]:game->GetPlayer()->GetBaseStats()){
@ -85,39 +90,56 @@ const void SaveFile::SaveGame(){
saveFile["Overworld Map Location"].SetString(State_OverworldMap::GetCurrentConnectionPoint().name); saveFile["Overworld Map Location"].SetString(State_OverworldMap::GetCurrentConnectionPoint().name);
saveFile["Chapter"].SetInt(game->GetCurrentChapter()); saveFile["Chapter"].SetInt(game->GetCurrentChapter());
saveFile["Save Name"].SetString(std::string(GetSaveFileName())); saveFile["Save Name"].SetString(std::string(GetSaveFileName()));
utils::datafile::INITIAL_SETUP_COMPLETE=true; saveFile["Game Time"].SetReal(game->GetRuntime());
utils::datafile::Write(saveFile,"save_file_path"_S+std::format("save.{:04}",saveFileID)); utils::datafile::Write(saveFile,"save_file_path"_S+std::format("save.{:04}",saveFileID));
utils::datafile metadata;
utils::datafile::Read(metadata,"save_file_path"_S+"metadata.dat");
metadata.GetProperty(std::format("save{}",saveFileID)).SetReal(game->GetRuntime(),0U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetInt(game->GetCurrentChapter(),1U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetInt(game->GetPlayer()->Level(),2U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetString(game->GetPlayer()->GetClassName(),3U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetString(std::string(SaveFile::GetSaveFileName()),4U);
utils::datafile::Write(metadata,"save_file_path"_S+"metadata.dat");
utils::datafile::INITIAL_SETUP_COMPLETE=true;
} }
const void SaveFile::LoadGame(){ const void SaveFile::LoadGame(){
utils::datafile loadFile; utils::datafile loadFile;
utils::datafile::Read(loadFile,"save_file_path"_S+std::format("save.{:04}",saveFileID)); utils::datafile::Read(loadFile,"save_file_path"_S+std::format("save.{:04}",saveFileID));
game->ResetGame(); game->ResetGame();
for(auto&[key,size]:loadFile["Items"]){ for(auto&[key,data]:loadFile["Items"].GetOrderedKeys()){
std::weak_ptr<Item>newItem=Inventory::AddItem(loadFile["Items"][key]["Item Name"].GetString(),loadFile["Items"][key]["Amt"].GetInt()); std::weak_ptr<Item>newItem=Inventory::AddItem(data["Item Name"].GetString(),data["Amt"].GetInt());
newItem.lock()->enhancementLevel=loadFile["Items"][key]["Enhancement Level"].GetInt(); newItem.lock()->enhancementLevel=data["Enhancement Level"].GetInt();
for(auto&[attr,size]:loadFile.GetProperty(std::format("{}.Attributes",key))){ if(loadFile.GetProperty(std::format("Items.{}",key)).HasProperty("Attributes")){
newItem.lock()->randomizedStats.A(attr)=loadFile.GetProperty(std::format("{}.Attributes.{}",key,attr)).GetReal(); for(auto&[attr,data]:loadFile.GetProperty(std::format("Items.{}.Attributes",key)).GetOrderedKeys()){
newItem.lock()->randomizedStats.A(attr)=data.GetReal();
}
} }
EquipSlot slot=EquipSlot(loadFile.GetProperty(std::format("{}.Equip Slot",key)).GetInt()); EquipSlot slot=EquipSlot(loadFile.GetProperty(std::format("Items.{}.Equip Slot",key)).GetInt());
if(slot!=EquipSlot::NONE){ //This should be equipped somewhere! if(slot!=EquipSlot::NONE){ //This should be equipped somewhere!
Inventory::EquipItem(newItem,slot); Inventory::EquipItem(newItem,slot);
} }
} }
game->ChangePlayerClass(classutils::StringToClass(loadFile["Player"]["Class"].GetString())); game->ChangePlayerClass(classutils::StringToClass(loadFile["Player"]["Class"].GetString()));
game->GetPlayer()->level=loadFile["Player"]["Level"].GetInt(); game->GetPlayer()->level=loadFile["Player"]["Level"].GetInt();
game->GetPlayer()->SetMoney(loadFile["Player"]["Money"].GetInt());
game->GetPlayer()->currentLevelXP=loadFile["Player"]["Current EXP"].GetInt(); game->GetPlayer()->currentLevelXP=loadFile["Player"]["Current EXP"].GetInt();
game->GetPlayer()->totalXPEarned=loadFile["Player"]["Total EXP"].GetInt(); game->GetPlayer()->totalXPEarned=loadFile["Player"]["Total EXP"].GetInt();
for(const auto&[key,size]:loadFile["Player"]["Base Stats"]){ for(auto&[key,data]:loadFile["Player"]["Base Stats"].GetOrderedKeys()){
game->GetPlayer()->SetBaseStat(key,loadFile["Player"]["Base Stats"][key].GetReal()); game->GetPlayer()->SetBaseStat(key,data.GetReal());
} }
for(const auto&[key,size]:loadFile["Unlocks"]){ for(const auto&[key,data]:loadFile["Unlocks"].GetOrderedKeys()){
Unlock::UnlockArea(key); Unlock::UnlockArea(key);
} }
DYNAMIC_CAST<State_OverworldMap*>(GameState::STATE)->SetStageMarker(loadFile["Overworld Map Location"].GetString()); State_OverworldMap::SetStageMarker(loadFile["Overworld Map Location"].GetString());
game->SetChapter(loadFile["Chapter"].GetInt()); game->SetChapter(loadFile["Chapter"].GetInt());
SaveFile::SetSaveFileName(loadFile["Save Name"].GetString()); SaveFile::SetSaveFileName(loadFile["Save Name"].GetString());
game->SetRuntime(loadFile["Game Time"].GetReal());
game->GetPlayer()->RecalculateEquipStats(); game->GetPlayer()->RecalculateEquipStats();
GameState::ChangeState(States::OVERWORLD_MAP,0.5f);
} }
const std::string_view SaveFile::GetSaveFileName(){ const std::string_view SaveFile::GetSaveFileName(){
@ -130,4 +152,24 @@ const void SaveFile::SetSaveFileName(std::string_view saveFileName){
const void SaveFile::SetSaveFileID(size_t saveFileID){ const void SaveFile::SetSaveFileID(size_t saveFileID){
SaveFile::saveFileID=saveFileID; SaveFile::saveFileID=saveFileID;
} }
const void SaveFile::UpdateSaveGameData(){
auto gameFilesList=Component<ScrollableWindowComponent>(LOAD_GAME,"Game Files List");
gameFilesList->RemoveAllComponents();
const size_t saveFileCount=GetSaveFileCount();
utils::datafile metadata;
utils::datafile::Read(metadata,"save_file_path"_S+"metadata.dat");
float offsetY=0;
for(size_t i=0;i<saveFileCount;i++){
if(metadata.HasProperty(std::format("save{}",i))){
gameFilesList->ADD(std::format("Load File Button - Save {}",i),LoadFileButton)({{0,offsetY},{gameFilesList->GetSize().x-13,48}},metadata[std::format("save{}",i)],i,[](MenuFuncData data){
LoadFileButton*comp=DYNAMIC_CAST<LoadFileButton*>(data.component);
saveFileID=comp->getSaveFileID();
SaveFile::LoadGame();
return true;
},ButtonAttr::NONE)END;
offsetY+=49;
}
}
}

@ -49,4 +49,6 @@ public:
static const void SaveGame(); static const void SaveGame();
static const void LoadGame(); static const void LoadGame();
static const void SetSaveFileID(size_t saveFileID); static const void SetSaveFileID(size_t saveFileID);
//Called whenever the save game data is updated.
static const void UpdateSaveGameData();
}; };

@ -130,7 +130,7 @@ protected:
if(mouseOverScrollbar||scrollBarSelected){ if(mouseOverScrollbar||scrollBarSelected){
scrollBarHoverTime=std::min(scrollBarHoverTime+game->GetElapsedTime(),"ThemeGlobal.HighlightTime"_F); scrollBarHoverTime=std::min(scrollBarHoverTime+game->GetElapsedTime(),"ThemeGlobal.HighlightTime"_F);
if(game->GetMouse(0).bPressed){ if(game->GetMouse(0).bPressed&&!geom2d::contains(rect,bounds)){
scrollBarSelected=true; scrollBarSelected=true;
} }
if(game->GetMouse(0).bReleased){ if(game->GetMouse(0).bReleased){
@ -201,7 +201,9 @@ protected:
for(MenuComponent*component:components){ for(MenuComponent*component:components){
component->_DrawDecal(subWindow,focused); component->_DrawDecal(subWindow,focused);
} }
DrawScrollbar(window,{},focused); if(!geom2d::contains(rect,bounds)){
DrawScrollbar(window,{},focused);
}
} }
virtual bool GetHoverState(Crawler*game,MenuComponent*child)override{ virtual bool GetHoverState(Crawler*game,MenuComponent*child)override{
return geom2d::overlaps(geom2d::rect<float>{Menu::menus[parentMenu]->pos+rect.pos,rect.size},game->GetMousePos())&& //Make sure the mouse is inside the parent window component first.... return geom2d::overlaps(geom2d::rect<float>{Menu::menus[parentMenu]->pos+rect.pos,rect.size},game->GetMousePos())&& //Make sure the mouse is inside the parent window component first....

@ -52,6 +52,7 @@ INCLUDE_MONSTER_LIST
INCLUDE_game INCLUDE_game
std::vector<ConnectionPoint>State_OverworldMap::connections; std::vector<ConnectionPoint>State_OverworldMap::connections;
ConnectionPoint*State_OverworldMap::currentConnectionPoint=nullptr;
State_OverworldMap::State_OverworldMap(){ State_OverworldMap::State_OverworldMap(){
SetStageMarker("Stage I-I"); //Eventually we will load the game from a file and this will not be necessary. We just set it to this for now. SetStageMarker("Stage I-I"); //Eventually we will load the game from a file and this will not be necessary. We just set it to this for now.
@ -111,7 +112,7 @@ void State_OverworldMap::OnUserUpdate(Crawler*game){
if(neighborInd==-1)continue; if(neighborInd==-1)continue;
ConnectionPoint&neighbor=ConnectionPointFromIndex(neighborInd); ConnectionPoint&neighbor=ConnectionPointFromIndex(neighborInd);
if(Unlock::IsUnlocked(neighbor.unlockCondition)&&&cp==&neighbor){ if(Unlock::IsUnlocked(neighbor.unlockCondition)&&&cp==&neighbor){
currentConnectionPoint=&neighbor; UpdateCurrentConnectionPoint(neighbor);
playerTargetPos=currentConnectionPoint->rect.pos+currentConnectionPoint->rect.size/2+vf2d{0,16}; playerTargetPos=currentConnectionPoint->rect.pos+currentConnectionPoint->rect.size/2+vf2d{0,16};
float angleTo=util::angleTo(game->GetPlayer()->GetPos(),playerTargetPos); float angleTo=util::angleTo(game->GetPlayer()->GetPos(),playerTargetPos);
if(angleTo>=-3*PI/4&&angleTo<-PI/4){ if(angleTo>=-3*PI/4&&angleTo<-PI/4){
@ -125,10 +126,6 @@ void State_OverworldMap::OnUserUpdate(Crawler*game){
}else{ }else{
game->GetPlayer()->UpdateWalkingAnimation(LEFT); game->GetPlayer()->UpdateWalkingAnimation(LEFT);
} }
Component<MenuLabel>(OVERWORLD_LEVEL_SELECT,"Stage Label")->SetLabel(currentConnectionPoint->name);
Component<EncountersSpawnListScrollableWindowComponent>(OVERWORLD_LEVEL_SELECT,"Spawns List")->UpdateSpawns(currentConnectionPoint->spawns);
Component<MenuComponent>(OVERWORLD_LEVEL_SELECT,"Enter Button")->Enable(currentConnectionPoint->levelDataExists);
Component<MenuComponent>(OVERWORLD_LEVEL_SELECT,"Change Loadout Button")->Enable(currentConnectionPoint->levelDataExists&&!(currentConnectionPoint->type=="STORY"||currentConnectionPoint->type=="SHOP"));
break; break;
} }
} }
@ -177,4 +174,14 @@ void State_OverworldMap::StartLevel(){
game->LoadLevel(LEVEL_NAMES.at(State_OverworldMap::GetCurrentConnectionPoint().map)); game->LoadLevel(LEVEL_NAMES.at(State_OverworldMap::GetCurrentConnectionPoint().map));
GameState::ChangeState(States::GAME_RUN); GameState::ChangeState(States::GAME_RUN);
} }
}
void State_OverworldMap::UpdateCurrentConnectionPoint(const ConnectionPoint&connection){
currentConnectionPoint=const_cast<ConnectionPoint*>(&connection);
Component<MenuLabel>(OVERWORLD_LEVEL_SELECT,"Stage Label")->SetLabel(currentConnectionPoint->name);
Component<EncountersSpawnListScrollableWindowComponent>(OVERWORLD_LEVEL_SELECT,"Spawns List")->UpdateSpawns(currentConnectionPoint->spawns);
Component<MenuComponent>(OVERWORLD_LEVEL_SELECT,"Enter Button")->Enable(currentConnectionPoint->levelDataExists);
Component<MenuComponent>(OVERWORLD_LEVEL_SELECT,"Change Loadout Button")->Enable(currentConnectionPoint->levelDataExists&&!(currentConnectionPoint->type=="STORY"||currentConnectionPoint->type=="SHOP"));
} }

@ -42,7 +42,7 @@ All rights reserved.
class State_OverworldMap:public GameState{ class State_OverworldMap:public GameState{
friend class Crawler; friend class Crawler;
ConnectionPoint*currentConnectionPoint; static ConnectionPoint*currentConnectionPoint;
float currentTime=0; float currentTime=0;
vf2d playerTargetPos; vf2d playerTargetPos;
const float playerMoveSpd=48.0; const float playerMoveSpd=48.0;
@ -50,10 +50,11 @@ public:
State_OverworldMap(); State_OverworldMap();
static std::vector<ConnectionPoint>connections; static std::vector<ConnectionPoint>connections;
static ConnectionPoint&GetCurrentConnectionPoint(); static ConnectionPoint&GetCurrentConnectionPoint();
void SetStageMarker(std::string connectionName); static void SetStageMarker(std::string connectionName);
static ConnectionPoint&ConnectionPointFromIndex(int ind); static ConnectionPoint&ConnectionPointFromIndex(int ind);
virtual void OnStateChange(GameState*prevState)override final; virtual void OnStateChange(GameState*prevState)override final;
virtual void OnUserUpdate(Crawler*game)override final; virtual void OnUserUpdate(Crawler*game)override final;
virtual void Draw(Crawler*game)override final; virtual void Draw(Crawler*game)override final;
static void StartLevel(); static void StartLevel();
static void UpdateCurrentConnectionPoint(const ConnectionPoint&connection);
}; };

@ -7,7 +7,15 @@ Save/Load Game
- Unlock Progress - Unlock Progress
- World Map Location - World Map Location
- Chapter # - Chapter #
- Gold
- Save File Name - Save File Name
- Game Play Time
Load Game Screen:
Save File Name
Game Play Time
Chapter #
Level / Class
- Start a New Game File - Start a New Game File
- Load a Game (Load list displays save files in reverse chronological order based on last access) - Load a Game (Load list displays save files in reverse chronological order based on last access)
@ -26,10 +34,12 @@ Audio Engine
- Attack / Enemy Sound Effects - Attack / Enemy Sound Effects
- Music Loading/Looping - Music Loading/Looping
Settings Menu Settings Menu
- Any settings should be saved to the save file!
- Volume Controls - Volume Controls
- Key Configuration - Key Configuration
-Upon pressing a key, check if the key is bound to another option, if so, -Upon pressing a key, check if the key is bound to another option, if so,
remove that bind from the list. Up to two keys may be binded per action. remove that bind from the list. Up to two keys may be binded per action.
-We have to save keybinds to the save file.
January 31st January 31st

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

@ -1,354 +0,0 @@
Items
{
Item[0]
{
Amt = 9
Enhancement Level = 0
Item Name = Bandages
Equip Slot = 0
}
Item[1]
{
Amt = 22
Enhancement Level = 0
Item Name = Blue Slime Remains
Equip Slot = 0
}
Item[2]
{
Amt = 1
Enhancement Level = 0
Item Name = Bone Armor
Equip Slot = 0
}
Item[3]
{
Amt = 1
Enhancement Level = 0
Item Name = Bone Gloves
Equip Slot = 8
}
Item[4]
{
Amt = 1
Enhancement Level = 0
Item Name = Bone Pants
Equip Slot = 16
}
Item[5]
{
Amt = 1
Enhancement Level = 0
Item Name = Copper Armor
Equip Slot = 0
}
Item[6]
{
Amt = 1
Enhancement Level = 0
Item Name = Copper Helmet
Equip Slot = 0
}
Item[7]
{
Amt = 1
Enhancement Level = 0
Item Name = Copper Pants
Equip Slot = 0
}
Item[8]
{
Amt = 1
Enhancement Level = 0
Item Name = Copper Shoes
Equip Slot = 0
}
Item[9]
{
Amt = 2
Enhancement Level = 0
Item Name = Elixir of Bear Strength
Equip Slot = 0
}
Item[10]
{
Amt = 42
Enhancement Level = 0
Item Name = Green Slime Remains
Equip Slot = 0
}
Item[11]
{
Amt = 1
Enhancement Level = 0
Item Name = Laser Sword
Equip Slot = 0
}
Item[12]
{
Amt = 1
Enhancement Level = 0
Item Name = Leather Gloves
Equip Slot = 0
}
Item[13]
{
Amt = 1
Enhancement Level = 0
Item Name = Leather Helmet
Equip Slot = 0
}
Item[14]
{
Amt = 1
Enhancement Level = 0
Item Name = Leather Pants
Equip Slot = 0
}
Item[15]
{
Amt = 1
Enhancement Level = 0
Item Name = Leather Shoes
Equip Slot = 0
}
Item[16]
{
Amt = 16
Enhancement Level = 0
Item Name = Minor Health Potion
Equip Slot = 0
}
Item[17]
{
Amt = 4
Enhancement Level = 0
Item Name = Red Slime Remains
Equip Slot = 0
}
Item[18]
{
Amt = 1
Enhancement Level = 0
Item Name = Ring of the Slime King
Equip Slot = 128
Attributes
{
Attack = 4.000000
Health = 17.000000
Mana = 1.000000
Move Spd % = 3.000000
}
}
Item[19]
{
Amt = 1
Enhancement Level = 0
Item Name = Ring of the Slime King
Equip Slot = 0
Attributes
{
Attack = 3.000000
Health = 8.000000
Mana = 3.000000
Move Spd % = 1.000000
}
}
Item[20]
{
Amt = 1
Enhancement Level = 0
Item Name = Ring of the Slime King
Equip Slot = 0
Attributes
{
Attack = 3.000000
Health = 6.000000
Mana = 1.000000
Move Spd % = 3.000000
}
}
Item[21]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Armor
Equip Slot = 0
}
Item[22]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Armor
Equip Slot = 0
}
Item[23]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Armor
Equip Slot = 0
}
Item[24]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Gloves
Equip Slot = 0
}
Item[25]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Gloves
Equip Slot = 0
}
Item[26]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Gloves
Equip Slot = 0
}
Item[27]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Gloves
Equip Slot = 0
}
Item[28]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Helmet
Equip Slot = 0
}
Item[29]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Shoes
Equip Slot = 0
}
Item[30]
{
Amt = 1
Enhancement Level = 0
Item Name = Shell Sword
Equip Slot = 0
}
Item[31]
{
Amt = 1
Enhancement Level = 0
Item Name = Wooden Sword
Equip Slot = 0
}
}
Player
{
Class = Wizard
Level = 1
Current EXP = 44
Total EXP = 44
Base Stats
{
Attack = 15.000000
CDR = 0.000000
Crit Dmg = 50.000000
Crit Rate = 0.000000
Defense = 0.000000
HP6 Recovery % = 0.000000
Health = 80.000000
Health % = 0.000000
Mana = 100.000000
Move Spd % = 100.000000
}
}
Unlocks
{
CAMPAIGN_1_1 = True
WORLD_MAP = True
}
Overworld Map Location = Stage I-I
Chapter = 1
Save Name = Test File

@ -1038,6 +1038,7 @@ namespace olc
// to specify the primary screen // to specify the primary screen
void SetDrawTarget(Sprite* target); void SetDrawTarget(Sprite* target);
double GetRuntime() const; double GetRuntime() const;
void SetRuntime(const double runTime);
// Gets the current Frames Per Second // Gets the current Frames Per Second
uint32_t GetFPS() const; uint32_t GetFPS() const;
// Gets last update of elapsed time // Gets last update of elapsed time
@ -2195,6 +2196,10 @@ namespace olc
double PixelGameEngine::GetRuntime() const double PixelGameEngine::GetRuntime() const
{ return dRunTime; } { return dRunTime; }
void PixelGameEngine::SetRuntime(const double runTime){
dRunTime=runTime;
}
uint32_t PixelGameEngine::GetFPS() const uint32_t PixelGameEngine::GetFPS() const
{ return nLastFPS; } { return nLastFPS; }

@ -145,6 +145,10 @@ namespace olc::utils
return m_mapObjects; return m_mapObjects;
} }
inline std::vector<std::pair<std::string,datafile>>&GetOrderedKeys(){
return m_vecObjects;
}
// Checks if a property exists - useful to avoid creating properties // Checks if a property exists - useful to avoid creating properties
// via reading them, though non-essential // via reading them, though non-essential
inline bool HasProperty(const std::string& sName) inline bool HasProperty(const std::string& sName)
@ -281,6 +285,8 @@ namespace olc::utils
inline static bool Read(datafile& n, const std::string& sFileName, const char sListSep = ',') inline static bool Read(datafile& n, const std::string& sFileName, const char sListSep = ',')
{ {
bool previousSetupState=INITIAL_SETUP_COMPLETE;
INITIAL_SETUP_COMPLETE=false;
// Open the file! // Open the file!
std::ifstream file(sFileName); std::ifstream file(sFileName);
if (file.is_open()) if (file.is_open())
@ -435,11 +441,13 @@ namespace olc::utils
// Close and exit! // Close and exit!
file.close(); file.close();
INITIAL_SETUP_COMPLETE=previousSetupState;
return true; return true;
} }
// File not found, so fail // File not found, so fail
ERR("WARNING! Could not open file "<<sFileName<<"!"); ERR("WARNING! Could not open file "<<sFileName<<"!");
INITIAL_SETUP_COMPLETE=previousSetupState;
return false; return false;
} }

Loading…
Cancel
Save