Implement default audio event property to stages. Add fanfare transition and post-boss song event to the game. Add appropriate triggers for when boss fight completes. Addresses Issue #66. Release Build 11576.

master
sigonasr2 2 months ago
parent 2fe0991920
commit 880b8e4335
  1. 24
      Adventures in Lestoria/Adventures in Lestoria.tiled-project
  2. 2
      Adventures in Lestoria/AdventuresInLestoria.cpp
  3. 8
      Adventures in Lestoria/Audio.cpp
  4. 5
      Adventures in Lestoria/Audio.h
  5. 9
      Adventures in Lestoria/Monster.cpp
  6. 1
      Adventures in Lestoria/PlayerTimerType.h
  7. 22
      Adventures in Lestoria/SoundEffect.cpp
  8. 5
      Adventures in Lestoria/SoundEffect.h
  9. 8
      Adventures in Lestoria/TMXParser.h
  10. 2
      Adventures in Lestoria/Version.h
  11. 2
      Adventures in Lestoria/Zephy.cpp
  12. 1
      Adventures in Lestoria/assets/Campaigns/Boss_2_B.tmx
  13. 12
      Adventures in Lestoria/assets/config/audio/bgm.txt
  14. 11
      Adventures in Lestoria/assets/config/audio/events.txt
  15. BIN
      Adventures in Lestoria/assets/gamepack.pak
  16. BIN
      Adventures in Lestoria/assets/sounds/AiL_fanfare.ogg
  17. BIN
      Adventures in Lestoria/promo/enchant1.png
  18. BIN
      Adventures in Lestoria/promo/enchant2.png
  19. BIN
      Adventures in Lestoria/promo/newclasses.png
  20. BIN
      Adventures in Lestoria/promo/thief1.png
  21. BIN
      Adventures in Lestoria/promo/thief2.png
  22. BIN
      Adventures in Lestoria/promo/trapper1.png
  23. BIN
      Adventures in Lestoria/promo/trapper2.png
  24. BIN
      Adventures in Lestoria/promo/witch1.png
  25. BIN
      Adventures in Lestoria/promo/witch2.png
  26. BIN
      x64/Release/Adventures in Lestoria.exe

@ -29,6 +29,24 @@
"object"
]
},
{
"id": 43,
"name": "AudioEvent",
"storageType": "string",
"type": "enum",
"values": [
"LowHealth",
"TitleScreenLoaded",
"BlacksmithUnlock",
"Chapter2Unlock",
"Chapter3Unlock",
"BossFanfare",
"BossDefeated",
"PreBossPhase",
"Default Volume"
],
"valuesAsFlags": false
},
{
"id": 30,
"name": "Backdrop",
@ -335,6 +353,12 @@
"drawFill": true,
"id": 19,
"members": [
{
"name": "Audio Event",
"propertyType": "AudioEvent",
"type": "string",
"value": "Default Volume"
},
{
"name": "Backdrop",
"propertyType": "Backdrop",

@ -2714,7 +2714,7 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
#pragma region Setup Pathfinding (Loading Phase 9)
LoadingScreen::AddPhase([&](){
Audio::SetAudioEvent("Default Volume");
Audio::SetAudioEvent(GetCurrentMap().GetDefaultAudioEvent());
#ifdef __EMSCRIPTEN__
Audio::muted=false;
Audio::UpdateBGMVolume();

@ -41,6 +41,7 @@ All rights reserved.
#include "util.h"
#include "LoadingScreen.h"
#include "Menu.h"
#include "SoundEffect.h"
INCLUDE_game
INCLUDE_DATA
@ -416,8 +417,11 @@ float Audio::GetCalculatedBGMVolume(const float channelVol){
}
return channelVol*GetBGMVolume()*GetMuteMult()*pauseMult;
}
float Audio::GetCalculatedSFXVolume(const float vol){
return vol*GetSFXVolume()*GetMuteMult();
float Audio::GetCalculatedSFXVolume(const SoundEffect&sfx){
return sfx.TreatAsBPM()?Audio::GetCalculatedBGMVolume(sfx.GetVolume()):sfx.GetVolume()*GetSFXVolume()*GetMuteMult();
}
float Audio::GetCalculatedSFXVolume(const float sfxVol){
return sfxVol*GetSFXVolume()*GetMuteMult();
}
float Audio::GetMuteMult(){
if(muted)return 0.f;

@ -49,6 +49,8 @@ using ChannelIDList=std::vector<ChannelID>;
using Volume=float;
using VolumeList=std::vector<Volume>;
class SoundEffect;
class Audio{
friend class AiL;
public:
@ -80,7 +82,8 @@ public:
//This will get a prepared BGM loop iteration count which is useful for loading stages.
static int GetPrepareBGMLoopIterations(std::string_view sound);
static float GetCalculatedBGMVolume(const float channelVol);
static float GetCalculatedSFXVolume(const float vol);
static float GetCalculatedSFXVolume(const SoundEffect&sfx);
static float GetCalculatedSFXVolume(const float sfxVol); //NOTE: This is a more manually invoked function! If you are trying to play a specific sound effect from an event, use the SoundEffect version instead!! This accounts for any additional flags related to volume.
private:
bool trackLoadStarted=false;
bool trackLoadComplete=false;

@ -1058,6 +1058,15 @@ void Monster::OnDeath(){
exitRing.zone.pos.y=std::clamp(exitRing.zone.pos.y,clampedArena.pos.y-"boss_spawn_ring_radius"_I,clampedArena.pos.y-"boss_spawn_ring_radius"_I+clampedArena.size.y);
game->AddZone("EndZone",exitRing); //Create a 144x144 ring around the dead boss.
Audio::SetAudioEvent("BossFanfare");
game->GetPlayer()->AddTimer(PlayerTimerType::FANFARE_WAIT_TIMER,Timer{"Fanfare Wait Timer",1.f,[](){
SoundEffect::PlaySFX("Fanfare",SoundEffect::CENTERED);
game->GetPlayer()->AddTimer(PlayerTimerType::FANFARE_WAIT_TIMER,Timer{"Fanfare Wait Timer",5.f,[](){
Audio::SetAudioEvent("BossDefeated");
}});
}});
}
}
}

@ -45,4 +45,5 @@ enum class PlayerTimerType{
ADVANCE_SHIELD_TIMER,
CAT_FORM_ALREADY_ACTIVE,
BLINK_PORTAL_SECOND_CAST,
FANFARE_WAIT_TIMER,
};

@ -58,8 +58,8 @@ void RepeatingSoundEffect::StopAllSounds(){
playingSoundEffects.clear();
}
SoundEffect::SoundEffect(const std::string_view filename,const float&vol,const float&minPitch,const float&maxPitch,const bool combatSound)
:filename(filename),vol(vol),minPitch(minPitch),maxPitch(maxPitch),combatSound(combatSound){
SoundEffect::SoundEffect(const std::string_view filename,const float&vol,const float&minPitch,const float&maxPitch,const bool combatSound,const bool treatAsBGM)
:filename(filename),vol(vol),minPitch(minPitch),maxPitch(maxPitch),combatSound(combatSound),treatAsBGM(treatAsBGM){
if(vol<0.f||vol>1.f)ERR(std::format("WARNING! Volume must be between 0.0f ~ 1.0f! Provided value {}",vol));
}
@ -67,16 +67,20 @@ void SoundEffect::Initialize(){
for(auto&[key,size]:DATA["Events"]["SFX"]){
int counter=0;
bool combatSound=false;
bool treatAsBGM{false};
if(DATA["Events"]["SFX"][key].HasProperty("CombatSound")){
combatSound=DATA["Events"]["SFX"][key]["CombatSound"].GetBool();
}
if(DATA["Events"]["SFX"][key].HasProperty("Treat as BGM")){
treatAsBGM=DATA["Events"]["SFX"][key]["Treat as BGM"].GetBool();
}
while(DATA["Events"]["SFX"][key].HasProperty(std::format("File[{}]",counter))){
utils::datafile&data=DATA["Events"]["SFX"][key][std::format("File[{}]",counter)];
float minPitch=0.9f;
float maxPitch=1.1f;
if(data.GetValueCount()>=3){minPitch=data.GetInt(2)/100.f;}
if(data.GetValueCount()>=4){maxPitch=data.GetInt(3)/100.f;}
SOUND_EFFECTS.insert({key,SoundEffect{data.GetString(0),data.GetInt(1)/100.f,minPitch,maxPitch,combatSound}});
SOUND_EFFECTS.insert({key,SoundEffect{data.GetString(0),data.GetInt(1)/100.f,minPitch,maxPitch,combatSound,treatAsBGM}});
counter++;
}
auto itr=SOUND_EFFECTS.equal_range(key);
@ -96,7 +100,8 @@ void SoundEffect::PlaySFX(const std::string&eventName,const vf2d&pos){
float pitch=util::random(pitchDiff)+sfx.minPitch;
if(pos==CENTERED){
Audio::Engine().Play(operator""_SFX(sfx.filename.c_str(),sfx.filename.length()),Audio::GetCalculatedSFXVolume(sfx.vol*Audio::GetSFXVolume()),0.0f,pitch);
float vol{Audio::GetCalculatedSFXVolume(sfx)};
Audio::Engine().Play(operator""_SFX(sfx.filename.c_str(),sfx.filename.length()),vol,0.0f,pitch);
}else{
const float soundActivationRange="Audio.Environmental Audio Activation Range"_F;
@ -105,7 +110,7 @@ void SoundEffect::PlaySFX(const std::string&eventName,const vf2d&pos){
float distRatio=1-distanceFromPlayer/soundActivationRange; //0-1 where 1 is full volume.
float xDistRatio=(pos.x-game->GetPlayer()->GetX())/soundActivationRange; //0-1 where 1 is full volume.
float vol=distRatio*sfx.vol*Audio::GetSFXVolume()*Audio::GetMuteMult();
float vol=distRatio*Audio::GetCalculatedSFXVolume(sfx);
float pan=xDistRatio;
Audio::Engine().Play(operator""_SFX(sfx.filename.c_str(),sfx.filename.length()),vol,pan,pitch);
}
@ -142,4 +147,11 @@ SoundEffect&SoundEffect::GetRandomSFXFromFile(const std::string&eventName){
}
return (*it).second;
}
const float&SoundEffect::GetVolume()const{
return vol;
}
const bool&SoundEffect::TreatAsBPM()const{
return treatAsBGM;
}

@ -53,18 +53,21 @@ private:
class SoundEffect{
public:
SoundEffect(const std::string_view filename,const float&vol,const float&minPitch=0.9f,const float&maxPitch=1.1f,const bool combatSound=false);
SoundEffect(const std::string_view filename,const float&vol,const float&minPitch=0.9f,const float&maxPitch=1.1f,const bool combatSound=false,const bool treatAsBGM=false);
static void PlaySFX(const std::string&eventName,const vf2d&pos);
//Sets up a sound to be looped continuously.
static size_t PlayLoopingSFX(const std::string&eventName,const vf2d&pos);
static void StopLoopingSFX(const int id);
static void Initialize();
static const vf2d CENTERED;
const float&GetVolume()const;
const bool&TreatAsBPM()const;
private:
static SoundEffect&GetRandomSFXFromFile(const std::string&eventName);
static std::multimap<EventName,SoundEffect>SOUND_EFFECTS;
std::string filename;
float vol;
bool treatAsBGM{false};
bool combatSound=false;
float minPitch=0.9f;
float maxPitch=1.1f;

@ -144,6 +144,7 @@ private:
std::vector<NPCData>npcs;
MapType mapType{};
std::string bgmSongName="";
std::string audioEvent{"Default Volume"};
std::unordered_map<Class,float>devCompletionTrialTime;
BackdropName backdrop="";
std::set<std::string>spawns;
@ -166,6 +167,7 @@ public:
const int Spawn_pop(); //Grabs the next spawn controller ID and removes it from the stack.
const Renderable*const GetOptimizedMap()const;
const std::map<std::string,std::vector<::ZoneData>>&GetZones()const;
const std::string&GetDefaultAudioEvent()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);
@ -352,6 +354,9 @@ class TMXParser{
const Renderable*const Map::GetOptimizedMap()const{
return optimizedTile;
}
const std::string&Map::GetDefaultAudioEvent()const{
return audioEvent;
}
NPCData::NPCData(){}
NPCData::NPCData(XMLTag npcTag){
const std::array<std::string,7>tags={"Function","NPC Name","Roaming Range","Unlock Condition","Spritesheet","x","y"};
@ -539,6 +544,9 @@ class TMXParser{
parsedMapInfo.backdrop=newTag.data["value"];
}
}else
if(newTag.tag=="property"&&newTag.data["name"]=="Audio Event"){
parsedMapInfo.audioEvent=newTag.data["value"];
}else
if (newTag.tag=="object"&&newTag.data["type"]=="AudioEnvironmentalSound") {
parsedMapInfo.environmentalAudioData.emplace_back();
prevAudioData=&parsedMapInfo.environmentalAudioData.back();

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

@ -92,6 +92,8 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
game->SetOverlay(ConfigString("Wind Attack.Wind Overlay Sprite"),ConfigPixel("Wind Attack.Wind Overlay Color"));
game->GetOverlay().Disable();
Audio::SetAudioEvent("Default Volume"); //Begin playing regular music.
m.SetStrategyDeathFunction([&](GameEvent&ev,Monster&m,const std::string&strategy){
game->SetWindSpeed({});
game->GetOverlay().Disable();

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="160" height="144" tilewidth="24" tileheight="24" infinite="0" nextlayerid="9" nextobjectid="26">
<properties>
<property name="Audio Event" propertytype="AudioEvent" value="PreBossPhase"/>
<property name="Backdrop" propertytype="Backdrop" value="mountain_night"/>
<property name="Background Music" propertytype="BGM" value="mountain_boss"/>
<property name="Level Type" type="int" propertytype="LevelType" value="1"/>

@ -44,6 +44,7 @@ BGM
Track Name = Foresty Boss
channel[0]=commercial_assets/foresty_boss.ogg
channel[1]=commercial_assets/AiL_forest_postBoss.ogg
# Transition time between one phase to the next.
Fade Time = 2.0
@ -52,7 +53,9 @@ BGM
Events
{
Default Volume = 70%
Default Volume = 70%, 0%
BossFanfare = 0%, 0%
BossDefeated = 0%, 70%
}
}
@ -151,6 +154,8 @@ BGM
Track Name = Mountain Boss
channel[0]=commercial_assets/AiL_mountain_boss.ogg
channel[1]=commercial_assets/AiL_mountain_postBoss.ogg
channel[2]=commercial_assets/AiL_mountain_boss_halfTrack.ogg
# Transition time between one phase to the next.
Fade Time = 2.0
@ -159,7 +164,10 @@ BGM
Events
{
Default Volume = 70%
Default Volume = 70%, 0%, 0%
BossFanfare = 0%, 0%, 0%
BossDefeated = 0%, 70%, 0%
PreBossPhase = 0%, 0%, 70%
}
}
}

@ -5,6 +5,9 @@ Events
BlacksmithUnlock = "Blacksmith appears in the camp."
Chapter2Unlock = "Chapter 2 is unlocked (beat the demo)"
Chapter3Unlock = "Chapter 3 is unlocked (After beating the Stone Golem)"
BossFanfare = "Fanfare is playing, music should be muted."
BossDefeated = "After a boss has been wiped out the event will transition over."
PreBossPhase = "A pre-boss phase to a boss fight."
SFX
{
@ -164,6 +167,14 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = explosion.ogg, 80%
}
Fanfare
{
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = AiL_fanfare.ogg, 100%, 100%, 100%
# If set to true, then this sound will use the volume controls from the BGM volume setting instead.
Treat as BGM = true
}
Footstep
{
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Loading…
Cancel
Save