sigonasr2, Sig, Sigo 9 months ago
commit b0f0e7dcdd
  1. 1
      .gitignore
  2. 39
      Adventures in Lestoria/Adventures in Lestoria.tiled-project
  3. 9
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  4. 9
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  5. 588
      Adventures in Lestoria/AdventuresInLestoria.cpp
  6. 10
      Adventures in Lestoria/AdventuresInLestoria.h
  7. 113
      Adventures in Lestoria/Audio.cpp
  8. 17
      Adventures in Lestoria/Audio.h
  9. 9
      Adventures in Lestoria/CharacterAbilityPreviewComponent.h
  10. 2
      Adventures in Lestoria/CharacterRotatingDisplay.h
  11. 2
      Adventures in Lestoria/DEFINES.h
  12. 1
      Adventures in Lestoria/GameState.h
  13. 6
      Adventures in Lestoria/ItemDrop.cpp
  14. 51
      Adventures in Lestoria/Key.cpp
  15. 5
      Adventures in Lestoria/Key.h
  16. 108
      Adventures in Lestoria/LoadingScreen.cpp
  17. 57
      Adventures in Lestoria/LoadingScreen.h
  18. 6
      Adventures in Lestoria/Monster.cpp
  19. 15
      Adventures in Lestoria/Player.cpp
  20. 3
      Adventures in Lestoria/Player.h
  21. 16
      Adventures in Lestoria/SoundEffect.cpp
  22. 3
      Adventures in Lestoria/SoundEffect.h
  23. 2
      Adventures in Lestoria/State_GameHub.cpp
  24. 1
      Adventures in Lestoria/State_GameHub.h
  25. 9
      Adventures in Lestoria/State_GameRun.cpp
  26. 1
      Adventures in Lestoria/State_GameRun.h
  27. 1
      Adventures in Lestoria/State_LevelComplete.cpp
  28. 1
      Adventures in Lestoria/State_LevelComplete.h
  29. 55
      Adventures in Lestoria/State_MainMenu.cpp
  30. 9
      Adventures in Lestoria/State_MainMenu.h
  31. 4
      Adventures in Lestoria/State_OverworldMap.cpp
  32. 1
      Adventures in Lestoria/State_OverworldMap.h
  33. 1
      Adventures in Lestoria/State_Story.cpp
  34. 1
      Adventures in Lestoria/State_Story.h
  35. 29
      Adventures in Lestoria/TMXParser.h
  36. 23
      Adventures in Lestoria/TODO.txt
  37. 4
      Adventures in Lestoria/Test.cpp
  38. 2
      Adventures in Lestoria/Version.h
  39. 10
      Adventures in Lestoria/VisualNovel.cpp
  40. 5
      Adventures in Lestoria/VisualNovel.h
  41. 1628
      Adventures in Lestoria/assets/Campaigns/Intro_Map.tmx
  42. 2
      Adventures in Lestoria/assets/Campaigns/World_Map.tmx
  43. BIN
      Adventures in Lestoria/assets/backgrounds/cave.png
  44. BIN
      Adventures in Lestoria/assets/backgrounds/cave_dark.png
  45. BIN
      Adventures in Lestoria/assets/backgrounds/rest.png
  46. BIN
      Adventures in Lestoria/assets/backgrounds/sea.png
  47. 27
      Adventures in Lestoria/assets/config/audio/events.txt
  48. 2
      Adventures in Lestoria/assets/config/configuration.txt
  49. 5
      Adventures in Lestoria/assets/config/levels.txt
  50. 5
      Adventures in Lestoria/assets/config/story/Chapter 1.txt
  51. 2
      Adventures in Lestoria/assets/maps/112x96_Forge_No_Shadow_12x12.tsx
  52. 2
      Adventures in Lestoria/assets/maps/Decorations_c1_No_Shadow24x24.tsx
  53. 2
      Adventures in Lestoria/assets/maps/Minifantasy_TinyOverworldAllTiles.tsx
  54. BIN
      Adventures in Lestoria/assets/themes/lmb.png
  55. BIN
      Adventures in Lestoria/assets/themes/mmb.png
  56. BIN
      Adventures in Lestoria/assets/themes/rmb.png
  57. 10
      Adventures in Lestoria/olcPGEX_TTF.h
  58. 3
      Adventures in Lestoria/packkey.cpp
  59. 1
      distribute.ps1
  60. 4005
      git-filter-repo

1
.gitignore vendored

@ -396,3 +396,4 @@ build/CMakeFiles/3.16.3/CompilerIdC/CMakeCCompilerId.c
build/CMakeFiles/3.16.3/CompilerIdCXX/CMakeCXXCompilerId.cpp build/CMakeFiles/3.16.3/CompilerIdCXX/CMakeCXXCompilerId.cpp
test.cpp test.cpp
/Adventures in Lestoria/Adventures in Lestoria /Adventures in Lestoria/Adventures in Lestoria
/Adventures in Lestoria/packkey.cpp

@ -7,6 +7,8 @@
"folders": [ "folders": [
"." "."
], ],
"properties": [
],
"propertyTypes": [ "propertyTypes": [
{ {
"color": "#ff3af8eb", "color": "#ff3af8eb",
@ -170,6 +172,26 @@
"layer" "layer"
] ]
}, },
{
"color": "#ff4f4f51",
"drawFill": true,
"id": 37,
"members": [
{
"name": "Scroll Direction",
"propertyType": "ScrollDirection",
"type": "string",
"value": "NORTH"
}
],
"name": "Focus Area",
"type": "class",
"useAs": [
"property",
"object",
"project"
]
},
{ {
"color": "#ffd9d929", "color": "#ffd9d929",
"drawFill": true, "drawFill": true,
@ -420,6 +442,23 @@
"tile" "tile"
] ]
}, },
{
"id": 38,
"name": "ScrollDirection",
"storageType": "string",
"type": "enum",
"values": [
"NORTH",
"NORTHEAST",
"EAST",
"SOUTHEAST",
"SOUTH",
"SOUTHWEST",
"WEST",
"NORTHWEST"
],
"valuesAsFlags": false
},
{ {
"color": "#ffe67300", "color": "#ffe67300",
"drawFill": true, "drawFill": true,

@ -387,6 +387,10 @@
<SubType> <SubType>
</SubType> </SubType>
</ClInclude> </ClInclude>
<ClInclude Include="LoadingScreen.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="MenuDefinitions.h" /> <ClInclude Include="MenuDefinitions.h" />
<ClInclude Include="MenuType.h"> <ClInclude Include="MenuType.h">
<SubType> <SubType>
@ -643,6 +647,10 @@
<SubType> <SubType>
</SubType> </SubType>
</ClCompile> </ClCompile>
<ClCompile Include="LoadingScreen.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" />
@ -670,6 +678,7 @@
<SubType> <SubType>
</SubType> </SubType>
</ClCompile> </ClCompile>
<ClCompile Include="packkey.cpp" />
<ClCompile Include="PauseMenu.cpp"> <ClCompile Include="PauseMenu.cpp">
<SubType> <SubType>
</SubType> </SubType>

@ -465,6 +465,9 @@
<ClInclude Include="IconType.h"> <ClInclude Include="IconType.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="LoadingScreen.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Player.cpp"> <ClCompile Include="Player.cpp">
@ -812,6 +815,12 @@
<ClCompile Include="GameSettings.cpp"> <ClCompile Include="GameSettings.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="LoadingScreen.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="packkey.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="cpp.hint" /> <None Include="cpp.hint" />

@ -76,11 +76,13 @@ All rights reserved.
#include "discord.h" #include "discord.h"
#endif #endif
#include "GameSettings.h" #include "GameSettings.h"
#include "LoadingScreen.h"
INCLUDE_EMITTER_LIST INCLUDE_EMITTER_LIST
INCLUDE_ITEM_CATEGORIES INCLUDE_ITEM_CATEGORIES
INCLUDE_BACKDROP_DATA INCLUDE_BACKDROP_DATA
INCLUDE_MONSTER_DATA INCLUDE_MONSTER_DATA
INCLUDE_PACK_KEY
bool _DEBUG_MAP_LOAD_INFO = false; bool _DEBUG_MAP_LOAD_INFO = false;
//360x240 //360x240
@ -226,8 +228,7 @@ AiL::AiL()
} }
bool AiL::OnUserCreate(){ bool AiL::OnUserCreate(){
std::string packKey="129jvgndsaf7dsa8932hj"; gamepack.LoadPack("assets/"+"gamepack_file"_S,PACK_KEY);
gamepack.LoadPack("assets/"+"gamepack_file"_S,packKey);
GamePad::init(); GamePad::init();
@ -278,7 +279,6 @@ bool AiL::OnUserCreate(){
Inventory::AddItem("Minor Health Potion"s,3); Inventory::AddItem("Minor Health Potion"s,3);
Inventory::AddItem("Bandages"s,10); Inventory::AddItem("Bandages"s,10);
LoadLevel("starting_map"_S);
ChangePlayerClass(WARRIOR); ChangePlayerClass(WARRIOR);
GameState::Initialize(); GameState::Initialize();
@ -320,7 +320,7 @@ bool AiL::OnUserCreate(){
gameInitialized=true; gameInitialized=true;
if(!gamepack.Loaded()&&"GENERATE_GAMEPACK"_B){ if(!gamepack.Loaded()&&"GENERATE_GAMEPACK"_B){
gamepack.SavePack("assets/"+"gamepack_file"_S,packKey); gamepack.SavePack("assets/"+"gamepack_file"_S,PACK_KEY);
std::cout<<"Game Pack has been generated!"<<std::endl<<"========================"<<std::endl<<std::endl; std::cout<<"Game Pack has been generated!"<<std::endl<<"========================"<<std::endl<<std::endl;
} }
@ -336,6 +336,7 @@ bool AiL::OnUserUpdate(float fElapsedTime){
if(!GamePaused()){ if(!GamePaused()){
GameState::STATE->OnUserUpdate(this); GameState::STATE->OnUserUpdate(this);
} }
LoadingScreen::Update();
InputListener::Update(); InputListener::Update();
Audio::Update(); Audio::Update();
RenderWorld(GetElapsedTime()); RenderWorld(GetElapsedTime());
@ -343,6 +344,7 @@ bool AiL::OnUserUpdate(float fElapsedTime){
RenderMenu(); RenderMenu();
GameState::STATE->DrawOverlay(this); GameState::STATE->DrawOverlay(this);
RenderFadeout(); RenderFadeout();
LoadingScreen::Draw();
RenderVersionInfo(); RenderVersionInfo();
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
if(Discord){ if(Discord){
@ -898,6 +900,7 @@ void AiL::RenderWorld(float fElapsedTime){
PopulateRenderLists(); PopulateRenderLists();
auto RenderPlayer=[&](vf2d pos,vf2d scale){ auto RenderPlayer=[&](vf2d pos,vf2d scale){
if(player->IsInvisible())return;
vf2d playerScale=vf2d(player->GetSizeMult(),player->GetSizeMult()); vf2d playerScale=vf2d(player->GetSizeMult(),player->GetSizeMult());
int count=0; int count=0;
for(vf2d&pos:player->ghostPositions){ for(vf2d&pos:player->ghostPositions){
@ -1153,7 +1156,9 @@ void AiL::RenderWorld(float fElapsedTime){
if(view.IsRectVisible(group.GetRange().pos,group.GetRange().size)){ if(view.IsRectVisible(group.GetRange().pos,group.GetRange().size)){
if(geom2d::overlaps(group.GetFadeRange(),player->pos)){ if(geom2d::overlaps(group.GetFadeRange(),player->pos)){
group.playerBehind=true; group.playerBehind=true;
group.fadeFactor=std::min(group.fadeFactor+fElapsedTime,TileGroup::FADE_TIME); if(GameState::STATE!=GameState::states[States::MAIN_MENU]){ //Don't fade out tile groups while we are on the main menu.
group.fadeFactor=std::min(group.fadeFactor+fElapsedTime,TileGroup::FADE_TIME);
}
} else { } else {
group.playerBehind=false; group.playerBehind=false;
group.fadeFactor=std::max(group.fadeFactor-fElapsedTime,0.f); group.fadeFactor=std::max(group.fadeFactor-fElapsedTime,0.f);
@ -1319,7 +1324,9 @@ void AiL::RenderWorld(float fElapsedTime){
if(view.IsRectVisible(group.GetRange().pos,group.GetRange().size)){ if(view.IsRectVisible(group.GetRange().pos,group.GetRange().size)){
if(geom2d::overlaps(group.GetFadeRange(),player->pos)){ if(geom2d::overlaps(group.GetFadeRange(),player->pos)){
group.playerBehind=true; group.playerBehind=true;
group.fadeFactor=std::min(group.fadeFactor+fElapsedTime,TileGroup::FADE_TIME); if(GameState::STATE!=GameState::states[States::MAIN_MENU]){ //Don't fade out tile groups while we are on the main menu.
group.fadeFactor=std::min(group.fadeFactor+fElapsedTime,TileGroup::FADE_TIME);
}
} else { } else {
group.playerBehind=false; group.playerBehind=false;
group.fadeFactor=std::max(group.fadeFactor-fElapsedTime,0.f); group.fadeFactor=std::max(group.fadeFactor-fElapsedTime,0.f);
@ -1582,6 +1589,16 @@ void AiL::RenderCooldowns(){
const auto DrawCooldown=[&](vf2d pos,Ability&a,int loadoutSlot=-1/*Set to 0-2 to get an item slot rendered instead*/){ const auto DrawCooldown=[&](vf2d pos,Ability&a,int loadoutSlot=-1/*Set to 0-2 to get an item slot rendered instead*/){
bool circle=loadoutSlot==-1; bool circle=loadoutSlot==-1;
if(a.name!="???"){ if(a.name!="???"){
vf2d keyDisplaySize=vf2d{GetTextSize(a.input->GetDisplayName())}*vf2d{0.5f,0.5f};
InputType controlType=KEY;
if(Input::UsingGamepad())controlType=CONTROLLER;
vf2d drawOffset={};
if(loadoutSlot!=-1){
drawOffset.y+=2.f;
}
a.input->DrawInput(game,pos+vf2d{14,0.f}+drawOffset,"",255,controlType,{0.75f,0.75f});
if(a.cooldown>0.1){ if(a.cooldown>0.1){
vf2d iconScale={1,1}; vf2d iconScale={1,1};
if(loadoutSlot!=-1)iconScale={0.7f,0.7f}; if(loadoutSlot!=-1)iconScale={0.7f,0.7f};
@ -1647,9 +1664,6 @@ void AiL::RenderCooldowns(){
vf2d manaCostSize=vf2d{GetTextSize(std::to_string(a.manaCost))}*vf2d{0.5f,0.75f}; vf2d manaCostSize=vf2d{GetTextSize(std::to_string(a.manaCost))}*vf2d{0.5f,0.75f};
DrawShadowStringDecal(pos+vf2d{20,4}-manaCostSize/2,std::to_string(a.manaCost),{192,192,255},manaCostShadowCol,{0.5f,0.75f}); DrawShadowStringDecal(pos+vf2d{20,4}-manaCostSize/2,std::to_string(a.manaCost),{192,192,255},manaCostShadowCol,{0.5f,0.75f});
} }
vf2d keyDisplaySize=vf2d{GetTextSize(a.input->GetDisplayName())}*vf2d{0.5f,0.5f};
DrawShadowStringDecal(pos+vf2d{12,-2}-keyDisplaySize/2,a.input->GetDisplayName(),keyDisplayCol,BLACK,{0.5f,0.5f},std::numeric_limits<float>::max(),1);
vf2d shortNameSize=vf2d{GetTextSize(a.shortName)}*vf2d{0.5f,0.75f}; vf2d shortNameSize=vf2d{GetTextSize(a.shortName)}*vf2d{0.5f,0.75f};
DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,{255,255,255,230},{0.5f,0.75f}); DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,{255,255,255,230},{0.5f,0.75f});
@ -1820,282 +1834,354 @@ void AiL::InitializeLevel(std::string mapFile,MapName map){
} }
} }
void AiL::LoadLevel(MapName map){ void AiL::LoadLevel(MapName map,MusicChange changeMusic){
if(game->MAP_DATA.count(map)==0)ERR(std::format("WARNING! Could not load map {}! Does not exist! Refer to levels.txt for valid maps.",map)); LoadingScreen::loading=true;
if(game->MAP_DATA[map].GetMapType()=="Hub"&&GameState::STATE!=GameState::states[States::GAME_HUB])ERR("WARNING! A hub level should only be initiated in the GAME_HUB game state!"); _PrepareLevel(map,changeMusic);
}
#pragma region Reset Environmental Audio
for(EnvironmentalAudio&audio:MAP_DATA[GetCurrentLevel()].environmentalAudioData){
audio.Deactivate();
}
#pragma endregion
SPAWNER_LIST.clear(); void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
foregroundTileGroups.clear(); LoadingScreen::Reset();
upperForegroundTileGroups.clear(); previousLevel=currentLevel;
MONSTER_LIST.clear();
ZONE_LIST.clear();
ItemDrop::drops.clear();
GameEvent::events.clear();
worldColor=WHITE;
worldColorFunc=[&](vi2d pos){return game->worldColor;};
currentLevel=map; currentLevel=map;
levelTime=0; #pragma region Reset all data (Loading phase 1)
bossName=""; LoadingScreen::AddPhase([&](){
encounterDuration=0; if(game->MAP_DATA.count(GetCurrentLevel())==0)ERR(std::format("WARNING! Could not load map {}! Does not exist! Refer to levels.txt for valid maps.",map));
totalDamageDealt=0; if(game->MAP_DATA[GetCurrentLevel()].GetMapType()=="Hub"&&GameState::STATE!=GameState::states[States::GAME_HUB])ERR("WARNING! A hub level should only be initiated in the GAME_HUB game state!");
encounterStarted=false;
totalBossEncounterMobs=0; #pragma region Reset Environmental Audio
Inventory::Clear("Monster Loot"); for(EnvironmentalAudio&audio:MAP_DATA[previousLevel].environmentalAudioData){
Inventory::Clear("Stage Loot"); audio.Deactivate();
}
GetPlayer()->hp=GetPlayer()->GetMaxHealth(); #pragma endregion
GetPlayer()->mana=GetPlayer()->GetMaxMana();
GetPlayer()->SetState(State::NORMAL); SPAWNER_LIST.clear();
GetPlayer()->GetCastInfo()={}; foregroundTileGroups.clear();
GetPlayer()->ResetAccumulatedXP(); upperForegroundTileGroups.clear();
MONSTER_LIST.clear();
ZONE_LIST=game->MAP_DATA[game->GetCurrentLevel()].ZoneData; ZONE_LIST.clear();
ItemDrop::drops.clear();
GameEvent::events.clear();
worldColor=WHITE;
worldColorFunc=[&](vi2d pos){return game->worldColor;};
levelTime=0;
bossName="";
encounterDuration=0;
totalDamageDealt=0;
encounterStarted=false;
totalBossEncounterMobs=0;
Inventory::Clear("Monster Loot");
Inventory::Clear("Stage Loot");
GetPlayer()->hp=GetPlayer()->GetMaxHealth();
GetPlayer()->mana=GetPlayer()->GetMaxMana();
GetPlayer()->SetState(State::NORMAL);
GetPlayer()->GetCastInfo()={};
GetPlayer()->ResetAccumulatedXP();
GetPlayer()->SetIframes(0.f);
GetPlayer()->SetInvisible(false);
ZONE_LIST=game->MAP_DATA[game->GetCurrentLevel()].ZoneData;
return true;
});
#pragma endregion
#pragma region Monster Spawn Data Setup #pragma region Monster Spawn Data Setup (Loading phase 2)
for(auto&[key,value]:MAP_DATA[map].SpawnerData){ LoadingScreen::AddPhase([&](){
SpawnerTag&spawnData=MAP_DATA[map].SpawnerData[key]; for(auto&[key,value]:MAP_DATA[GetCurrentLevel()].SpawnerData){
std::vector<std::pair<std::string,vf2d>>monster_list; SpawnerTag&spawnData=MAP_DATA[GetCurrentLevel()].SpawnerData[key];
std::vector<std::pair<std::string,vf2d>>monster_list;
vf2d spawnerRadius=vf2d{spawnData.ObjectData.GetFloat("width"),spawnData.ObjectData.GetFloat("height")}/2; vf2d spawnerRadius=vf2d{spawnData.ObjectData.GetFloat("width"),spawnData.ObjectData.GetFloat("height")}/2;
for(XMLTag&monster:spawnData.monsters){ for(XMLTag&monster:spawnData.monsters){
std::string monsterName=monster.GetString("value"); std::string monsterName=monster.GetString("value");
monster_list.push_back({monsterName,{monster.GetInteger("x")-spawnData.ObjectData.GetFloat("x"),monster.GetInteger("y")-spawnData.ObjectData.GetFloat("y")}}); monster_list.push_back({monsterName,{monster.GetInteger("x")-spawnData.ObjectData.GetFloat("x"),monster.GetInteger("y")-spawnData.ObjectData.GetFloat("y")}});
}
SPAWNER_LIST.push_back(MonsterSpawner{{spawnData.ObjectData.GetFloat("x"),spawnData.ObjectData.GetFloat("y")},spawnerRadius*2,monster_list,spawnData.upperLevel,spawnData.bossNameDisplay});
} }
SPAWNER_LIST.push_back(MonsterSpawner{{spawnData.ObjectData.GetFloat("x"),spawnData.ObjectData.GetFloat("y")},spawnerRadius*2,monster_list,spawnData.upperLevel,spawnData.bossNameDisplay}); return true;
} });
#pragma endregion #pragma endregion
#pragma region Identify Upper Foreground Tiles #pragma region Identify Upper Foreground Tiles (Loading phase 3)
auto GetUpperZones=[&](){ LoadingScreen::AddPhase([&](){
for(auto&zoneSet:MAP_DATA[map].ZoneData){ auto GetUpperZones=[&](){
if(zoneSet.first=="UpperZone"){ //We are interested in all upper zones. for(auto&zoneSet:MAP_DATA[GetCurrentLevel()].ZoneData){
return zoneSet.second; if(zoneSet.first=="UpperZone"){ //We are interested in all upper zones.
return zoneSet.second;
}
} }
} return std::vector<ZoneData>{};
return std::vector<ZoneData>{}; };
}; for(ZoneData&zone:GetUpperZones()){
for(ZoneData&zone:GetUpperZones()){ int zoneX=zone.zone.pos.x/game->GetCurrentMapData().tilewidth; //snap to grid
int zoneX=zone.zone.pos.x/game->GetCurrentMapData().tilewidth; //snap to grid int zoneY=zone.zone.pos.y/game->GetCurrentMapData().tilewidth;
int zoneY=zone.zone.pos.y/game->GetCurrentMapData().tilewidth; int zoneW=zone.zone.right().start.x/game->GetCurrentMapData().tilewidth-zoneX;
int zoneW=zone.zone.right().start.x/game->GetCurrentMapData().tilewidth-zoneX; int zoneH=zone.zone.bottom().start.y/game->GetCurrentMapData().tilewidth-zoneY;
int zoneH=zone.zone.bottom().start.y/game->GetCurrentMapData().tilewidth-zoneY; for(int x=zoneX;x<zoneX+zoneW;x++){
for(int x=zoneX;x<zoneX+zoneW;x++){ for(int y=zoneY;y<zoneY+zoneH;y++){
for(int y=zoneY;y<zoneY+zoneH;y++){ for(LayerTag&layer:MAP_DATA[GetCurrentLevel()].LayerData){
for(LayerTag&layer:MAP_DATA[map].LayerData){ int tile=layer.tiles[y][x]-1;
int tile=layer.tiles[y][x]-1; TilesheetData tileSheet=GetTileSheet(currentLevel,tile);
TilesheetData tileSheet=GetTileSheet(currentLevel,tile); int tileSheetIndex=tile-(tileSheet.firstgid-1);
int tileSheetIndex=tile-(tileSheet.firstgid-1); if(IsForegroundTile(tileSheet,tileSheetIndex)){
if(IsForegroundTile(tileSheet,tileSheetIndex)){ layer.tiles[y][x]+=1000000;
layer.tiles[y][x]+=1000000; }
} }
} }
} }
} }
} return true;
});
#pragma endregion #pragma endregion
#pragma region Foreground and Upper Foreground Tile Fade Group Setup #pragma region Foreground and Upper Foreground Tile Fade Group Setup (Loading phase 4)
std::set<vi2d>foregroundTilesAdded,upperForegroundTilesAdded; LoadingScreen::AddPhase([&](){
for(int x=0;x<GetCurrentMapData().width;x++){ std::set<vi2d>foregroundTilesAdded,upperForegroundTilesAdded;
for(int y=0;y<GetCurrentMapData().height;y++){ for(int x=0;x<GetCurrentMapData().width;x++){
int layerID=0; for(int y=0;y<GetCurrentMapData().height;y++){
for(LayerTag&layer:MAP_DATA[currentLevel].LayerData){ int layerID=0;
if(Unlock::IsUnlocked(layer.unlockCondition)){ for(LayerTag&layer:MAP_DATA[currentLevel].LayerData){
int tileID=layer.tiles[y][x]-1; if(Unlock::IsUnlocked(layer.unlockCondition)){
if(tileID!=-1){ int tileID=layer.tiles[y][x]-1;
TilesheetData tileSheet=GetTileSheet(currentLevel,tileID); if(tileID!=-1){
int tileSheetWidth=tileSheet.tileset->tileset->Sprite()->width/tileSheet.tileset->tilewidth; TilesheetData tileSheet=GetTileSheet(currentLevel,tileID);
int tileSheetHeight=tileSheet.tileset->tileset->Sprite()->height/tileSheet.tileset->tileheight; int tileSheetWidth=tileSheet.tileset->tileset->Sprite()->width/tileSheet.tileset->tilewidth;
int tileSheetIndex=tileID-(tileSheet.firstgid-1); int tileSheetHeight=tileSheet.tileset->tileset->Sprite()->height/tileSheet.tileset->tileheight;
int realTileSheetIndex=(tileID%1000000)-(tileSheet.firstgid-1); int tileSheetIndex=tileID-(tileSheet.firstgid-1);
int tileSheetX=realTileSheetIndex%tileSheetWidth; int realTileSheetIndex=(tileID%1000000)-(tileSheet.firstgid-1);
int tileSheetY=realTileSheetIndex/tileSheetWidth; int tileSheetX=realTileSheetIndex%tileSheetWidth;
int checkTileIndex=tileID; int tileSheetY=realTileSheetIndex/tileSheetWidth;
int checkTileID=tileSheetIndex; int checkTileIndex=tileID;
#pragma region TileGroupShenanigans int checkTileID=tileSheetIndex;
auto SetupTileGroups=[&](std::function<bool(TilesheetData,int)>IsForeground,TileRenderData tile,std::set<vi2d>&foregroundTilesIncluded,std::vector<TileGroup>&groups){ #pragma region TileGroupShenanigans
if(foregroundTilesIncluded.find({x,y})==foregroundTilesIncluded.end()&&IsForeground(tileSheet,tileSheetIndex)){ auto SetupTileGroups=[&](std::function<bool(TilesheetData,int)>IsForeground,TileRenderData tile,std::set<vi2d>&foregroundTilesIncluded,std::vector<TileGroup>&groups){
std::queue<vi2d>tileGroupChecks; if(foregroundTilesIncluded.find({x,y})==foregroundTilesIncluded.end()&&IsForeground(tileSheet,tileSheetIndex)){
TileGroup group; std::queue<vi2d>tileGroupChecks;
foregroundTilesIncluded.insert({x,y}); TileGroup group;
group.InsertTile(tile); foregroundTilesIncluded.insert({x,y});
if(x>0&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{-1,0})==foregroundTilesIncluded.end())tileGroupChecks.push({x-1,y}); group.InsertTile(tile);
if(x<GetCurrentMapData().width-1&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{1,0})==foregroundTilesIncluded.end())tileGroupChecks.push({x+1,y}); if(x>0&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{-1,0})==foregroundTilesIncluded.end())tileGroupChecks.push({x-1,y});
if(y>0&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{0,-1})==foregroundTilesIncluded.end())tileGroupChecks.push({x,y-1}); if(x<GetCurrentMapData().width-1&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{1,0})==foregroundTilesIncluded.end())tileGroupChecks.push({x+1,y});
if(y<GetCurrentMapData().height-1&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{0,1})==foregroundTilesIncluded.end())tileGroupChecks.push({x,y+1}); if(y>0&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{0,-1})==foregroundTilesIncluded.end())tileGroupChecks.push({x,y-1});
auto IterateThroughOtherLayers=[&](vi2d pos,bool loopAll=false){ if(y<GetCurrentMapData().height-1&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{0,1})==foregroundTilesIncluded.end())tileGroupChecks.push({x,y+1});
int layer2ID=0; auto IterateThroughOtherLayers=[&](vi2d pos,bool loopAll=false){
bool hadForeground=false; int layer2ID=0;
for(LayerTag&layer2:MAP_DATA[currentLevel].LayerData){ bool hadForeground=false;
if(!loopAll&&&layer==&layer2){layer2ID++;continue;}; for(LayerTag&layer2:MAP_DATA[currentLevel].LayerData){
int tileID=layer2.tiles[pos.y][pos.x]-1; if(!loopAll&&&layer==&layer2){layer2ID++;continue;};
TilesheetData tileSheet=GetTileSheet(currentLevel,tileID%1000000); int tileID=layer2.tiles[pos.y][pos.x]-1;
int tileSheetWidth=tileSheet.tileset->tileset->Sprite()->width/tileSheet.tileset->tilewidth; TilesheetData tileSheet=GetTileSheet(currentLevel,tileID%1000000);
int tileSheetHeight=tileSheet.tileset->tileset->Sprite()->height/tileSheet.tileset->tileheight; int tileSheetWidth=tileSheet.tileset->tileset->Sprite()->width/tileSheet.tileset->tilewidth;
int tileSheetIndex=tileID-(tileSheet.firstgid-1); int tileSheetHeight=tileSheet.tileset->tileset->Sprite()->height/tileSheet.tileset->tileheight;
int realTileSheetIndex=(tileID%1000000)-(tileSheet.firstgid-1); int tileSheetIndex=tileID-(tileSheet.firstgid-1);
int tileSheetX=realTileSheetIndex%tileSheetWidth; int realTileSheetIndex=(tileID%1000000)-(tileSheet.firstgid-1);
int tileSheetY=realTileSheetIndex/tileSheetWidth; int tileSheetX=realTileSheetIndex%tileSheetWidth;
TileRenderData tile={tileSheet,vi2d{pos.x,pos.y}*game->GetCurrentMapData().tilewidth,vi2d{tileSheetX,tileSheetY}*game->GetCurrentMapData().tilewidth,realTileSheetIndex,layer2ID}; int tileSheetY=realTileSheetIndex/tileSheetWidth;
if(IsForeground(tileSheet,tileSheetIndex)){ TileRenderData tile={tileSheet,vi2d{pos.x,pos.y}*game->GetCurrentMapData().tilewidth,vi2d{tileSheetX,tileSheetY}*game->GetCurrentMapData().tilewidth,realTileSheetIndex,layer2ID};
foregroundTilesIncluded.insert({pos.x,pos.y}); if(IsForeground(tileSheet,tileSheetIndex)){
group.InsertTile(tile); foregroundTilesIncluded.insert({pos.x,pos.y});
hadForeground=true; group.InsertTile(tile);
hadForeground=true;
}
layer2ID++;
} }
layer2ID++; return hadForeground;
} };
return hadForeground; IterateThroughOtherLayers({x,y});
}; while(!tileGroupChecks.empty()){
IterateThroughOtherLayers({x,y}); vi2d&pos=tileGroupChecks.front();
while(!tileGroupChecks.empty()){ if(IterateThroughOtherLayers(pos,true)){
vi2d&pos=tileGroupChecks.front(); foregroundTilesIncluded.insert({pos.x,pos.y}); //Regardless of if we found a foreground tile or not, we need to add this to not get stuck in an infinite loop.
if(IterateThroughOtherLayers(pos,true)){ vi2d targetPos=pos+vi2d{-1,0};
foregroundTilesIncluded.insert({pos.x,pos.y}); //Regardless of if we found a foreground tile or not, we need to add this to not get stuck in an infinite loop. if(pos.x>0&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);}
vi2d targetPos=pos+vi2d{-1,0}; targetPos=pos+vi2d{1,0};
if(pos.x>0&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);} if(pos.x<GetCurrentMapData().width-1&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);}
targetPos=pos+vi2d{1,0}; targetPos=pos+vi2d{0,-1};
if(pos.x<GetCurrentMapData().width-1&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);} if(pos.y>0&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);}
targetPos=pos+vi2d{0,-1}; targetPos=pos+vi2d{0,1};
if(pos.y>0&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);} if(pos.y<GetCurrentMapData().height-1&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);}
targetPos=pos+vi2d{0,1}; }
if(pos.y<GetCurrentMapData().height-1&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);} tileGroupChecks.pop();
} }
tileGroupChecks.pop(); groups.push_back(group);
} }
groups.push_back(group); };
} TileRenderData tile={tileSheet,vi2d{x,y}*game->GetCurrentMapData().tilewidth,vi2d{tileSheetX,tileSheetY}*game->GetCurrentMapData().tilewidth,realTileSheetIndex,layerID};
}; SetupTileGroups([&](TilesheetData sheet,int tileID){return IsForegroundTile(sheet,tileID);},tile,foregroundTilesAdded,foregroundTileGroups);
TileRenderData tile={tileSheet,vi2d{x,y}*game->GetCurrentMapData().tilewidth,vi2d{tileSheetX,tileSheetY}*game->GetCurrentMapData().tilewidth,realTileSheetIndex,layerID}; SetupTileGroups([&](TilesheetData sheet,int tileID){return IsUpperForegroundTile(tileID);},tile,upperForegroundTilesAdded,upperForegroundTileGroups);
SetupTileGroups([&](TilesheetData sheet,int tileID){return IsForegroundTile(sheet,tileID);},tile,foregroundTilesAdded,foregroundTileGroups); #pragma endregion
SetupTileGroups([&](TilesheetData sheet,int tileID){return IsUpperForegroundTile(tileID);},tile,upperForegroundTilesAdded,upperForegroundTileGroups); }
#pragma endregion
} }
layerID++;
} }
layerID++;
} }
} }
}
for(TileGroup&group:foregroundTileGroups){ for(TileGroup&group:foregroundTileGroups){
std::sort(group.GetTiles().begin(),group.GetTiles().end(),[](TileRenderData&t1,TileRenderData&t2){return t1.layerID<t2.layerID;}); std::sort(group.GetTiles().begin(),group.GetTiles().end(),[](TileRenderData&t1,TileRenderData&t2){return t1.layerID<t2.layerID;});
} }
for(TileGroup&group:upperForegroundTileGroups){ for(TileGroup&group:upperForegroundTileGroups){
std::sort(group.GetTiles().begin(),group.GetTiles().end(),[](TileRenderData&t1,TileRenderData&t2){return t1.layerID<t2.layerID;}); std::sort(group.GetTiles().begin(),group.GetTiles().end(),[](TileRenderData&t1,TileRenderData&t2){return t1.layerID<t2.layerID;});
} }
return true;
});
#pragma endregion #pragma endregion
#pragma region Foreground and Upper Foreground Tile Fade Group Individual Object Grouping Splitting #pragma region Foreground and Upper Foreground Tile Fade Group Individual Object Grouping Splitting (Loading phase 5)
auto SplitUp=[&](std::vector<TileGroup>&group){ LoadingScreen::AddPhase([&](){
std::multimap<vi2d,TileRenderData>data; auto SplitUp=[&](std::vector<TileGroup>&group){
using TileDataGroup=std::multimap<vi2d,TileRenderData>; //See below. std::multimap<vi2d,TileRenderData>data;
std::vector<TileDataGroup>splitUpData; //This stores every tile group with tiles as a multi map. using TileDataGroup=std::multimap<vi2d,TileRenderData>; //See below.
std::set<vi2d>iteratedTiles; std::vector<TileDataGroup>splitUpData; //This stores every tile group with tiles as a multi map.
for(TileGroup&group:group){ std::set<vi2d>iteratedTiles;
for(TileRenderData&tile:group.GetTiles()){ for(TileGroup&group:group){
data.insert({tile.pos,tile}); for(TileRenderData&tile:group.GetTiles()){
data.insert({tile.pos,tile});
}
} }
}
auto IsAdjacent=[](int tile1,int tile2,int tileSheetWidth){ auto IsAdjacent=[](int tile1,int tile2,int tileSheetWidth){
return abs(tile1-tile2)==1||abs(tile1-tile2)>=tileSheetWidth-1&&abs(tile1-tile2)<=tileSheetWidth+1; return abs(tile1-tile2)==1||abs(tile1-tile2)>=tileSheetWidth-1&&abs(tile1-tile2)<=tileSheetWidth+1;
}; };
for(auto&[key,tile]:data){ for(auto&[key,tile]:data){
if(iteratedTiles.count(key))continue; if(iteratedTiles.count(key))continue;
vi2d loc=key; vi2d loc=key;
auto loc_tiles=data.equal_range(loc); auto loc_tiles=data.equal_range(loc);
for(auto&it=loc_tiles.first;it!=loc_tiles.second;++it){ //For each tile that exists at this position... for(auto&it=loc_tiles.first;it!=loc_tiles.second;++it){ //For each tile that exists at this position...
TileRenderData&tile=(*it).second; TileRenderData&tile=(*it).second;
bool groupFound=false; bool groupFound=false;
for(TileDataGroup&group:splitUpData){ //See if this position can fit into any existing tile groups for(TileDataGroup&group:splitUpData){ //See if this position can fit into any existing tile groups
for(int y=-game->GetCurrentMapData().tileheight;y<=game->GetCurrentMapData().tileheight;y+=game->GetCurrentMapData().tileheight){ for(int y=-game->GetCurrentMapData().tileheight;y<=game->GetCurrentMapData().tileheight;y+=game->GetCurrentMapData().tileheight){
for(int x=-game->GetCurrentMapData().tilewidth;x<=game->GetCurrentMapData().tilewidth;x+=game->GetCurrentMapData().tilewidth){ for(int x=-game->GetCurrentMapData().tilewidth;x<=game->GetCurrentMapData().tilewidth;x+=game->GetCurrentMapData().tilewidth){
if(x!=0||y!=0){ //Check every adjacent location for a possible adjacent tile. if(x!=0||y!=0){ //Check every adjacent location for a possible adjacent tile.
vi2d checkOffset=loc+vi2d{x,y}; vi2d checkOffset=loc+vi2d{x,y};
auto find_tiles=group.equal_range(checkOffset);//Each tile group consists of tiles that may be adjacent to us. Find all tiles that are adjacent to us in this tile group. auto find_tiles=group.equal_range(checkOffset);//Each tile group consists of tiles that may be adjacent to us. Find all tiles that are adjacent to us in this tile group.
for(auto&it=find_tiles.first;it!=find_tiles.second;++it){ for(auto&it=find_tiles.first;it!=find_tiles.second;++it){
//These are all tiles that were found adjacent to the location we are checking for. See if they match a potential group. //These are all tiles that were found adjacent to the location we are checking for. See if they match a potential group.
TileRenderData&foundTile=(*it).second; TileRenderData&foundTile=(*it).second;
if(tile.tileSheet.tileset==foundTile.tileSheet.tileset){ //Let's first see if they are even in the same tileset. if(tile.tileSheet.tileset==foundTile.tileSheet.tileset){ //Let's first see if they are even in the same tileset.
//Let's get how many tiles wide this tile sheet is. //Let's get how many tiles wide this tile sheet is.
int tileWidth=tile.tileSheet.tileset->tilewidth; int tileWidth=tile.tileSheet.tileset->tilewidth;
int tileSheetWidth=tile.tileSheet.tileset->tileset->Sprite()->width/tileWidth; int tileSheetWidth=tile.tileSheet.tileset->tileset->Sprite()->width/tileWidth;
if(IsAdjacent(tile.tileID,foundTile.tileID,tileSheetWidth)){ if(IsAdjacent(tile.tileID,foundTile.tileID,tileSheetWidth)){
group.insert({loc,tile});//We add this tile to the group! It is adjacent! group.insert({loc,tile});//We add this tile to the group! It is adjacent!
groupFound=true; groupFound=true;
goto groupIterationDone; goto groupIterationDone;
}
} }
} }
} }
} }
} }
} }
groupIterationDone:
if(!groupFound){
splitUpData.push_back({});
splitUpData.back().insert({loc,tile}); //Since we could not find a group to fit in, we had to start a brand new group.
}
} }
groupIterationDone: iteratedTiles.insert(loc);
if(!groupFound){
splitUpData.push_back({});
splitUpData.back().insert({loc,tile}); //Since we could not find a group to fit in, we had to start a brand new group.
}
} }
iteratedTiles.insert(loc); group.clear();
} for(auto&split:splitUpData){
group.clear(); TileGroup newGroup;
for(auto&split:splitUpData){ for(auto&[key,value]:split){
TileGroup newGroup; newGroup.InsertTile(value);
for(auto&[key,value]:split){ }
newGroup.InsertTile(value); group.push_back(newGroup);
} }
group.push_back(newGroup); };
} SplitUp(foregroundTileGroups);
}; SplitUp(upperForegroundTileGroups);
SplitUp(foregroundTileGroups); return true;
SplitUp(upperForegroundTileGroups); });
#pragma endregion #pragma endregion
#pragma region Bridge Layer Setup #pragma region Bridge Layer Setup (Loading Phase 6)
bridgeLayerIndex=-1; LoadingScreen::AddPhase([&](){
for(int counter=0;LayerTag&layer:MAP_DATA[map].LayerData){ bridgeLayerIndex=-1;
if(IsBridgeLayer(layer)){ for(int counter=0;LayerTag&layer:MAP_DATA[GetCurrentLevel()].LayerData){
bridgeLayerIndex=counter; if(IsBridgeLayer(layer)){
bridgeLayerIndex=counter;
}
counter++;
} }
counter++; return true;
} });
#pragma endregion #pragma endregion
for(NPCData data:game->MAP_DATA[game->GetCurrentLevel()].npcs){ #pragma region Setup NPCs (Loading Phase 7)
if(Unlock::IsUnlocked(data.unlockCondition)){ LoadingScreen::AddPhase([&](){
MONSTER_LIST.push_back(Monster{data.spawnPos,MONSTER_DATA[data.name]}); for(NPCData data:game->MAP_DATA[game->GetCurrentLevel()].npcs){
MONSTER_LIST.back().iframe_timer=INFINITE; if(Unlock::IsUnlocked(data.unlockCondition)){
MONSTER_LIST.back().npcData=data; MONSTER_LIST.push_back(Monster{data.spawnPos,MONSTER_DATA[data.name]});
} MONSTER_LIST.back().iframe_timer=INFINITE;
} MONSTER_LIST.back().npcData=data;
}
}
return true;
});
#pragma endregion
player->GetAbility1().cooldown=0.f; #pragma region Setup Player and Camera (Loading Phase 8)
player->GetAbility2().cooldown=0.f; LoadingScreen::AddPhase([&](){
player->GetAbility3().cooldown=0.f; player->GetAbility1().cooldown=0.f;
player->GetAbility4().cooldown=0.f; player->GetAbility2().cooldown=0.f;
player->GetRightClickAbility().cooldown=0.f; player->GetAbility3().cooldown=0.f;
player->useItem1.cooldown=0.f; player->GetAbility4().cooldown=0.f;
player->useItem2.cooldown=0.f; player->GetRightClickAbility().cooldown=0.f;
player->useItem3.cooldown=0.f; player->useItem1.cooldown=0.f;
player->useItem2.cooldown=0.f;
player->useItem3.cooldown=0.f;
player->upperLevel=false; //Assume player starts on lower level. player->upperLevel=false; //Assume player starts on lower level.
player->ForceSetPos(MAP_DATA[map].MapData.playerSpawnLocation); //Normal set pos does one axis and then the other, so this will make sure that we actually end up at the right spot and ignore collision rules. player->ForceSetPos(MAP_DATA[GetCurrentLevel()].MapData.playerSpawnLocation); //Normal set pos does one axis and then the other, so this will make sure that we actually end up at the right spot and ignore collision rules.
vf2d cameraStartPos=player->GetPos()+vf2d(-24*6,0); vf2d cameraStartPos=player->GetPos()+vf2d(-24*6,0);
camera.MoveCamera(cameraStartPos); camera.MoveCamera(cameraStartPos);
return true;
});
#pragma endregion
pathfinder.Initialize(); #pragma region Setup Pathfinding (Loading Phase 9)
Audio::SetAudioEvent("Default Volume"); LoadingScreen::AddPhase([&](){
game->audioEngine.fullyLoaded=true; //We assume there's no audio to load, so we just set the audio as fully loaded by default. pathfinder.Initialize();
if(MAP_DATA[map].bgmSongName.length()>0){ return true;
Audio::PlayBGM(MAP_DATA[map].bgmSongName); });
DisableFadeIn(true); #pragma endregion
if(changeMusic==PLAY_LEVEL_MUSIC){
#pragma region Audio Preparation (Loading Phase 10)
LoadingScreen::AddPhase([&](){
Audio::SetAudioEvent("Default Volume");
game->audioEngine.fullyLoaded=true; //We assume there's no audio to load, so we just set the audio as fully loaded by default.
if(MAP_DATA[GetCurrentLevel()].bgmSongName.length()>0){
Audio::PrepareBGM(MAP_DATA[GetCurrentLevel()].bgmSongName);
DisableFadeIn(true);
}
return true;
});
#pragma endregion
//Until the audio has stopped (by waiting for a set amount of time), we will respect the audio engine's wishes and not proceed.
LoadingScreen::DeferLoad([&](){return audioEngine.playBGMWaitTime==0.f;}); //This is the wait time for the audio engine to finish.
#pragma region Audio Channel Loading (Count based on Audio::GetPrepareBGMLoopIterations)
for(int i=0;i<Audio::GetPrepareBGMLoopIterations(MAP_DATA[GetCurrentLevel()].bgmSongName);i++){
LoadingScreen::AddPhase([&](){
Audio::UpdateLoop();
return true;
});
}
#pragma endregion
LoadingScreen::AddPhase([&](){
Audio::BGM&track=audioEngine.bgm[audioEngine.GetTrackName()];
for(int trackID:track.GetChannelIDs()){
audioEngine.Engine().Play(trackID,true);
}
return true;
});
} }
} }
@ -2204,10 +2290,6 @@ const MapName&AiL::GetCurrentLevel()const{
return currentLevel; return currentLevel;
} }
std::map<std::string,std::vector<ZoneData>>&AiL::GetZoneData(MapName map){
return MAP_DATA[map].ZoneData;
}
void AiL::ChangePlayerClass(Class cl){ void AiL::ChangePlayerClass(Class cl){
Ability itemAbility1=player->useItem1; Ability itemAbility1=player->useItem1;
Ability itemAbility2=player->useItem2; Ability itemAbility2=player->useItem2;
@ -3100,7 +3182,7 @@ void AiL::RenderFadeout(){
} }
bool AiL::GamePaused(){ bool AiL::GamePaused(){
return fadeOutDuration>0||disableFadeIn||paused; return fadeOutDuration>0||disableFadeIn||paused||LoadingScreen::loading;
} }
void AiL::PauseGame(){ void AiL::PauseGame(){
@ -3338,12 +3420,22 @@ const bool AiL::GameInitialized()const {
rcode AiL::LoadResource(Renderable&renderable,std::string_view imgPath,bool filter,bool clamp){ rcode AiL::LoadResource(Renderable&renderable,std::string_view imgPath,bool filter,bool clamp){
rcode returnCode; rcode returnCode;
if(gamepack.Loaded()){ if(gamepack.Loaded()){
returnCode=renderable.Load(std::string(imgPath),&gamepack); returnCode=renderable.Load(std::string(imgPath),&gamepack,filter,clamp);
}else{ }else{
returnCode=renderable.Load(std::string(imgPath)); returnCode=renderable.Load(std::string(imgPath),nullptr,filter,clamp);
if("GENERATE_GAMEPACK"_B){ if("GENERATE_GAMEPACK"_B){
gamepack.AddFile(std::string(imgPath)); gamepack.AddFile(std::string(imgPath));
} }
} }
return returnCode; return returnCode;
}
void AiL::UpdateMonsters(){
for(Monster&m:MONSTER_LIST){
m.Update(game->GetElapsedTime());
}
for(Monster&m:game->monstersToBeSpawned){
MONSTER_LIST.push_back(m);
}
game->monstersToBeSpawned.clear();
} }

@ -72,6 +72,10 @@ class AiL : public olc::PixelGameEngine
std::unique_ptr<Player>player; std::unique_ptr<Player>player;
SplashScreen splash; SplashScreen splash;
public: public:
enum MusicChange{
NO_MUSIC_CHANGE,
PLAY_LEVEL_MUSIC,
};
Pathfinding pathfinder; Pathfinding pathfinder;
static InputGroup KEY_BACK; static InputGroup KEY_BACK;
static InputGroup KEY_CONFIRM; static InputGroup KEY_CONFIRM;
@ -127,6 +131,7 @@ private:
float lastWorldShakeAdjust=0; float lastWorldShakeAdjust=0;
vf2d worldShakeVel={}; vf2d worldShakeVel={};
const float WORLD_SHAKE_ADJUST_MAX_TIME=0.4f; const float WORLD_SHAKE_ADJUST_MAX_TIME=0.4f;
MapName previousLevel="CAMPAIGN_1_1";
MapName currentLevel="CAMPAIGN_1_1"; MapName currentLevel="CAMPAIGN_1_1";
std::vector<TileGroup>foregroundTileGroups; std::vector<TileGroup>foregroundTileGroups;
std::vector<TileGroup>upperForegroundTileGroups; std::vector<TileGroup>upperForegroundTileGroups;
@ -169,6 +174,7 @@ private:
ResourcePack gamepack; ResourcePack gamepack;
void ValidateGameStatus(); void ValidateGameStatus();
void _PrepareLevel(MapName map,MusicChange changeMusic);
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
::discord::Result SetupDiscord(); ::discord::Result SetupDiscord();
#endif #endif
@ -186,7 +192,7 @@ public:
geom2d::rect<float>NO_COLLISION={{0.f,0.f,},{0.f,0.f}}; geom2d::rect<float>NO_COLLISION={{0.f,0.f,},{0.f,0.f}};
TileTransformedView view; TileTransformedView view;
void InitializeLevel(std::string mapFile,MapName map); void InitializeLevel(std::string mapFile,MapName map);
void LoadLevel(MapName map); void LoadLevel(MapName map,MusicChange changeMusic=PLAY_LEVEL_MUSIC);
void HandleUserInput(float fElapsedTime); void HandleUserInput(float fElapsedTime);
void UpdateCamera(float fElapsedTime); void UpdateCamera(float fElapsedTime);
void UpdateEffects(float fElapsedTime); void UpdateEffects(float fElapsedTime);
@ -225,7 +231,6 @@ public:
bool HasTileCollision(MapName map,vf2d pos,bool upperLevel=false); bool HasTileCollision(MapName map,vf2d pos,bool upperLevel=false);
const MapName&GetCurrentLevel()const; const MapName&GetCurrentLevel()const;
bool IsBridgeLayer(LayerTag&layer); bool IsBridgeLayer(LayerTag&layer);
std::map<std::string,std::vector<ZoneData>>&GetZoneData(MapName map);
void PopulateRenderLists(); void PopulateRenderLists();
void ChangePlayerClass(Class cl); void ChangePlayerClass(Class cl);
std::string GetString(std::string key); std::string GetString(std::string key);
@ -290,6 +295,7 @@ public:
void ResumeGame(); void ResumeGame();
const bool GameInitialized()const; const bool GameInitialized()const;
rcode LoadResource(Renderable&renderable,std::string_view imgPath,bool filter=false,bool clamp=true); rcode LoadResource(Renderable&renderable,std::string_view imgPath,bool filter=false,bool clamp=true);
void UpdateMonsters();
struct TileGroupData{ struct TileGroupData{
vi2d tilePos; vi2d tilePos;

@ -39,6 +39,7 @@ All rights reserved.
#include "AdventuresInLestoria.h" #include "AdventuresInLestoria.h"
#include "DEFINES.h" #include "DEFINES.h"
#include "util.h" #include "util.h"
#include "LoadingScreen.h"
INCLUDE_game INCLUDE_game
INCLUDE_DATA INCLUDE_DATA
@ -121,12 +122,21 @@ const size_t Audio::LoadAndPlay(const std::string_view sound,const bool loop){
Engine().Play(soundID,loop); Engine().Play(soundID,loop);
return soundID; return soundID;
}; };
void Audio::PlayBGM(const std::string_view sound,const bool loop){ void Audio::PrepareBGM(const std::string_view sound,const bool loop){
BGM&track=Self().bgm[std::string(sound)]; BGM&track=Self().bgm[std::string(sound)];
Self().fullyLoaded=false; Self().fullyLoaded=false;
StopBGM(); //Stop any currently playing track. StopBGM(); //Stop any currently playing track.
Self().playParams={std::string(sound),loop}; Self().playParams={std::string(sound),loop};
Self().playBGMWaitTime=0.7f; Self().playBGMWaitTime=0.7f;
#pragma region Internal Loading Loop Setup
Self().trackLoadStarted=false;
Self().trackLoadComplete=false;
Self().channelPlayingStarted=false;
Self().channelPlayingComplete=false;
int currentLoopIndex=0;
#pragma endregion
Self().immediatelyLoadAudio=false;
}; };
void Audio::StopBGM(){ void Audio::StopBGM(){
@ -145,24 +155,39 @@ const bool Audio::BGMIsPlaying(){
return Self().currentBGM.length()>0; return Self().currentBGM.length()>0;
} }
const Volume&Audio::BGM::GetVolume(const Event&eventName,const ChannelID&id)const{ const Volume&Audio::BGM::GetVolume(const Event&eventName,const int&index)const{
return eventVolumes.GetVolumes(eventName).at(id); return eventVolumes.GetVolumes(eventName).at(index);
} }
void Audio::BGM::Load(){ void Audio::BGM::Load(){
if(Self().BGMIsPlaying()){ if(!Self().trackLoadStarted){
if(Self().GetTrackName()==songFileName)return; //We are already playing the current track. Self().trackLoadStarted=true;
BGM&bgm=Self().bgm[Self().GetTrackName()];
if(Self().BGMIsPlaying()){ if(Self().BGMIsPlaying()){
bgm.Unload(); if(Self().GetTrackName()==songFileName){ //We are already playing the current track.
Self().trackLoadComplete=Self().channelPlayingComplete=Self().fullyLoaded=true;
return;
}else{
BGM&bgm=Self().bgm[Self().GetTrackName()];
if(Self().BGMIsPlaying()){
bgm.Unload();
}
}
} }
} Self().currentBGM=songFileName;
Self().currentBGM=songFileName; Self().currentLoopIndex=0;
BGM&newBgm=Self().bgm[songFileName]; BGM&newBgm=Self().bgm[songFileName];
if(newBgm.channels.size()>0)ERR(std::format("WARNING! The size of the channels list is greater than zero! Size: {}",newBgm.channels.size())); if(newBgm.channels.size()>0)ERR(std::format("WARNING! The size of the channels list is greater than zero! Size: {}",newBgm.channels.size()));
for(const ChannelName&channel:newBgm.GetChannels()){ }else{
BGM&newBgm=Self().bgm[songFileName];
const ChannelName&channel=newBgm.GetChannels()[Self().currentLoopIndex];
ChannelID soundID=Engine().LoadSound("bgm_directory"_S+channel); ChannelID soundID=Engine().LoadSound("bgm_directory"_S+channel);
newBgm.channels.push_back(soundID); newBgm.channels.push_back(soundID);
#pragma region Handle threaded loop indexing
Self().currentLoopIndex++;
if(Self().currentLoopIndex>=newBgm.GetChannels().size()){
Self().trackLoadComplete=true;
}
#pragma endregion
} }
} }
@ -258,27 +283,60 @@ const SongName&Audio::GetTrackName(){
return Self().currentBGM; return Self().currentBGM;
} }
void Audio::Update(){ void Audio::UpdateLoop(){
if(Self().playBGMWaitTime>0.f){ if(Self().playBGMWaitTime==0.f){
Self().playBGMWaitTime=std::max(Self().playBGMWaitTime-game->GetElapsedTime(),0.f); BGM&track=Self().bgm[Self().playParams.sound];
if(Self().playBGMWaitTime==0.f){ if(!Self().trackLoadComplete){
BGM&track=Self().bgm[Self().playParams.sound];
track.Load(); track.Load();
Self().prevVolumes.clear(); }else
Self().targetVolumes.clear(); if(!Self().channelPlayingComplete){
Self().fadeToTargetVolumeTime=0.f; if(!Self().channelPlayingStarted){
for(int channelListIndex=0;int trackID:track.GetChannelIDs()){ Self().prevVolumes.clear();
float channelVol=track.GetVolume(Self().currentAudioEvent,channelListIndex); Self().targetVolumes.clear();
Self().fadeToTargetVolumeTime=0.f;
Self().currentLoopIndex=0;
Self().channelPlayingStarted=true;
}else{
int trackID=track.GetChannelIDs()[Self().currentLoopIndex];
float channelVol=track.GetVolume(Self().currentAudioEvent,Self().currentLoopIndex);
Self().prevVolumes.push_back(channelVol); Self().prevVolumes.push_back(channelVol);
Self().targetVolumes.push_back(channelVol); Self().targetVolumes.push_back(channelVol);
Engine().SetVolume(trackID,channelVol*GetBGMVolume()); Engine().SetVolume(trackID,channelVol*GetBGMVolume());
Engine().Play(trackID,Self().playParams.loop); #pragma region Handle threaded loop indexing
channelListIndex++; Self().currentLoopIndex++;
if(Self().currentLoopIndex>=track.GetChannelIDs().size()){
Self().channelPlayingComplete=true;
Self().fullyLoaded=true;
}
#pragma endregion
} }
Self().fullyLoaded=true; }else
if(!Self().fullyLoaded){
ERR("Invalid loading state or too many calls to initialize audio loop! The audio is still not fully loaded!");
} }
} }
}
void Audio::PlayBGM(const std::string_view sound,const bool loop){
PrepareBGM(sound,loop);
Self().immediatelyLoadAudio=true;
}
void Audio::Update(){
if(Self().fadeToTargetVolumeTime==0.f&&Self().playBGMWaitTime>0.f){
Self().playBGMWaitTime=std::max(Self().playBGMWaitTime-game->GetElapsedTime(),0.f);
if(Self().playBGMWaitTime==0.f&&Self().immediatelyLoadAudio){
while(!Self().BGMFullyLoaded()){
UpdateLoop(); //We immediately load the file. In a loading screen setting we would defer UpdateLoop() such that we have extra time to update the screen, UpdateLoop() is divided into many parts of the music loading process.
}
//Start playing the tracks.
Audio::BGM&track=Self().bgm[Self().GetTrackName()];
for(int trackID:track.GetChannelIDs()){
Self().Engine().Play(trackID,true);
}
}
}
if(Self().fadeToTargetVolumeTime>0.f){ if(Self().fadeToTargetVolumeTime>0.f){
Self().fadeToTargetVolumeTime=std::max(0.f,Self().fadeToTargetVolumeTime-game->GetElapsedTime()); Self().fadeToTargetVolumeTime=std::max(0.f,Self().fadeToTargetVolumeTime-game->GetElapsedTime());
for(int counter=0;float&vol:Self().prevVolumes){ for(int counter=0;float&vol:Self().prevVolumes){
@ -318,4 +376,9 @@ float&Audio::GetSFXVolume(){
float Audio::GetMuteMult(){ float Audio::GetMuteMult(){
if(muted)return 0.f; if(muted)return 0.f;
return 1.f; return 1.f;
}
int Audio::GetPrepareBGMLoopIterations(std::string_view sound){
BGM&newBgm=Self().bgm[std::string(sound)];
return newBgm.GetChannels().size()*2+2; //The channels list gets populated by calling newBgm.Load(), which then provides the list of channels that need to be loaded and played. This is why we multiply by 2. Each of the loading phases also consist of an initialization phase, so we add 2 as well.
} }

@ -56,10 +56,13 @@ public:
static MiniAudio&Engine(); static MiniAudio&Engine();
static void Initialize(); static void Initialize();
static void Update(); static void Update();
static void UpdateLoop();
static void Play(const std::string_view sound); static void Play(const std::string_view sound);
[[nodiscard]] [[nodiscard]]
static const size_t LoadAndPlay(const std::string_view sound,const bool loop=true); static const size_t LoadAndPlay(const std::string_view sound,const bool loop=true);
//Play a BGM given a name found in bgm.txt configuration file. //Prepares a BGM for loading. This means we call UpdateLoop() repeatedly until the loading of the music is complete. Names are found in bgm.txt configuration file.
static void PrepareBGM(const std::string_view sound,const bool loop=true);
//Play immediately a BGM given a name found in bgm.txt configuration file.
static void PlayBGM(const std::string_view sound,const bool loop=true); static void PlayBGM(const std::string_view sound,const bool loop=true);
static void StopBGM(); static void StopBGM();
static const Event&GetAudioEvent(); static const Event&GetAudioEvent();
@ -72,7 +75,17 @@ public:
static float&GetBGMVolume(); static float&GetBGMVolume();
static float&GetSFXVolume(); static float&GetSFXVolume();
static float GetMuteMult(); static float GetMuteMult();
//This will get a prepared BGM loop iteration count which is useful for loading stages.
static int GetPrepareBGMLoopIterations(std::string_view sound);
private: private:
bool trackLoadStarted=false;
bool trackLoadComplete=false;
bool channelPlayingStarted=false;
bool channelPlayingComplete=false;
int currentLoopIndex=0;
//Set to false by PrepareBGM(). If PlayBGM() is called instead, it will set the state of this variable to true, such that the loading is performed in Audio::Update()!
bool immediatelyLoadAudio=false;
struct BGMPlayParams{ struct BGMPlayParams{
std::string sound; std::string sound;
bool loop; bool loop;
@ -92,7 +105,7 @@ private:
const size_t GetChannelCount()const; const size_t GetChannelCount()const;
const std::vector<ChannelName>&GetChannels()const; const std::vector<ChannelName>&GetChannels()const;
const SongName&GetName()const; const SongName&GetName()const;
const Volume&GetVolume(const Event&eventName,const ChannelID&id)const; const Volume&GetVolume(const Event&eventName,const int&index)const;
void SetName(std::string_view name); void SetName(std::string_view name);
void SetFileName(std::string_view name); void SetFileName(std::string_view name);
void AddChannel(const ChannelName&name); void AddChannel(const ChannelName&name);

@ -69,11 +69,10 @@ protected:
vi2d descriptionPos=iconPos+vi2d{int(rect.size.y)-2,-1}; vi2d descriptionPos=iconPos+vi2d{int(rect.size.y)-2,-1};
window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4); window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
InputType controlType=KEY;
if(Input::UsingGamepad())controlType=CONTROLLER;
if(textWidth>boxWidth){ ability->input->DrawInput(&window,textPos+vf2d{boxWidth/2,7},"",255,controlType,{0.5f,0.5f});
window.DrawShadowStringPropDecal(textPos,ability->input->GetDisplayName(),WHITE,BLACK,{boxWidth/textWidth*0.5f,0.5});
}else{
window.DrawShadowStringPropDecal(textPos,ability->input->GetDisplayName(),WHITE,BLACK,{0.5,0.5});
}
} }
}; };

@ -50,7 +50,7 @@ protected:
float perspectiveFactor=6; float perspectiveFactor=6;
public: public:
inline CharacterRotatingDisplay(geom2d::rect<float>rect,Decal*icon) inline CharacterRotatingDisplay(geom2d::rect<float>rect,Decal*icon)
:MenuComponent(rect,"",DO_NOTHING),icon(icon){} :MenuComponent(rect,"",DO_NOTHING,ButtonAttr::UNSELECTABLE),icon(icon){}
inline void SetIcon(Decal*icon){ inline void SetIcon(Decal*icon){
this->icon=icon; this->icon=icon;
} }

@ -59,6 +59,8 @@ using BackdropName=std::string;
#define INCLUDE_WINDOW_SIZE extern vi2d WINDOW_SIZE; #define INCLUDE_WINDOW_SIZE extern vi2d WINDOW_SIZE;
#define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS; #define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS;
#define INCLUDE_PACK_KEY extern std::string PACK_KEY;
#define INCLUDE_BACKDROP_DATA extern std::map<BackdropName,Renderable>BACKDROP_DATA; #define INCLUDE_BACKDROP_DATA extern std::map<BackdropName,Renderable>BACKDROP_DATA;

@ -73,4 +73,5 @@ public:
virtual void GetAnyMousePress(int32_t mouseButton); virtual void GetAnyMousePress(int32_t mouseButton);
virtual void GetAnyMouseRelease(int32_t mouseButton); virtual void GetAnyMouseRelease(int32_t mouseButton);
static void ChangeState(States::State newState,float fadeOutDuration=0); static void ChangeState(States::State newState,float fadeOutDuration=0);
virtual void OnLevelLoad()=0;
}; };

@ -118,13 +118,13 @@ void ItemDrop::UpdateDrops(float fElapsedTime){
#pragma region Handle Upper/Lower Level Zone Intersecting #pragma region Handle Upper/Lower Level Zone Intersecting
if(drop.speed.mag()>0){ if(drop.speed.mag()>0){
std::map<std::string,std::vector<ZoneData>>&zoneData=game->GetZoneData(game->GetCurrentLevel()); const std::map<std::string,std::vector<ZoneData>>&zoneData=game->GetZones(game->GetCurrentLevel());
for(ZoneData&upperLevelZone:zoneData["UpperZone"]){ for(const ZoneData&upperLevelZone:zoneData.at("UpperZone")){
if(geom2d::overlaps(upperLevelZone.zone,drop.pos)){ if(geom2d::overlaps(upperLevelZone.zone,drop.pos)){
drop.upperLevel=true; drop.upperLevel=true;
} }
} }
for(ZoneData&lowerLevelZone:zoneData["LowerZone"]){ for(const ZoneData&lowerLevelZone:zoneData.at("LowerZone")){
if(geom2d::overlaps(lowerLevelZone.zone,drop.pos)){ if(geom2d::overlaps(lowerLevelZone.zone,drop.pos)){
drop.upperLevel=false; drop.upperLevel=false;
} }

@ -302,7 +302,7 @@ std::string InputGroup::GetDisplayName(){
return combinationDisplay; return combinationDisplay;
} }
void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha,InputType type)const{ void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*const,ViewPort*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha,InputType type,vf2d textScale)const{
std::optional<Input>primaryKey; std::optional<Input>primaryKey;
switch(type){ switch(type){
case CONTROLLER:primaryKey=GetPrimaryKey(CONTROLLER);break; case CONTROLLER:primaryKey=GetPrimaryKey(CONTROLLER);break;
@ -317,16 +317,16 @@ void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*cons
for(const Input&input:keyOrder){ for(const Input&input:keyOrder){
if(input.GetType()==MOUSE){ if(input.GetType()==MOUSE){
if(input.HasExtendedIcons()){ if(input.HasExtendedIcons()){
buttonImgSize.x+=input.GetIcon(GameSettings::GetIconType()).Sprite()->width+"Interface.InputHelperSpacing"_F; buttonImgSize.x+=input.GetIcon(GameSettings::GetIconType()).Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon(GameSettings::GetIconType()).Sprite()->height)); buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon(GameSettings::GetIconType()).Sprite()->height));
buttonImgs.push_back(input.GetIcon(GameSettings::GetIconType()).Decal()); buttonImgs.push_back(input.GetIcon(GameSettings::GetIconType()).Decal());
}else }else
if(input.HasIcon()){ if(input.HasIcon()){
buttonImgSize.x+=input.GetIcon().Sprite()->width+"Interface.InputHelperSpacing"_F; buttonImgSize.x+=input.GetIcon().Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon().Sprite()->height)); buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon().Sprite()->height));
buttonImgs.push_back(input.GetIcon().Decal()); buttonImgs.push_back(input.GetIcon().Decal());
}else{ }else{
buttonImgSize.x+=game->GetTextSizeProp(input.GetDisplayName()).x+"Interface.InputHelperSpacing"_F; buttonImgSize.x+=game->GetTextSizeProp(input.GetDisplayName()).x*textScale.x+"Interface.InputHelperSpacing"_F;
buttonImgSize.y=std::max(buttonImgSize.y,float(game->GetTextSizeProp(input.GetDisplayName()).y)+"Interface.InputHelperSpacing"_F); buttonImgSize.y=std::max(buttonImgSize.y,float(game->GetTextSizeProp(input.GetDisplayName()).y)+"Interface.InputHelperSpacing"_F);
buttonImgs.push_back(input.GetDisplayName()); buttonImgs.push_back(input.GetDisplayName());
} }
@ -336,16 +336,16 @@ void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*cons
if(primaryKey.has_value()){ if(primaryKey.has_value()){
if(primaryKey.value().HasExtendedIcons()){ if(primaryKey.value().HasExtendedIcons()){
buttonImgSize.x+=primaryKey.value().GetIcon(GameSettings::GetIconType()).Sprite()->width+"Interface.InputHelperSpacing"_F; buttonImgSize.x+=primaryKey.value().GetIcon(GameSettings::GetIconType()).Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
buttonImgSize.y=std::max(buttonImgSize.y,float(primaryKey.value().GetIcon(GameSettings::GetIconType()).Sprite()->height)); buttonImgSize.y=std::max(buttonImgSize.y,float(primaryKey.value().GetIcon(GameSettings::GetIconType()).Sprite()->height));
buttonImgs.push_back(primaryKey.value().GetIcon(GameSettings::GetIconType()).Decal()); buttonImgs.push_back(primaryKey.value().GetIcon(GameSettings::GetIconType()).Decal());
}else }else
if(primaryKey.value().HasIcon()){ if(primaryKey.value().HasIcon()){
buttonImgSize.x+=primaryKey.value().GetIcon().Sprite()->width+"Interface.InputHelperSpacing"_F; buttonImgSize.x+=primaryKey.value().GetIcon().Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
buttonImgSize.y=std::max(buttonImgSize.y,float(primaryKey.value().GetIcon().Sprite()->height)); buttonImgSize.y=std::max(buttonImgSize.y,float(primaryKey.value().GetIcon().Sprite()->height));
buttonImgs.push_back(primaryKey.value().GetIcon().Decal()); buttonImgs.push_back(primaryKey.value().GetIcon().Decal());
}else{ }else{
buttonImgSize.x+=game->GetTextSizeProp(primaryKey.value().GetDisplayName()).x+"Interface.InputHelperSpacing"_F; buttonImgSize.x+=game->GetTextSizeProp(primaryKey.value().GetDisplayName()).x*textScale.x+"Interface.InputHelperSpacing"_F;
buttonImgSize.y=std::max(buttonImgSize.y,float(game->GetTextSizeProp(primaryKey.value().GetDisplayName()).y)+"Interface.InputHelperSpacing"_F); buttonImgSize.y=std::max(buttonImgSize.y,float(game->GetTextSizeProp(primaryKey.value().GetDisplayName()).y)+"Interface.InputHelperSpacing"_F);
buttonImgs.push_back(primaryKey.value().GetDisplayName()); buttonImgs.push_back(primaryKey.value().GetDisplayName());
} }
@ -359,19 +359,22 @@ void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*cons
#pragma region Render Macro #pragma region Render Macro
#define Render(rendererType) \ #define Render(rendererType) \
std::get<rendererType*const>(renderer)->DrawDecal(pos+offset-vf2d{0.f,2.f},img,{1.f,1.f},{255,255,255,alpha}); std::get<rendererType*const>(renderer)->DrawDecal(pos+offset-vf2d{0.f,2.f},img,textScale,{255,255,255,alpha});
#pragma endregion #pragma endregion
if(std::holds_alternative<AiL*const>(renderer)){ if(std::holds_alternative<AiL*const>(renderer)){
Render(AiL); Render(AiL);
}else }else
if(std::holds_alternative<TileTransformedView*const>(renderer)){ if(std::holds_alternative<TileTransformedView*const>(renderer)){
Render(TileTransformedView); Render(TileTransformedView);
} }else
offset.x+=img->sprite->width+"Interface.InputHelperSpacing"_I; if(std::holds_alternative<ViewPort*const>(renderer)){
Render(ViewPort);
}else ERR("Could not find proper renderer for rendering Inputs!");
offset.x+=img->sprite->width*textScale.x+"Interface.InputHelperSpacing"_I;
}else }else
if(std::holds_alternative<std::string>(button)){ if(std::holds_alternative<std::string>(button)){
std::string label=std::get<std::string>(button); std::string label=std::get<std::string>(button);
vf2d textSize=game->GetTextSizeProp(label); vf2d textSize=game->GetTextSizeProp(label)*textScale;
Pixel buttonBackCol="Interface.InputButtonBackCol"_Pixel; Pixel buttonBackCol="Interface.InputButtonBackCol"_Pixel;
Pixel buttonTextCol="Interface.InputButtonTextCol"_Pixel; Pixel buttonTextCol="Interface.InputButtonTextCol"_Pixel;
buttonBackCol.a=alpha; buttonBackCol.a=alpha;
@ -382,7 +385,7 @@ void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*cons
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-2.f,0.f},vf2d{textSize.x+4,textSize.y},buttonBackCol); \ std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-2.f,0.f},vf2d{textSize.x+4,textSize.y},buttonBackCol); \
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,-1.f},vf2d{textSize.x+2,textSize.y},buttonBackCol); \ std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,-1.f},vf2d{textSize.x+2,textSize.y},buttonBackCol); \
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,0.f},vf2d{textSize.x+2,textSize.y+1.f},buttonBackCol); \ std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,0.f},vf2d{textSize.x+2,textSize.y+1.f},buttonBackCol); \
std::get<rendererType*const>(renderer)->DrawStringPropDecal(pos+offset+vf2d{0.f,0.f},label,buttonTextCol); std::get<rendererType*const>(renderer)->DrawStringPropDecal(pos+offset+vf2d{0.f,0.f},label,buttonTextCol,textScale);
#pragma endregion #pragma endregion
if(std::holds_alternative<AiL*const>(renderer)){ if(std::holds_alternative<AiL*const>(renderer)){
@ -390,15 +393,33 @@ void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*cons
}else }else
if(std::holds_alternative<TileTransformedView*const>(renderer)){ if(std::holds_alternative<TileTransformedView*const>(renderer)){
Render(TileTransformedView); Render(TileTransformedView);
} }else
if(std::holds_alternative<ViewPort*const>(renderer)){
Render(ViewPort);
}else ERR("Could not find proper renderer for rendering Inputs!");
offset.x+=textSize.x+"Interface.InputHelperSpacing"_I; offset.x+=textSize.x+"Interface.InputHelperSpacing"_I;
}else [[unlikely]]ERR("WARNING! Hit a state where no data is inside of the button! THIS SHOULD NOT BE HAPPENING!"); }else [[unlikely]]ERR("WARNING! Hit a state where no data is inside of the button! THIS SHOULD NOT BE HAPPENING!");
} }
#pragma region Render Display Text
#pragma region Render Macro
#define Render(rendererType) \
std::get<rendererType*const>(renderer)->DrawShadowStringPropDecal(pos+offset,displayText,{255,255,255,alpha},{0,0,0,alpha},textScale);
#pragma endregion
game->view.DrawShadowStringPropDecal(pos+offset,displayText,{255,255,255,alpha},{0,0,0,alpha}); if(std::holds_alternative<AiL*const>(renderer)){
Render(AiL);
}else
if(std::holds_alternative<TileTransformedView*const>(renderer)){
Render(TileTransformedView);
}else
if(std::holds_alternative<ViewPort*const>(renderer)){
Render(ViewPort);
}else ERR("Could not find proper renderer for rendering Inputs!");
#pragma endregion
} }
void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha)const{ void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*const,ViewPort*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha)const{
InputType primaryType; InputType primaryType;
if(Input::UsingGamepad())primaryType=CONTROLLER; if(Input::UsingGamepad())primaryType=CONTROLLER;
else if(Menu::UsingMouseNavigation())primaryType=MOUSE; else if(Menu::UsingMouseNavigation())primaryType=MOUSE;

@ -44,6 +44,7 @@ All rights reserved.
#include "olcPGEX_TransformedView.h" #include "olcPGEX_TransformedView.h"
#include <variant> #include <variant>
#include "IconType.h" #include "IconType.h"
#include "olcPGEX_ViewPort.h"
class AiL; class AiL;
@ -119,8 +120,8 @@ public:
const float AnalogDAS(const float threshold=0.2f); const float AnalogDAS(const float threshold=0.2f);
std::string GetDisplayName(); std::string GetDisplayName();
//Draws an input display with accompanying text centered at given position. //Draws an input display with accompanying text centered at given position.
void DrawInput(const std::variant<AiL*const,TileTransformedView*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha)const; void DrawInput(const std::variant<AiL*const,TileTransformedView*const,ViewPort*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha)const;
void DrawInput(const std::variant<AiL*const,TileTransformedView*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha,const InputType type)const; void DrawInput(const std::variant<AiL*const,TileTransformedView*const,ViewPort*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha,const InputType type,vf2d textScale={1.f,1.f})const;
const std::optional<Input>GetPrimaryKey(InputType type)const; const std::optional<Input>GetPrimaryKey(InputType type)const;
}; };

@ -0,0 +1,108 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "AdventuresInLestoria.h"
#include "LoadingScreen.h"
#include "util.h"
INCLUDE_game
INCLUDE_WINDOW_SIZE
INCLUDE_ANIMATION_DATA
bool LoadingScreen::loading=false;
int LoadingScreen::currentProgress=0;
int LoadingScreen::totalProgress=0;
std::queue<std::function<bool()>>LoadingScreen::loadingPhases;
bool LoadingScreen::showGhost=false;
bool LoadingScreen::showLarge=false;
void LoadingScreen::DeferLoad(std::function<bool()>waitCondition){
AddPhase(waitCondition);
}
void LoadingScreen::Update(){
if(loading){
if(loadingPhases.size()>0){
std::function<bool()>&loadFunc=loadingPhases.front();
if(loadFunc()){
currentProgress++;
loadingPhases.pop();
}
}else{
loading=false;
GameState::STATE->OnLevelLoad();
}
}
}
void LoadingScreen::Draw(){
if(loading){
game->FillRectDecal({0,0},WINDOW_SIZE,{VERY_DARK_GREEN.r,VERY_DARK_GREEN.g,VERY_DARK_GREEN.b,230});
game->FillRectDecal({22.f,WINDOW_SIZE.y-46.f},{(float(currentProgress)/totalProgress)*(WINDOW_SIZE.x-48.f)+4.f,28.f},VERY_DARK_GREEN/2);
game->FillRectDecal({24.f,WINDOW_SIZE.y-48.f},{(float(currentProgress)/totalProgress)*(WINDOW_SIZE.x-48.f),24.f},DARK_YELLOW);
game->DrawShadowStringPropDecal({24.f,WINDOW_SIZE.y-60.f},"Loading...",{170,210,0},BLACK,{1.f,1.5f});
vf2d playerScale=vf2d(game->GetPlayer()->GetSizeMult(),game->GetPlayer()->GetSizeMult());
float scale=1.f;
if(showLarge){
scale=2.f;
}
const std::vector<Buff>attackBuffs=game->GetPlayer()->GetStatBuffs({"Attack","Attack %"});
Pixel blendCol=attackBuffs.size()>0?Pixel{255,uint8_t(255*abs(sin(1.4*attackBuffs[0].duration))),uint8_t(255*abs(sin(1.4*attackBuffs[0].duration)))}:WHITE;
if(showGhost){
blendCol=BLACK;
}
game->GetPlayer()->GetWalkEAnimation();
Animate2D::FrameSequence&playerWalkE=ANIMATION_DATA[game->GetPlayer()->GetWalkEAnimation()];
game->DrawPartialRotatedDecal({(float(currentProgress)/totalProgress)*(WINDOW_SIZE.x-48.f),WINDOW_SIZE.y-36.f},playerWalkE.GetFrame(game->GetRuntime()).GetSourceImage()->Decal(),game->GetPlayer()->GetSpinAngle(),{12,12},playerWalkE.GetFrame(game->GetRuntime()).GetSourceRect().pos,playerWalkE.GetFrame(game->GetRuntime()).GetSourceRect().size,playerScale*scale,blendCol);
}
}
void LoadingScreen::Reset(){
currentProgress=0;
totalProgress=0;
showGhost=util::random()%6==0;
showLarge=util::random()%6==0;
while(loadingPhases.size()>0)ERR("WARNING! Previous loading phase was not properly cleared! This probably means some part of a previous load did not execute!");
}
void LoadingScreen::AddPhase(std::function<bool()>loadFunc){
loadingPhases.push(loadFunc);
totalProgress++;
}

@ -0,0 +1,57 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
#include <queue>
#include "olcUTIL_Animate2D.h"
class LoadingScreen{
public:
static bool loading;
static int currentProgress;
static int totalProgress;
static float waitTime;
static bool showGhost;
static bool showLarge;
static std::queue<std::function<bool()>>loadingPhases;
static void Update();
static void Draw();
static void DeferLoad(std::function<bool()>waitCondition);
static void Reset();
static void AddPhase(std::function<bool()>loadFunc);
};

@ -455,13 +455,13 @@ bool Monster::SetPos(vf2d pos){
return resultX||resultY; return resultX||resultY;
} }
void Monster::Moved(){ void Monster::Moved(){
std::map<std::string,std::vector<ZoneData>>&zoneData=game->GetZoneData(game->GetCurrentLevel()); const std::map<std::string,std::vector<ZoneData>>&zoneData=game->GetZones(game->GetCurrentLevel());
for(ZoneData&upperLevelZone:zoneData["UpperZone"]){ for(const ZoneData&upperLevelZone:zoneData.at("UpperZone")){
if(geom2d::overlaps(upperLevelZone.zone,pos)){ if(geom2d::overlaps(upperLevelZone.zone,pos)){
upperLevel=true; upperLevel=true;
} }
} }
for(ZoneData&lowerLevelZone:zoneData["LowerZone"]){ for(const ZoneData&lowerLevelZone:zoneData.at("LowerZone")){
if(geom2d::overlaps(lowerLevelZone.zone,pos)){ if(geom2d::overlaps(lowerLevelZone.zone,pos)){
upperLevel=false; upperLevel=false;
} }

@ -783,13 +783,13 @@ void Player::Moved(){
spawner.SetTriggered(true); spawner.SetTriggered(true);
} }
} }
std::map<std::string,std::vector<ZoneData>>&zoneData=game->GetZoneData(game->GetCurrentLevel()); const std::map<std::string,std::vector<ZoneData>>&zoneData=game->GetZones(game->GetCurrentLevel());
for(ZoneData&upperLevelZone:zoneData["UpperZone"]){ for(const ZoneData&upperLevelZone:zoneData.at("UpperZone")){
if(geom2d::overlaps(upperLevelZone.zone,pos)){ if(geom2d::overlaps(upperLevelZone.zone,pos)){
upperLevel=true; upperLevel=true;
} }
} }
for(ZoneData&lowerLevelZone:zoneData["LowerZone"]){ for(const ZoneData&lowerLevelZone:zoneData.at("LowerZone")){
if(geom2d::overlaps(lowerLevelZone.zone,pos)){ if(geom2d::overlaps(lowerLevelZone.zone,pos)){
upperLevel=false; upperLevel=false;
} }
@ -1349,4 +1349,13 @@ void Player::UpdateHealthAndMana(){
hp=std::min(hp,int(GetStat("Health"))); hp=std::min(hp,int(GetStat("Health")));
mana=std::min(mana,GetMaxMana()); mana=std::min(mana,GetMaxMana());
} }
}
void Player::SetInvisible(const bool invisibleState){
invisibility=invisibleState;
}
const bool Player::IsInvisible()const{
return invisibility;
} }

@ -245,6 +245,8 @@ public:
void SetXP(const uint32_t xp); void SetXP(const uint32_t xp);
void SetTotalXPEarned(const uint32_t totalXP); void SetTotalXPEarned(const uint32_t totalXP);
void SetLevel(uint8_t newLevel); void SetLevel(uint8_t newLevel);
void SetInvisible(const bool invisibleState);
const bool IsInvisible()const;
private: private:
int hp="Warrior.BaseHealth"_I; int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I; int mana="Player.BaseMana"_I;
@ -293,6 +295,7 @@ private:
uint32_t money="Player.Starting Money"_I; uint32_t money="Player.Starting Money"_I;
EntityStats stats; EntityStats stats;
ItemAttribute&Get(std::string_view attr); ItemAttribute&Get(std::string_view attr);
bool invisibility=false;
//Returns true if the move was valid and successful. //Returns true if the move was valid and successful.
//If playerInvoked is true, this means the player was the one that instantiated this input, and it's not an extra movement done via collision. //If playerInvoked is true, this means the player was the one that instantiated this input, and it's not an extra movement done via collision.
//Set playerInvoked to false when you don't want a movement loop due to collisions. //Set playerInvoked to false when you don't want a movement loop due to collisions.

@ -49,23 +49,31 @@ INCLUDE_game
std::multimap<EventName,SoundEffect>SoundEffect::SOUND_EFFECTS; std::multimap<EventName,SoundEffect>SoundEffect::SOUND_EFFECTS;
const vf2d SoundEffect::CENTERED={-8419.f,-3289.f}; const vf2d SoundEffect::CENTERED={-8419.f,-3289.f};
SoundEffect::SoundEffect(const std::string_view filename,const float&vol,const float&minPitch,const float&maxPitch) SoundEffect::SoundEffect(const std::string_view filename,const float&vol,const float&minPitch,const float&maxPitch,const bool combatSound)
:filename(filename),vol(vol),minPitch(minPitch),maxPitch(maxPitch){ :filename(filename),vol(vol),minPitch(minPitch),maxPitch(maxPitch),combatSound(combatSound){
if(vol<0.f||vol>1.f)ERR(std::format("WARNING! Volume must be between 0.0f ~ 1.0f! Provided value {}",vol)); if(vol<0.f||vol>1.f)ERR(std::format("WARNING! Volume must be between 0.0f ~ 1.0f! Provided value {}",vol));
} }
void SoundEffect::Initialize(){ void SoundEffect::Initialize(){
for(auto&[key,size]:DATA["Events"]["SFX"]){ for(auto&[key,size]:DATA["Events"]["SFX"]){
int counter=0; int counter=0;
bool combatSound=false;
if(DATA["Events"]["SFX"][key].HasProperty("CombatSound")){
combatSound=DATA["Events"]["SFX"][key]["CombatSound"].GetBool();
}
while(DATA["Events"]["SFX"][key].HasProperty(std::format("File[{}]",counter))){ while(DATA["Events"]["SFX"][key].HasProperty(std::format("File[{}]",counter))){
utils::datafile&data=DATA["Events"]["SFX"][key][std::format("File[{}]",counter)]; utils::datafile&data=DATA["Events"]["SFX"][key][std::format("File[{}]",counter)];
float minPitch=0.9f; float minPitch=0.9f;
float maxPitch=1.1f; float maxPitch=1.1f;
if(data.GetValueCount()>=3){minPitch=data.GetInt(2)/100.f;} if(data.GetValueCount()>=3){minPitch=data.GetInt(2)/100.f;}
if(data.GetValueCount()>=4){maxPitch=data.GetInt(3)/100.f;} if(data.GetValueCount()>=4){maxPitch=data.GetInt(3)/100.f;}
SOUND_EFFECTS.insert({key,SoundEffect{data.GetString(0),data.GetInt(1)/100.f,minPitch,maxPitch}}); SOUND_EFFECTS.insert({key,SoundEffect{data.GetString(0),data.GetInt(1)/100.f,minPitch,maxPitch,combatSound}});
counter++; counter++;
} }
auto itr=SOUND_EFFECTS.equal_range(key);
for(auto it=itr.first;it!=itr.second;++it){
it->second.combatSound=combatSound;
}
} }
} }
@ -84,6 +92,8 @@ void SoundEffect::PlaySFX(const std::string_view eventName,const vf2d&pos){
} }
const SoundEffect&sfx=(*it).second; const SoundEffect&sfx=(*it).second;
if(GameState::STATE==GameState::states[States::MAIN_MENU])return; //Do not play combat sounds on the main menu.
float pitchDiff=sfx.maxPitch-sfx.minPitch; float pitchDiff=sfx.maxPitch-sfx.minPitch;
float pitch=util::random(pitchDiff)+sfx.minPitch; float pitch=util::random(pitchDiff)+sfx.minPitch;

@ -45,7 +45,7 @@ using EventName=std::string;
class SoundEffect{ class SoundEffect{
public: public:
SoundEffect(const std::string_view filename,const float&vol,const float&minPitch=0.9f,const float&maxPitch=1.1f); SoundEffect(const std::string_view filename,const float&vol,const float&minPitch=0.9f,const float&maxPitch=1.1f,const bool combatSound=false);
static void PlaySFX(const std::string_view eventName,const vf2d&pos); static void PlaySFX(const std::string_view eventName,const vf2d&pos);
static void Initialize(); static void Initialize();
static const vf2d CENTERED; static const vf2d CENTERED;
@ -53,6 +53,7 @@ private:
static std::multimap<EventName,SoundEffect>SOUND_EFFECTS; static std::multimap<EventName,SoundEffect>SOUND_EFFECTS;
std::string filename; std::string filename;
float vol; float vol;
bool combatSound=false;
float minPitch=0.9f; float minPitch=0.9f;
float maxPitch=1.1f; float maxPitch=1.1f;
}; };

@ -59,6 +59,8 @@ void State_GameHub::OnStateChange(GameState*prevState){
game->GetPlayer()->SetState(State::NORMAL); game->GetPlayer()->SetState(State::NORMAL);
game->LoadLevel("HUB"); game->LoadLevel("HUB");
}
void State_GameHub::OnLevelLoad(){
game->UpdateDiscordStatus("Hub Area",game->GetPlayer()->GetClassName()); game->UpdateDiscordStatus("Hub Area",game->GetPlayer()->GetClassName());
} }
void State_GameHub::OnUserUpdate(AiL*game){ void State_GameHub::OnUserUpdate(AiL*game){

@ -45,4 +45,5 @@ class State_GameHub:public State_GameRun{
virtual void OnStateChange(GameState*prevState)override final; virtual void OnStateChange(GameState*prevState)override final;
virtual void OnUserUpdate(AiL*game)override final; virtual void OnUserUpdate(AiL*game)override final;
virtual void Draw(AiL*game)override final; virtual void Draw(AiL*game)override final;
virtual void OnLevelLoad()override final;
}; };

@ -73,6 +73,7 @@ void State_GameRun::OnStateChange(GameState*prevState){
game->LoadLevel(State_OverworldMap::GetCurrentConnectionPoint().map); game->LoadLevel(State_OverworldMap::GetCurrentConnectionPoint().map);
} }
void State_GameRun::OnLevelLoad(){}
void State_GameRun::OnUserUpdate(AiL*game){ void State_GameRun::OnUserUpdate(AiL*game){
game->bossDisplayTimer=std::max(0.f,game->bossDisplayTimer-game->GetElapsedTime()); game->bossDisplayTimer=std::max(0.f,game->bossDisplayTimer-game->GetElapsedTime());
if(game->encounterStarted&&game->totalBossEncounterMobs>0){ if(game->encounterStarted&&game->totalBossEncounterMobs>0){
@ -84,13 +85,7 @@ void State_GameRun::OnUserUpdate(AiL*game){
game->UpdateEffects(game->GetElapsedTime()); game->UpdateEffects(game->GetElapsedTime());
GameEvent::UpdateEvents(); GameEvent::UpdateEvents();
game->GetPlayer()->Update(game->GetElapsedTime()); game->GetPlayer()->Update(game->GetElapsedTime());
for(Monster&m:MONSTER_LIST){ game->UpdateMonsters();
m.Update(game->GetElapsedTime());
}
for(Monster&m:game->monstersToBeSpawned){
MONSTER_LIST.push_back(m);
}
game->monstersToBeSpawned.clear();
ItemDrop::UpdateDrops(game->GetElapsedTime()); ItemDrop::UpdateDrops(game->GetElapsedTime());
game->UpdateBullets(game->GetElapsedTime()); game->UpdateBullets(game->GetElapsedTime());

@ -46,6 +46,7 @@ protected:
virtual void OnStateChange(GameState*prevState)override; virtual void OnStateChange(GameState*prevState)override;
virtual void OnUserUpdate(AiL*game)override; virtual void OnUserUpdate(AiL*game)override;
virtual void Draw(AiL*game)override; virtual void Draw(AiL*game)override;
virtual void OnLevelLoad()override;
void FontTest(); void FontTest();
void FontSpriteTest(); void FontSpriteTest();
}; };

@ -76,6 +76,7 @@ void State_LevelComplete::OnStateChange(GameState*prevState){
game->GetPlayer()->SetState(State::NORMAL); game->GetPlayer()->SetState(State::NORMAL);
Menu::OpenMenu(LEVEL_COMPLETE); Menu::OpenMenu(LEVEL_COMPLETE);
}; };
void State_LevelComplete::OnLevelLoad(){}
void State_LevelComplete::OnUserUpdate(AiL*game){ void State_LevelComplete::OnUserUpdate(AiL*game){
if(levelUpTimer>0.f){ if(levelUpTimer>0.f){
levelUpTimer=std::max(0.f,levelUpTimer-game->GetElapsedTime()); levelUpTimer=std::max(0.f,levelUpTimer-game->GetElapsedTime());

@ -48,4 +48,5 @@ class State_LevelComplete:public GameState{
virtual void OnUserUpdate(AiL*game)override final; virtual void OnUserUpdate(AiL*game)override final;
virtual void Draw(AiL*game)override final; virtual void Draw(AiL*game)override final;
virtual void DrawOverlay(AiL*game)override final; virtual void DrawOverlay(AiL*game)override final;
virtual void OnLevelLoad()override final;
}; };

@ -40,6 +40,8 @@ All rights reserved.
#include "Menu.h" #include "Menu.h"
#include "TitleScreen.h" #include "TitleScreen.h"
#include "Key.h" #include "Key.h"
#include "ItemDrop.h"
#include "util.h"
INCLUDE_game INCLUDE_game
@ -47,13 +49,64 @@ void State_MainMenu::OnStateChange(GameState*prevState){
Audio::PlayBGM("title_screen"); Audio::PlayBGM("title_screen");
TitleScreen::Reset(); TitleScreen::Reset();
game->UpdateDiscordStatus("Main Menu",""); game->UpdateDiscordStatus("Main Menu","");
game->LoadLevel("starting_map"_S,AiL::NO_MUSIC_CHANGE);
}; };
void State_MainMenu::OnLevelLoad(){
game->GetPlayer()->SetIframes(999999.f);
game->GetPlayer()->SetInvisible(true);
SelectAndMoveToNewFocusArea();
}
void State_MainMenu::OnUserUpdate(AiL*game){ void State_MainMenu::OnUserUpdate(AiL*game){
game->GetPlayer()->ForceSetPos(game->GetPlayer()->GetPos()+cameraMoveDir*8*game->GetElapsedTime());
lastMoveTime+=game->GetElapsedTime();
if(lastMoveTime>8.f)SelectAndMoveToNewFocusArea();
TitleScreen::Update(); TitleScreen::Update();
if(AiL::KEY_CONFIRM.Released()){ if(AiL::KEY_CONFIRM.Released()){
TitleScreen::Skip(); TitleScreen::Skip();
} }
game->UpdateEffects(game->GetElapsedTime());
GameEvent::UpdateEvents();
game->UpdateMonsters();
ItemDrop::UpdateDrops(game->GetElapsedTime());
game->UpdateBullets(game->GetElapsedTime());
game->UpdateCamera(game->GetElapsedTime());
}; };
void State_MainMenu::Draw(AiL*game){ void State_MainMenu::Draw(AiL*game){
TitleScreen::Draw(); TitleScreen::Draw();
}; };
const ZoneData&State_MainMenu::ChooseRandomFocusArea(){
//std::vector<ZoneData>
if(game->GetZones().count("Focus Area")>0){
const std::vector<ZoneData>&zones=game->GetZones().at("Focus Area");
newSelectedFocusAreaIndex=util::random()%zones.size();
return zones[newSelectedFocusAreaIndex];
}else ERR("WARNING! No focus areas included in the intro map!");
return game->GetZones().at("Focus Area")[0];
}
void State_MainMenu::SelectAndMoveToNewFocusArea(){
const ZoneData&newFocusArea=ChooseRandomFocusArea();
if(lastSelectedFocusAreaIndex==newSelectedFocusAreaIndex)return;
game->camera.MoveCamera(newFocusArea.zone.pos);
game->GetPlayer()->ForceSetPos(newFocusArea.zone.pos);
cameraMoveDir={};
for(const XMLTag&tag:newFocusArea.properties){
if(tag.data.at("name")=="Scroll Direction"){
std::string_view dir=tag.data.at("value");
if(dir=="NORTH"sv)cameraMoveDir={0.f,-1.f};else
if(dir=="NORTHEAST"sv)cameraMoveDir={1.f,-1.f};else
if(dir=="EAST"sv)cameraMoveDir={1.f,0.f};else
if(dir=="SOUTHEAST"sv)cameraMoveDir={1.f,1.f};else
if(dir=="SOUTH"sv)cameraMoveDir={0.f,1.f};else
if(dir=="SOUTHWEST"sv)cameraMoveDir={-1.f,1.f};else
if(dir=="WEST"sv)cameraMoveDir={-1.f,0.f};else
if(dir=="NORTHWEST"sv)cameraMoveDir={-1.f,-1.f};
}
}
lastMoveTime=0.f;
lastSelectedFocusAreaIndex=newSelectedFocusAreaIndex;
}

@ -36,9 +36,18 @@ All rights reserved.
*/ */
#pragma endregion #pragma endregion
#include "GameState.h" #include "GameState.h"
#include "TMXParser.h"
class State_MainMenu:public GameState{ class State_MainMenu:public GameState{
virtual void OnStateChange(GameState*prevState)override final; virtual void OnStateChange(GameState*prevState)override final;
virtual void OnUserUpdate(AiL*game)override final; virtual void OnUserUpdate(AiL*game)override final;
virtual void Draw(AiL*game)override final; virtual void Draw(AiL*game)override final;
virtual void OnLevelLoad()override final;
const ZoneData&ChooseRandomFocusArea();
vf2d cameraMoveDir;
float lastMoveTime=0.f;
size_t newSelectedFocusAreaIndex=0;
size_t lastSelectedFocusAreaIndex=0;
void SelectAndMoveToNewFocusArea();
}; };

@ -63,6 +63,8 @@ void State_OverworldMap::OnStateChange(GameState*prevState){
Component<MenuComponent>(MenuType::PAUSE,"Return to Camp Button")->SetGrayedOut(false); Component<MenuComponent>(MenuType::PAUSE,"Return to Camp Button")->SetGrayedOut(false);
SaveFile::SaveGame(); SaveFile::SaveGame();
game->LoadLevel("WORLD_MAP"); game->LoadLevel("WORLD_MAP");
};
void State_OverworldMap::OnLevelLoad(){
if(Menu::IsMenuOpen()){ if(Menu::IsMenuOpen()){
Menu::CloseAllMenus(); Menu::CloseAllMenus();
} }
@ -82,7 +84,7 @@ void State_OverworldMap::OnStateChange(GameState*prevState){
} }
Menu::OpenMenu(OVERWORLD_LEVEL_SELECT,false); Menu::OpenMenu(OVERWORLD_LEVEL_SELECT,false);
game->UpdateDiscordStatus("Overworld Map",game->GetPlayer()->GetClassName()); game->UpdateDiscordStatus("Overworld Map",game->GetPlayer()->GetClassName());
}; }
void State_OverworldMap::OnUserUpdate(AiL*game){ void State_OverworldMap::OnUserUpdate(AiL*game){
if(Menu::stack.size()>1)return; if(Menu::stack.size()>1)return;

@ -60,6 +60,7 @@ public:
virtual void OnUserUpdate(AiL*game)override final; virtual void OnUserUpdate(AiL*game)override final;
virtual void Draw(AiL*game)override final; virtual void Draw(AiL*game)override final;
virtual void DrawOverlay(AiL*game)override final; virtual void DrawOverlay(AiL*game)override final;
virtual void OnLevelLoad()override final;
static void StartLevel(); static void StartLevel();
static void UpdateCurrentConnectionPoint(const ConnectionPoint&connection); static void UpdateCurrentConnectionPoint(const ConnectionPoint&connection);
}; };

@ -42,6 +42,7 @@ All rights reserved.
void State_Story::OnStateChange(GameState*prevState){ void State_Story::OnStateChange(GameState*prevState){
Menu::CloseAllMenus(); Menu::CloseAllMenus();
}; };
void State_Story::OnLevelLoad(){}
void State_Story::OnUserUpdate(AiL*game){ void State_Story::OnUserUpdate(AiL*game){
VisualNovel::novel.Update(); VisualNovel::novel.Update();
}; };

@ -42,4 +42,5 @@ class State_Story:public GameState{
virtual void OnStateChange(GameState*prevState)override final; virtual void OnStateChange(GameState*prevState)override final;
virtual void OnUserUpdate(AiL*game)override final; virtual void OnUserUpdate(AiL*game)override final;
virtual void Draw(AiL*game)override final; virtual void Draw(AiL*game)override final;
virtual void OnLevelLoad()override final;
}; };

@ -98,6 +98,7 @@ struct SpawnerTag{
struct ZoneData{ struct ZoneData{
geom2d::rect<int>zone; geom2d::rect<int>zone;
bool isUpper=false; bool isUpper=false;
std::vector<XMLTag>properties;
}; };
struct NPCData{ struct NPCData{
@ -478,7 +479,11 @@ class TMXParser{
} }
} else } else
if (newTag.tag=="object"&&newTag.data["type"]=="PlayerSpawnLocation") { if (newTag.tag=="object"&&newTag.data["type"]=="PlayerSpawnLocation") {
parsedMapInfo.MapData.playerSpawnLocation={newTag.GetInteger("x")-newTag.GetInteger("width")/2,newTag.GetInteger("y")-newTag.GetInteger("height")/2}; float width=1.f;
float height=1.f;
if(newTag.data.count("width")>0)width=newTag.GetFloat("width");
if(newTag.data.count("height")>0)height=newTag.GetFloat("height");
parsedMapInfo.MapData.playerSpawnLocation={int(newTag.GetFloat("x")-width/2),int(newTag.GetFloat("y")-height/2)};
} else } else
if (newTag.tag=="object"&&newTag.data["type"]=="NPC") { if (newTag.tag=="object"&&newTag.data["type"]=="NPC") {
if(inNPCTag)parsedMapInfo.npcs.push_back(NPCData{npcTag}); if(inNPCTag)parsedMapInfo.npcs.push_back(NPCData{npcTag});
@ -513,19 +518,19 @@ class TMXParser{
if(newTag.tag=="property"&&currentStagePlate!=nullptr){ if(newTag.tag=="property"&&currentStagePlate!=nullptr){
currentStagePlate->properties[newTag.data["name"]]={newTag.data["name"],newTag.data["value"]}; currentStagePlate->properties[newTag.data["name"]]={newTag.data["name"],newTag.data["value"]};
}else }else
if(newTag.tag=="property"&&prevZoneData!=nullptr){
//This is a property for a zone that doesn't fit into the other categories, we add it to the previous zone data encountered.
prevZoneData->properties.push_back(newTag);
}else
if (newTag.tag=="object"&&newTag.data.find("type")!=newTag.data.end()){ if (newTag.tag=="object"&&newTag.data.find("type")!=newTag.data.end()){
//This is an object with a type that doesn't fit into other categories, we can add it to ZoneData. //This is an object with a type that doesn't fit into other categories, we can add it to ZoneData.
if(parsedMapInfo.ZoneData.find(newTag.data["type"])!=parsedMapInfo.ZoneData.end()){ std::vector<ZoneData>&zones=parsedMapInfo.ZoneData[newTag.data["type"]];
std::vector<ZoneData>&zones=parsedMapInfo.ZoneData.at(newTag.data["type"]); float width=1.f;
zones.emplace_back(geom2d::rect<int>{{newTag.GetInteger("x"),newTag.GetInteger("y")},{newTag.GetInteger("width"),newTag.GetInteger("height")}}); float height=1.f;
prevZoneData=&zones.back(); if(newTag.data.count("width")>0)width=newTag.GetFloat("width");
} else { if(newTag.data.count("height")>0)height=newTag.GetFloat("height");
if(newTag.data["width"].length()>0&&newTag.data["height"].length()>0){ //This ensures the zone is valid to begin with. zones.emplace_back(geom2d::rect<int>{{newTag.GetInteger("x"),newTag.GetInteger("y")},{int(width),int(height)}});
std::vector<ZoneData>&zones=parsedMapInfo.ZoneData[newTag.data["type"]]; prevZoneData=&zones.back();
zones.emplace_back(geom2d::rect<int>{{newTag.GetInteger("x"),newTag.GetInteger("y")},{newTag.GetInteger("width"),newTag.GetInteger("height")}});
prevZoneData=&zones.back();
}
}
}else{ }else{
#ifdef _DEBUG #ifdef _DEBUG
if(_DEBUG_MAP_LOAD_INFO)std::cout<<"Unsupported tag format! Ignoring."<<"\n"; if(_DEBUG_MAP_LOAD_INFO)std::cout<<"Unsupported tag format! Ignoring."<<"\n";

@ -1,27 +1,26 @@
January 1st February 28th -> Begin Internal Game Playtesting
=========== March 6th -> Discord/Friend Playtesting
March 30th -> Public Demo Release
- Foreground tile depth correction for tiles w/hitboxes - Foreground tile depth correction for tiles w/hitboxes
- Add Death screen (Zoom in on fatal blow, slow time down... Display some game over text... Allow retry or return to world map.) - Add Death screen (Zoom in on fatal blow, slow time down... Display some game over text... Allow retry or return to world map.)
- Track items used during a stage, on death, restore the loadout item quantities used. - Track items used during a stage, on death, restore the loadout item quantities used.
- Icon displays / Proper key displays above skill keys - Mosaic transition on level load
Add Bonus XP when completing a stage Add Bonus XP when completing a stage
January 31st
============
- Loading Screen
- Title Screen setpieces
- Hide mouse cursor during controller play. Reveal it again during mouse play. - Hide mouse cursor during controller play. Reveal it again during mouse play.
- Auto aim causes retreat-type moves to aim away from the auto target, and prefer the direction the player's moving in. - Auto aim causes retreat-type moves to aim away from the auto target, and prefer the direction the player's moving in.
- Condense stage track (loading times) - Condense stage track (loading times)
- Credits/Licensing - Credits/Licensing
- Basic tutorial on the first stage, Only allow the player to select "Change Loadout", explain how to setup items, can only start once a loadout item is set.
- show inputs that can be used by the player to navigate, ability usage, and defensive. When player takes enough damage show how to use recovery items.

@ -54,8 +54,8 @@ void Test::is(std::string conditionStr,bool testResult){
void Test::RunMapTests(){ void Test::RunMapTests(){
is("There are two LowerBridgeCollision zones in Campaign I-I", is("There are two LowerBridgeCollision zones in Campaign I-I",
game->GetZoneData("CAMPAIGN_1_1").count("LowerBridgeCollision") game->GetZones("CAMPAIGN_1_1").count("LowerBridgeCollision")
&&game->GetZoneData("CAMPAIGN_1_1").at("LowerBridgeCollision").size()>=2); &&game->GetZones("CAMPAIGN_1_1").at("LowerBridgeCollision").size()>=2);
for(auto&[key,value]:game->MAP_DATA){ for(auto&[key,value]:game->MAP_DATA){
is("A Map type has been selected for map "+key, is("A Map type has been selected for map "+key,
value.GetMapType()!=""&&value.GetMapType()!="Unspecified"); value.GetMapType()!=""&&value.GetMapType()!="Unspecified");

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 3 #define VERSION_MINOR 3
#define VERSION_PATCH 0 #define VERSION_PATCH 0
#define VERSION_BUILD 7515 #define VERSION_BUILD 7609
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -193,6 +193,9 @@ void VisualNovel::Update(){
locationDisplayTime=std::max(0.f,locationDisplayTime-game->GetElapsedTime()); locationDisplayTime=std::max(0.f,locationDisplayTime-game->GetElapsedTime());
transitionTime=std::max(0.f,transitionTime-game->GetElapsedTime()); transitionTime=std::max(0.f,transitionTime-game->GetElapsedTime());
textScrollTime=std::max(0.f,textScrollTime-game->GetElapsedTime()); textScrollTime=std::max(0.f,textScrollTime-game->GetElapsedTime());
if(backgroundScrollAmt<90.f){
backgroundScrollAmt=std::min(90.f,backgroundScrollAmt+backgroundScrollSpd*game->GetElapsedTime());
}
} }
void VisualNovel::ExecuteNextCommand(){ void VisualNovel::ExecuteNextCommand(){
if(commandIndex<commands.size()){ if(commandIndex<commands.size()){
@ -211,9 +214,9 @@ void VisualNovel::Draw(){
alpha=util::lerp(0,1,1-(transitionTime/maxTransitionTime)); alpha=util::lerp(0,1,1-(transitionTime/maxTransitionTime));
} }
if(prevBackgroundFilename!=""){ if(prevBackgroundFilename!=""){
game->DrawDecal({0,0},GFX["story_background_image_location"_S+prevBackgroundFilename].Decal()); game->DrawDecal({0,-prevBackgroundScrollAmt},GFX["story_background_image_location"_S+prevBackgroundFilename].Decal());
} }
game->DrawDecal({0,0},GFX["story_background_image_location"_S+backgroundFilename].Decal(),{1,1},{255,255,255,uint8_t(255*alpha)}); game->DrawDecal({0,-backgroundScrollAmt},GFX["story_background_image_location"_S+backgroundFilename].Decal(),{1,1},{255,255,255,uint8_t(255*alpha)});
}else{ }else{
game->FillRectDecal({0,0},game->GetScreenSize()); game->FillRectDecal({0,0},game->GetScreenSize());
} }
@ -289,6 +292,8 @@ void BackgroundCommand::Execute(VisualNovel&vn){
vn.prevBackgroundFilename=vn.backgroundFilename; vn.prevBackgroundFilename=vn.backgroundFilename;
vn.backgroundFilename=backgroundFilename; vn.backgroundFilename=backgroundFilename;
vn.transitionTime=2.0f; vn.transitionTime=2.0f;
vn.prevBackgroundScrollAmt=vn.backgroundScrollAmt;
vn.backgroundScrollAmt=0.f;
vn.ExecuteNextCommand(); vn.ExecuteNextCommand();
} }
BackgroundCommand::BackgroundCommand(std::string backgroundFilename) BackgroundCommand::BackgroundCommand(std::string backgroundFilename)
@ -323,6 +328,7 @@ SpeakerCommand::SpeakerCommand(std::string displayedName,std::string speaker)
CommandType::CommandType SpeakerCommand::GetType(){return CommandType::SPEAKER;} CommandType::CommandType SpeakerCommand::GetType(){return CommandType::SPEAKER;}
void DialogCommand::Execute(VisualNovel&vn){ void DialogCommand::Execute(VisualNovel&vn){
if(dialog.size()<=0)return;
vn.textScrollTime=VisualNovel::maxTextScrollTime; vn.textScrollTime=VisualNovel::maxTextScrollTime;
bool mustDisplay=vn.activeText.length()==0; bool mustDisplay=vn.activeText.length()==0;
Font*displayFont=&VisualNovel::font; Font*displayFont=&VisualNovel::font;

@ -73,7 +73,7 @@ public:
}; };
class DialogCommand final:public Command{ class DialogCommand final:public Command{
std::string dialog; std::string dialog="";
public: public:
void Execute(VisualNovel&vn)override; void Execute(VisualNovel&vn)override;
DialogCommand(std::string dialog); DialogCommand(std::string dialog);
@ -143,12 +143,15 @@ class VisualNovel{
std::string prevBackgroundFilename; std::string prevBackgroundFilename;
float transitionTime=0; float transitionTime=0;
static constexpr float maxTransitionTime=2.0f; static constexpr float maxTransitionTime=2.0f;
const float backgroundScrollSpd=2.0f;
std::vector<Command*>commands; std::vector<Command*>commands;
int commandIndex=0; int commandIndex=0;
std::string locationDisplayText=""; std::string locationDisplayText="";
float locationDisplayTime=0; float locationDisplayTime=0;
std::string prevTheme=""; std::string prevTheme="";
float textScrollTime=0; float textScrollTime=0;
float backgroundScrollAmt=0;
float prevBackgroundScrollAmt=0;
static constexpr float maxTextScrollTime=1.0f; static constexpr float maxTextScrollTime=1.0f;
public: public:
static Font font,narratorFont,locationFont; static Font font,narratorFont,locationFont;

File diff suppressed because it is too large Load Diff

@ -571,7 +571,7 @@
<property name="Connection 4 - West" type="object" value="7"/> <property name="Connection 4 - West" type="object" value="7"/>
<property name="Map" propertytype="Level" value="CAMPAIGN_1_2"/> <property name="Map" propertytype="Level" value="CAMPAIGN_1_2"/>
<property name="Type" propertytype="StageType" value="DUNGEON"/> <property name="Type" propertytype="StageType" value="DUNGEON"/>
<property name="Unlock Condition" propertytype="Level" value="CAMPAIGN_1_1"/> <property name="Unlock Condition" propertytype="Level" value="STORY_1_1"/>
</properties> </properties>
</object> </object>
<object id="5" name="Story I" type="StagePlate" x="344" y="476" width="20" height="24"> <object id="5" name="Story I" type="StagePlate" x="344" y="476" width="20" height="24">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@ -9,6 +9,7 @@ Events
{ {
Bear Slam Attack Bear Slam Attack
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = bear_slam.ogg, 70% File[0] = bear_slam.ogg, 70%
} }
@ -112,70 +113,83 @@ Events
} }
Monster Hurt Monster Hurt
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = monster_hurt.ogg, 40% File[0] = monster_hurt.ogg, 40%
} }
Player Hit Player Hit
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = player_hit1.ogg, 40% File[0] = player_hit1.ogg, 40%
File[1] = player_hit2.ogg, 100% File[1] = player_hit2.ogg, 100%
} }
Ranger Auto Attack Ranger Auto Attack
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ranger_auto1.ogg, 50% File[0] = ranger_auto1.ogg, 50%
File[1] = ranger_auto2.ogg, 50% File[1] = ranger_auto2.ogg, 50%
} }
Ranger Retreat Ranger Retreat
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ranger_backstep.ogg, 90% File[0] = ranger_backstep.ogg, 90%
} }
Ranger Multishot Ranger Multishot
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ranger_multishot.ogg, 100% File[0] = ranger_multishot.ogg, 100%
} }
Ranger Rapid Fire Ranger Rapid Fire
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ranger_rapid_fire.ogg, 100% File[0] = ranger_rapid_fire.ogg, 100%
} }
Ranger Charged Shot Ranger Charged Shot
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ranger_charged_shot.ogg, 70% File[0] = ranger_charged_shot.ogg, 70%
} }
Slime Dead Slime Dead
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_dead.ogg, 60% File[0] = slime_dead.ogg, 60%
File[1] = slime_dead2.ogg, 60% File[1] = slime_dead2.ogg, 60%
} }
Monster Dead Monster Dead
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_dead2.ogg, 60% File[0] = slime_dead2.ogg, 60%
} }
Slime King Land Slime King Land
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_king_landing.ogg, 100% File[0] = slime_king_landing.ogg, 100%
} }
Slime King Shoot Slime King Shoot
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_king_shoot.ogg, 60% File[0] = slime_king_shoot.ogg, 60%
} }
Slime Shoot Slime Shoot
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_shoot.ogg, 100% File[0] = slime_shoot.ogg, 100%
File[1] = slime_shoot2.ogg, 80% File[1] = slime_shoot2.ogg, 80%
} }
Slime Walk Slime Walk
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_walk.ogg, 10% File[0] = slime_walk.ogg, 10%
File[1] = slime_walk2.ogg, 10% File[1] = slime_walk2.ogg, 10%
@ -203,37 +217,44 @@ Events
} }
Ursule Dead Ursule Dead
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ursule_dead.ogg, 100% File[0] = ursule_dead.ogg, 100%
} }
Ursule Phase Transition Ursule Phase Transition
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ursule_phase_transition.ogg, 100% File[0] = ursule_phase_transition.ogg, 100%
} }
Warrior Auto Attack Warrior Auto Attack
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = warrior_auto1.ogg, 60% File[0] = warrior_auto1.ogg, 60%
} }
Warrior Battlecry Warrior Battlecry
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = warrior_battlecry.ogg, 100% File[0] = warrior_battlecry.ogg, 100%
} }
Warrior Block Hit Warrior Block Hit
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = warrior_blockhit1.ogg, 100% File[0] = warrior_blockhit1.ogg, 100%
File[1] = warrior_blockhit2.ogg, 100% File[1] = warrior_blockhit2.ogg, 100%
} }
Warrior Ground Slam Warrior Ground Slam
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = warrior_groundslam.ogg, 100% File[0] = warrior_groundslam.ogg, 100%
} }
Warrior Sonic Slash Warrior Sonic Slash
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = warrior_sonicslash.ogg, 70% File[0] = warrior_sonicslash.ogg, 70%
} }
@ -255,31 +276,37 @@ Events
} }
Wizard Fire Bolt Hit Wizard Fire Bolt Hit
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = wizard_firebolt_hit.ogg, 100% File[0] = wizard_firebolt_hit.ogg, 100%
} }
Wizard Lightning Bolt Shoot Wizard Lightning Bolt Shoot
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = wizard_lightningbolt.ogg, 60% File[0] = wizard_lightningbolt.ogg, 60%
} }
Wizard Lightning Bolt Hit Wizard Lightning Bolt Hit
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = wizard_firebolt_hit.ogg, 100% File[0] = wizard_firebolt_hit.ogg, 100%
} }
Wizard Meteor Wizard Meteor
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = wizard_meteor.ogg, 100% File[0] = wizard_meteor.ogg, 100%
} }
Wizard Meteor Flames Wizard Meteor Flames
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = wizard_meteor_lingering.ogg, 100% File[0] = wizard_meteor_lingering.ogg, 100%
} }
Wizard Teleport Wizard Teleport
{ {
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = wizard_teleport.ogg, 100% File[0] = wizard_teleport.ogg, 100%
} }

@ -20,7 +20,7 @@ save_server = https://projectdivar.com:4505/AiL
map_config = levels.txt map_config = levels.txt
# Starting map when loading the game. # Starting map when loading the game.
starting_map = WORLD_MAP starting_map = INTRO_MAP
# Player Properties Loading Config # Player Properties Loading Config
player_config = Player.txt player_config = Player.txt

@ -2,6 +2,11 @@ map_path = assets/Campaigns/
Levels Levels
{ {
INTRO_MAP
{
Map File = Intro_Map.tmx
}
WORLD_MAP WORLD_MAP
{ {
Map File = World_Map.tmx Map File = World_Map.tmx

@ -14,6 +14,7 @@ Merchant crossing is a Crossway that connects Bleakport, the Lestorian Forest, E
The crossing is famous for merchants to rest on their travels and trade goods with each other. The crossing is famous for merchants to rest on their travels and trade goods with each other.
{BACKGROUND commercial_assets/Burning Trading Post.png}
While you are getting closer you realise something isnt right. The Smoke in the sky is way thicker then what you would expect from just a few campfires... While you are getting closer you realise something isnt right. The Smoke in the sky is way thicker then what you would expect from just a few campfires...
You reach the crossing where a #FF0000horrible scenery#FFFFFF awaits you. You reach the crossing where a #FF0000horrible scenery#FFFFFF awaits you.
@ -110,7 +111,7 @@ Oh what shall I do? I can't return to the Kingdom without it!
[You] [You]
First you need more rest. Once you are able to walk on your own again, we can think about a solution for your lost object. First you need more rest. Once you are able to walk on your own again, we can think about a solution for your lost object.
{BACKGROUND rest.png} {BACKGROUND commercial_assets/Forest Clearing Campsite.png}
[] []
You set up a tent and a campfire a little outside of the main camp where the tragedy happened and helped Sherman over. You set up a tent and a campfire a little outside of the main camp where the tragedy happened and helped Sherman over.
@ -121,9 +122,9 @@ Without the corpses surrounding you, you relax a little bit.
You keep an eye on Sherman as he recovers. You keep an eye on Sherman as he recovers.
{BACKGROUND sea.png}
The next day you wake to find Sherman no longer lying down but is instead sitting on the grass. The next day you wake to find Sherman no longer lying down but is instead sitting on the grass.
{BACKGROUND commercial_assets/Forest Clearing Campsite_Day.png}
[Sherman] [Sherman]
Good Morning! You told me you are an Adventurer right? Good Morning! You told me you are an Adventurer right?

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.10.1" name="112x96_Forge_No_Shadow_12x12" tilewidth="12" tileheight="12" tilecount="336" columns="56"> <tileset version="1.10" tiledversion="1.10.2" name="112x96_Forge_No_Shadow_12x12" tilewidth="12" tileheight="12" tilecount="336" columns="56">
<image source="commercial_assets/112x96_Forge_No_Shadow_12x12.png" width="672" height="72"/> <image source="commercial_assets/112x96_Forge_No_Shadow_12x12.png" width="672" height="72"/>
<tile id="0" type="ForegroundTile"> <tile id="0" type="ForegroundTile">
<properties> <properties>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.10.1" name="Decorations_c1_No_Shadow24x24" tilewidth="24" tileheight="24" tilecount="1620" columns="45"> <tileset version="1.10" tiledversion="1.10.2" name="Decorations_c1_No_Shadow24x24" tilewidth="24" tileheight="24" tilecount="1620" columns="45">
<image source="commercial_assets/Decorations_c1_No_Shadow24x24.png" width="1080" height="864"/> <image source="commercial_assets/Decorations_c1_No_Shadow24x24.png" width="1080" height="864"/>
<tile id="68" type="ForegroundTile"/> <tile id="68" type="ForegroundTile"/>
<tile id="73" type="ForegroundTile"/> <tile id="73" type="ForegroundTile"/>

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.10.1" name="Minifantasy_TinyOverworldAllTiles" class="Terrain" tilewidth="4" tileheight="4" tilecount="8137" columns="79"> <tileset version="1.10" tiledversion="1.10.2" name="Minifantasy_TinyOverworldAllTiles" class="Terrain" tilewidth="4" tileheight="4" tilecount="8137" columns="79">
<image source="commercial_assets/Minifantasy_TinyOverworldAllTiles.png" width="316" height="412"/> <image source="commercial_assets/Minifantasy_TinyOverworldAllTiles.png" width="316" height="412"/>
</tileset> </tileset>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

@ -159,10 +159,18 @@ namespace olc {
FT_Set_Transform(toUse->fontFace, &rotMat, &pen); FT_Set_Transform(toUse->fontFace, &rotMat, &pen);
FT_Error error = FT_Load_Char(toUse->fontFace, chr, FT_LOAD_RENDER); FT_Error error = FT_Load_Char(toUse->fontFace, chr, FT_LOAD_RENDER);
if(error){
std::cout<<"FT Error: "<<error<<std::endl;
continue;
}
FT_GlyphSlot slot = toUse->fontFace->glyph; FT_GlyphSlot slot = toUse->fontFace->glyph;
FT_Glyph glyph; FT_Glyph glyph;
FT_Get_Glyph(slot, &glyph); error = FT_Get_Glyph(slot, &glyph);
if(error){
std::cout<<"FT Error: "<<error<<std::endl;
continue;
}
FT_BBox bbox; FT_BBox bbox;
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox); FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);

@ -0,0 +1,3 @@
#include <string>
std::string PACK_KEY="INSERT_PACK_KEY_HERE";

@ -10,6 +10,7 @@ cp -R "Adventures in Lestoria/assets/music" bin/assets
cp -R "Adventures in Lestoria/assets/npcs" bin/assets cp -R "Adventures in Lestoria/assets/npcs" bin/assets
cp -R "Adventures in Lestoria/assets/sounds" bin/assets cp -R "Adventures in Lestoria/assets/sounds" bin/assets
cp -R "Adventures in Lestoria/assets/gamepack.pak" bin/assets cp -R "Adventures in Lestoria/assets/gamepack.pak" bin/assets
cp -R "Adventures in Lestoria/assets/*.ttf" bin/assets
cp -R "x64/Release/*" bin cp -R "x64/Release/*" bin
rm bin/*.pdb rm bin/*.pdb

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save