Stage and Monster Loot tables only populate in DUNGEON and BOSS map types. Moved the inventory generator UI updating code for loading and saving the game to the very end of the load/save process. Reducing the number of calls dramatically for improved loading speeds. Release Build 12811. 235/235 Tests passing.
Some checks failed
Emscripten Build / Build_and_Deploy_Web_Build (push) Failing after 6m14s
Some checks failed
Emscripten Build / Build_and_Deploy_Web_Build (push) Failing after 6m14s
This commit is contained in:
parent
829388d9d3
commit
974b4ad226
@ -83,7 +83,7 @@ namespace BuffTests
|
||||
#pragma region Setup a fake test map
|
||||
game->MAP_DATA.Unlock();
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
game->_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
Game::_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
ItemDrop::ClearDrops();
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ namespace FileTests
|
||||
#pragma region Setup a fake test map
|
||||
game->MAP_DATA.Unlock();
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
game->_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
Game::_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
ItemDrop::ClearDrops();
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@ -90,10 +90,22 @@ namespace Game{
|
||||
pl=game->GetPlayer(); //The player pointer has been reassigned...
|
||||
}
|
||||
|
||||
inline void LoadLevel(const std::string&mapKeyname,AiL*const game){
|
||||
inline void LoadLevelWithTMX(const std::string&mapKeyname,AiL*const game){
|
||||
game->InitializeLevel("map_path"_S+DATA["Levels"][mapKeyname]["Map File"].GetString(),mapKeyname);
|
||||
game->SetupLevel(game->MAP_DATA[mapKeyname]);
|
||||
game->_SetCurrentLevel(mapKeyname);
|
||||
_SetCurrentLevel(mapKeyname);
|
||||
}
|
||||
|
||||
inline void LoadFakeLevel(const std::string&mapKeyname,AiL*const game){
|
||||
game->MAP_DATA.at(mapKeyname).name=mapKeyname;
|
||||
game->SetupLevel(game->MAP_DATA[mapKeyname]);
|
||||
_SetCurrentLevel(mapKeyname);
|
||||
}
|
||||
|
||||
//NOTE: This will modify the currentLevel variable without triggering anything else in-game, this will normally mess up the state in the game. Ideally this is only used when initializing a test level.
|
||||
inline void _SetCurrentLevel(const MapName map){
|
||||
game->ResetLevelStates();
|
||||
game->currentLevel=map;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,6 +43,8 @@ All rights reserved.
|
||||
#include "ItemDrop.h"
|
||||
#include <ranges>
|
||||
#include "GameHelper.h"
|
||||
#include"RowInventoryScrollableWindowComponent.h"
|
||||
#include"SaveFile.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
using namespace olc::utils;
|
||||
@ -71,6 +73,9 @@ namespace ItemTests
|
||||
testGame->InitializeGraphics();
|
||||
testGame->InitializeClasses();
|
||||
sig::Animation::InitializeAnimations();
|
||||
Monster::InitializeStrategies();
|
||||
MonsterData::InitializeMonsterData();
|
||||
sig::Animation::InitializeAnimations();
|
||||
testGame->InitializeDefaultKeybinds();
|
||||
testGame->InitializePlayer();
|
||||
sig::Animation::SetupPlayerAnimations();
|
||||
@ -84,6 +89,8 @@ namespace ItemTests
|
||||
#pragma region Setup a fake test map and test monster
|
||||
game->MAP_DATA.Unlock();
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
game->MAP_DATA["HUB_TYPE_TEST_MAP"];
|
||||
game->MAP_DATA["WORLD_TYPE_TEST_MAP"];
|
||||
ItemDrop::ClearDrops();
|
||||
MonsterData testMonsterData{"TestName","Test Monster",1000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||
MONSTER_DATA["TestName"]=testMonsterData;
|
||||
@ -97,6 +104,9 @@ namespace ItemTests
|
||||
Menu::themes.SetInitialized();
|
||||
GFX.SetInitialized();
|
||||
game->MAP_DATA.SetInitialized();
|
||||
MapTag dungeonMapData{};
|
||||
game->MAP_DATA["HUB_TYPE_TEST_MAP"].mapType=Map::MapType::HUB;
|
||||
game->MAP_DATA["WORLD_TYPE_TEST_MAP"].mapType=Map::MapType::WORLD_MAP;
|
||||
}
|
||||
TEST_METHOD_CLEANUP(ItemCleanupTests){
|
||||
testGame->EndGame();
|
||||
@ -107,6 +117,46 @@ namespace ItemTests
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
Assert::AreEqual(3U,Inventory::GetItemCount("Health Potion"s),L"Player has 3 Health Potions.");
|
||||
}
|
||||
TEST_METHOD(ItemAddAndRemoveAffectsStageLootTest){
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it when the game starts.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it when the game starts.");
|
||||
Game::LoadLevelWithTMX("CAMPAIGN_1_1",testGame.get());
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it after loading a new stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it after loading a new stage.");
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
Assert::AreEqual(size_t(1),Inventory::get("Stage Loot").size(),L"Stage Loot has 1 Item in it after collecting an item in a dungeon stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it after collecting a non-monster drop item in a dungeon stage.");
|
||||
Inventory::AddItem("Health Potion"s,3,true);
|
||||
Assert::AreEqual(size_t(1),Inventory::get("Stage Loot").size(),L"Stage Loot has 1 Item in it after collecting an item in a dungeon stage.");
|
||||
Assert::AreEqual(size_t(1),Inventory::get("Monster Loot").size(),L"Monster Loot has 1 Item in it after collecting a monster drop item in a dungeon stage.");
|
||||
Game::LoadLevelWithTMX("BOSS_1",testGame.get());
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it after loading a new stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it after loading a new stage.");
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
Assert::AreEqual(size_t(1),Inventory::get("Stage Loot").size(),L"Stage Loot has 1 Item in it after collecting an item in a boss stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it after collecting a non-monster item in a boss stage.");
|
||||
Inventory::AddItem("Health Potion"s,3,true);
|
||||
Assert::AreEqual(size_t(1),Inventory::get("Stage Loot").size(),L"Stage Loot has 1 Item in it after collecting an item in a boss stage.");
|
||||
Assert::AreEqual(size_t(1),Inventory::get("Monster Loot").size(),L"Monster Loot has 1 Item in it after collecting a monster drop item in a boss stage.");
|
||||
Game::LoadFakeLevel("HUB_TYPE_TEST_MAP",testGame.get());
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it after loading a new stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it after loading a new stage.");
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it due to not being a dungeon or boss stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it due to not being a dungeon or boss stage.");
|
||||
Inventory::AddItem("Health Potion"s,3,true);
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it when picking up monster drops due to not being a dungeon or boss stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in when picking up monster drops it due to not being a dungeon or boss stage.");
|
||||
Game::LoadFakeLevel("WORLD_TYPE_TEST_MAP",testGame.get());
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it after loading a new stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it after loading a new stage.");
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it due to not being a dungeon or boss stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in it due to not being a dungeon or boss stage.");
|
||||
Inventory::AddItem("Health Potion"s,3,true);
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Stage Loot").size(),L"Stage Loot has 0 Items in it when picking up monster drops due to not being a dungeon or boss stage.");
|
||||
Assert::AreEqual(size_t(0),Inventory::get("Monster Loot").size(),L"Monster Loot has 0 Items in when picking up monster drops it due to not being a dungeon or boss stage.");
|
||||
}
|
||||
TEST_METHOD(ItemRemoveTest){
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
for(std::weak_ptr<Item>&item:Inventory::GetItem("Health Potion"s))Inventory::RemoveItem(item,3);
|
||||
@ -353,5 +403,28 @@ namespace ItemTests
|
||||
Assert::AreEqual(true,extraRing.lock()->CanBeEnchanted(),L"Ring can be enchanted again with the right amount of fragments.");
|
||||
Assert::AreEqual(uint32_t(2000-"Fragment Enchant Cost"_i[1]),player->GetMoney(),util::wformat("Lost {} money due to enchanting ring.","Fragment Enchant Cost"_i[1]).c_str());
|
||||
}
|
||||
TEST_METHOD(UIUpdatesWhenItemsAreReceivedTest){
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
for(const auto&category:Item::GetItemCategories()|std::views::filter([](const std::string&category){return DATA["ItemCategory"][category].GetString()!="!HIDE";})){
|
||||
const size_t itemCount{Component<RowInventoryScrollableWindowComponent>(MenuType::INVENTORY,std::format("Inventory Display - {}",category))->GetMainComponentCount()};
|
||||
Assert::AreEqual(Inventory::get(category).size(),itemCount,util::wformat("UI component count in inventory window does not match player inventory item count in category {}",category).c_str());
|
||||
}
|
||||
}
|
||||
TEST_METHOD(UIUpdatesWhenGameFileWithItemsIsLoadedTest){
|
||||
SaveFile::SetSaveFileID(-1);
|
||||
SaveFile::LoadGame();
|
||||
for(const auto&category:Item::GetItemCategories()|std::views::filter([](const std::string&category){return DATA["ItemCategory"][category].GetString()!="!HIDE";})){
|
||||
const size_t itemCount{Component<RowInventoryScrollableWindowComponent>(MenuType::INVENTORY,std::format("Inventory Display - {}",category))->GetMainComponentCount()};
|
||||
Assert::AreEqual(Inventory::get(category).size(),itemCount,util::wformat("UI component count in inventory window does not match player inventory item count in category {}",category).c_str());
|
||||
}
|
||||
}
|
||||
TEST_METHOD(UIDoesNotUpdateWhenSavingFileFlagIsEnabledTest){
|
||||
game->savingFile=true;
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
for(const auto&category:Item::GetItemCategories()|std::views::filter([](const std::string&category){return DATA["ItemCategory"][category].GetString()!="!HIDE";})){
|
||||
const size_t itemCount{Component<RowInventoryScrollableWindowComponent>(MenuType::INVENTORY,std::format("Inventory Display - {}",category))->GetMainComponentCount()};
|
||||
Assert::AreEqual(size_t(0),itemCount,util::wformat("UI component count in inventory window is still empty in category {} during file saving",category).c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ namespace MonsterTests
|
||||
#pragma region Setup a fake test map
|
||||
game->MAP_DATA.Unlock();
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
game->_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
Game::_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
ItemDrop::ClearDrops();
|
||||
game->MAP_DATA.SetInitialized();
|
||||
#pragma endregion
|
||||
@ -560,7 +560,7 @@ namespace MonsterTests
|
||||
Monster&parrot{game->SpawnMonster({},MONSTER_DATA.at("Parrot"))};
|
||||
}
|
||||
TEST_METHOD(MonsterRunRightTest){
|
||||
Game::LoadLevel("TEST_MAP",testGame.get());
|
||||
Game::LoadLevelWithTMX("TEST_MAP",testGame.get());
|
||||
testGame->GetPlayer()->ForceSetPos({100,100});
|
||||
Monster&boar{game->SpawnMonster({24,24},MONSTER_DATA.at("Boar"))};
|
||||
Game::Update(0.f); // Make sure monster spawns first.
|
||||
@ -570,7 +570,7 @@ namespace MonsterTests
|
||||
Assert::IsTrue(originalBoarPos+vf2d{50.f*boar.GetMoveSpdMult(),0.f}==boar.GetPos(),util::wformat("The boar should have moved {} units in half a second. Expected {}=={}",50.f*boar.GetMoveSpdMult(),(originalBoarPos+vf2d{50.f*boar.GetMoveSpdMult(),0.f}).str(),boar.GetPos().str()).c_str());
|
||||
}
|
||||
TEST_METHOD(MonsterHasteMoveTest){
|
||||
Game::LoadLevel("TEST_MAP",testGame.get());
|
||||
Game::LoadLevelWithTMX("TEST_MAP",testGame.get());
|
||||
Monster&parrot{game->SpawnMonster({24,24},MONSTER_DATA.at("Parrot"))};
|
||||
Game::Update(0.f); // Make sure monster spawns first.
|
||||
parrot.strategy="[TEST]Run Right";
|
||||
|
||||
@ -802,7 +802,7 @@ namespace PlayerTests
|
||||
Assert::AreEqual(player->GetMaxHealth()-10,player->GetHealth(),L"Player now takes damage with 0 shield.");
|
||||
}
|
||||
TEST_METHOD(PlayerHasteMoveCheck){
|
||||
Game::LoadLevel("TEST_MAP",testGame.get());
|
||||
Game::LoadLevelWithTMX("TEST_MAP",testGame.get());
|
||||
player=testGame->GetPlayer();
|
||||
player->ForceSetPos({12,12});
|
||||
testKeyboardInput.AddKeybind(Input{InputType::KEY,D});
|
||||
@ -829,7 +829,7 @@ namespace PlayerTests
|
||||
}
|
||||
}
|
||||
TEST_METHOD(PlayerHasteCooldownCheck){
|
||||
Game::LoadLevel("TEST_MAP",testGame.get());
|
||||
Game::LoadLevelWithTMX("TEST_MAP",testGame.get());
|
||||
player=testGame->GetPlayer();
|
||||
player->ForceSetPos({12,12});
|
||||
testKeyboardInput.AddKeybind(Input{InputType::KEY,D});
|
||||
|
||||
@ -2281,10 +2281,16 @@ void AiL::InitializeLevel(std::string mapFile,MapName map){
|
||||
|
||||
size_t slashMarker = mapFile.find_last_of('/');
|
||||
std::string baseDir=mapFile.substr(0,slashMarker+1);
|
||||
if(TestingModeEnabled())MAP_DATA.Unlock();
|
||||
MAP_DATA[map]=level.GetData();
|
||||
if(TestingModeEnabled()){
|
||||
if(!MAP_DATA.contains(map)){
|
||||
MAP_DATA.Unlock();
|
||||
MAP_DATA[map]=level.GetData();
|
||||
MAP_DATA.SetInitialized();
|
||||
}
|
||||
}else{
|
||||
MAP_DATA[map]=level.GetData();
|
||||
}
|
||||
MAP_DATA.at(map).name=map;
|
||||
if(TestingModeEnabled())MAP_DATA.SetInitialized();
|
||||
}
|
||||
|
||||
void AiL::LoadLevel(MapName map,MusicChange changeMusic){
|
||||
@ -3827,9 +3833,20 @@ const std::weak_ptr<Item>AiL::GetLoadoutItem(int slot){
|
||||
}
|
||||
|
||||
void AiL::RestockLoadoutItems(){
|
||||
for(int i=0;i<GetLoadoutSize();i++){
|
||||
if(!ISBLANK(GetLoadoutItem(i))){
|
||||
SetLoadoutItem(i,GetLoadoutItem(i).lock()->ActualName());
|
||||
for(int slot=0;slot<GetLoadoutSize();slot++){
|
||||
if(!ISBLANK(GetLoadoutItem(slot))){
|
||||
SetLoadoutItem(slot,GetLoadoutItem(slot).lock()->ActualName());
|
||||
|
||||
//Set the loadout slot selection for this loadout item.
|
||||
auto&itemsList=Component<InventoryScrollableWindowComponent>(INVENTORY_CONSUMABLES,"inventory")->GetComponents();
|
||||
auto item=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&component){
|
||||
if(DYNAMIC_POINTER_CAST<MenuItemButton>(component)->GetItem().lock()->ActualName()==loadout[slot]->ActualName()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(item!=itemsList.end())DYNAMIC_POINTER_CAST<MenuItemButton>(*item)->selected=slot;
|
||||
else ERR(std::format("WARNING! Could not find item {} in inventory while selecting a loadout item from the consumables menu! THIS SHOULD NOT BE HAPPENING!",loadout[slot]->ActualName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3895,18 +3912,6 @@ void AiL::SetLoadoutItem(int slot,std::string itemName){
|
||||
}break;
|
||||
}
|
||||
|
||||
//Set the loadout slot selection for this loadout item.
|
||||
auto&itemsList=Component<InventoryScrollableWindowComponent>(INVENTORY_CONSUMABLES,"inventory")->GetComponents();
|
||||
auto item=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&component){
|
||||
if(DYNAMIC_POINTER_CAST<MenuItemButton>(component)->GetItem().lock()->ActualName()==loadout[slot]->ActualName()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(item!=itemsList.end()){
|
||||
DYNAMIC_POINTER_CAST<MenuItemButton>(*item)->selected=slot;
|
||||
}else ERR(std::format("WARNING! Could not find item {} in inventory while selecting a loadout item from the consumables menu! THIS SHOULD NOT BE HAPPENING!",loadout[slot]->ActualName()));
|
||||
|
||||
}else{
|
||||
ERR("Trying to set item "+itemName+" in Loadout slot "+std::to_string(slot)+" when said item does not exist in our inventory!");
|
||||
}
|
||||
@ -4112,6 +4117,7 @@ void AiL::InitializePlayerLevelCap(){
|
||||
}
|
||||
|
||||
void AiL::ResetGame(bool changeToMainMenu){
|
||||
resettingGame=true;
|
||||
if(changeToMainMenu){
|
||||
GameState::ChangeState(States::MAIN_MENU,0.5f);
|
||||
}
|
||||
@ -4132,15 +4138,18 @@ void AiL::ResetGame(bool changeToMainMenu){
|
||||
game->ClearLoadoutItem(i);
|
||||
}
|
||||
Unlock::Initialize();
|
||||
State_OverworldMap::SetStageMarker("Player.Starting Location"_S);
|
||||
State_OverworldMap::UpdateCurrentConnectionPoint(*State_OverworldMap::currentConnectionPoint);
|
||||
State_OverworldMap::ResetConnectionPoints();
|
||||
if(!TestingModeEnabled()){
|
||||
State_OverworldMap::SetStageMarker("Player.Starting Location"_S);
|
||||
State_OverworldMap::UpdateCurrentConnectionPoint(*State_OverworldMap::currentConnectionPoint);
|
||||
State_OverworldMap::ResetConnectionPoints();
|
||||
}
|
||||
SetChapter(1);
|
||||
SaveFile::SetSaveFileName("");
|
||||
Tutorial::Initialize();
|
||||
minimap.SetMinimapMode(MinimapMode::SMALL);
|
||||
minimap.EraseChunkData();
|
||||
ClearDecalInstances();
|
||||
resettingGame=false;
|
||||
}
|
||||
|
||||
void AiL::OnRequestCompleted(const std::string_view receivedData)const{
|
||||
@ -4546,10 +4555,6 @@ void AiL::AddToSpecialMarkedTargetList(std::tuple<std::weak_ptr<Monster>,StackCo
|
||||
lockOnSpecialTargets.emplace_back(markData);
|
||||
}
|
||||
|
||||
void AiL::_SetCurrentLevel(const MapName map){
|
||||
currentLevel=map;
|
||||
}
|
||||
|
||||
std::vector<Effect*>AiL::GetForegroundEffects()const{
|
||||
std::vector<Effect*>outputVec;
|
||||
std::for_each(foregroundEffects.begin(),foregroundEffects.end(),[&outputVec](const std::unique_ptr<Effect>&eff){outputVec.emplace_back(eff.get());});
|
||||
@ -4594,6 +4599,8 @@ void AiL::ResetLevelStates(){
|
||||
lockOnTargets.clear();
|
||||
lockOnSpecialTargets.clear();
|
||||
ItemDrop::drops.clear();
|
||||
Inventory::Clear("Monster Loot");
|
||||
Inventory::Clear("Stage Loot");
|
||||
GameEvent::events.clear();
|
||||
Audio::SetBGMPitch(1.f);
|
||||
RepeatingSoundEffect::StopAllSounds();
|
||||
|
||||
@ -90,9 +90,9 @@ enum class KnockbackCondition{
|
||||
KNOCKBACK_UNHURT_TARGETS, //Knockback only targets that did not get hit.
|
||||
};
|
||||
|
||||
namespace PlayerTests{
|
||||
class PlayerTest;
|
||||
}
|
||||
namespace Game{
|
||||
void _SetCurrentLevel(const MapName map);
|
||||
};
|
||||
|
||||
class AiL : public olc::PixelGameEngine
|
||||
{
|
||||
@ -106,6 +106,7 @@ class AiL : public olc::PixelGameEngine
|
||||
friend class Arc;
|
||||
friend class LoadingScreen;
|
||||
friend class TileGroupDataFile;
|
||||
friend void Game::_SetCurrentLevel(const MapName map);
|
||||
std::unique_ptr<Player>player;
|
||||
SplashScreen splash;
|
||||
public:
|
||||
@ -332,7 +333,6 @@ public:
|
||||
void AddToMarkedTargetList(std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>markData);
|
||||
void AddToSpecialMarkedTargetList(std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>markData);
|
||||
void InitializeClasses();
|
||||
void _SetCurrentLevel(const MapName map); //NOTE: This will modify the currentLevel variable without triggering anything else in-game, this will normally mess up the state in the game. Ideally this is only used when initializing a test level.
|
||||
void InitializeCamera();
|
||||
void AddNotification(const Notification&n);
|
||||
|
||||
@ -351,6 +351,8 @@ public:
|
||||
};
|
||||
|
||||
void PrecacheNewLevels();
|
||||
bool savingFile=false;
|
||||
bool resettingGame=false;
|
||||
private:
|
||||
std::vector<std::unique_ptr<Effect>>foregroundEffects,backgroundEffects,foregroundEffectsToBeInserted,backgroundEffectsToBeInserted;
|
||||
std::vector<TileRenderData*>tilesWithCollision,tilesWithoutCollision;
|
||||
@ -405,7 +407,6 @@ private:
|
||||
float loadingWaitTime=0.f;
|
||||
bool displayHud=true;
|
||||
float vignetteDisplayTime=0.f;
|
||||
bool savingFile=false;
|
||||
bool prevStageCompleted=false;
|
||||
vf2d windSpd{};
|
||||
bool MapCacheIsRunning{false};
|
||||
|
||||
@ -59,6 +59,7 @@ All rights reserved.
|
||||
INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
|
||||
#pragma region CharacterMenuWindow
|
||||
namespace CharacterMenuWindow{
|
||||
struct AttributeData{
|
||||
std::string attrName;
|
||||
@ -116,6 +117,7 @@ namespace CharacterMenuWindow{
|
||||
return component;
|
||||
}
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
void Menu::InitializeCharacterMenuWindow(){
|
||||
static bool equipmentWindowOpened=false;
|
||||
|
||||
@ -80,6 +80,7 @@ void Menu::InitializeHubPauseWindow(){
|
||||
return true;
|
||||
},ButtonAttr::FIT_TO_LABEL)END;
|
||||
hubPauseWindow->ADD("Change Loadout Button",MenuComponent)(geom2d::rect<float>{{6.f,56.f},{84.f,24.f}},"Change\nLoadout",[](MenuFuncData data){
|
||||
game->RestockLoadoutItems();
|
||||
Menu::OpenMenu(ITEM_HUB_LOADOUT);
|
||||
return true;
|
||||
},ButtonAttr::FIT_TO_LABEL)END;
|
||||
|
||||
@ -39,6 +39,8 @@ All rights reserved.
|
||||
#include "RowInventoryScrollableWindowComponent.h"
|
||||
#include "AccessoryRowItemDisplay.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
#define DEFINE(SubName) InventoryCreator InventoryCreator::SubName##_InventoryUpdate(&InventoryCreator::SubName##_InventorySlotsUpdate,&InventoryCreator::SubName##_AddButtonOnSlotUpdate);
|
||||
|
||||
DEFINE(Player);
|
||||
@ -50,10 +52,12 @@ DEFINE(RowPlayerArmor);
|
||||
#pragma region Player Inventory Updates
|
||||
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::Player_InventorySlotsUpdate=
|
||||
[](InventoryScrollableWindowComponent&component,ITCategory cat){
|
||||
size_t invSize=Inventory::get(cat).size();
|
||||
component.RemoveAllComponents();
|
||||
for(std::weak_ptr<Item> item:Inventory::get(cat)){
|
||||
component.AddButtonOnSlotUpdate(cat);
|
||||
if(!game->savingFile&&!game->resettingGame){
|
||||
size_t invSize=Inventory::get(cat).size();
|
||||
component.RemoveAllComponents();
|
||||
for(std::weak_ptr<Item> item:Inventory::get(cat)){
|
||||
component.AddButtonOnSlotUpdate(cat);
|
||||
}
|
||||
}
|
||||
};
|
||||
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::Player_AddButtonOnSlotUpdate=
|
||||
|
||||
@ -65,7 +65,7 @@ void Menu::InitializeInventoryWindow(){
|
||||
|
||||
auto GetSortedCategories=[](){
|
||||
std::vector<std::pair<std::string,int>>categories;
|
||||
for(auto&[category,items]:ITEM_CATEGORIES){
|
||||
for(auto&category:Item::GetItemCategories()){
|
||||
if(DATA["ItemCategory"][category].GetString(0)=="!HIDE")continue; //This category is meant to be hidden!
|
||||
categories.push_back({category,DATA["ItemCategory"][category].GetInt(0)}); //We assume the first value becomes the sort order we wish to use.
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ All rights reserved.
|
||||
#endif
|
||||
#include <ranges>
|
||||
#include "ItemEnchant.h"
|
||||
#include<algorithm>
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_DATA
|
||||
@ -629,6 +630,7 @@ void Inventory::InsertIntoSortedInv(std::shared_ptr<Item>itemRef){
|
||||
}
|
||||
|
||||
void Inventory::InsertIntoStageInventoryCategory(std::shared_ptr<Item>itemRef,const bool monsterDrop){
|
||||
if(game->GetCurrentMap().GetMapType()!=Map::MapType::BOSS&&game->GetCurrentMap().GetMapType()!=Map::MapType::DUNGEON)return;
|
||||
std::string stageInventoryCategory="Stage Loot";
|
||||
if(monsterDrop){
|
||||
stageInventoryCategory="Monster Loot";
|
||||
@ -1522,4 +1524,11 @@ const float Item::CastSize()const{
|
||||
|
||||
const float ItemInfo::CastSize()const{
|
||||
return castSize;
|
||||
}
|
||||
|
||||
std::vector<std::string> Item::GetItemCategories(){
|
||||
if(ITEM_CATEGORIES.size()!=DATA["ItemCategory"].GetKeys().size())ERR(std::format("WARNING! ITEM_CATEGORIES should be populated before this function is used! Expected {} values in ITEM_CATEGORIES, Current Size: {}",DATA["ItemCategory"].GetKeys().size(),ITEM_CATEGORIES.size()));
|
||||
static std::vector<std::string>categories{};
|
||||
if(categories.empty())std::transform(ITEM_CATEGORIES.begin(),ITEM_CATEGORIES.end(),std::back_inserter(categories),[](const auto&pair){return pair.first;});
|
||||
return categories;
|
||||
}
|
||||
@ -187,19 +187,6 @@ class Item{
|
||||
friend void Merchant::SellItem(std::weak_ptr<Item>,uint32_t amt);
|
||||
friend class PlayerTests::PlayerTest;
|
||||
friend class EnchantTests::EnchantTest;
|
||||
private:
|
||||
//The amount in the current item stack.
|
||||
uint32_t amt;
|
||||
uint8_t enhancementLevel;
|
||||
ItemInfo*it;
|
||||
Stats randomizedStats;
|
||||
bool locked=false;
|
||||
std::optional<ItemEnchant>enchant{};
|
||||
|
||||
void SetAmt(uint32_t newAmt);
|
||||
static ItemEnhancementFunctionPrimingData enhanceFunctionPrimed;
|
||||
static int IsBlankStaticCallCounter;
|
||||
const bool _IsBlank()const;
|
||||
public:
|
||||
static const std::string BLANK_ITEM_NAME;
|
||||
Item();
|
||||
@ -275,7 +262,21 @@ public:
|
||||
friend const bool operator==(const IT&lhs,std::shared_ptr<Item>rhs){return operator==(rhs,lhs);};
|
||||
const Pixel GetDisplayNameColor()const;
|
||||
static const bool SelectedEquipIsDifferent(const std::weak_ptr<Item>equipAttemptingToEquip,const EquipSlot slotToEquipTo);
|
||||
static std::vector<std::string>GetItemCategories();
|
||||
const std::optional<std::string>&FragmentIcon()const;
|
||||
private:
|
||||
//The amount in the current item stack.
|
||||
uint32_t amt;
|
||||
uint8_t enhancementLevel;
|
||||
ItemInfo*it;
|
||||
Stats randomizedStats;
|
||||
bool locked=false;
|
||||
std::optional<ItemEnchant>enchant{};
|
||||
|
||||
void SetAmt(uint32_t newAmt);
|
||||
static ItemEnhancementFunctionPrimingData enhanceFunctionPrimed;
|
||||
static int IsBlankStaticCallCounter;
|
||||
const bool _IsBlank()const;
|
||||
};
|
||||
|
||||
class Inventory{
|
||||
|
||||
@ -83,9 +83,9 @@ Menu::Menu(vf2d pos,vf2d size)
|
||||
void Menu::InitializeMenus(){
|
||||
std::for_each(menus.begin(),menus.end(),[](std::pair<MenuType,Menu*>data){delete data.second;}); //EW RAW POINTER.
|
||||
menus.clear();
|
||||
for(auto&[key,value]:DATA["ItemCategory"].GetKeys()){
|
||||
inventoryListeners[key].clear();
|
||||
merchantInventoryListeners[key].clear();
|
||||
for(auto&categories:Item::GetItemCategories()){
|
||||
inventoryListeners[categories].clear();
|
||||
merchantInventoryListeners[categories].clear();
|
||||
}
|
||||
equipStatListeners.clear();
|
||||
chapterListeners.clear();
|
||||
|
||||
@ -49,11 +49,12 @@ All rights reserved.
|
||||
#include "InputDisplayComponent.h"
|
||||
#include "GameSettings.h"
|
||||
#include "Tutorial.h"
|
||||
#include"InventoryCreator.h"
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_ADMIN_MODE
|
||||
|
||||
size_t SaveFile::saveFileID=0;
|
||||
int SaveFile::saveFileID=0;
|
||||
std::string SaveFile::saveFileName="";
|
||||
std::string SaveFile::username="";
|
||||
bool SaveFile::onlineMode=false;
|
||||
@ -231,6 +232,7 @@ const bool SaveFile::SaveGame(){
|
||||
}
|
||||
|
||||
utils::datafile::INITIAL_SETUP_COMPLETE=true;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if(onlineMode){
|
||||
std::function<void(std::string_view response)>RetryResponse;
|
||||
@ -304,10 +306,11 @@ const bool SaveFile::SaveGame(){
|
||||
#else
|
||||
game->SetQuitAllowed(true);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool SaveFile::LoadFile(){
|
||||
const bool SaveFile::_LoadFile(){
|
||||
utils::datafile loadFile;
|
||||
|
||||
std::string loadFilename="save_file_path"_S+std::format("save.{:04}",saveFileID);
|
||||
@ -359,24 +362,26 @@ const bool SaveFile::LoadFile(){
|
||||
for(auto&[key,data]:loadFile["Player"]["Base Stats"].GetOrderedKeys()){
|
||||
game->GetPlayer()->SetBaseStat(key,data.GetReal());
|
||||
}
|
||||
if(loadFile.HasProperty("Unlocks")){
|
||||
for(const auto&[key,data]:loadFile["Unlocks"].GetOrderedKeys()){
|
||||
Unlock::UnlockArea(key);
|
||||
if(data.GetValueCount()>1&&data.GetBool(1U)){
|
||||
auto cps=State_OverworldMap::ConnectionPointsFromString(key);
|
||||
for(ConnectionPoint&cp:cps)cp.SetVisited();
|
||||
if(!game->TestingModeEnabled()){
|
||||
if(loadFile.HasProperty("Unlocks")){
|
||||
for(const auto&[key,data]:loadFile["Unlocks"].GetOrderedKeys()){
|
||||
Unlock::UnlockArea(key);
|
||||
if(data.GetValueCount()>1&&data.GetBool(1U)){
|
||||
auto cps=State_OverworldMap::ConnectionPointsFromString(key);
|
||||
for(ConnectionPoint&cp:cps)cp.SetVisited();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(State_OverworldMap::HasStageMarker(loadFile["Overworld Map Location"].GetString())){
|
||||
State_OverworldMap::SetStageMarker(loadFile["Overworld Map Location"].GetString());
|
||||
}
|
||||
State_OverworldMap::UpdateCurrentConnectionPoint(const_cast<ConnectionPoint&>(State_OverworldMap::GetCurrentConnectionPoint()));
|
||||
}
|
||||
if(State_OverworldMap::HasStageMarker(loadFile["Overworld Map Location"].GetString())){
|
||||
State_OverworldMap::SetStageMarker(loadFile["Overworld Map Location"].GetString());
|
||||
}
|
||||
State_OverworldMap::UpdateCurrentConnectionPoint(const_cast<ConnectionPoint&>(State_OverworldMap::GetCurrentConnectionPoint()));
|
||||
game->SetChapter(loadFile["Chapter"].GetInt());
|
||||
SaveFile::SetSaveFileName(loadFile["Save Name"].GetString());
|
||||
game->SetRuntime(loadFile["Game Time"].GetReal());
|
||||
game->GetPlayer()->RecalculateEquipStats();
|
||||
if(loadFile.HasProperty("TravelingMerchant"))Merchant::SetTravelingMerchant(loadFile["TravelingMerchant"].GetString());
|
||||
if(loadFile.HasProperty("TravelingMerchant")&&!game->TestingModeEnabled())Merchant::SetTravelingMerchant(loadFile["TravelingMerchant"].GetString());
|
||||
|
||||
if(loadFile.HasProperty("Minimap")){
|
||||
for(auto&[key,size]:loadFile["Minimap"].GetKeys()){
|
||||
@ -434,6 +439,11 @@ const void SaveFile::LoadGame(){
|
||||
|
||||
game->SetQuitAllowed(false);
|
||||
|
||||
const auto TurnOffSavingFlagAndUpdateInventoryUIs{[](){
|
||||
game->SetQuitAllowed(true);
|
||||
for(auto&category:Item::GetItemCategories())Menu::InventorySlotsUpdated(category);
|
||||
}};
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if(onlineMode){
|
||||
Server_GetFile([&](std::string_view response){
|
||||
@ -441,14 +451,16 @@ const void SaveFile::LoadGame(){
|
||||
std::ofstream file("save_file_path"_S+std::format("save.{:04}",saveFileID));
|
||||
file<<response;
|
||||
file.close();
|
||||
LoadFile();
|
||||
_LoadFile();
|
||||
TurnOffSavingFlagAndUpdateInventoryUIs();
|
||||
}else{
|
||||
game->SetQuitAllowed(true);
|
||||
LOG("WARNING! Could not load save file!");
|
||||
}
|
||||
game->SetQuitAllowed(true);
|
||||
TurnOffSavingFlagAndUpdateInventoryUIs();
|
||||
});
|
||||
}else{
|
||||
emscripten_idb_async_load("/assets",("save_file_path"_S+std::format("save.{:04}",saveFileID)).c_str(),0,[](void*arg,void*data,int length){
|
||||
emscripten_idb_async_load("/assets",("save_file_path"_S+std::format("save.{:04}",saveFileID)).c_str(),0,[&TurnOffSavingFlagAndUpdateInventoryUIs](void*arg,void*data,int length){
|
||||
LOG("Loaded Save File "<<saveFileID<<" successfully!");
|
||||
|
||||
std::string rawMetadata=(char*)data;
|
||||
@ -457,16 +469,16 @@ const void SaveFile::LoadGame(){
|
||||
file<<rawMetadata[i];
|
||||
}
|
||||
file.close();
|
||||
LoadFile();
|
||||
game->SetQuitAllowed(true);
|
||||
_LoadFile();
|
||||
TurnOffSavingFlagAndUpdateInventoryUIs();
|
||||
},[](void*arg){
|
||||
LOG("Failed to load Save File "<<saveFileID<<"!");
|
||||
game->SetQuitAllowed(true);
|
||||
});
|
||||
}
|
||||
#else
|
||||
LoadFile();
|
||||
game->SetQuitAllowed(true);
|
||||
_LoadFile();
|
||||
TurnOffSavingFlagAndUpdateInventoryUIs();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -478,7 +490,7 @@ const void SaveFile::SetSaveFileName(std::string_view saveFileName){
|
||||
SaveFile::saveFileName=saveFileName;
|
||||
}
|
||||
|
||||
const void SaveFile::SetSaveFileID(size_t saveFileID){
|
||||
const void SaveFile::SetSaveFileID(int saveFileID){
|
||||
SaveFile::saveFileID=saveFileID;
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ namespace SaveFileOperation{
|
||||
};
|
||||
|
||||
class SaveFile{
|
||||
static size_t saveFileID;
|
||||
static int saveFileID;
|
||||
static std::string saveFileName;
|
||||
static std::string username;
|
||||
static bool onlineMode;
|
||||
@ -65,8 +65,8 @@ public:
|
||||
static const void SetUserID(std::string_view userID);
|
||||
static const bool SaveGame();
|
||||
static const void LoadGame();
|
||||
static const bool LoadFile();
|
||||
static const void SetSaveFileID(size_t saveFileID);
|
||||
static const bool _LoadFile();
|
||||
static const void SetSaveFileID(int saveFileID);
|
||||
static const void SetSaveFileOfflineID_TransitionToOverworldMap();
|
||||
//Called whenever the save game data is updated.
|
||||
//WARNING! In Emscripten, this function also opens the Load Menu window!
|
||||
|
||||
@ -59,9 +59,6 @@ ConnectionPoint*State_OverworldMap::currentConnectionPoint=nullptr;
|
||||
|
||||
State_OverworldMap::State_OverworldMap(){}
|
||||
void State_OverworldMap::OnStateChange(GameState*prevState){
|
||||
Inventory::Clear("Monster Loot");
|
||||
Inventory::Clear("Stage Loot");
|
||||
|
||||
Component<MenuComponent>(MenuType::PAUSE,"Return to Camp Button")->SetGrayedOut(false);
|
||||
SaveFile::SaveGame();
|
||||
|
||||
|
||||
@ -127,9 +127,19 @@ namespace MonsterTests{
|
||||
class MonsterTest;
|
||||
}
|
||||
|
||||
namespace ItemTests{
|
||||
class ItemTest;
|
||||
}
|
||||
|
||||
namespace Game{
|
||||
void LoadFakeLevel(const std::string&mapKeyname,AiL*const game);
|
||||
};
|
||||
|
||||
struct Map{
|
||||
friend class AiL;
|
||||
friend class TMXParser;
|
||||
friend class ItemTests::ItemTest;
|
||||
friend void Game::LoadFakeLevel(const std::string&mapKeyname,AiL*const game);
|
||||
enum class MapType{
|
||||
DUNGEON,
|
||||
BOSS,
|
||||
|
||||
@ -23,7 +23,8 @@ Cherry pick 66a10cee9f874f024633efdc84ebb4432752ab15 from SkeletonFireMage
|
||||
Snow particles must fade away when Blizzard dies
|
||||
Blizzard blue area circle should remain during the duration of the attack
|
||||
Add comments to Effect child Update and Draw declarations
|
||||
Check for precache locally generated file to re-add to pack first if the date is still current.
|
||||
|
||||
The Start button on the overworld map should progress to the item loadout screen.
|
||||
|
||||
|
||||
DEMO
|
||||
|
||||
@ -39,7 +39,7 @@ All rights reserved.
|
||||
#define VERSION_MAJOR 1
|
||||
#define VERSION_MINOR 3
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_BUILD 12776
|
||||
#define VERSION_BUILD 12812
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
INTRO_MAP = 2026-02-25 23:43:58.2817113
|
||||
HUB = 2026-02-25 23:44:04.3412437
|
||||
INTRO_MAP = 2026-02-26 21:44:13.5486385
|
||||
HUB = 2026-02-26 21:44:13.5469761
|
||||
TEST_MAP = 2026-01-26 20:40:12.7201277
|
||||
BOSS_1 = 2026-02-25 22:36:51.8834583
|
||||
WORLD_MAP = 2026-02-25 23:43:58.2792179
|
||||
BOSS_1 = 2026-02-26 21:44:13.5449625
|
||||
WORLD_MAP = 2026-02-26 21:44:13.5509566
|
||||
CAMPAIGN_1_1 = 2026-01-21 20:30:58.3723266
|
||||
BOSS_1_B = 2024-09-13 21:28:58.8886132
|
||||
BOSS_2 = 2026-01-21 20:30:58.4312572
|
||||
@ -14,12 +14,12 @@ BOSS_2_B = 2026-01-21 20:30:58.4322614
|
||||
CAMPAIGN_1_2 = 2026-01-21 20:30:58.3736269
|
||||
CAMPAIGN_1_3 = 2024-09-13 21:28:58.8532684
|
||||
CAMPAIGN_1_5 = 2024-09-13 21:28:58.8557772
|
||||
CAMPAIGN_4_8 = 2026-02-02 21:11:43.0924363
|
||||
CAMPAIGN_4_8 = 2026-02-26 21:44:13.5419545
|
||||
CAMPAIGN_1_6 = 2024-09-13 21:28:58.8570782
|
||||
CAMPAIGN_1_7 = 2024-09-13 21:28:58.8580547
|
||||
CAMPAIGN_1_B1 = 2024-09-13 21:28:58.8605592
|
||||
CAMPAIGN_1_8 = 2024-09-13 21:28:58.8595553
|
||||
CAMPAIGN_4_5 = 2026-02-02 21:11:24.4671912
|
||||
CAMPAIGN_4_5 = 2026-02-26 21:44:13.5315825
|
||||
CAMPAIGN_2_1 = 2026-01-21 20:30:58.3756334
|
||||
CAMPAIGN_2_2 = 2026-01-21 20:30:58.3769261
|
||||
CAMPAIGN_2_3 = 2026-01-21 20:30:58.3794325
|
||||
@ -29,7 +29,7 @@ CAMPAIGN_2_6 = 2026-01-21 20:30:58.3836104
|
||||
CAMPAIGN_2_7 = 2026-01-21 20:30:58.3846135
|
||||
CAMPAIGN_2_8 = 2026-01-21 20:30:58.3866133
|
||||
CAMPAIGN_2_B1 = 2026-01-21 20:30:58.3876140
|
||||
CAMPAIGN_4_3 = 2026-02-02 21:11:15.8085512
|
||||
CAMPAIGN_4_3 = 2026-02-26 21:44:13.5235645
|
||||
CAMPAIGN_3_1 = 2026-01-21 20:30:58.3896136
|
||||
CAMPAIGN_3_2 = 2026-01-21 20:30:58.3916720
|
||||
CAMPAIGN_3_3 = 2026-01-21 20:30:58.3937337
|
||||
@ -39,8 +39,8 @@ CAMPAIGN_3_6 = 2026-01-21 20:30:58.4008008
|
||||
CAMPAIGN_3_7 = 2026-01-21 20:30:58.4023071
|
||||
CAMPAIGN_3_8 = 2026-01-21 20:30:58.4049027
|
||||
CAMPAIGN_3_B1 = 2026-01-21 20:30:58.4336641
|
||||
CAMPAIGN_4_1 = 2026-02-18 22:01:22.3999658
|
||||
CAMPAIGN_4_2 = 2026-02-02 21:11:09.6593901
|
||||
CAMPAIGN_4_4 = 2026-02-02 21:11:20.4201244
|
||||
CAMPAIGN_4_6 = 2026-02-02 21:11:29.3428325
|
||||
CAMPAIGN_4_7 = 2026-02-02 21:11:36.4665125
|
||||
CAMPAIGN_4_1 = 2026-02-26 21:44:13.5188507
|
||||
CAMPAIGN_4_2 = 2026-02-26 21:44:13.5210572
|
||||
CAMPAIGN_4_4 = 2026-02-26 21:44:13.5270745
|
||||
CAMPAIGN_4_6 = 2026-02-26 21:44:13.5342554
|
||||
CAMPAIGN_4_7 = 2026-02-26 21:44:13.5389009
|
||||
|
||||
@ -181,6 +181,12 @@ public:
|
||||
auto end()const{
|
||||
return items.end();
|
||||
}
|
||||
auto contains(T&key){
|
||||
return count(key);
|
||||
}
|
||||
auto contains(const T&key){
|
||||
return count(key);
|
||||
}
|
||||
auto contains(O&obj){
|
||||
return std::ranges::contains(items,obj);
|
||||
}
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user