Add RepeatingSoundEffect class to allow easy creation and handling of looping sound effects ingame. Add rock breaking, rock toss cast, pillar rise, dig sound effects. Fix typo on Hawk Feather item description key name. Placeholder item icons added. Add sound effects to Stone Elemental's attacks. Release Build 9656.

This commit is contained in:
sigonasr2 2024-06-18 11:38:03 -05:00
parent cd1a734b19
commit a740d6eb01
28 changed files with 132 additions and 16 deletions

View File

@ -2290,6 +2290,7 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
ItemDrop::drops.clear();
GameEvent::events.clear();
Audio::SetBGMPitch(1.f);
RepeatingSoundEffect::StopAllSounds();
#ifdef __EMSCRIPTEN__
Audio::muted=true;
Audio::UpdateBGMVolume();

View File

@ -80,4 +80,10 @@ public:
}
return std::get<std::vector<std::any>>(attributes[a]);
};
inline size_t&GetSizeT(Attribute a){
if(attributes.count(a)==0){
attributes[a]=size_t(0);
}
return std::get<size_t>(attributes[a]);
};
};

View File

@ -72,7 +72,7 @@ using MonsterSpawnerID=int;
#define ACCESS_PLAYER Player*p=game->GetPlayer();
#define VARIANTS float,int,std::string,bool,vf2d,std::vector<std::any>
#define VARIANTS float,int,std::string,bool,vf2d,std::vector<std::any>,size_t
#undef INFINITE
#define INFINITE 999999

View File

@ -40,6 +40,7 @@ All rights reserved.
#include "AdventuresInLestoria.h"
#include "FallingDebris.h"
#include "util.h"
#include "SoundEffect.h"
INCLUDE_game
INCLUDE_MONSTER_LIST
@ -82,6 +83,8 @@ void LargeStone::Update(float fElapsedTime){
#pragma endregion
fadeOutTime=0.5f;
SoundEffect::PlaySFX("Large Rock Break",pos);
}else
if(z>0.f&&fadeOutTime>=0.f){
pos=startingPos.lerp(landingPos,(GetTimeAlive()-initialMoveWaitTime)/stoneThrowTime);

View File

@ -823,6 +823,11 @@ std::map<ItemInfo*,uint16_t>Monster::SpawnDrops(){
void Monster::OnDeath(){
animation.ChangeState(internal_animState,GetDeathAnimationName());
if(GetSizeT(Attribute::LOOPING_SOUND_ID)!=std::numeric_limits<size_t>::max()){//Just make sure on death any looping sound effect has been discarded proper.
SoundEffect::StopLoopingSFX(GetSizeT(Attribute::LOOPING_SOUND_ID));
GetSizeT(Attribute::LOOPING_SOUND_ID)=std::numeric_limits<size_t>::max();
}
if(HasMountedMonster()){
for(DeathSpawnInfo&deathInfo:deathData){
deathInfo.Spawn(GetPos(),OnUpperLevel());

View File

@ -39,6 +39,7 @@ All rights reserved.
#include <string>
#define F(attr) GetFloat(attr)
#define I(attr) GetInt(attr)
#define SIZET(attr) GetSizeT(attr)
#define S(attr) GetString(attr)
#define B(attr) GetBool(attr)
#define V(attr) GetVf2d(attr)
@ -121,4 +122,6 @@ enum class Attribute{
WIND_STRENGTH,
WIND_PHASE_TIMER,
MARKED_DEAD,
LOOPING_SOUND_ID,
PLAYED_FLAG,
};

View File

@ -48,6 +48,15 @@ INCLUDE_game
std::multimap<EventName,SoundEffect>SoundEffect::SOUND_EFFECTS;
const vf2d SoundEffect::CENTERED={-8419.f,-3289.f};
std::unordered_set<size_t>RepeatingSoundEffect::playingSoundEffects;
void RepeatingSoundEffect::StopAllSounds(){
for(auto&id:playingSoundEffects){
Audio::Engine().Stop(id);
Audio::Engine().UnloadSound(id);
}
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){
@ -79,18 +88,7 @@ void SoundEffect::Initialize(){
void SoundEffect::PlaySFX(const std::string_view eventName,const vf2d&pos){
if(eventName.length()==0)return;
auto itr=SOUND_EFFECTS.equal_range(std::string(eventName));
size_t soundCount=std::distance(itr.first,itr.second);
if(soundCount==0)ERR("WARNING! Sound Effect "<<std::quoted(eventName)<<" does not have any sound effects loaded/doesn't exist!")
size_t soundEffectChoice=util::random()%soundCount;
int counter=0;
auto it=itr.first;
while(counter!=soundEffectChoice){
++counter;
++it;
}
const SoundEffect&sfx=(*it).second;
const SoundEffect&sfx=GetRandomSFXFromFile(eventName);
if(GameState::STATE==GameState::states[States::MAIN_MENU]&&sfx.combatSound)return; //Do not play combat sounds on the main menu.
@ -112,4 +110,35 @@ void SoundEffect::PlaySFX(const std::string_view eventName,const vf2d&pos){
Audio::Engine().Play(operator""_SFX(sfx.filename.c_str(),sfx.filename.length()),vol,pan,pitch);
}
}
}
size_t SoundEffect::PlayLoopingSFX(const std::string_view eventName,const vf2d&pos){
const SoundEffect&sfx=GetRandomSFXFromFile(eventName);
const size_t id=Audio::Engine().LoadSound(operator""_SFX(sfx.filename.c_str(),sfx.filename.length()));
RepeatingSoundEffect::playingSoundEffects.insert(id);
Audio::Engine().Play(id,true);
return id;
}
void SoundEffect::StopLoopingSFX(const int id){
if(!RepeatingSoundEffect::playingSoundEffects.count(id))return; //Prevent crashes from stopping a sound more than once. Called by accident probably.
Audio::Engine().Stop(id);
Audio::Engine().UnloadSound(id);
RepeatingSoundEffect::playingSoundEffects.erase(id);
}
SoundEffect&SoundEffect::GetRandomSFXFromFile(const std::string_view eventName){
auto itr=SOUND_EFFECTS.equal_range(std::string(eventName));
size_t soundCount=std::distance(itr.first,itr.second);
if(soundCount==0)ERR("WARNING! Sound Effect "<<std::quoted(eventName)<<" does not have any sound effects loaded/doesn't exist!")
size_t soundEffectChoice=util::random()%soundCount;
int counter=0;
auto it=itr.first;
while(counter!=soundEffectChoice){
++counter;
++it;
}
return (*it).second;
}

View File

@ -38,18 +38,30 @@ All rights reserved.
#pragma once
#include <string>
#include <map>
#include <unordered_set>
#include "Error.h"
using EventName=std::string;
class RepeatingSoundEffect{
friend class AiL;
friend class SoundEffect;
private:
static void StopAllSounds();
static std::unordered_set<size_t>playingSoundEffects;
};
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);
static void PlaySFX(const std::string_view eventName,const vf2d&pos);
//Sets up a sound to be looped continuously.
static size_t PlayLoopingSFX(const std::string_view eventName,const vf2d&pos);
static void StopLoopingSFX(const int id);
static void Initialize();
static const vf2d CENTERED;
private:
static SoundEffect&GetRandomSFXFromFile(const std::string_view eventName);
static std::multimap<EventName,SoundEffect>SOUND_EFFECTS;
std::string filename;
float vol;

View File

@ -42,6 +42,7 @@ All rights reserved.
#include "DEFINES.h"
#include "util.h"
#include "BulletTypes.h"
#include "SoundEffect.h"
INCLUDE_game
@ -67,6 +68,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
m.F(A::RECOVERY_TIME)-=fElapsedTime;
if(m.F(A::RECOVERY_TIME)<=0.f){
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
m.SIZET(A::LOOPING_SOUND_ID)=SoundEffect::PlayLoopingSFX("Rock Toss Cast",m.GetPos());
m.PerformAnimation("CAST",m.GetFacingDirectionToTarget(m.V(A::LOCKON_POS)));
game->AddEffect(std::make_unique<SpellCircle>(m.V(A::LOCKON_POS),ConfigFloat("Beginning Phase.Pillar Cast Time"),"range_indicator.png","spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Golem Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Golem Pillar").GetSizeMult()/12.f)*1.25f,0.3f,vf2d{},ConfigPixel("Beginning Phase.Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Beginning Phase.Pillar Spell Circle Rotation Spd")),false,vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Golem Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Golem Pillar").GetSizeMult()/12.f)*0.9f,0.3f,vf2d{},ConfigPixel("Beginning Phase.Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Beginning Phase.Pillar Spell Insignia Rotation Spd"))),true);
m.F(A::CASTING_TIMER)=ConfigFloat("Beginning Phase.Pillar Cast Time");
@ -76,6 +78,8 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
case SPAWN_PILLAR_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){
SoundEffect::StopLoopingSFX(m.SIZET(A::LOOPING_SOUND_ID));
SoundEffect::PlaySFX("Pillar Rise",m.V(A::LOCKON_POS));
m.I(A::PATTERN_REPEAT_COUNT)--;
game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Golem Pillar"),m.OnUpperLevel());
game->Hurt(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Golem Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Golem Pillar").GetSizeMult(),MONSTER_DATA.at("Stone Golem Pillar").GetAttack(),m.OnUpperLevel(),0.f,HurtType::PLAYER);
@ -107,6 +111,8 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
//Physics!! Kinematic equation from https://openstax.org/books/physics/pages/3-2-representing-acceleration-with-equations-and-graphs a=(2d)/(t^2)
const float acc{(2*-ConfigFloat("Standard Attack.Stone Throw Height Offset"))/std::pow(stoneTossTime,2.f)};
m.SIZET(A::LOOPING_SOUND_ID)=SoundEffect::PlayLoopingSFX("Rock Toss Cast",m.GetPos());
CreateBullet(LargeStone)(m.GetPos()+vf2d{0,ConfigFloat("Standard Attack.Stone Throw Height Offset")/2.f},ConfigFloat("Standard Attack.Stone Throw Time"),m.V(A::LOCKON_POS),m.F(A::CASTING_TIMER),ConfigPixels("Standard Attack.Stone Radius"),ConfigFloat("Standard Attack.Stone Throw Height Offset"),acc,ConfigInt("Standard Attack.Stone Damage"),ConfigFloat("Standard Attack.Stone Throw Knockback Factor"),m.OnUpperLevel(),false,INFINITY,false,WHITE,vf2d{1,1}*m.GetSizeMult(),util::random(2*PI))EndBullet;
}
}
@ -114,6 +120,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
case STONE_THROW_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){
SoundEffect::StopLoopingSFX(m.SIZET(A::LOOPING_SOUND_ID));
m.PerformAnimation("TOSS ROCK");
m.F(A::RECOVERY_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration();
m.phase=STONE_THROW_FINISH_ANIMATION;

View File

@ -41,6 +41,7 @@ All rights reserved.
#include "MonsterStrategyHelpers.h"
#include "BulletTypes.h"
#include "util.h"
#include "SoundEffect.h"
INCLUDE_game
INCLUDE_BULLET_LIST
@ -93,6 +94,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
switch(randomAttackChoice){
case 0:{
m.PerformAnimation("STONE PILLAR CAST");
m.SIZET(A::LOOPING_SOUND_ID)=SoundEffect::PlayLoopingSFX("Rock Toss Cast",m.GetPos());
m.phase=STONE_PILLAR_CAST;
m.F(A::CASTING_TIMER)=ConfigFloat("Stone Pillar Cast Time");
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
@ -100,7 +102,9 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
}break;
case 1:{
m.PerformAnimation("ROCK TOSS CAST");
m.SIZET(A::LOOPING_SOUND_ID)=SoundEffect::PlayLoopingSFX("Rock Toss Cast",m.GetPos());
m.phase=SHOOT_STONE_CAST;
m.B(A::PLAYED_FLAG)=false;
m.F(A::CASTING_TIMER)=ConfigFloat("Rock Toss Track Time")+ConfigFloat("Rock Toss Wait Time");
CreateBullet(LevitatingRock)(m,game->GetPlayer()->GetPos(),1.f,0.f,ConfigPixels("Rock Toss Max Spawn Distance"),ConfigFloat("Rock Toss Track Time"),ConfigFloat("Rock Toss Wait Time"),ConfigFloat("Rock Toss Bullet Speed"),ConfigFloat("Rock Radius"),std::max(1,ConfigInt("Rock Toss Damage")/5),m.OnUpperLevel(),false,WHITE,vf2d{1,1})EndBullet;
const int masterRockInd=BULLET_LIST.size()-1;
@ -115,6 +119,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
}
}break;
case 2:{
SoundEffect::PlaySFX("Dig",m.GetPos());
m.PerformAnimation("BURROW UNDERGROUND");
m.phase=DIVE_UNDERGROUND_DIG;
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
@ -125,6 +130,8 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
case STONE_PILLAR_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){
SoundEffect::StopLoopingSFX(m.SIZET(A::LOOPING_SOUND_ID));
SoundEffect::PlaySFX("Pillar Rise",m.V(A::LOCKON_POS));
game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar"),m.OnUpperLevel());
ReturnToWaitingPhase();
game->Hurt(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult(),m.GetAttack(),m.OnUpperLevel(),0.f,HurtType::PLAYER);
@ -141,10 +148,16 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
case SHOOT_STONE_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)>=ConfigFloat("Rock Toss Wait Time")-ConfigFloat("Rock Toss Track Time")){
SoundEffect::StopLoopingSFX(m.SIZET(A::LOOPING_SOUND_ID));
m.PerformAnimation("STONE PILLAR CAST");
m.UpdateFacingDirection(game->GetPlayer()->GetPos());
}
if(m.F(A::CASTING_TIMER)>=ConfigFloat("Rock Toss Track Time")&&!m.B(A::PLAYED_FLAG)){
SoundEffect::PlaySFX("Rock Break",m.GetPos());
m.B(A::PLAYED_FLAG)=true;
}
if(m.F(A::CASTING_TIMER)<=0.f){
SoundEffect::StopLoopingSFX(m.SIZET(A::LOOPING_SOUND_ID));
ReturnToWaitingPhase();
}
}break;
@ -180,6 +193,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
if(m.F(A::CASTING_TIMER)<=0.f){
m.B(A::IGNORE_DEFAULT_ANIMATIONS)=false;
SoundEffect::PlaySFX("Rise",m.GetPos());
m.PerformAnimation("RISE FROM UNDERGROUND");
m.phase=DIVE_UNDERGROUND_SURFACE;
m.targetAcquireTimer=0;

View File

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
#define VERSION_BUILD 9648
#define VERSION_BUILD 9656
#define stringify(a) stringify_(a)
#define stringify_(a) #a

View File

@ -49,6 +49,12 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = craft_equip.ogg, 70%
}
Dig
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = dig.ogg, 100%
}
Equip Armor
{
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
@ -115,6 +121,12 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = item_collect.ogg, 40%
}
Large Rock Break
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = rockbreak.ogg, 90%, 40%, 50%
}
Level Up
{
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
@ -138,6 +150,12 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = monster_hurt.ogg, 40%
}
Pillar Rise
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = pillar_rise.ogg, 90%
}
Player Hit
{
CombatSound = True
@ -176,6 +194,24 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ranger_charged_shot.ogg, 70%
}
Rise
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = rise.ogg, 100%
}
Rock Break
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = rockbreak.ogg, 90%
}
Rock Toss Cast
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = rocktosscast.ogg, 100%
}
Slime Dead
{
CombatSound = True

View File

@ -228,7 +228,7 @@ ItemDatabase
}
Hawk Feather
{
Desciption = A Feather of a Hawk. Sadly no longer in perfect condition.
Description = A Feather of a Hawk. Sadly no longer in perfect condition.
ItemCategory = Materials
SellValue = 7
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.