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.
@ -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%)
|
||||
|
BIN
Adventures in Lestoria/assets/sounds/AiL_fanfare.ogg
Normal file
BIN
Adventures in Lestoria/promo/enchant1.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
Adventures in Lestoria/promo/enchant2.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
Adventures in Lestoria/promo/newclasses.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
Adventures in Lestoria/promo/thief1.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
Adventures in Lestoria/promo/thief2.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
Adventures in Lestoria/promo/trapper1.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Adventures in Lestoria/promo/trapper2.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
Adventures in Lestoria/promo/witch1.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
Adventures in Lestoria/promo/witch2.png
Normal file
After Width: | Height: | Size: 18 KiB |