Implemented environmental audio loading in maps. Added stb_vorbis header/cpp file for ogg format support. Converted track parts and sound effect to be ogg instead.

pull/29/head
sigonasr2 11 months ago
parent cb86417a38
commit 725a858fab
  1. 2
      Adventures in Lestoria/Ability.h
  2. 36
      Adventures in Lestoria/Adventures in Lestoria.tiled-project
  3. 27
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  4. 33
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  5. 116
      Adventures in Lestoria/AdventuresInLestoria.cpp
  6. 6
      Adventures in Lestoria/AdventuresInLestoria.h
  7. 5
      Adventures in Lestoria/Audio.cpp
  8. 2
      Adventures in Lestoria/Audio.h
  9. 1
      Adventures in Lestoria/CREDITS
  10. 99
      Adventures in Lestoria/EnvironmentalAudio.cpp
  11. 67
      Adventures in Lestoria/EnvironmentalAudio.h
  12. 5
      Adventures in Lestoria/Item.cpp
  13. 3
      Adventures in Lestoria/Item.h
  14. 14
      Adventures in Lestoria/Map.cpp
  15. 11
      Adventures in Lestoria/Map.h
  16. 48
      Adventures in Lestoria/MapName.h
  17. 1
      Adventures in Lestoria/Menu.h
  18. 16
      Adventures in Lestoria/Monster.cpp
  19. 3
      Adventures in Lestoria/Monster.h
  20. 82
      Adventures in Lestoria/Pathfinding.cpp
  21. 15
      Adventures in Lestoria/Player.cpp
  22. 2
      Adventures in Lestoria/Player.h
  23. 4
      Adventures in Lestoria/SlimeKing.cpp
  24. 2
      Adventures in Lestoria/State_MainMenu.cpp
  25. 24
      Adventures in Lestoria/TMXParser.h
  26. 2
      Adventures in Lestoria/TextEntryLabel.h
  27. 2
      Adventures in Lestoria/Version.h
  28. 12
      Adventures in Lestoria/assets/Campaigns/1_1_v2.tmx
  29. 4
      Adventures in Lestoria/assets/config/audio/audio.txt
  30. 33
      Adventures in Lestoria/assets/config/audio/bgm.txt
  31. 8
      Adventures in Lestoria/assets/config/audio/environmentalaudio.txt
  32. 0
      Adventures in Lestoria/assets/config/audio/events.txt
  33. 51
      Adventures in Lestoria/assets/config/bgm/bgm.txt
  34. 10
      Adventures in Lestoria/assets/config/configuration.txt
  35. BIN
      Adventures in Lestoria/assets/music/foresty0.mp3
  36. BIN
      Adventures in Lestoria/assets/music/foresty0_alt.mp3
  37. BIN
      Adventures in Lestoria/assets/music/foresty1_1_1.mp3
  38. BIN
      Adventures in Lestoria/assets/music/foresty1_1_2.mp3
  39. BIN
      Adventures in Lestoria/assets/music/foresty1_1_alt1.mp3
  40. BIN
      Adventures in Lestoria/assets/music/foresty1_1_alt2.mp3
  41. BIN
      Adventures in Lestoria/assets/music/foresty1_1_bass.ogg
  42. BIN
      Adventures in Lestoria/assets/music/foresty1_1_crash.ogg
  43. BIN
      Adventures in Lestoria/assets/music/foresty1_1_drums.ogg
  44. BIN
      Adventures in Lestoria/assets/music/foresty1_1_flute.ogg
  45. BIN
      Adventures in Lestoria/assets/music/foresty1_1_piano 1.ogg
  46. BIN
      Adventures in Lestoria/assets/music/foresty1_1_piano 2.ogg
  47. BIN
      Adventures in Lestoria/assets/music/foresty1_1_pizzicato.ogg
  48. BIN
      Adventures in Lestoria/assets/music/foresty1_1_staccato.ogg
  49. BIN
      Adventures in Lestoria/assets/music/foresty1_1_strings.ogg
  50. BIN
      Adventures in Lestoria/assets/music/foresty1_1_xtra perc.ogg
  51. BIN
      Adventures in Lestoria/assets/sounds/sfx100v2_loop_water_01.mp3
  52. BIN
      Adventures in Lestoria/assets/sounds/sfx100v2_loop_water_01.ogg
  53. 4
      Adventures in Lestoria/loc.sh
  54. 2
      Adventures in Lestoria/miniaudio.h
  55. 6
      Adventures in Lestoria/olcPGEX_MiniAudio.h
  56. 5234
      Adventures in Lestoria/stb_vorbis.cpp
  57. 520
      Adventures in Lestoria/stb_vorbis.h

@ -39,7 +39,7 @@ All rights reserved.
#include "olcPixelGameEngine.h"
#include "Key.h"
struct Player;
class Player;
struct PrecastData{
float castTime;

@ -8,6 +8,31 @@
"."
],
"propertyTypes": [
{
"color": "#ff3af8eb",
"drawFill": true,
"id": 28,
"members": [
{
"name": "Sound Name",
"propertyType": "EnvironmentalSounds",
"type": "string",
"value": "None"
}
],
"name": "AudioEnvironmentalSound",
"type": "class",
"useAs": [
"property",
"map",
"layer",
"object",
"tile",
"tileset",
"wangcolor",
"wangset"
]
},
{
"id": 27,
"name": "BGM",
@ -89,6 +114,17 @@
"object"
]
},
{
"id": 29,
"name": "EnvironmentalSounds",
"storageType": "string",
"type": "enum",
"values": [
"None",
"Waterfall"
],
"valuesAsFlags": false
},
{
"color": "#ffd9d929",
"drawFill": true,

@ -134,6 +134,7 @@
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\sigon\Documents\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/MP8 %(AdditionalOptions)</AdditionalOptions>
<TreatSpecificWarningsAsErrors>4099;5030;4715;4172;4834</TreatSpecificWarningsAsErrors>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -200,6 +201,7 @@
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\sigon\Documents\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/MP8 %(AdditionalOptions)</AdditionalOptions>
<TreatSpecificWarningsAsErrors>4099;5030;4715;4172;4834</TreatSpecificWarningsAsErrors>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -355,6 +357,10 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="EnvironmentalAudio.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="EquipSlotButton.h">
<SubType>
</SubType>
@ -377,6 +383,10 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="MapName.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="olcPGEX_SplashScreen.h" />
<ClInclude Include="PlayerMoneyLabel.h">
<SubType>
@ -467,6 +477,10 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="stb_vorbis.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="TextEntryLabel.h">
<SubType>
</SubType>
@ -547,6 +561,10 @@
<ClCompile Include="Effect.cpp" />
<ClCompile Include="Emitter.cpp" />
<ClCompile Include="EnergyBolt.cpp" />
<ClCompile Include="EnvironmentalAudio.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="FallingDebris.h" />
<ClCompile Include="FireBolt.cpp" />
<ClCompile Include="GameState.cpp" />
@ -621,6 +639,10 @@
<ClCompile Include="State_MainMenu.cpp" />
<ClCompile Include="State_OverworldMap.cpp" />
<ClCompile Include="State_Story.cpp" />
<ClCompile Include="stb_vorbis.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="Test.cpp" />
<ClCompile Include="Thief.cpp" />
<ClCompile Include="TitleScreen.cpp" />
@ -640,8 +662,13 @@
<ItemGroup>
<None Include="ClassDiagram2.cd" />
<None Include="cpp.hint" />
<None Include="CREDITS" />
</ItemGroup>
<ItemGroup>
<Text Include="assets\config\audio\audio.txt" />
<Text Include="assets\config\audio\bgm.txt" />
<Text Include="assets\config\audio\environmentalaudio.txt" />
<Text Include="assets\config\audio\events.txt" />
<Text Include="assets\config\bgm\bgm.txt" />
<Text Include="assets\config\bgm\events.txt" />
<Text Include="assets\config\classes\Ranger.txt" />

@ -85,6 +85,9 @@
<Filter Include="Documentation\Mechanics">
<UniqueIdentifier>{a54ab812-9474-465f-9f0d-4dceecb9e963}</UniqueIdentifier>
</Filter>
<Filter Include="Configurations\Audio">
<UniqueIdentifier>{c28287fa-b401-4715-b2f2-8ac1021d5ec9}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="olcPixelGameEngine.h">
@ -411,6 +414,15 @@
<ClInclude Include="Audio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EnvironmentalAudio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MapName.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stb_vorbis.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Player.cpp">
@ -692,10 +704,19 @@
<ClCompile Include="Audio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EnvironmentalAudio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stb_vorbis.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
<None Include="ClassDiagram2.cd" />
<None Include="CREDITS">
<Filter>Documentation</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Text Include="InitialConcept.txt">
@ -807,6 +828,18 @@
<Text Include="Adventures in Lestoria_System_Overview.txt" />
<Text Include="assets\config\bgm\bgm.txt" />
<Text Include="assets\config\bgm\events.txt" />
<Text Include="assets\config\audio\audio.txt">
<Filter>Configurations\Audio</Filter>
</Text>
<Text Include="assets\config\audio\bgm.txt">
<Filter>Configurations\Audio</Filter>
</Text>
<Text Include="assets\config\audio\environmentalaudio.txt">
<Filter>Configurations\Audio</Filter>
</Text>
<Text Include="assets\config\audio\events.txt">
<Filter>Configurations\Audio</Filter>
</Text>
</ItemGroup>
<ItemGroup>
<Image Include="assets\heart.ico">

@ -157,6 +157,12 @@ AiL::AiL()
std::string BGM_EVENTS_CONFIG = CONFIG_PATH + "event_config"_S;
utils::datafile::Read(DATA,BGM_EVENTS_CONFIG);
std::string AUDIO_CONFIG = CONFIG_PATH + "audio_config"_S;
utils::datafile::Read(DATA,AUDIO_CONFIG);
std::string ENVIRONMENTAL_AUDIO_CONFIG = CONFIG_PATH + "environmental_audio_config"_S;
utils::datafile::Read(DATA,ENVIRONMENTAL_AUDIO_CONFIG);
utils::datafile::DEBUG_ACCESS_OPTIONS="debug_access_options"_I;
sAppName = "GAME_NAME"_S;
@ -177,7 +183,7 @@ bool AiL::OnUserCreate(){
camera=Camera2D{WINDOW_SIZE};
camera.SetMode(olc::utils::Camera2D::Mode::LazyFollow);
camera.SetTarget(player->GetPos());
camera.SetWorldBoundary({0,0},GetCurrentMap().MapSize*GetCurrentMap().TileSize);
camera.SetWorldBoundary({0,0},GetCurrentMap().MapData.MapSize*GetCurrentMap().MapData.TileSize);
camera.EnableWorldBoundary(false);
ItemAttribute::Initialize();
@ -230,6 +236,7 @@ bool AiL::OnUserCreate(){
Inventory::AddItem("Ring of the Slime King",3);
Audio::Initialize();
EnvironmentalAudio::Initialize();
LoadLevel(LEVEL_NAMES["starting_map"_S]);
ChangePlayerClass(WARRIOR);
@ -340,8 +347,8 @@ void AiL::HandleUserInput(float fElapsedTime){
if(player->GetVelocity()==vf2d{0,0}&&player->CanMove()){
auto GetPlayerStaircaseDirection=[&](){
for(LayerTag&layer:MAP_DATA[GetCurrentLevel()].LayerData){
int truncatedPlayerX=int(player->GetX())/game->GetCurrentMap().tilewidth;
int truncatedPlayerY=int(player->GetY())/game->GetCurrentMap().tilewidth;
int truncatedPlayerX=int(player->GetX())/game->GetCurrentMapData().tilewidth;
int truncatedPlayerY=int(player->GetY())/game->GetCurrentMapData().tilewidth;
int tileID=layer.tiles[truncatedPlayerY][truncatedPlayerX];
TilesheetData dat=GetTileSheet(GetCurrentLevel(),tileID);
if (dat.tileset->staircaseTiles.find(tileID)!=dat.tileset->staircaseTiles.end()){
@ -727,9 +734,9 @@ void AiL::RenderTile(vi2d pos,TilesheetData tileSheet,int tileSheetIndex,vi2d ti
int tileSheetWidth=tileSheet.tileset->tileset->Sprite()->width/tileSheet.tileset->tilewidth;
int tileSheetX=animatedIndex%tileSheetWidth;
int tileSheetY=animatedIndex/tileSheetWidth;
view.DrawPartialDecal(pos*game->GetCurrentMap().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},tileSheet.tileset->tileset->Decal(),vi2d{tileSheetX,tileSheetY}*tileSheet.tileset->tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)});
view.DrawPartialDecal(pos*game->GetCurrentMapData().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},tileSheet.tileset->tileset->Decal(),vi2d{tileSheetX,tileSheetY}*tileSheet.tileset->tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)});
}else{
view.DrawPartialDecal(pos*game->GetCurrentMap().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},tileSheet.tileset->tileset->Decal(),tileSheetPos*tileSheet.tileset->tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)});
view.DrawPartialDecal(pos*game->GetCurrentMapData().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},tileSheet.tileset->tileset->Decal(),tileSheetPos*tileSheet.tileset->tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)});
}
}
@ -805,7 +812,7 @@ void AiL::RenderWorld(float fElapsedTime){
SetDecalMode(DecalMode::ADDITIVE);
float reflectionHeight=(float(player->GetFrame().GetSourceRect().size.y)-8)*player->GetSizeMult();
float reflectionBottom=player->GetPos().y+reflectionHeight;
float cutOff=reflectionBottom-GetCurrentMap().height*GetCurrentMap().tileheight;
float cutOff=reflectionBottom-GetCurrentMapData().height*GetCurrentMapData().tileheight;
float multiplierX=0.9f;
multiplierX*=(1-abs(sin(reflectionStepTime))*"water_reflection_scale_factor"_F);
multiplierX*=(1-abs(cos(1.5f*reflectionStepTime))*"water_reflection_scale_factor"_F);
@ -816,13 +823,13 @@ void AiL::RenderWorld(float fElapsedTime){
}
SetDecalMode(DecalMode::NORMAL);
}
if(GetCurrentMap().optimized){
view.FillRectDecal(-WINDOW_SIZE,vf2d{float(GetCurrentMap().width),float(GetCurrentMap().height)}*float(GetCurrentMap().tilewidth)+vf2d{WINDOW_SIZE}*2,{100,180,100});
if(GetCurrentMapData().optimized){
view.FillRectDecal(-WINDOW_SIZE,vf2d{float(GetCurrentMapData().width),float(GetCurrentMapData().height)}*float(GetCurrentMapData().tilewidth)+vf2d{WINDOW_SIZE}*2,{100,180,100});
view.DrawDecal({0,0},MAP_DATA[GetCurrentLevel()].optimizedTile->Decal());
}else{
for (int x = view.GetTopLeftTile().x/GetCurrentMap().tilewidth-1; x <= view.GetBottomRightTile().x/GetCurrentMap().tilewidth; x++){
for (int y = view.GetTopLeftTile().y/GetCurrentMap().tilewidth-1; y <= view.GetBottomRightTile().y/GetCurrentMap().tilewidth; y++){
if(x>=0&&x<GetCurrentMap().width&&y>=0&&y<GetCurrentMap().height){
for (int x = view.GetTopLeftTile().x/GetCurrentMapData().tilewidth-1; x <= view.GetBottomRightTile().x/GetCurrentMapData().tilewidth; x++){
for (int y = view.GetTopLeftTile().y/GetCurrentMapData().tilewidth-1; y <= view.GetBottomRightTile().y/GetCurrentMapData().tilewidth; y++){
if(x>=0&&x<GetCurrentMapData().width&&y>=0&&y<GetCurrentMapData().height){
switch(mode){
case RenderMode::NORMAL_TILES:{
for(LayerTag&layer:MAP_DATA[currentLevel].LayerData){
@ -831,8 +838,8 @@ void AiL::RenderWorld(float fElapsedTime){
if(!bridgeLayerFade&&!player->upperLevel){
int tileID=layer.tiles[y][x]-1;
if(tileID!=-1){
int playerXTruncated=int(player->GetPos().x)/GetCurrentMap().tilewidth;
int playerYTruncated=int(player->GetPos().y)/GetCurrentMap().tilewidth;
int playerXTruncated=int(player->GetPos().x)/GetCurrentMapData().tilewidth;
int playerYTruncated=int(player->GetPos().y)/GetCurrentMapData().tilewidth;
if(playerXTruncated==x&&playerYTruncated==y){
bridgeLayerFade=true;
}
@ -855,8 +862,8 @@ void AiL::RenderWorld(float fElapsedTime){
if("debug_collision_boxes"_I){
if(tileSheet.tileset->collision.find(tileSheetIndex)!=tileSheet.tileset->collision.end()){
geom2d::rect<int>collision=tileSheet.tileset->collision[tileSheetIndex].collision;
view.FillRectDecal(vi2d{x,y}*GetCurrentMap().tilewidth+collision.pos,collision.size,{0,0,0,128});
view.DrawRectDecal(vi2d{x,y}*GetCurrentMap().tilewidth+collision.pos,collision.size,GREY);
view.FillRectDecal(vi2d{x,y}*GetCurrentMapData().tilewidth+collision.pos,collision.size,{0,0,0,128});
view.DrawRectDecal(vi2d{x,y}*GetCurrentMapData().tilewidth+collision.pos,collision.size,GREY);
}
}
#endif
@ -883,8 +890,8 @@ void AiL::RenderWorld(float fElapsedTime){
if("debug_collision_boxes"_I){
if(tileSheet.tileset->collision.find(tileSheetIndex)!=tileSheet.tileset->collision.end()){
geom2d::rect<int>collision=tileSheet.tileset->collision[tileSheetIndex].collision;
view.FillRectDecal(vi2d{x,y}*GetCurrentMap().tilewidth+collision.pos,collision.size,{0,0,0,128});
view.DrawRectDecal(vi2d{x,y}*GetCurrentMap().tilewidth+collision.pos,collision.size,GREY);
view.FillRectDecal(vi2d{x,y}*GetCurrentMapData().tilewidth+collision.pos,collision.size,{0,0,0,128});
view.DrawRectDecal(vi2d{x,y}*GetCurrentMapData().tilewidth+collision.pos,collision.size,GREY);
}
}
#endif
@ -893,12 +900,12 @@ void AiL::RenderWorld(float fElapsedTime){
}break;
case RenderMode::EMPTY_TILES:{
if(visibleTiles.count({x,y})){
view.FillRectDecal(vi2d{x,y}*GetCurrentMap().tilewidth,{float(GetCurrentMap().tilewidth),float(GetCurrentMap().tilewidth)},{100,180,100});
view.FillRectDecal(vi2d{x,y}*GetCurrentMapData().tilewidth,{float(GetCurrentMapData().tilewidth),float(GetCurrentMapData().tilewidth)},{100,180,100});
}
}break;
}
}else{
view.FillRectDecal(vi2d{x,y}*GetCurrentMap().tilewidth,{float(GetCurrentMap().tilewidth),float(GetCurrentMap().tilewidth)},{100,180,100});
view.FillRectDecal(vi2d{x,y}*GetCurrentMapData().tilewidth,{float(GetCurrentMapData().tilewidth),float(GetCurrentMapData().tilewidth)},{100,180,100});
}
}
}
@ -1053,9 +1060,9 @@ void AiL::RenderWorld(float fElapsedTime){
#pragma region Bridge Layer Rendering
if(bridgeLayer!=nullptr){
for (int x = view.GetTopLeftTile().x/game->GetCurrentMap().tilewidth-1; x <= view.GetBottomRightTile().x/game->GetCurrentMap().tilewidth; x++){
for (int y = view.GetTopLeftTile().y/game->GetCurrentMap().tilewidth-1; y <= view.GetBottomRightTile().y/game->GetCurrentMap().tilewidth; y++){
if(x>=0&&x<GetCurrentMap().width&&y>=0&&y<GetCurrentMap().height){
for (int x = view.GetTopLeftTile().x/game->GetCurrentMapData().tilewidth-1; x <= view.GetBottomRightTile().x/game->GetCurrentMapData().tilewidth; x++){
for (int y = view.GetTopLeftTile().y/game->GetCurrentMapData().tilewidth-1; y <= view.GetBottomRightTile().y/game->GetCurrentMapData().tilewidth; y++){
if(x>=0&&x<GetCurrentMapData().width&&y>=0&&y<GetCurrentMapData().height){
int tileID=bridgeLayer->tiles[y][x]-1;
if(tileID!=-1){
TilesheetData tileSheet=GetTileSheet(currentLevel,tileID);
@ -1064,13 +1071,13 @@ void AiL::RenderWorld(float fElapsedTime){
int tileSheetIndex=tileID-(tileSheet.firstgid-1);
int tileSheetX=tileSheetIndex%tileSheetWidth;
int tileSheetY=tileSheetIndex/tileSheetWidth;
view.DrawPartialDecal(vi2d{x,y}*game->GetCurrentMap().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},tileSheet.tileset->tileset->Decal(),vi2d{tileSheetX,tileSheetY}*game->GetCurrentMap().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},{255,255,255,uint8_t(255-bridgeFadeFactor/TileGroup::FADE_TIME*TileGroup::FADE_AMT)});
view.DrawPartialDecal(vi2d{x,y}*game->GetCurrentMapData().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},tileSheet.tileset->tileset->Decal(),vi2d{tileSheetX,tileSheetY}*game->GetCurrentMapData().tilewidth,{float(tileSheet.tileset->tilewidth),float(tileSheet.tileset->tileheight)},{255,255,255,uint8_t(255-bridgeFadeFactor/TileGroup::FADE_TIME*TileGroup::FADE_AMT)});
#ifdef _DEBUG
if("debug_collision_boxes"_I){
if(tileSheet.tileset->collision.find(tileSheetIndex)!=tileSheet.tileset->collision.end()){
geom2d::rect<int>collision=tileSheet.tileset->collision[tileSheetIndex].collision;
view.FillRectDecal(vi2d{x,y}*game->GetCurrentMap().tilewidth+collision.pos,collision.size,{0,0,0,128});
view.DrawRectDecal(vi2d{x,y}*game->GetCurrentMap().tilewidth+collision.pos,collision.size,GREY);
view.FillRectDecal(vi2d{x,y}*game->GetCurrentMapData().tilewidth+collision.pos,collision.size,{0,0,0,128});
view.DrawRectDecal(vi2d{x,y}*game->GetCurrentMapData().tilewidth+collision.pos,collision.size,GREY);
}
}
#endif
@ -1251,7 +1258,7 @@ void AiL::RenderWorld(float fElapsedTime){
if(DEBUG_PATHFINDING){
std::vector<vf2d>pathing=game->pathfinder.Solve_AStar(player.get()->GetPos(),GetWorldMousePos(),8,player.get()->OnUpperLevel());
for(vf2d&square:pathing){
view.FillRectDecal(square*float(game->GetCurrentMap().tilewidth),{float(game->GetCurrentMap().tilewidth),float(game->GetCurrentMap().tilewidth)},DARK_GREEN);
view.FillRectDecal(square*float(game->GetCurrentMapData().tilewidth),{float(game->GetCurrentMapData().tilewidth),float(game->GetCurrentMapData().tilewidth)},DARK_GREEN);
}
}
#endif
@ -1580,6 +1587,12 @@ void AiL::LoadLevel(MapName map){
GetPlayer()->GetCastInfo()={};
GetPlayer()->ResetAccumulatedXP();
#pragma region Reset Environmental Audio
for(EnvironmentalAudio&audio:MAP_DATA[map].environmentalAudioData){
audio.Deactivate();
}
#pragma endregion
#pragma region Monster Spawn Data Setup
for(auto&[key,value]:MAP_DATA[map].SpawnerData){
SpawnerTag&spawnData=MAP_DATA[map].SpawnerData[key];
@ -1604,10 +1617,10 @@ void AiL::LoadLevel(MapName map){
return std::vector<ZoneData>{};
};
for(ZoneData&zone:GetUpperZones()){
int zoneX=zone.zone.pos.x/game->GetCurrentMap().tilewidth; //snap to grid
int zoneY=zone.zone.pos.y/game->GetCurrentMap().tilewidth;
int zoneW=zone.zone.right().start.x/game->GetCurrentMap().tilewidth-zoneX;
int zoneH=zone.zone.bottom().start.y/game->GetCurrentMap().tilewidth-zoneY;
int zoneX=zone.zone.pos.x/game->GetCurrentMapData().tilewidth; //snap to grid
int zoneY=zone.zone.pos.y/game->GetCurrentMapData().tilewidth;
int zoneW=zone.zone.right().start.x/game->GetCurrentMapData().tilewidth-zoneX;
int zoneH=zone.zone.bottom().start.y/game->GetCurrentMapData().tilewidth-zoneY;
for(int x=zoneX;x<zoneX+zoneW;x++){
for(int y=zoneY;y<zoneY+zoneH;y++){
for(LayerTag&layer:MAP_DATA[map].LayerData){
@ -1625,8 +1638,8 @@ void AiL::LoadLevel(MapName map){
#pragma region Foreground and Upper Foreground Tile Fade Group Setup
std::set<vi2d>foregroundTilesAdded,upperForegroundTilesAdded;
for(int x=0;x<GetCurrentMap().width;x++){
for(int y=0;y<GetCurrentMap().height;y++){
for(int x=0;x<GetCurrentMapData().width;x++){
for(int y=0;y<GetCurrentMapData().height;y++){
int layerID=0;
for(LayerTag&layer:MAP_DATA[currentLevel].LayerData){
int tileID=layer.tiles[y][x]-1;
@ -1648,9 +1661,9 @@ void AiL::LoadLevel(MapName map){
foregroundTilesIncluded.insert({x,y});
group.InsertTile(tile);
if(x>0&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{-1,0})==foregroundTilesIncluded.end())tileGroupChecks.push({x-1,y});
if(x<GetCurrentMap().width-1&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{1,0})==foregroundTilesIncluded.end())tileGroupChecks.push({x+1,y});
if(x<GetCurrentMapData().width-1&&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(y<GetCurrentMap().height-1&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{0,1})==foregroundTilesIncluded.end())tileGroupChecks.push({x,y+1});
if(y<GetCurrentMapData().height-1&&foregroundTilesIncluded.find(vi2d{x,y}+vi2d{0,1})==foregroundTilesIncluded.end())tileGroupChecks.push({x,y+1});
auto IterateThroughOtherLayers=[&](vi2d pos,bool loopAll=false){
int layer2ID=0;
bool hadForeground=false;
@ -1664,7 +1677,7 @@ void AiL::LoadLevel(MapName map){
int realTileSheetIndex=(tileID%1000000)-(tileSheet.firstgid-1);
int tileSheetX=realTileSheetIndex%tileSheetWidth;
int tileSheetY=realTileSheetIndex/tileSheetWidth;
TileRenderData tile={tileSheet,vi2d{pos.x,pos.y}*game->GetCurrentMap().tilewidth,vi2d{tileSheetX,tileSheetY}*game->GetCurrentMap().tilewidth,realTileSheetIndex,layer2ID};
TileRenderData tile={tileSheet,vi2d{pos.x,pos.y}*game->GetCurrentMapData().tilewidth,vi2d{tileSheetX,tileSheetY}*game->GetCurrentMapData().tilewidth,realTileSheetIndex,layer2ID};
if(IsForeground(tileSheet,tileSheetIndex)){
foregroundTilesIncluded.insert({pos.x,pos.y});
group.InsertTile(tile);
@ -1682,18 +1695,18 @@ void AiL::LoadLevel(MapName map){
vi2d targetPos=pos+vi2d{-1,0};
if(pos.x>0&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);}
targetPos=pos+vi2d{1,0};
if(pos.x<GetCurrentMap().width-1&&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{0,-1};
if(pos.y>0&&foregroundTilesIncluded.find(targetPos)==foregroundTilesIncluded.end()){tileGroupChecks.push(targetPos);foregroundTilesIncluded.insert(targetPos);}
targetPos=pos+vi2d{0,1};
if(pos.y<GetCurrentMap().height-1&&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);}
}
tileGroupChecks.pop();
}
groups.push_back(group);
}
};
TileRenderData tile={tileSheet,vi2d{x,y}*game->GetCurrentMap().tilewidth,vi2d{tileSheetX,tileSheetY}*game->GetCurrentMap().tilewidth,realTileSheetIndex,layerID};
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);
SetupTileGroups([&](TilesheetData sheet,int tileID){return IsUpperForegroundTile(tileID);},tile,upperForegroundTilesAdded,upperForegroundTileGroups);
#pragma endregion
@ -1833,7 +1846,7 @@ TilesheetData AiL::GetTileSheet(MapName map,int tileID){
bool AiL::HasTileCollision(MapName map,vf2d pos,bool upperLevel){
geom2d::rect<int>collisionRect=GetTileCollision(map,pos,upperLevel);
vi2d collisionRectSnapPos=vi2d{pos/float(game->GetCurrentMap().tilewidth)}*game->GetCurrentMap().tilewidth;
vi2d collisionRectSnapPos=vi2d{pos/float(game->GetCurrentMapData().tilewidth)}*game->GetCurrentMapData().tilewidth;
collisionRect.pos+=collisionRectSnapPos;
return geom2d::overlaps(collisionRect,pos);
}
@ -1843,19 +1856,19 @@ bool AiL::IsBridgeLayer(LayerTag&layer){
}
geom2d::rect<int>AiL::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
if(pos.x<0||pos.y<0||pos.x>=GetCurrentMap().width*game->GetCurrentMap().tilewidth||pos.y>=GetCurrentMap().height*game->GetCurrentMap().tilewidth)return NO_COLLISION;
if(pos.x<0||pos.y<0||pos.x>=GetCurrentMapData().width*game->GetCurrentMapData().tilewidth||pos.y>=GetCurrentMapData().height*game->GetCurrentMapData().tilewidth)return NO_COLLISION;
#pragma region Lower Bridge Collision Check
if(!upperLevel){ //We are looking for lower bridge collisions.
for(ZoneData&zone:MAP_DATA[map].ZoneData["LowerBridgeCollision"]){
if(geom2d::contains(zone.zone,pos)){
return {{0,0},{game->GetCurrentMap().tilewidth,game->GetCurrentMap().tilewidth}};
return {{0,0},{game->GetCurrentMapData().tilewidth,game->GetCurrentMapData().tilewidth}};
}
}
}
#pragma endregion
//The logic here is, if there's a tile on the bridge, we respect that tile instead if we're on the upper level. So we don't check other layers when we are on the upper level and there is a tile below us.
if(upperLevel&&bridgeLayerIndex!=-1){
int tileID=MAP_DATA[map].LayerData[bridgeLayerIndex].tiles[int(pos.y)/game->GetCurrentMap().tilewidth][int(pos.x)/game->GetCurrentMap().tilewidth]-1;
int tileID=MAP_DATA[map].LayerData[bridgeLayerIndex].tiles[int(pos.y)/game->GetCurrentMapData().tilewidth][int(pos.x)/game->GetCurrentMapData().tilewidth]-1;
if(tileID!=-1){
if (GetTileSheet(map,tileID%1000000).tileset->collision.find(tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1)!=GetTileSheet(map,tileID%1000000).tileset->collision.end()){
return GetTileSheet(map,tileID%1000000).tileset->collision[tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1].collision;
@ -1867,7 +1880,7 @@ geom2d::rect<int>AiL::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
for(int counter=0;LayerTag&layer:MAP_DATA[map].LayerData){
//auto HasNoClass=[&](){return layer.tag.data.find("class")==layer.tag.data.end();};
if(counter!=bridgeLayerIndex){
int tileID=layer.tiles[int(pos.y)/game->GetCurrentMap().tilewidth][int(pos.x)/game->GetCurrentMap().tilewidth]-1;
int tileID=layer.tiles[int(pos.y)/game->GetCurrentMapData().tilewidth][int(pos.x)/game->GetCurrentMapData().tilewidth]-1;
if(tileID!=-1&&GetTileSheet(map,tileID%1000000).tileset->collision.find(tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1)!=GetTileSheet(map,tileID%1000000).tileset->collision.end()){
geom2d::rect<int>collisionRect=GetTileSheet(map,tileID%1000000).tileset->collision[tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1].collision;
if(foundRect.pos==NO_COLLISION.pos&&foundRect.size==NO_COLLISION.size){
@ -1886,7 +1899,7 @@ geom2d::rect<int>AiL::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
return foundRect;
}
MapName AiL::GetCurrentLevel(){
const MapName&AiL::GetCurrentLevel()const{
return currentLevel;
}
@ -2346,8 +2359,14 @@ void AiL::InitializeGraphics(){
std::cout<<GFX.size()<<" images have been loaded."<<std::endl;
}
MapTag AiL::GetCurrentMap(){
return MAP_DATA[GetCurrentLevel()].MapData;
const Map&AiL::GetCurrentMap()const{
return MAP_DATA.at(GetCurrentLevel());
}
const MapTag&AiL::GetCurrentMapData()const{
return GetCurrentMap().MapData;
}
const MapName&AiL::GetCurrentMapName()const{
return GetCurrentLevel();
}
void AiL::ValidateGameStatus(){
@ -2366,6 +2385,11 @@ void AiL::ValidateGameStatus(){
if(ItemDrop::gravity!="ItemDrop.Item Drop Gravity"_F){
ERR("WARNING! Gravity constant for item drops was not initialized to "<<"Item Drop Gravity"_F<<". Actual value: "<<ItemDrop::gravity);
}
for(auto&[name,map]:MAP_DATA){
for(EnvironmentalAudio&audio:map.environmentalAudioData){
if(EnvironmentalAudio::SOUND_DATA.find(audio.audioName)==EnvironmentalAudio::SOUND_DATA.end())ERR(std::format("WARNING! Could not find environmental audio data {} for Map {}. Check audio/environmentalaudio.txt configuration!",audio.audioName,map.name));
}
}
}
void AiL::RenderVersionInfo(){

@ -167,7 +167,7 @@ public:
geom2d::rect<int>GetTileCollision(MapName map,vf2d pos,bool upperLevel=false);
//Checks if the point resides inside of a collision tile.
bool HasTileCollision(MapName map,vf2d pos,bool upperLevel=false);
MapName GetCurrentLevel();
const MapName&GetCurrentLevel()const;
bool IsBridgeLayer(LayerTag&layer);
std::map<std::string,std::vector<ZoneData>>&GetZoneData(MapName map);
void PopulateRenderLists();
@ -198,7 +198,9 @@ public:
void ReduceBossEncounterMobCount();
void InitializeGraphics();
void RenderVersionInfo();
MapTag GetCurrentMap();
const Map&GetCurrentMap()const;
const MapTag&GetCurrentMapData()const;
const MapName&GetCurrentMapName()const;
int GetCurrentChapter();
void SetChapter(int chapter);
const std::weak_ptr<Item>GetLoadoutItem(int slot);

@ -108,6 +108,11 @@ MiniAudio&Audio::Engine(){
void Audio::Play(const std::string_view sound){
Engine().Play(std::string(sound));
};
const size_t Audio::LoadAndPlay(const std::string_view sound,const bool loop){
size_t soundID=Engine().LoadSound(std::string(sound));
Engine().Play(soundID,loop);
return soundID;
};
void Audio::PlayBGM(const std::string_view sound,const bool loop){
BGM&track=Self().bgm[std::string(sound)];
Self().fullyLoaded=false;

@ -57,6 +57,8 @@ public:
static void Initialize();
static void Update();
static void Play(const std::string_view sound);
[[nodiscard]]
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.
static void PlayBGM(const std::string_view sound,const bool loop=true);
static void StopBGM();

@ -26,5 +26,6 @@ Fonts obtained from:
Nb Pixel Font Bundle: https://nimblebeastscollective.itch.io/nb-pixel-font-bundle
Nb Pixel Font Bundle 2: https://nimblebeastscollective.itch.io/nb-pixel-font-bundle-2
Ogg Vorbis audio decoder - v1.22 - public domain http://nothings.org/stb_vorbis/
miniaudio library Copyright 2023 by David Reid under the MIT No Attribution License
olcPGEX_MiniAudio Copyright 2023 by Moros Smith under the OLC-3 License

@ -0,0 +1,99 @@
#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 © 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "EnvironmentalAudio.h"
#include "Audio.h"
#include "AdventuresInLestoria.h"
INCLUDE_game
INCLUDE_DATA
float EnvironmentalAudio::ACTIVATION_RANGE;
std::map<EnvironmentalAudioSoundName,EnvironmentalAudioData>EnvironmentalAudio::SOUND_DATA;
void EnvironmentalAudio::Initialize(){
ACTIVATION_RANGE="Audio.Environmental Audio Activation Range"_F;
for(auto&[key,size]:DATA["Environmental Audio"]){
SOUND_DATA[key].file=DATA["Environmental Audio"][key]["File"].GetString();
if(!std::filesystem::exists(operator""_SFX(SOUND_DATA[key].file.c_str(),SOUND_DATA[key].file.length())))ERR(std::format("WARNING! Could not load Environmental Audio {} due to missing file {} in {}",key,SOUND_DATA[key].file,"sfx_directory"_S));
SOUND_DATA[key].volume=DATA["Environmental Audio"][key]["Volume"].GetReal()/100.f;
}
}
EnvironmentalAudio::EnvironmentalAudio()
:audioName(""),pos({}){}
EnvironmentalAudio::EnvironmentalAudio(const std::string_view audioName,const vf2d pos)
:audioName(audioName),pos(pos){}
void EnvironmentalAudio::SetPos(const vf2d&pos){
this->pos=pos;
}
void EnvironmentalAudio::SetAudioName(const std::string_view audioName){
this->audioName=audioName;
}
void EnvironmentalAudio::Activate(){
if(activated)return;
soundInstance=Audio::LoadAndPlay(operator""_SFX(SOUND_DATA[audioName].file.c_str(),SOUND_DATA[audioName].file.length()),true);
activated=true;
}
void EnvironmentalAudio::Deactivate(){
if(!activated)return;
if(soundInstance!=std::numeric_limits<size_t>::max()){
Audio::Engine().UnloadSound(soundInstance);
}
soundInstance=std::numeric_limits<size_t>::max();
activated=false;
}
void EnvironmentalAudio::UpdateEnvironmentalAudio(){
for(const EnvironmentalAudio&aud:game->GetCurrentMap().environmentalAudioData){
EnvironmentalAudio&audio=const_cast<EnvironmentalAudio&>(aud);
audio.Update();
}
}
void EnvironmentalAudio::Update(){
float distanceFromPlayer=geom2d::line<float>(game->GetPlayer()->GetPos(),pos).length();
if(distanceFromPlayer<ACTIVATION_RANGE)Activate();
if(distanceFromPlayer>ACTIVATION_RANGE){
Deactivate();
return; //Do not need to continue processing if the sound is not even going to be heard.
}
float distRatio=1-distanceFromPlayer/ACTIVATION_RANGE; //0-1 where 1 is full volume.
float xDistRatio=(pos.x-game->GetPlayer()->GetX())/ACTIVATION_RANGE; //0-1 where 1 is full volume.
Audio::Engine().SetVolume(soundInstance,distRatio);
Audio::Engine().SetPan(soundInstance,xDistRatio);
}

@ -0,0 +1,67 @@
#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 © 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
#include "olcPixelGameEngine.h"
using EnvironmentalAudioSoundName=std::string;
using FileName=std::string;
struct EnvironmentalAudioData{
FileName file;
float volume; //0.0f-1.0f
};
class EnvironmentalAudio{
friend class AiL;
vf2d pos;
std::string audioName;
static float ACTIVATION_RANGE;
bool activated=false;
size_t soundInstance=std::numeric_limits<size_t>::max();
static std::map<EnvironmentalAudioSoundName,EnvironmentalAudioData>SOUND_DATA;
public:
static void Initialize();
EnvironmentalAudio();
EnvironmentalAudio(const std::string_view audioName,const vf2d pos);
void SetPos(const vf2d&pos);
void SetAudioName(const std::string_view audioName);
void Activate();
void Deactivate();
static void UpdateEnvironmentalAudio();
void Update();
};

@ -49,6 +49,7 @@ INCLUDE_game
INCLUDE_DATA
INCLUDE_GFX
const std::string Item::BLANK_ITEM_NAME="";
safemap<std::string,ItemInfo>ITEM_DATA;
safemap<std::string,ItemScript>ITEM_SCRIPTS;
safemap<std::string,std::set<std::string>>ITEM_CATEGORIES;
@ -563,11 +564,11 @@ uint32_t Item::Amt()const{
return amt;
};
const std::string&Item::ActualName()const{
if(_IsBlank())return "";
if(_IsBlank())return BLANK_ITEM_NAME;
return it->Name();
};
const std::string Item::DisplayName()const{
if(_IsBlank())return "";
if(_IsBlank())return BLANK_ITEM_NAME;
std::string name=ActualName();
if(IsEquippable()&&EnhancementLevel()>0){
name+=" [+"+std::to_string(EnhancementLevel())+"]";

@ -109,7 +109,7 @@ public:
friend const bool operator==(const Stats&lhs,const Stats&rhs){return lhs.attributes==rhs.attributes;}
};
class Stats;
struct Stats;
class CraftingRequirement;
struct EnhancementLevelInfo{
@ -171,6 +171,7 @@ private:
static int IsBlankStaticCallCounter;
const bool _IsBlank()const;
public:
static const std::string BLANK_ITEM_NAME;
Item();
Item(uint32_t amt,IT item,uint8_t enhancementLevel=0);
uint32_t Amt()const;

@ -51,23 +51,23 @@ Map&MapHelper::MapFromString(std::string mapName){
void TileGroup::InsertTile(TileRenderData tile){
if(tiles.size()==0){
range={tile.pos,{game->GetCurrentMap().tilewidth,game->GetCurrentMap().tilewidth}};
range={tile.pos,{game->GetCurrentMapData().tilewidth,game->GetCurrentMapData().tilewidth}};
minX=tile.pos.x;
maxX=minX+game->GetCurrentMap().tilewidth;
maxX=minX+game->GetCurrentMapData().tilewidth;
minY=tile.pos.y;
maxY=minY+game->GetCurrentMap().tilewidth;
maxY=minY+game->GetCurrentMapData().tilewidth;
} else {
if(tile.pos.x<minX){
minX=tile.pos.x;
}
if(tile.pos.x+game->GetCurrentMap().tilewidth>maxX){
maxX=tile.pos.x+game->GetCurrentMap().tilewidth;
if(tile.pos.x+game->GetCurrentMapData().tilewidth>maxX){
maxX=tile.pos.x+game->GetCurrentMapData().tilewidth;
}
if(tile.pos.y<minY){
minY=tile.pos.y;
}
if(tile.pos.y+game->GetCurrentMap().tilewidth>maxY){
maxY=tile.pos.y+game->GetCurrentMap().tilewidth;
if(tile.pos.y+game->GetCurrentMapData().tilewidth>maxY){
maxY=tile.pos.y+game->GetCurrentMapData().tilewidth;
}
range={{minX,minY},{maxX-minX,maxY-minY}};
}

@ -39,19 +39,10 @@ All rights reserved.
#include "olcUTIL_Geometry2D.h"
#include <set>
#include "TMXParser.h"
#include "MapName.h"
struct XMLTag;
enum MapName{
CAMPAIGN_1_1,
CAMPAIGN_1_2,
CAMPAIGN_1_3,
CAMPAIGN_1_4,
CAMPAIGN_1_5,
BOSS_1,
WORLD_MAP
};
class MapHelper{
public:
static Map&MapFromString(std::string mapName);

@ -0,0 +1,48 @@
#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 © 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
enum MapName{
CAMPAIGN_1_1,
CAMPAIGN_1_2,
CAMPAIGN_1_3,
CAMPAIGN_1_4,
CAMPAIGN_1_5,
BOSS_1,
WORLD_MAP
};

@ -108,7 +108,6 @@ class Menu:public IAttributable{
static void InitializeUserIDWindow();
friend class AiL;
friend struct Player;
friend class ItemInfo;
friend class EntityStats;

@ -113,17 +113,17 @@ void Monster::PerformIdleAnimation(){
}
bool Monster::SetX(float x){
vf2d newPos={x,pos.y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMap().tilewidth))*game->GetCurrentMap().tilewidth;
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<int>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
if(collisionRect.pos==vi2d{0,0}&&collisionRect.size==vi2d{1,1}){
pos.x=std::clamp(x,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().width*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
} else {
geom2d::rect<float>collision={collisionRect.pos,collisionRect.size};
collision.pos+=tilePos;
if(!geom2d::overlaps(geom2d::circle<float>(newPos,12*GetSizeMult()),collision)){
pos.x=std::clamp(x,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().width*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
}
@ -132,17 +132,17 @@ bool Monster::SetX(float x){
}
bool Monster::SetY(float y){
vf2d newPos={pos.x,y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMap().tilewidth))*game->GetCurrentMap().tilewidth;
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<int>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
if(collisionRect.pos==vi2d{0,0}&&collisionRect.size==vi2d{1,1}){
pos.y=std::clamp(y,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().height*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
} else {
geom2d::rect<float>collision={collisionRect.pos,collisionRect.size};
collision.pos+=tilePos;
if(!geom2d::overlaps(geom2d::circle<float>(newPos,game->GetCurrentMap().tilewidth/2*GetSizeMult()),collision)){
pos.y=std::clamp(y,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().height*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
if(!geom2d::overlaps(geom2d::circle<float>(newPos,game->GetCurrentMapData().tilewidth/2*GetSizeMult()),collision)){
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
}
@ -385,7 +385,7 @@ void Monster::StartPathfinding(float pathingTime){
void Monster::PathAroundBehavior(float fElapsedTime){
if(path.size()>0){
//Move towards the new path.
geom2d::line moveTowardsLine=geom2d::line(pos,path[pathIndex]*float(game->GetCurrentMap().tilewidth));
geom2d::line moveTowardsLine=geom2d::line(pos,path[pathIndex]*float(game->GetCurrentMapData().tilewidth));
if(moveTowardsLine.length()>2){
SetPos(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult());
if(moveTowardsLine.vector().x>0){

@ -49,7 +49,6 @@ All rights reserved.
INCLUDE_ITEM_DATA
struct DamageNumber;
struct Player;
class AiL;
enum class Attribute;
@ -109,7 +108,7 @@ struct MonsterData{
};
struct Monster:IAttributable{
class Monster:IAttributable{
friend struct STRATEGY;
public:
Monster()=delete;

@ -45,68 +45,68 @@ void Pathfinding::Initialize(){
if(nodes!=nullptr){
delete[] nodes;
}
nodes = NEW sNode[game->GetCurrentMap().width * game->GetCurrentMap().height];
for (int x = 0; x < game->GetCurrentMap().width; x++)
for (int y = 0; y < game->GetCurrentMap().height; y++)
nodes = NEW sNode[game->GetCurrentMapData().width * game->GetCurrentMapData().height];
for (int x = 0; x < game->GetCurrentMapData().width; x++)
for (int y = 0; y < game->GetCurrentMapData().height; y++)
{
nodes[y * game->GetCurrentMap().width + x].x = x; // ...because we give each node its own coordinates
nodes[y * game->GetCurrentMap().width + x].y = y;
geom2d::rect<int>tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMap().tilewidth),float(y*game->GetCurrentMap().tilewidth)});
nodes[y * game->GetCurrentMap().width + x].bObstacle = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMap().tilewidth),float(y*game->GetCurrentMap().tilewidth)},true);
nodes[y * game->GetCurrentMap().width + x].bObstacleUpper = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
nodes[y * game->GetCurrentMap().width + x].parent = nullptr;
nodes[y * game->GetCurrentMap().width + x].bVisited = false;
nodes[y * game->GetCurrentMapData().width + x].x = x; // ...because we give each node its own coordinates
nodes[y * game->GetCurrentMapData().width + x].y = y;
geom2d::rect<int>tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMapData().tilewidth),float(y*game->GetCurrentMapData().tilewidth)});
nodes[y * game->GetCurrentMapData().width + x].bObstacle = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMapData().tilewidth),float(y*game->GetCurrentMapData().tilewidth)},true);
nodes[y * game->GetCurrentMapData().width + x].bObstacleUpper = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
nodes[y * game->GetCurrentMapData().width + x].parent = nullptr;
nodes[y * game->GetCurrentMapData().width + x].bVisited = false;
}
for (int x = 0; x < game->GetCurrentMap().width; x++)
for (int y = 0; y < game->GetCurrentMap().height; y++)
for (int x = 0; x < game->GetCurrentMapData().width; x++)
for (int y = 0; y < game->GetCurrentMapData().height; y++)
{
if(y>0)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMap().width + (x + 0)]);
if(y<game->GetCurrentMap().height-1)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMap().width + (x + 0)]);
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMapData().width + (x + 0)]);
if(y<game->GetCurrentMapData().height-1)
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMapData().width + (x + 0)]);
if (x>0)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y + 0) * game->GetCurrentMap().width + (x - 1)]);
if(x<game->GetCurrentMap().width-1)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y + 0) * game->GetCurrentMap().width + (x + 1)]);
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 0) * game->GetCurrentMapData().width + (x - 1)]);
if(x<game->GetCurrentMapData().width-1)
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 0) * game->GetCurrentMapData().width + (x + 1)]);
if (y>0 && x>0)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMap().width + (x - 1)]);
if (y<game->GetCurrentMap().height-1 && x>0)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMap().width + (x - 1)]);
if (y>0 && x<game->GetCurrentMap().width-1)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMap().width + (x + 1)]);
if (y<game->GetCurrentMap().height - 1 && x<game->GetCurrentMap().width-1)
nodes[y*game->GetCurrentMap().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMap().width + (x + 1)]);
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMapData().width + (x - 1)]);
if (y<game->GetCurrentMapData().height-1 && x>0)
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMapData().width + (x - 1)]);
if (y>0 && x<game->GetCurrentMapData().width-1)
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMapData().width + (x + 1)]);
if (y<game->GetCurrentMapData().height - 1 && x<game->GetCurrentMapData().width-1)
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMapData().width + (x + 1)]);
}
// Manually position the start and end markers so they are not nullptr
nodeStart = &nodes[(game->GetCurrentMap().height / 2) * game->GetCurrentMap().width + 1];
nodeEnd = &nodes[(game->GetCurrentMap().height / 2) * game->GetCurrentMap().width + game->GetCurrentMap().width-2];
nodeStart = &nodes[(game->GetCurrentMapData().height / 2) * game->GetCurrentMapData().width + 1];
nodeEnd = &nodes[(game->GetCurrentMapData().height / 2) * game->GetCurrentMapData().width + game->GetCurrentMapData().width-2];
}
std::vector<vf2d> Pathfinding::Solve_AStar(vf2d startPos,vf2d endPos,float maxRange,bool upperLevel){
float dist=float(sqrt(pow(endPos.x-startPos.x,2)+pow(endPos.y-startPos.y,2)));
if(dist>maxRange*game->GetCurrentMap().tilewidth)return {};
if(dist>maxRange*game->GetCurrentMapData().tilewidth)return {};
nodeStart=&nodes[int(startPos.y/game->GetCurrentMap().tilewidth)*game->GetCurrentMap().width+int(startPos.x/game->GetCurrentMap().tilewidth)];
nodeEnd=&nodes[int(endPos.y/game->GetCurrentMap().tilewidth)*game->GetCurrentMap().width+int(endPos.x/game->GetCurrentMap().tilewidth)];
nodeStart=&nodes[int(startPos.y/game->GetCurrentMapData().tilewidth)*game->GetCurrentMapData().width+int(startPos.x/game->GetCurrentMapData().tilewidth)];
nodeEnd=&nodes[int(endPos.y/game->GetCurrentMapData().tilewidth)*game->GetCurrentMapData().width+int(endPos.x/game->GetCurrentMapData().tilewidth)];
geom2d::rect<int>posPerimeter{{int(std::min(startPos.x,endPos.x)),int(std::min(startPos.y,endPos.y))},{int(abs(endPos.x-startPos.x)),int(abs(endPos.y-startPos.y))}};
posPerimeter.pos={int(std::clamp(posPerimeter.pos.x-maxRange*game->GetCurrentMap().tilewidth,0.f,game->GetCurrentMap().width*float(game->GetCurrentMap().tilewidth))),int(std::clamp(posPerimeter.pos.y-maxRange*game->GetCurrentMap().tilewidth,0.f,game->GetCurrentMap().height*float(game->GetCurrentMap().tilewidth)))};
posPerimeter.size={int(std::clamp(posPerimeter.size.x+maxRange*game->GetCurrentMap().tilewidth*2,0.f,game->GetCurrentMap().width*float(game->GetCurrentMap().tilewidth)-posPerimeter.pos.x)),int(std::clamp(posPerimeter.size.y+maxRange*game->GetCurrentMap().tilewidth*2,0.f,game->GetCurrentMap().height*float(game->GetCurrentMap().tilewidth)-posPerimeter.pos.y))};
posPerimeter.pos={int(std::clamp(posPerimeter.pos.x-maxRange*game->GetCurrentMapData().tilewidth,0.f,game->GetCurrentMapData().width*float(game->GetCurrentMapData().tilewidth))),int(std::clamp(posPerimeter.pos.y-maxRange*game->GetCurrentMapData().tilewidth,0.f,game->GetCurrentMapData().height*float(game->GetCurrentMapData().tilewidth)))};
posPerimeter.size={int(std::clamp(posPerimeter.size.x+maxRange*game->GetCurrentMapData().tilewidth*2,0.f,game->GetCurrentMapData().width*float(game->GetCurrentMapData().tilewidth)-posPerimeter.pos.x)),int(std::clamp(posPerimeter.size.y+maxRange*game->GetCurrentMapData().tilewidth*2,0.f,game->GetCurrentMapData().height*float(game->GetCurrentMapData().tilewidth)-posPerimeter.pos.y))};
for (int x = 0; x < game->GetCurrentMap().width; x++){
for (int y = 0; y < game->GetCurrentMap().height; y++){
if(geom2d::overlaps(posPerimeter,vi2d{x*game->GetCurrentMap().tilewidth,y*game->GetCurrentMap().tilewidth})){
nodes[y*game->GetCurrentMap().width + x].bVisited = false;
for (int x = 0; x < game->GetCurrentMapData().width; x++){
for (int y = 0; y < game->GetCurrentMapData().height; y++){
if(geom2d::overlaps(posPerimeter,vi2d{x*game->GetCurrentMapData().tilewidth,y*game->GetCurrentMapData().tilewidth})){
nodes[y*game->GetCurrentMapData().width + x].bVisited = false;
} else {
nodes[y*game->GetCurrentMap().width + x].bVisited = true;
nodes[y*game->GetCurrentMapData().width + x].bVisited = true;
}
nodes[y*game->GetCurrentMap().width + x].fGlobalGoal = INFINITY;
nodes[y*game->GetCurrentMap().width + x].fLocalGoal = INFINITY;
nodes[y*game->GetCurrentMap().width + x].parent = nullptr; // No parents
nodes[y*game->GetCurrentMapData().width + x].fGlobalGoal = INFINITY;
nodes[y*game->GetCurrentMapData().width + x].fLocalGoal = INFINITY;
nodes[y*game->GetCurrentMapData().width + x].parent = nullptr; // No parents
}
}

@ -100,13 +100,13 @@ void Player::Initialize(){
bool Player::SetX(float x){
vf2d newPos={x,pos.y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMap().tilewidth))*game->GetCurrentMap().tilewidth;
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<int>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
#pragma region lambdas
auto NoTileCollisionExistsHere=[&](){return collisionRect.pos==game->NO_COLLISION.pos&&collisionRect.size==game->NO_COLLISION.size;};
#pragma endregion
if(NoTileCollisionExistsHere()){
pos.x=std::clamp(x,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().width*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
} else {
@ -116,7 +116,7 @@ bool Player::SetX(float x){
#pragma endregion
collision.pos+=tilePos;
if(NoPlayerCollisionWithTile()){
pos.x=std::clamp(x,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().width*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
}
@ -126,13 +126,13 @@ bool Player::SetX(float x){
bool Player::SetY(float y){
vf2d newPos={pos.x,y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMap().tilewidth))*game->GetCurrentMap().tilewidth;
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<int>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
#pragma region lambdas
auto NoTileCollisionExistsHere=[&](){return collisionRect.pos==game->NO_COLLISION.pos&&collisionRect.size==game->NO_COLLISION.size;};
#pragma endregion
if(NoTileCollisionExistsHere()){
pos.y=std::clamp(y,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().height*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
} else {
@ -142,7 +142,7 @@ bool Player::SetY(float y){
#pragma endregion
collision.pos+=tilePos;
if(NoPlayerCollisionWithTile()){
pos.y=std::clamp(y,game->GetCurrentMap().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMap().height*game->GetCurrentMap().tilewidth-game->GetCurrentMap().tilewidth/2.f*GetSizeMult()));
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
return true;
}
@ -713,6 +713,7 @@ void Player::Moved(){
upperLevel=false;
}
}
EnvironmentalAudio::UpdateEnvironmentalAudio();
}
void Player::Spin(float duration,float spinSpd){
@ -803,7 +804,7 @@ CastInfo&Player::GetCastInfo(){
}
bool Player::CanPathfindTo(vf2d pos,vf2d targetPos,float range){
if(targetPos.x<0||targetPos.y<0||targetPos.x>game->GetCurrentMap().width*game->GetCurrentMap().tilewidth||targetPos.y>game->GetCurrentMap().height*game->GetCurrentMap().tileheight)return false;
if(targetPos.x<0||targetPos.y<0||targetPos.x>game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth||targetPos.y>game->GetCurrentMapData().height*game->GetCurrentMapData().tileheight)return false;
std::vector<vf2d>pathing=game->pathfinder.Solve_AStar(pos,targetPos,range,upperLevel);
return pathing.size()>0&&pathing.size()<range;//We'll say 7 tiles or less is close enough to 650 range. Have a little bit of wiggle room.
}

@ -78,7 +78,7 @@ public:
void Reset();
};
struct Player{
class Player{
friend class AiL;
friend class sig::Animation;
friend struct Warrior;

@ -334,8 +334,8 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
}
if(m.I(A::PHASE_REPEAT_COUNT)>=5){
m.I(A::PHASE_REPEAT_COUNT)=0;
float jumpAngle=util::angleTo(m.GetPos(),game->GetCurrentMap().MapSize*game->GetCurrentMap().tilewidth/2); //We jump towards the center to keep the player from constantly dealing with a stuck boss.
float jumpDistance=ConfigFloat("Phase4.JumpDistance")/100*game->GetCurrentMap().tilewidth;
float jumpAngle=util::angleTo(m.GetPos(),game->GetCurrentMapData().MapSize*game->GetCurrentMapData().tilewidth/2); //We jump towards the center to keep the player from constantly dealing with a stuck boss.
float jumpDistance=ConfigFloat("Phase4.JumpDistance")/100*game->GetCurrentMapData().tilewidth;
float jumpSpd=jumpDistance/ConfigFloat("Phase4.JumpDuration");
StartJump(ConfigFloat("Phase4.JumpDuration"),m.GetPos()+vf2d{cos(jumpAngle)*jumpDistance,sin(jumpAngle)*jumpDistance},0,jumpSpd);
}else

@ -49,7 +49,7 @@ void State_MainMenu::OnStateChange(GameState*prevState){
};
void State_MainMenu::OnUserUpdate(AiL*game){
TitleScreen::Update();
if(AiL::KEY_CONFIRM.Pressed()){
if(AiL::KEY_CONFIRM.Released()){
TitleScreen::Skip();
}
};

@ -40,6 +40,12 @@ All rights reserved.
#include "olcUTIL_Geometry2D.h"
#include <sstream>
#include <set>
#include "EnvironmentalAudio.h"
#include "DEFINES.h"
#include "MapName.h"
#include "safemap.h"
INCLUDE_LEVEL_NAMES
using namespace olc;
@ -94,11 +100,13 @@ struct Map{
Renderable*optimizedTile=nullptr;
std::vector<XMLTag> TilesetData;
std::vector<LayerTag> LayerData;
std::vector<EnvironmentalAudio>environmentalAudioData;
std::string mapType="";
std::string bgmSongName="";
std::set<std::string>spawns;
std::map<int,SpawnerTag> SpawnerData; //Spawn groups have IDs, mobs associate which spawner they are tied to via this ID.
std::map<std::string,std::vector<::ZoneData>> ZoneData;
const MapName&GetMapName()const;
std::string FormatLayerData(std::ostream& os, std::vector<LayerTag>tiles);
std::string FormatSpawnerData(std::ostream& os, std::map<int,SpawnerTag>tiles);
friend std::ostream& operator << (std::ostream& os, Map& rhs);
@ -128,6 +136,7 @@ class TMXParser{
SpawnerTag obj;
int prevSpawner;
ZoneData*prevZoneData=nullptr;
EnvironmentalAudio*prevAudioData=nullptr;
void ParseTag(std::string tag);
int monsterPropertyTagCount=-1;
XMLTag monsterTag;
@ -236,6 +245,9 @@ class TMXParser{
}
return displayStr;
}
const MapName&Map::GetMapName()const{
return LEVEL_NAMES[name];
}
std::ostream& operator <<(std::ostream& os, std::vector<XMLTag>& rhs) {
for(XMLTag&tag:rhs){
os <<
@ -347,7 +359,19 @@ class TMXParser{
parsedMapInfo.mapType=newTag.data["value"];
} else
if (newTag.tag=="property"&&newTag.data["name"]=="Background Music") {
if(newTag.data["value"]!="None"){ //None is a default value that we ignore.
parsedMapInfo.bgmSongName=newTag.data["value"];
}
} else
if (newTag.tag=="object"&&newTag.data["type"]=="AudioEnvironmentalSound") {
parsedMapInfo.environmentalAudioData.emplace_back();
prevAudioData=&parsedMapInfo.environmentalAudioData.back();
prevAudioData->SetPos({newTag.GetFloat("x"),newTag.GetFloat("y")});
} else
if (newTag.tag=="property"&&newTag.data["propertytype"]=="EnvironmentalSounds") {
if(newTag.data["value"]!="None"){ //None is a default value that we ignore.
prevAudioData->SetAudioName(newTag.data["value"]);
}
} else
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};

@ -81,7 +81,7 @@ public:
if(censored){
return game->TextEntryGetString();
}else{
MenuLabel::GetLabel();
return MenuLabel::GetLabel();
}
}
};

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

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="205" height="205" tilewidth="24" tileheight="24" infinite="0" backgroundcolor="#475500" nextlayerid="9" nextobjectid="166">
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="205" height="205" tilewidth="24" tileheight="24" infinite="0" backgroundcolor="#475500" nextlayerid="10" nextobjectid="168">
<properties>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
<property name="Level Type" propertytype="LevelType" value="Dungeon"/>
@ -1936,7 +1936,6 @@
</properties>
<point/>
</object>
<object id="163" name="Player Spawn" type="PlayerSpawnLocation" x="4674" y="2256" width="24" height="24"/>
<object id="164" name="Flower" type="Monster" x="4694.67" y="2396">
<properties>
<property name="Type" propertytype="MonsterName" value="Flower Turret"/>
@ -1951,5 +1950,14 @@
</properties>
<point/>
</object>
<object id="167" name="Player Spawn" type="PlayerSpawnLocation" x="624" y="4272" width="24" height="24"/>
</objectgroup>
<objectgroup id="9" name="Environmental Audio">
<object id="166" name="Waterfall Sound" type="AudioEnvironmentalSound" x="3204" y="4152">
<properties>
<property name="Sound Name" propertytype="EnvironmentalSounds" value="Waterfall"/>
</properties>
<point/>
</object>
</objectgroup>
</map>

@ -0,0 +1,4 @@
Audio
{
Environmental Audio Activation Range = 300
}

@ -0,0 +1,33 @@
BGM
{
Default Fade Time = 1.0
#Song title followed by filenames for individual parts
foresty1_1
{
Track Name = Foresty
channel[0]=foresty1_1_bass.ogg
channel[1]=foresty1_1_crash.ogg
channel[2]=foresty1_1_drums.ogg
channel[3]=foresty1_1_flute.ogg
channel[4]=foresty1_1_piano 1.ogg
channel[5]=foresty1_1_piano 2.ogg
channel[6]=foresty1_1_pizzicato.ogg
channel[7]=foresty1_1_staccato.ogg
channel[8]=foresty1_1_strings.ogg
channel[9]=foresty1_1_xtra perc.ogg
Default Volume = 70%,70%,70%,70%,70%,70%,70%,70%,70%,70%
# Transition time between one phase to the next.
Fade Time = 2.0
Events
{
LowHealth = 100%,100%,100%,20%,20%,20%,100%,100%,20%,100%
InCombat = 100%,100%,100%,100%,100%,100%,100%,100%,100%,100%
Underwater = 10%,10%,10%,60%,80%,80%,10%,10%,70%,10%
}
}
}

@ -0,0 +1,8 @@
Environmental Audio
{
Waterfall
{
File = sfx100v2_loop_water_01.ogg
Volume = 100%
}
}

@ -1,51 +0,0 @@
BGM
{
Default Fade Time = 1.0
#Song title followed by filenames for individual parts
foresty1_1
{
Track Name = Foresty
# High
channel[0]=foresty1_1_1.mp3
# Low
channel[1]=foresty1_1_2.mp3
# Underwater High
channel[2]=foresty1_1_alt1.mp3
# Underwater Low
channel[3]=foresty1_1_alt2.mp3
Default Volume = 70%,50%,0%,0%
# Transition time between one phase to the next.
Fade Time = 2.0
Events
{
LowHealth = 50%,100%,20%,20%
InCombat = 90%,100%,0%,0%
Underwater = 0%,0%,100%,100%
}
}
foresty0
{
Track Name = Foresty Preview
channel[0]=foresty0.mp3
channel[1]=foresty0_alt.mp3
Default Volume = 70%,0%
# Transition time between one phase to the next.
Fade Time = 2.0
Events
{
LowHealth = 50%,20%
InCombat = 100%,0%
Underwater = 0%,100%
}
}
}

@ -65,11 +65,17 @@ sfx_directory = assets/sounds/
# Path to bgm files
bgm_directory = assets/music/
# Path to general audio configuration
audio_config = audio/audio.txt
# Path to environmental audio configuration
environmental_audio_config = audio/environmentalaudio.txt
# Path to bgm configuration
bgm_config = bgm/bgm.txt
bgm_config = audio/bgm.txt
# Path to bgm events configuration
event_config = bgm/events.txt
event_config = audio/events.txt
# Path to character images
character_image_location = characters/

@ -1,6 +1,6 @@
ALLTOTAL=$(wc -l $(find *.cpp *.h)|grep "total"|awk '{print $1}')
TOTAL=$(wc -l $(find *.cpp *.h -not -name "miniaudio.h" -not -name "olc*.h")|grep "total"|awk '{print $1}')
FILECOUNT=$(find *.cpp *.h -not -name "miniaudio.h" -not -name "olc*.h"|wc -l)
TOTAL=$(wc -l $(find *.cpp *.h -not -name "stb_vorbis.cpp" -not -name "stb_vorbis.h" -not -name "miniaudio.h" -not -name "olc*.h")|grep "total"|awk '{print $1}')
FILECOUNT=$(find *.cpp *.h -not -name "stb_vorbis.cpp" -not -name "stb_vorbis.h" -not -name "miniaudio.h" -not -name "olc*.h"|wc -l)
echo "ALL: $[ALLTOTAL]"
echo "$[TOTAL-FILECOUNT*37] (License lines Excluded)"

@ -3722,6 +3722,8 @@ not all have been tested.
is due to 64-bit file APIs not being available.
*/
#include "stb_vorbis.h"
#ifndef miniaudio_h
#define miniaudio_h

@ -82,7 +82,7 @@ namespace olc
void SetBackgroundPlay(bool state);
public: // LOADING ROUTINES
const int LoadSound(const std::string& path);
const size_t LoadSound(const std::string& path);
void UnloadSound(const int id);
public: // PLAYBACK CONTROLS
@ -290,7 +290,7 @@ namespace olc
MiniAudio::backgroundPlay = state;
}
const int MiniAudio::LoadSound(const std::string& path)
const size_t MiniAudio::LoadSound(const std::string& path)
{
// create the sound
ma_sound* sound = new ma_sound();
@ -310,7 +310,7 @@ namespace olc
}
// no empty slots, make more room!
const int id = vecSounds.size();
const size_t id = vecSounds.size();
vecSounds.push_back(sound);
return id;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,520 @@
// Ogg Vorbis audio decoder - v1.22 - public domain
// http://nothings.org/stb_vorbis/
//
// Original version written by Sean Barrett in 2007.
//
// Originally sponsored by RAD Game Tools. Seeking implementation
// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
// Elias Software, Aras Pranckevicius, and Sean Barrett.
//
// LICENSE
//
// See end of file for license information.
//
// Limitations:
//
// - floor 0 not supported (used in old ogg vorbis files pre-2004)
// - lossless sample-truncation at beginning ignored
// - cannot concatenate multiple vorbis streams
// - sample positions are 32-bit, limiting seekable 192Khz
// files to around 6 hours (Ogg supports 64-bit)
//
// Feature contributors:
// Dougall Johnson (sample-exact seeking)
//
// Bugfix/warning contributors:
// Terje Mathisen Niklas Frykholm Andy Hill
// Casey Muratori John Bolton Gargaj
// Laurent Gomila Marc LeBlanc Ronny Chevalier
// Bernhard Wodo Evan Balster github:alxprd
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
// Phillip Bennefall Rohit Thiago Goulart
// github:manxorist Saga Musix github:infatum
// Timur Gagiev Maxwell Koo Peter Waller
// github:audinowho Dougall Johnson David Reid
// github:Clownacy Pedro J. Estebanez Remi Verschelde
// AnthoFoxo github:morlat Gabriel Ravier
//
// Partial history:
// 1.22 - 2021-07-11 - various small fixes
// 1.21 - 2021-07-02 - fix bug for files with no comments
// 1.20 - 2020-07-11 - several small fixes
// 1.19 - 2020-02-05 - warnings
// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.
// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)
// 1.16 - 2019-03-04 - fix warnings
// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
// 1.14 - 2018-02-11 - delete bogus dealloca usage
// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
// 1.11 - 2017-07-23 - fix MinGW compilation
// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version
// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
// some crash fixes when out of memory or with corrupt files
// fix some inappropriately signed shifts
// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
// 1.04 - 2014-08-27 - fix missing const-correct case in API
// 1.03 - 2014-08-07 - warning fixes
// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
// (API change) report sample rate for decode-full-file funcs
//
// See end of file for full version history.
//////////////////////////////////////////////////////////////////////////////
//
// HEADER BEGINS HERE
//
#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
#define STB_VORBIS_INCLUDE_STB_VORBIS_H
#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
#define STB_VORBIS_NO_STDIO 1
#endif
#ifndef STB_VORBIS_NO_STDIO
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/////////// THREAD SAFETY
// Individual stb_vorbis* handles are not thread-safe; you cannot decode from
// them from multiple threads at the same time. However, you can have multiple
// stb_vorbis* handles and decode from them independently in multiple thrads.
/////////// MEMORY ALLOCATION
// normally stb_vorbis uses malloc() to allocate memory at startup,
// and alloca() to allocate temporary memory during a frame on the
// stack. (Memory consumption will depend on the amount of setup
// data in the file and how you set the compile flags for speed
// vs. size. In my test files the maximal-size usage is ~150KB.)
//
// You can modify the wrapper functions in the source (setup_malloc,
// setup_temp_malloc, temp_malloc) to change this behavior, or you
// can use a simpler allocation model: you pass in a buffer from
// which stb_vorbis will allocate _all_ its memory (including the
// temp memory). "open" may fail with a VORBIS_outofmem if you
// do not pass in enough data; there is no way to determine how
// much you do need except to succeed (at which point you can
// query get_info to find the exact amount required. yes I know
// this is lame).
//
// If you pass in a non-NULL buffer of the type below, allocation
// will occur from it as described above. Otherwise just pass NULL
// to use malloc()/alloca()
typedef struct
{
char *alloc_buffer;
int alloc_buffer_length_in_bytes;
} stb_vorbis_alloc;
/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES
typedef struct stb_vorbis stb_vorbis;
typedef struct
{
unsigned int sample_rate;
int channels;
unsigned int setup_memory_required;
unsigned int setup_temp_memory_required;
unsigned int temp_memory_required;
int max_frame_size;
} stb_vorbis_info;
typedef struct
{
char *vendor;
int comment_list_length;
char **comment_list;
} stb_vorbis_comment;
// get general information about the file
extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
// get ogg comments
extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f);
// get the last error detected (clears it, too)
extern int stb_vorbis_get_error(stb_vorbis *f);
// close an ogg vorbis file and free all memory in use
extern void stb_vorbis_close(stb_vorbis *f);
// this function returns the offset (in samples) from the beginning of the
// file that will be returned by the next decode, if it is known, or -1
// otherwise. after a flush_pushdata() call, this may take a while before
// it becomes valid again.
// NOT WORKING YET after a seek with PULLDATA API
extern int stb_vorbis_get_sample_offset(stb_vorbis *f);
// returns the current seek point within the file, or offset from the beginning
// of the memory buffer. In pushdata mode it returns 0.
extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
/////////// PUSHDATA API
#ifndef STB_VORBIS_NO_PUSHDATA_API
// this API allows you to get blocks of data from any source and hand
// them to stb_vorbis. you have to buffer them; stb_vorbis will tell
// you how much it used, and you have to give it the rest next time;
// and stb_vorbis may not have enough data to work with and you will
// need to give it the same data again PLUS more. Note that the Vorbis
// specification does not bound the size of an individual frame.
extern stb_vorbis *stb_vorbis_open_pushdata(
const unsigned char * datablock, int datablock_length_in_bytes,
int *datablock_memory_consumed_in_bytes,
int *error,
const stb_vorbis_alloc *alloc_buffer);
// create a vorbis decoder by passing in the initial data block containing
// the ogg&vorbis headers (you don't need to do parse them, just provide
// the first N bytes of the file--you're told if it's not enough, see below)
// on success, returns an stb_vorbis *, does not set error, returns the amount of
// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;
// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed
// if returns NULL and *error is VORBIS_need_more_data, then the input block was
// incomplete and you need to pass in a larger block from the start of the file
extern int stb_vorbis_decode_frame_pushdata(
stb_vorbis *f,
const unsigned char *datablock, int datablock_length_in_bytes,
int *channels, // place to write number of float * buffers
float ***output, // place to write float ** array of float * buffers
int *samples // place to write number of output samples
);
// decode a frame of audio sample data if possible from the passed-in data block
//
// return value: number of bytes we used from datablock
//
// possible cases:
// 0 bytes used, 0 samples output (need more data)
// N bytes used, 0 samples output (resynching the stream, keep going)
// N bytes used, M samples output (one frame of data)
// note that after opening a file, you will ALWAYS get one N-bytes,0-sample
// frame, because Vorbis always "discards" the first frame.
//
// Note that on resynch, stb_vorbis will rarely consume all of the buffer,
// instead only datablock_length_in_bytes-3 or less. This is because it wants
// to avoid missing parts of a page header if they cross a datablock boundary,
// without writing state-machiney code to record a partial detection.
//
// The number of channels returned are stored in *channels (which can be
// NULL--it is always the same as the number of channels reported by
// get_info). *output will contain an array of float* buffers, one per
// channel. In other words, (*output)[0][0] contains the first sample from
// the first channel, and (*output)[1][0] contains the first sample from
// the second channel.
//
// *output points into stb_vorbis's internal output buffer storage; these
// buffers are owned by stb_vorbis and application code should not free
// them or modify their contents. They are transient and will be overwritten
// once you ask for more data to get decoded, so be sure to grab any data
// you need before then.
extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
// inform stb_vorbis that your next datablock will not be contiguous with
// previous ones (e.g. you've seeked in the data); future attempts to decode
// frames will cause stb_vorbis to resynchronize (as noted above), and
// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it
// will begin decoding the _next_ frame.
//
// if you want to seek using pushdata, you need to seek in your file, then
// call stb_vorbis_flush_pushdata(), then start calling decoding, then once
// decoding is returning you data, call stb_vorbis_get_sample_offset, and
// if you don't like the result, seek your file again and repeat.
#endif
////////// PULLING INPUT API
#ifndef STB_VORBIS_NO_PULLDATA_API
// This API assumes stb_vorbis is allowed to pull data from a source--
// either a block of memory containing the _entire_ vorbis stream, or a
// FILE * that you or it create, or possibly some other reading mechanism
// if you go modify the source to replace the FILE * case with some kind
// of callback to your code. (But if you don't support seeking, you may
// just want to go ahead and use pushdata.)
#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
#endif
#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
#endif
// decode an entire file and output the data interleaved into a malloc()ed
// buffer stored in *output. The return value is the number of samples
// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.
// When you're done with it, just free() the pointer returned in *output.
extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
// this must be the entire stream!). on failure, returns NULL and sets *error
#ifndef STB_VORBIS_NO_STDIO
extern stb_vorbis * stb_vorbis_open_filename(const char *filename,
int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from a filename via fopen(). on failure,
// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
// note that stb_vorbis must "own" this stream; if you seek it in between
// calls to stb_vorbis, it will become confused. Moreover, if you attempt to
// perform stb_vorbis_seek_*() operations on this file, it will assume it
// owns the _entire_ rest of the file after the start point. Use the next
// function, stb_vorbis_open_file_section(), to limit it.
extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
// this stream; if you seek it in between calls to stb_vorbis, it will become
// confused.
#endif
extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
// these functions seek in the Vorbis file to (approximately) 'sample_number'.
// after calling seek_frame(), the next call to get_frame_*() will include
// the specified sample. after calling stb_vorbis_seek(), the next call to
// stb_vorbis_get_samples_* will start with the specified sample. If you
// do not need to seek to EXACTLY the target sample when using get_samples_*,
// you can also use seek_frame().
extern int stb_vorbis_seek_start(stb_vorbis *f);
// this function is equivalent to stb_vorbis_seek(f,0)
extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
// these functions return the total length of the vorbis stream
extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
// decode the next frame and return the number of samples. the number of
// channels returned are stored in *channels (which can be NULL--it is always
// the same as the number of channels reported by get_info). *output will
// contain an array of float* buffers, one per channel. These outputs will
// be overwritten on the next call to stb_vorbis_get_frame_*.
//
// You generally should not intermix calls to stb_vorbis_get_frame_*()
// and stb_vorbis_get_samples_*(), since the latter calls the former.
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples);
#endif
// decode the next frame and return the number of *samples* per channel.
// Note that for interleaved data, you pass in the number of shorts (the
// size of your array), but the return value is the number of samples per
// channel, not the total number of samples.
//
// The data is coerced to the number of channels you request according to the
// channel coercion rules (see below). You must pass in the size of your
// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
// The maximum buffer size needed can be gotten from get_info(); however,
// the Vorbis I specification implies an absolute maximum of 4096 samples
// per channel.
// Channel coercion rules:
// Let M be the number of channels requested, and N the number of channels present,
// and Cn be the nth channel; let stereo L be the sum of all L and center channels,
// and stereo R be the sum of all R and center channels (channel assignment from the
// vorbis spec).
// M N output
// 1 k sum(Ck) for all k
// 2 * stereo L, stereo R
// k l k > l, the first l channels, then 0s
// k l k <= l, the first k channels
// Note that this is not _good_ surround etc. mixing at all! It's just so
// you get something useful.
extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
// gets num_samples samples, not necessarily on a frame boundary--this requires
// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.
// Returns the number of samples stored per channel; it may be less than requested
// at the end of the file. If there are no more samples in the file, returns 0.
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
#endif
// gets num_samples samples, not necessarily on a frame boundary--this requires
// buffering so you have to supply the buffers. Applies the coercion rules above
// to produce 'channels' channels. Returns the number of samples stored per channel;
// it may be less than requested at the end of the file. If there are no more
// samples in the file, returns 0.
#endif
//////// ERROR CODES
enum STBVorbisError
{
VORBIS__no_error,
VORBIS_need_more_data=1, // not a real error
VORBIS_invalid_api_mixing, // can't mix API modes
VORBIS_outofmem, // not enough memory
VORBIS_feature_not_supported, // uses floor 0
VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small
VORBIS_file_open_failure, // fopen() failed
VORBIS_seek_without_length, // can't seek in unknown-length file
VORBIS_unexpected_eof=10, // file is truncated?
VORBIS_seek_invalid, // seek past EOF
// decoding errors (corrupt/invalid stream) -- you probably
// don't care about the exact details of these
// vorbis errors:
VORBIS_invalid_setup=20,
VORBIS_invalid_stream,
// ogg errors:
VORBIS_missing_capture_pattern=30,
VORBIS_invalid_stream_structure_version,
VORBIS_continued_packet_flag_invalid,
VORBIS_incorrect_stream_serial_number,
VORBIS_invalid_first_page,
VORBIS_bad_packet_type,
VORBIS_cant_find_last_page,
VORBIS_seek_failed,
VORBIS_ogg_skeleton_not_supported
};
#ifdef __cplusplus
}
#endif
#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
//
// HEADER ENDS HERE
//
//////////////////////////////////////////////////////////////////////////////
/* Version history
1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223
found with Mayhem by ForAllSecure
1.16 - 2019-03-04 - fix warnings
1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
1.14 - 2018-02-11 - delete bogus dealloca usage
1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
1.11 - 2017-07-23 - fix MinGW compilation
1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version
1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks;
avoid discarding last frame of audio data
1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API
some more crash fixes when out of memory or with corrupt files
1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
some crash fixes when out of memory or with corrupt files
1.05 - 2015-04-19 - don't define __forceinline if it's redundant
1.04 - 2014-08-27 - fix missing const-correct case in API
1.03 - 2014-08-07 - Warning fixes
1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows
1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float
1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel
(API change) report sample rate for decode-full-file funcs
0.99996 - bracket #include <malloc.h> for macintosh compilation by Laurent Gomila
0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem
0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence
0.99993 - remove assert that fired on legal files with empty tables
0.99992 - rewind-to-start
0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo
0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++
0.9998 - add a full-decode function with a memory source
0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition
0.9996 - query length of vorbis stream in samples/seconds
0.9995 - bugfix to another optimization that only happened in certain files
0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors
0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation
0.9992 - performance improvement of IMDCT; now performs close to reference implementation
0.9991 - performance improvement of IMDCT
0.999 - (should have been 0.9990) performance improvement of IMDCT
0.998 - no-CRT support from Casey Muratori
0.997 - bugfixes for bugs found by Terje Mathisen
0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen
0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen
0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen
0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen
0.992 - fixes for MinGW warning
0.991 - turn fast-float-conversion on by default
0.990 - fix push-mode seek recovery if you seek into the headers
0.98b - fix to bad release of 0.98
0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode
0.97 - builds under c++ (typecasting, don't use 'class' keyword)
0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code
0.95 - clamping code for 16-bit functions
0.94 - not publically released
0.93 - fixed all-zero-floor case (was decoding garbage)
0.92 - fixed a memory leak
0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION
0.90 - first public release
*/
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/
Loading…
Cancel
Save