diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 6541e9a02..d501cb884 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -237,6 +237,7 @@ Note: All fields default to false unless mentioned otherwise. scalex = ; // Vertical scaling on thing. Default = 0 (ignored). scaley = ; // Horizontal scaling on thing. Default = 0 (ignored). scale = ; // Vertical and horizontal scaling on thing. Default = 0 (ignored). + floatbobphase = ; // Sets the thing's floatbobphase. Valid phase values are 0-63. Default = -1 (use actor class default). * Note about arg0str diff --git a/src/actor.h b/src/actor.h index 470258d8a..0f85afa76 100644 --- a/src/actor.h +++ b/src/actor.h @@ -375,6 +375,7 @@ enum ActorFlag7 MF7_NODECAL = 0x00040000, // [ZK] Forces puff to have no impact decal MF7_FORCEDECAL = 0x00080000, // [ZK] Forces puff's decal to override the weapon's. MF7_LAXTELEFRAGDMG = 0x00100000, // [MC] Telefrag damage can be reduced. + MF7_ICESHATTER = 0x00200000, // [MC] Shatters ice corpses regardless of damagetype. }; // --- mobj.renderflags --- diff --git a/src/am_map.cpp b/src/am_map.cpp index 045d48589..46a2d5a84 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2328,7 +2328,9 @@ void AM_drawWalls (bool allmap) AM_drawMline (&l, AMColors.LockedColor); // locked special } } - else if (am_showtriggerlines && AMColors.isValid(AMColors.SpecialWallColor) && lines[i].special != 0 + else if (am_showtriggerlines && AMColors.isValid(AMColors.SpecialWallColor) + && LineSpecialsInfo[lines[i].special] != NULL + && LineSpecialsInfo[lines[i].special]->max_args >= 0 && lines[i].special != Door_Open && lines[i].special != Door_Close && lines[i].special != Door_CloseWaitOpen diff --git a/src/configfile.cpp b/src/configfile.cpp index b8db74eb3..13cfea517 100644 --- a/src/configfile.cpp +++ b/src/configfile.cpp @@ -368,8 +368,7 @@ bool FConfigFile::DeleteCurrentSection() LastSectionPtr = &sec->Next; } - CurrentSection->~FConfigSection(); - delete[] (char *)CurrentSection; + delete CurrentSection; CurrentSection = sec->Next; return CurrentSection != NULL; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 1085c6ca7..72c5bd0ff 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -842,7 +842,7 @@ static int PatchThing (int thingy) bool hadStyle = false; FStateDefinitions statedef; bool patchedStates = false; - int oldflags; + ActorFlags oldflags; PClassActor *type; SWORD *ednum, dummyed; @@ -1197,28 +1197,28 @@ static int PatchThing (int thingy) } if (vchanged[1]) { - info->flags2 = ActorFlags2::FromInt (value[1]); - if (info->flags2 & 0x00000004) // old BOUNCE1 + if (value[1] & 0x00000004) // old BOUNCE1 { - info->flags2 &= ActorFlags2::FromInt (~4); + value[1] &= ~0x00000004; info->BounceFlags = BOUNCE_DoomCompat; } // Damage types that once were flags but now are not - if (info->flags2 & 0x20000000) + if (value[1] & 0x20000000) { info->DamageType = NAME_Ice; - info->flags2 &= ActorFlags2::FromInt (~0x20000000); + value[1] &= ~0x20000000; } - if (info->flags2 & 0x10000) + if (value[1] & 0x10000000) { info->DamageType = NAME_Fire; - info->flags2 &= ActorFlags2::FromInt (~0x10000); + value[1] &= ~0x10000000; } - if (info->flags2 & 1) + if (value[1] & 0x00000001) { info->gravity = FRACUNIT/4; - info->flags2 &= ActorFlags2::FromInt (~1); + value[1] &= ~0x00000001; } + info->flags2 = ActorFlags2::FromInt (value[1]); } if (vchanged[2]) { diff --git a/src/decallib.cpp b/src/decallib.cpp index 454f54ea5..37a9dc649 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -432,7 +432,7 @@ WORD FDecalLib::GetDecalID (FScanner &sc) unsigned long num = strtoul (sc.String, NULL, 10); if (num < 1 || num > 65535) { - sc.MustGetStringName ("Decal ID must be between 1 and 65535"); + sc.ScriptError ("Decal ID must be between 1 and 65535"); } return (WORD)num; } @@ -603,16 +603,18 @@ void FDecalLib::ParseGenerator (FScanner &sc) { PClassActor *type; FDecalBase *decal; - AActor *actor; + bool optional = false; // Get name of generator (actor) sc.MustGetString (); - type = PClass::FindActor(sc.String); + optional = sc.Compare("optional"); + if (optional) sc.MustGetString(); + + type = PClass::FindActor (sc.String); if (type == NULL) { - sc.ScriptError ("%s is not an actor.", sc.String); + if (!optional) sc.ScriptError ("%s is not an actor.", sc.String); } - actor = (AActor *)type->Defaults; // Get name of generated decal sc.MustGetString (); @@ -625,14 +627,17 @@ void FDecalLib::ParseGenerator (FScanner &sc) decal = ScanTreeForName (sc.String, Root); if (decal == NULL) { - sc.ScriptError ("%s has not been defined.", sc.String); + if (!optional) sc.ScriptError ("%s has not been defined.", sc.String); } } - - actor->DecalGenerator = decal; - if (decal != NULL) + if (type != NULL) { - decal->Users.Push (type); + AActor *actor = (AActor *)type->Defaults; + actor->DecalGenerator = decal; + if (decal != NULL) + { + decal->Users.Push(type); + } } } diff --git a/src/doomdata.h b/src/doomdata.h index 71e581e26..0877aee90 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -365,6 +365,7 @@ struct FMapThing short pitch; short roll; DWORD RenderStyle; + int FloatbobPhase; }; diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index f57c0b95b..6cecf1a60 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -1236,7 +1236,7 @@ void FParser::SF_ObjFlag(void) t_return.type = svt_int; if (mo && flagnum<26) { - t_return.value.i = !!(mo->flags & (1 << flagnum)); + t_return.value.i = !!(mo->flags & ActorFlags::FromInt(1 << flagnum)); } else t_return.value.i = 0; } diff --git a/src/g_hexen/a_clericstaff.cpp b/src/g_hexen/a_clericstaff.cpp index 56be017b1..2cc30a7ae 100644 --- a/src/g_hexen/a_clericstaff.cpp +++ b/src/g_hexen/a_clericstaff.cpp @@ -79,7 +79,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck) { pmo->angle = R_PointToAngle2 (pmo->x, pmo->y, linetarget->x, linetarget->y); if (((linetarget->player && (!linetarget->IsTeammate (pmo) || level.teamdamage != 0))|| linetarget->flags3&MF3_ISMONSTER) - && (!(linetarget->flags2&(MF2_DORMANT+MF2_INVULNERABLE)))) + && (!(linetarget->flags2&(MF2_DORMANT|MF2_INVULNERABLE)))) { newLife = player->health+(damage>>3); newLife = newLife > max ? max : newLife; diff --git a/src/g_level.cpp b/src/g_level.cpp index 15d88ccaf..aba58e810 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -698,6 +698,13 @@ void G_DoCompleted (void) gameaction = ga_nothing; + if ( gamestate == GS_DEMOSCREEN + || gamestate == GS_FULLCONSOLE + || gamestate == GS_STARTUP) + { + return; + } + if (gamestate == GS_TITLELEVEL) { level.MapName = nextlevel; diff --git a/src/g_level.h b/src/g_level.h index cedfc7417..4611b4e34 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -561,8 +561,10 @@ enum ESkillProperty SKILLP_FriendlyHealth, SKILLP_NoPain, SKILLP_ArmorFactor, + SKILLP_HealthFactor, SKILLP_EasyKey, SKILLP_SlowMonsters, + SKILLP_Infight, }; int G_SkillProperty(ESkillProperty prop); const char * G_SkillName(); @@ -600,7 +602,9 @@ struct FSkillInfo fixed_t MonsterHealth; fixed_t FriendlyHealth; bool NoPain; + int Infighting; fixed_t ArmorFactor; + fixed_t HealthFactor; FSkillInfo() {} FSkillInfo(const FSkillInfo &other) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index b25f98130..3fee21925 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -270,10 +270,12 @@ bool P_GiveBody (AActor *actor, int num, int max) return true; } } - else + else if (num > 0) { if (player->health < max) { + num = FixedMul(num, G_SkillProperty(SKILLP_HealthFactor)); + if (num < 1) num = 1; player->health += num; if (player->health > max) { @@ -726,6 +728,7 @@ AInventory *AInventory::CreateCopy (AActor *other) { AInventory *copy; + Amount = MIN(Amount, MaxAmount); if (GoAway ()) { copy = static_cast(Spawn (GetClass(), 0, 0, 0, NO_REPLACE)); diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index 4d5e8df34..3e3782a6b 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -15,10 +15,23 @@ #include "gstrings.h" #include "a_action.h" #include "thingdef/thingdef.h" +#include "v_text.h" #define MAX_RANDOMSPAWNERS_RECURSION 32 // Should be largely more than enough, honestly. static FRandom pr_randomspawn("RandomSpawn"); +static bool IsMonster(DDropItem *di) +{ + const PClass *pclass = PClass::FindClass(di->Name); + + if (NULL == pclass) + { + return false; + } + + return 0 != (GetDefaultByType(pclass)->flags3 & MF3_ISMONSTER); +} + class ARandomSpawner : public AActor { DECLARE_CLASS (ARandomSpawner, AActor) @@ -41,7 +54,7 @@ class ARandomSpawner : public AActor { if (di->Name != NAME_None) { - if (!nomonsters || !(GetDefaultByType(PClass::FindClass(di->Name))->flags3 & MF3_ISMONSTER)) + if (!nomonsters || !IsMonster(di)) { if (di->Amount < 0) di->Amount = 1; // default value is -1, we need a positive value. n += di->Amount; // this is how we can weight the list. @@ -62,7 +75,7 @@ class ARandomSpawner : public AActor while (n > -1 && di != NULL) { if (di->Name != NAME_None && - (!nomonsters || !(GetDefaultByType(PClass::FindClass(di->Name))->flags3 & MF3_ISMONSTER))) + (!nomonsters || !IsMonster(di))) { n -= di->Amount; if ((di->Next != NULL) && (n > -1)) @@ -106,6 +119,7 @@ class ARandomSpawner : public AActor } else { + Printf(TEXTCOLOR_RED "Unknown item class %s to drop from a random spawner\n", di->Name.GetChars()); Species = NAME_None; } } diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index d62a2a07b..54661ea14 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -186,10 +186,12 @@ void ASkyPicker::PostBeginPlay () if (0 == (args[1] & 2)) { Sector->CeilingSkyBox = box; + if (box == NULL) Sector->MoreFlags |= SECF_NOCEILINGSKYBOX; // sector should ignore the level's default skybox } if (0 == (args[1] & 1)) { Sector->FloorSkyBox = box; + if (box == NULL) Sector->MoreFlags |= SECF_NOFLOORSKYBOX; // sector should ignore the level's default skybox } } Destroy (); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 333dc1d26..59029caf1 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1338,17 +1338,17 @@ void DBaseStatusBar::Draw (EHudState state) { if (Scaled) { - y -= Scale (10, SCREENHEIGHT, 200); + y -= Scale (11, SCREENHEIGHT, 200); } else { if (SCREENWIDTH < 640) { - y -= 11; + y -= 12; } else { // Get past the tops of the gargoyles' wings - y -= 26; + y -= 28; } } } diff --git a/src/g_skill.cpp b/src/g_skill.cpp index c4c0c34fb..c589f44d4 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -83,6 +83,8 @@ void FMapInfoParser::ParseSkill () skill.FriendlyHealth = FRACUNIT; skill.NoPain = false; skill.ArmorFactor = FRACUNIT; + skill.Infighting = 0; + skill.HealthFactor = FRACUNIT; sc.MustGetString(); skill.Name = sc.String; @@ -266,6 +268,20 @@ void FMapInfoParser::ParseSkill () sc.MustGetFloat(); skill.ArmorFactor = FLOAT2FIXED(sc.Float); } + else if (sc.Compare("HealthFactor")) + { + ParseAssign(); + sc.MustGetFloat(); + skill.HealthFactor = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare("NoInfighting")) + { + skill.Infighting = LEVEL2_NOINFIGHTING; + } + else if (sc.Compare("TotalInfighting")) + { + skill.Infighting = LEVEL2_TOTALINFIGHTING; + } else if (sc.Compare("DefaultSkill")) { if (DefaultSkill >= 0) @@ -384,6 +400,17 @@ int G_SkillProperty(ESkillProperty prop) case SKILLP_ArmorFactor: return AllSkills[gameskill].ArmorFactor; + + case SKILLP_HealthFactor: + return AllSkills[gameskill].HealthFactor; + + case SKILLP_Infight: + // This property also needs to consider the level flags for the same info. + if (level.flags2 & LEVEL2_TOTALINFIGHTING) return 1; + if (level.flags2 & LEVEL2_NOINFIGHTING) return -1; + if (AllSkills[gameskill].Infighting == LEVEL2_TOTALINFIGHTING) return 1; + if (AllSkills[gameskill].Infighting == LEVEL2_NOINFIGHTING) return -1; + return infighting; } } return 0; @@ -463,7 +490,9 @@ FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other) MonsterHealth = other.MonsterHealth; FriendlyHealth = other.FriendlyHealth; NoPain = other.NoPain; + Infighting = other.Infighting; ArmorFactor = other.ArmorFactor; + HealthFactor = other.HealthFactor; return *this; } diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 402c1d5e9..0f50d1e00 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -385,6 +385,11 @@ FListMenuItemStaticPatch::FListMenuItemStaticPatch(int x, int y, FTextureID patc void FListMenuItemStaticPatch::Drawer(bool selected) { + if (!mTexture.Exists()) + { + return; + } + int x = mXpos; FTexture *tex = TexMan(mTexture); if (mYpos >= 0) @@ -486,7 +491,7 @@ bool FListMenuItemSelectable::MouseEvent(int type, int x, int y) { if (type == DMenu::MOUSE_Release) { - if (DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true)) + if (NULL != DMenu::CurrentMenu && DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true)) { return true; } diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 73101a76e..04c10760e 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -95,6 +95,18 @@ static void DeinitMenus() ClearSaveGames(); } +static FTextureID GetMenuTexture(const char* const name) +{ + const FTextureID texture = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch); + + if (!texture.Exists()) + { + Printf("Missing menu texture: \"%s\"\n", name); + } + + return texture; +} + //============================================================================= // // @@ -235,7 +247,7 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) else if (sc.Compare("Selector")) { sc.MustGetString(); - desc->mSelector = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + desc->mSelector = GetMenuTexture(sc.String); sc.MustGetStringName(","); sc.MustGetNumber(); desc->mSelectOfsX = sc.Number; @@ -278,7 +290,7 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) int y = sc.Number; sc.MustGetStringName(","); sc.MustGetString(); - FTextureID tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(sc.String); FListMenuItem *it = new FListMenuItemStaticPatch(x, y, tex, centered); desc->mItems.Push(it); @@ -299,7 +311,7 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) else if (sc.Compare("PatchItem")) { sc.MustGetString(); - FTextureID tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(sc.String); sc.MustGetStringName(","); sc.MustGetString(); int hotkey = sc.String[0]; @@ -1045,7 +1057,7 @@ static void BuildEpisodeMenu() FListMenuItem *it; if (AllEpisodes[i].mPicName.IsNotEmpty()) { - FTextureID tex = TexMan.CheckForTexture(AllEpisodes[i].mPicName, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(AllEpisodes[i].mPicName); it = new FListMenuItemPatch(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, tex, NAME_Skillmenu, i); } @@ -1442,7 +1454,7 @@ void M_StartupSkillMenu(FGameStartup *gs) if (skill.PicName.Len() != 0 && pItemText == NULL) { - FTextureID tex = TexMan.CheckForTexture(skill.PicName, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(skill.PicName); li = new FListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, i); } else diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h index e87e78483..c63359f4b 100644 --- a/src/menu/optionmenuitems.h +++ b/src/menu/optionmenuitems.h @@ -1045,6 +1045,19 @@ public: return text; } + int Draw(FOptionMenuDescriptor*desc, int y, int indent, bool selected) + { + if (mEntering) + { + // reposition the text so that the cursor is visible when in entering mode. + FString text = Represent(); + int tlen = SmallFont->StringWidth(text) * CleanXfac_1; + int newindent = screen->GetWidth() - tlen - CURSORSPACE; + if (newindent < indent) indent = newindent; + } + return FOptionMenuFieldBase::Draw(desc, y, indent, selected); + } + bool MenuEvent ( int mkey, bool fromcontroller ) { if ( mkey == MKEY_Enter ) diff --git a/src/namedef.h b/src/namedef.h index 7055ee3c9..e62515e8d 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -408,6 +408,7 @@ xx(Roll) xx(Scale) xx(ScaleX) xx(ScaleY) +xx(Floatbobphase) xx(Blocking) xx(Blockmonsters) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 23cd39206..c9f839c76 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -133,6 +133,16 @@ enum ARMORINFO_ACTUALSAVEAMOUNT, }; +// PickActor +// [JP] I've renamed these flags to something else to avoid confusion with the other PAF_ flags +enum +{ +// PAF_FORCETID, +// PAF_RETURNTID + PICKAF_FORCETID = 1, + PICKAF_RETURNTID = 2, +}; + struct CallReturn { CallReturn(int pc, ScriptFunction *func, FBehavior *module, SDWORD *locals, ACSLocalArrays *arrays, bool discard, unsigned int runaway) @@ -3364,6 +3374,14 @@ int DLevelScript::DoSpawn (int type, fixed_t x, fixed_t y, fixed_t z, int tid, i if (info != NULL) { + info = info->GetReplacement (); + + if ((GetDefaultByType (info)->flags3 & MF3_ISMONSTER) && + ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) + { + return 0; + } + actor = Spawn (info, x, y, z, ALLOW_REPLACE); if (actor != NULL) { @@ -5753,9 +5771,9 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return 0; } - DWORD actorMask = MF_SHOOTABLE; + ActorFlags actorMask = MF_SHOOTABLE; if (argCount >= 6) { - actorMask = args[5]; + actorMask = ActorFlags::FromInt(args[5]); } DWORD wallMask = ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN; @@ -5763,11 +5781,10 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) wallMask = args[6]; } - bool forceTID = 0; + int flags = 0; if (argCount >= 8) { - if (args[7] != 0) - forceTID = 1; + flags = args[7]; } AActor* pickedActor = P_LinePickActor(actor, args[1] << 16, args[3], args[2] << 16, actorMask, wallMask); @@ -5775,15 +5792,19 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return 0; } - if (!(forceTID) && (args[4] == 0) && (pickedActor->tid == 0)) + if (!(flags & PICKAF_FORCETID) && (args[4] == 0) && (pickedActor->tid == 0)) return 0; - if ((pickedActor->tid == 0) || (forceTID)) + if ((pickedActor->tid == 0) || (flags & PICKAF_FORCETID)) { pickedActor->RemoveFromHash(); pickedActor->tid = args[4]; pickedActor->AddToHash(); } + if (flags & PICKAF_RETURNTID) + { + return pickedActor->tid; + } return 1; } break; @@ -5856,6 +5877,8 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) const char *statename = argCount > 6 ? FBehavior::StaticLookupString(args[6]) : ""; bool exact = argCount > 7 ? !!args[7] : false; fixed_t heightoffset = argCount > 8 ? args[8] : 0; + fixed_t radiusoffset = argCount > 9 ? args[9] : 0; + fixed_t pitch = argCount > 10 ? args[10] : 0; FState *state = argCount > 6 ? activator->GetClass()->FindStateByString(statename, exact) : 0; @@ -5873,7 +5896,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) if (!reference) return false; - if (P_Thing_Warp(activator, reference, xofs, yofs, zofs, angle, flags, heightoffset)) + if (P_Thing_Warp(activator, reference, xofs, yofs, zofs, angle, flags, heightoffset, radiusoffset, pitch)) { if (state && argCount > 6) { diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index fa5af987f..e94beedf6 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -707,6 +707,7 @@ static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites, mapthings[count].RenderStyle = STYLE_Count; mapthings[count].alpha = -1; mapthings[count].health = -1; + mapthings[count].FloatbobPhase = -1; if (xsprites != NULL && sprites[i].lotag == 710) { // Blood ambient sound diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 10d7080a3..fa0df283c 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -965,7 +965,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } if (target->health <= 0) { - if (inflictor && mod == NAME_Ice) + if (inflictor && mod == NAME_Ice && !(inflictor->flags7 & MF7_ICESHATTER)) { return -1; } @@ -1634,10 +1634,8 @@ bool AActor::OkayToSwitchTarget (AActor *other) int infight; if (flags5 & MF5_NOINFIGHTING) infight=-1; - else if (level.flags2 & LEVEL2_TOTALINFIGHTING) infight=1; - else if (level.flags2 & LEVEL2_NOINFIGHTING) infight=-1; - else infight = infighting; - + else infight = G_SkillProperty(SKILLP_Infight); + if (infight < 0 && other->player == NULL && !IsHostile (other)) { return false; // infighting off: Non-friendlies don't target other non-friendlies diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 0282e5ea1..8e5e0f027 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -56,6 +56,7 @@ #include "p_3dmidtex.h" #include "d_net.h" #include "d_event.h" +#include "gstrings.h" #include "r_data/colormaps.h" #define FUNC(a) static int a (line_t *ln, AActor *it, bool backSide, \ @@ -1599,6 +1600,11 @@ FUNC(LS_Thing_Move) // [BC] return P_Thing_Move (arg0, it, arg1, arg2 ? false : true); } +enum +{ + TRANSLATION_ICE = 0x100007 +}; + FUNC(LS_Thing_SetTranslation) // Thing_SetTranslation (tid, range) { @@ -1615,6 +1621,10 @@ FUNC(LS_Thing_SetTranslation) { range = TRANSLATION(TRANSLATION_LevelScripted, (arg1-1)); } + else if (arg1 == TRANSLATION_ICE) + { + range = TRANSLATION(TRANSLATION_Standard, 7); + } else { range = 0; @@ -2985,13 +2995,14 @@ FUNC(LS_SendToCommunicator) { S_StopSound (CHAN_VOICE); S_Sound (CHAN_VOICE, name, 1, ATTN_NORM); - if (arg2 == 0) + + // Get the message from the LANGUAGE lump. + FString msg; + msg.Format("TXT_COMM%d", arg2); + const char *str = GStrings[msg]; + if (str != NULL) { - Printf (PRINT_CHAT, "Incoming Message\n"); - } - else if (arg2 == 1) - { - Printf (PRINT_CHAT, "Incoming Message from BlackBird\n"); + Printf (PRINT_CHAT, "%s\n", str); } } return true; diff --git a/src/p_local.h b/src/p_local.h index c1c43c5f8..14749a85d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -180,7 +180,7 @@ bool P_Thing_Raise(AActor *thing, AActor *raiser); bool P_Thing_CanRaise(AActor *thing); PClassActor *P_GetSpawnableType(int spawnnum); void InitSpawnablesFromMapinfo(); -int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t zofs, angle_t angle, int flags, fixed_t heightoffset); +int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t zofs, angle_t angle, int flags, fixed_t heightoffset, fixed_t radiusoffset, angle_t pitch); enum WARPF { @@ -202,6 +202,8 @@ enum WARPF WARPF_MOVEPTR = 0x1000, WARPF_USEPTR = 0x2000, WARPF_USETID = 0x2000, + WARPF_COPYVELOCITY = 0x4000, + WARPF_COPYPITCH = 0x8000, }; @@ -504,7 +506,7 @@ enum // P_LineAttack flags AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, PClassActor *pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, FName pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); -AActor *P_LinePickActor (AActor *t1, angle_t angle, fixed_t distance, int pitch, DWORD actorMask, DWORD wallMask); +AActor *P_LinePickActor (AActor *t1, angle_t angle, fixed_t distance, int pitch, ActorFlags actorMask, DWORD wallMask); void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *target, angle_t angle, int pitch); void P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch); void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile version diff --git a/src/p_map.cpp b/src/p_map.cpp index 21f6f3833..1addb464e 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -938,10 +938,7 @@ static bool CanAttackHurt(AActor *victim, AActor *shooter) // to harm / be harmed by anything. if (!victim->player && !shooter->player) { - int infight; - if (level.flags2 & LEVEL2_TOTALINFIGHTING) infight = 1; - else if (level.flags2 & LEVEL2_NOINFIGHTING) infight = -1; - else infight = infighting; + int infight = G_SkillProperty(SKILLP_Infight); if (infight < 0) { @@ -3912,7 +3909,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, //========================================================================== AActor *P_LinePickActor(AActor *t1, angle_t angle, fixed_t distance, int pitch, - DWORD actorMask, DWORD wallMask) + ActorFlags actorMask, DWORD wallMask) { fixed_t vx, vy, vz, shootz; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index e716f4e75..9e04e2a24 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5026,6 +5026,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) mobj->SpawnPoint[2] = mthing->z; mobj->SpawnAngle = mthing->angle; mobj->SpawnFlags = mthing->flags; + if (mthing->FloatbobPhase >= 0 && mthing->FloatbobPhase < 64) mobj->FloatBobPhase = mthing->FloatbobPhase; if (mthing->gravity < 0) mobj->gravity = -mthing->gravity; else if (mthing->gravity > 0) mobj->gravity = FixedMul(mobj->gravity, mthing->gravity); else mobj->flags &= ~MF_NOGRAVITY; @@ -6564,25 +6565,25 @@ void PrintMiscActorInfo(AActor *query) Printf("%s @ %p has the following flags:\n flags: %x", query->GetTag(), query, query->flags.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags & 1<flags & ActorFlags::FromInt(1<flags2.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags2 & 1<flags2 & ActorFlags2::FromInt(1<flags3.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags3 & 1<flags3 & ActorFlags3::FromInt(1<flags4.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags4 & 1<flags4 & ActorFlags4::FromInt(1<flags5.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags5 & 1<flags5 & ActorFlags5::FromInt(1<flags6.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags6 & 1<flags6 & ActorFlags6::FromInt(1<flags7.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags7 & 1<flags7 & ActorFlags7::FromInt(1<BounceFlags.GetValue(), FIXED2FLOAT(query->bouncefactor), FIXED2FLOAT(query->wallbouncefactor)); diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 904c76c1b..361b220a7 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -29,6 +29,7 @@ #include "po_man.h" #include "farchive.h" #include "r_utility.h" +#include "a_sharedglobal.h" #include "r_data/colormaps.h" @@ -800,6 +801,23 @@ int sector_t::GetCeilingLight () const } } + +ASkyViewpoint *sector_t::GetSkyBox(int which) +{ + if (which == floor) + { + if (FloorSkyBox != NULL) return FloorSkyBox; + if (MoreFlags & SECF_NOFLOORSKYBOX) return NULL; + } + else + { + if (CeilingSkyBox != NULL) return CeilingSkyBox; + if (MoreFlags & SECF_NOCEILINGSKYBOX) return NULL; + } + return level.DefaultSkybox; +} + + sector_t *sector_t::GetHeightSec() const { if (heightsec == NULL) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 3e51eda95..19f2e1b57 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1755,6 +1755,7 @@ void P_LoadThings (MapData * map) mti[i].RenderStyle = STYLE_Count; mti[i].alpha = -1; mti[i].health = 1; + mti[i].FloatbobPhase = -1; flags &= ~MTF_SKILLMASK; mti[i].flags = (short)((flags & 0xf) | 0x7e0); if (gameinfo.gametype == GAME_Strife) @@ -1839,6 +1840,7 @@ void P_LoadThings2 (MapData * map) mti[i].RenderStyle = STYLE_Count; mti[i].alpha = -1; mti[i].health = 1; + mti[i].FloatbobPhase = -1; } delete[] mtp; } @@ -2350,7 +2352,16 @@ static void P_LoopSidedefs (bool firstloop) // instead of as part of another loop if (line->frontsector == line->backsector) { - right = DWORD(line->sidedef[!sidetemp[i].b.lineside] - sides); + const side_t* const rightside = line->sidedef[!sidetemp[i].b.lineside]; + + if (NULL == rightside) + { + // There is no right side! + if (firstloop) Printf ("Line %d's right edge is unconnected\n", linemap[unsigned(line-lines)]); + continue; + } + + right = DWORD(rightside - sides); } else { diff --git a/src/p_things.cpp b/src/p_things.cpp index 7ac50a395..34ecb4912 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -680,7 +680,7 @@ void InitSpawnablesFromMapinfo() } -int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t zofs, angle_t angle, int flags, fixed_t heightoffset) +int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t zofs, angle_t angle, int flags, fixed_t heightoffset, fixed_t radiusoffset, angle_t pitch) { if (flags & WARPF_MOVEPTR) { @@ -692,25 +692,27 @@ int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t oldx = caller->x; fixed_t oldy = caller->y; fixed_t oldz = caller->z; - zofs += FixedMul(reference->height, heightoffset); - + if (!(flags & WARPF_ABSOLUTEANGLE)) { angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; } + + const fixed_t rad = FixedMul(radiusoffset, reference->radius); + const angle_t fineangle = angle >> ANGLETOFINESHIFT; + if (!(flags & WARPF_ABSOLUTEPOSITION)) { if (!(flags & WARPF_ABSOLUTEOFFSET)) { - angle_t fineangle = angle >> ANGLETOFINESHIFT; fixed_t xofs1 = xofs; // (borrowed from A_SpawnItemEx, assumed workable) // in relative mode negative y values mean 'left' and positive ones mean 'right' // This is the inverse orientation of the absolute mode! - + xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); } @@ -722,15 +724,16 @@ int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, // assigning position again with. // extra unlink, link and environment calculation caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->floorz + zofs); + reference->x + xofs + FixedMul(rad, finecosine[fineangle]), + reference->y + yofs + FixedMul(rad, finesine[fineangle]), + reference->z); + caller->z = caller->floorz + zofs; } else { caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, + reference->x + xofs + FixedMul(rad, finecosine[fineangle]), + reference->y + yofs + FixedMul(rad, finesine[fineangle]), reference->z + zofs); } } @@ -738,11 +741,12 @@ int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, { if (flags & WARPF_TOFLOOR) { - caller->SetOrigin(xofs, yofs, caller->floorz + zofs); + caller->SetOrigin(xofs + FixedMul(rad, finecosine[fineangle]), yofs + FixedMul(rad, finesine[fineangle]), zofs); + caller->z = caller->floorz + zofs; } else { - caller->SetOrigin(xofs, yofs, zofs); + caller->SetOrigin(xofs + FixedMul(rad, finecosine[fineangle]), yofs + FixedMul(rad, finesine[fineangle]), zofs); } } @@ -756,6 +760,18 @@ int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, { caller->angle = angle; + if (flags & WARPF_COPYPITCH) + caller->SetPitch(reference->pitch, false); + + if (pitch) + caller->SetPitch(caller->pitch + pitch, false); + + if (flags & WARPF_COPYVELOCITY) + { + caller->velx = reference->velx; + caller->vely = reference->vely; + caller->velz = reference->velz; + } if (flags & WARPF_STOP) { caller->velx = 0; diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 07d23294b..4b4683be5 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -42,7 +42,8 @@ struct FTraceInfo { fixed_t StartX, StartY, StartZ; fixed_t Vx, Vy, Vz; - DWORD ActorMask, WallMask; + ActorFlags ActorMask; + DWORD WallMask; AActor *IgnoreThis; FTraceResults *Results; sector_t *CurSector; @@ -70,7 +71,7 @@ static bool EditTraceResult (DWORD flags, FTraceResults &res); bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist, - DWORD actorMask, DWORD wallMask, AActor *ignore, + ActorFlags actorMask, DWORD wallMask, AActor *ignore, FTraceResults &res, DWORD flags, ETraceStatus (*callback)(FTraceResults &res, void *), void *callbackdata) { diff --git a/src/p_trace.h b/src/p_trace.h index 403b1f9cf..ecf37e01e 100644 --- a/src/p_trace.h +++ b/src/p_trace.h @@ -35,6 +35,7 @@ #define __P_TRACE_H__ #include +#include "actor.h" #include "textures/textures.h" struct sector_t; @@ -96,7 +97,7 @@ enum ETraceStatus bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist, - DWORD ActorMask, DWORD WallMask, AActor *ignore, + ActorFlags ActorMask, DWORD WallMask, AActor *ignore, FTraceResults &res, DWORD traceFlags=0, ETraceStatus (*callback)(FTraceResults &res, void *)=NULL, void *callbackdata=NULL); diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 0434d8bce..269ce7e3d 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -474,6 +474,7 @@ public: th->RenderStyle = STYLE_Count; th->alpha = -1; th->health = 1; + th->FloatbobPhase = -1; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { @@ -631,6 +632,11 @@ public: Flag(th->flags, MTF_SECRET, key); break; + case NAME_Floatbobphase: + CHECK_N(Zd | Zdt) + th->FloatbobPhase = CheckInt(key); + break; + case NAME_Renderstyle: { FName style = CheckString(key); diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 410a106f5..3fc2b2aed 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -342,17 +342,19 @@ VideoModes[] = { 1680, 1050 }, // 16:10 { 1920, 1080 }, { 1920, 1200 }, + { 2048, 1152 }, // 16:9, iMac Retina 4K 21.5", HiDPI off { 2048, 1536 }, - { 2304, 1440 }, + { 2304, 1440 }, // 16:10, MacBook Retina 12" { 2560, 1440 }, { 2560, 1600 }, { 2560, 2048 }, - { 2880, 1800 }, + { 2880, 1800 }, // 16:10, MacBook Pro Retina 15" { 3200, 1800 }, { 3840, 2160 }, { 3840, 2400 }, { 4096, 2160 }, - { 5120, 2880 } + { 4096, 2304 }, // 16:9, iMac Retina 4K 21.5" + { 5120, 2880 } // 16:9, iMac Retina 5K 27" }; diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index bf0ac910f..545d009d9 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -1087,7 +1087,8 @@ void R_Subsector (subsector_t *sub) basecolormap = frontsector->ColorMap; } - skybox = frontsector->CeilingSkyBox != NULL ? frontsector->CeilingSkyBox : level.DefaultSkybox; + skybox = frontsector->GetSkyBox(sector_t::ceiling); + ceilingplane = frontsector->ceilingplane.PointOnSide(viewx, viewy, viewz) > 0 || frontsector->GetTexture(sector_t::ceiling) == skyflatnum || (skybox != NULL && skybox->bAlways) || @@ -1127,7 +1128,7 @@ void R_Subsector (subsector_t *sub) // killough 3/7/98: Add (x,y) offsets to flats, add deep water check // killough 3/16/98: add floorlightlevel // killough 10/98: add support for skies transferred from sidedefs - skybox = frontsector->FloorSkyBox != NULL ? frontsector->FloorSkyBox : level.DefaultSkybox; + skybox = frontsector->GetSkyBox(sector_t::floor); floorplane = frontsector->floorplane.PointOnSide(viewx, viewy, viewz) > 0 || // killough 3/7/98 frontsector->GetTexture(sector_t::floor) == skyflatnum || (skybox != NULL && skybox->bAlways) || diff --git a/src/r_defs.h b/src/r_defs.h index afda92089..a93622b41 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -340,6 +340,8 @@ enum SECF_UNDERWATERMASK = 32+64, SECF_DRAWN = 128, // sector has been drawn at least once SECF_HIDDEN = 256, // Do not draw on textured automap + SECF_NOFLOORSKYBOX = 512, // force use of regular sky + SECF_NOCEILINGSKYBOX = 1024, // force use of regular sky }; enum @@ -451,6 +453,8 @@ struct sector_t DInterpolation *SetInterpolation(int position, bool attach); void StopInterpolation(int position); + ASkyViewpoint *GetSkyBox(int which); + enum { floor, diff --git a/src/r_main.cpp b/src/r_main.cpp index 5da0b8992..b136d9cc6 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -254,7 +254,7 @@ void R_InitTextureMapping () void R_SetVisibility (float vis) { // Allow negative visibilities, just for novelty's sake - //vis = clamp (vis, -204.7f, 204.7f); + vis = clamp (vis, -204.7f, 204.7f); // (205 and larger do not work in 5:4 aspect ratio) CurrentVisibility = vis; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index f6b24a70c..6b554aa3a 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2375,6 +2375,16 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } } + FName *aliasp = MusicAliases.CheckKey(musicname); + if (aliasp != NULL) + { + if (*aliasp == NAME_None) + { + return true; // flagged to be ignored + } + musicname = aliasp->GetChars(); + } + if (!mus_playing.name.IsEmpty() && mus_playing.handle != NULL && stricmp (mus_playing.name, musicname) == 0 && @@ -2413,16 +2423,8 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) int length = 0; int device = MDEV_DEFAULT; MusInfo *handle = NULL; - FName musicasname = musicname; - FName *aliasp = MusicAliases.CheckKey(musicasname); - if (aliasp != NULL) - { - musicname = (musicasname = *aliasp).GetChars(); - if (musicasname == NAME_None) return true; - } - - int *devp = MidiDevices.CheckKey(musicasname); + int *devp = MidiDevices.CheckKey(musicname); if (devp != NULL) device = *devp; // Strip off any leading file:// component. diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index ec2843c79..dbc56cc56 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -713,11 +713,11 @@ BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode) if ( hRT ) { - // Must wait process to terminate to guarantee that it has exited... - WaitForSingleObject(hProcess, INFINITE); - + // Must wait for process to terminate to guarantee that it has exited... + DWORD res = WaitForSingleObject(hProcess, 1000); CloseHandle(hRT); - bSuccess = TRUE; + bSuccess = (res == WAIT_OBJECT_0); + dwErr = WAIT_TIMEOUT; } if ( !bSuccess ) diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 662e426e6..5c980a793 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -1,66 +1,66 @@ -/* -** oalsound.cpp -** System interface for sound; uses OpenAL -** -**--------------------------------------------------------------------------- -** Copyright 2008-2010 Chris Robinson -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. -**--------------------------------------------------------------------------- -** -*/ - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#define USE_WINDOWS_DWORD -#endif - -#include "except.h" -#include "doomstat.h" -#include "templates.h" -#include "oalsound.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "i_system.h" -#include "v_text.h" -#include "gi.h" -#include "actor.h" -#include "r_state.h" -#include "w_wad.h" -#include "i_music.h" -#include "i_musicinterns.h" -#include "tempfiles.h" - - -CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - - -bool IsOpenALPresent() -{ +/* +** oalsound.cpp +** System interface for sound; uses OpenAL +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Chris Robinson +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define USE_WINDOWS_DWORD +#endif + +#include "except.h" +#include "doomstat.h" +#include "templates.h" +#include "oalsound.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "i_system.h" +#include "v_text.h" +#include "gi.h" +#include "actor.h" +#include "r_state.h" +#include "w_wad.h" +#include "i_music.h" +#include "i_musicinterns.h" +#include "tempfiles.h" + + +CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + + +bool IsOpenALPresent() +{ #ifdef NO_OPENAL return false; #elif !defined _WIN32 @@ -87,1889 +87,1889 @@ bool IsOpenALPresent() } return cached_result; #endif -} - -void I_BuildALDeviceList(FOptionValues *opt) -{ - opt->mValues.Resize(1); - opt->mValues[0].TextValue = "Default"; - opt->mValues[0].Text = "Default"; - -#ifndef NO_OPENAL - if (IsOpenALPresent()) - { - const ALCchar *names = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? - alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : - alcGetString(NULL, ALC_DEVICE_SPECIFIER)); - if (!names) - Printf("Failed to get device list: %s\n", alcGetString(NULL, alcGetError(NULL))); - else while (*names) - { - unsigned int i = opt->mValues.Reserve(1); - opt->mValues[i].TextValue = names; - opt->mValues[i].Text = names; - - names += strlen(names) + 1; - } - } -#endif -} - -#ifndef NO_OPENAL - - -EXTERN_CVAR (Int, snd_channels) -EXTERN_CVAR (Int, snd_samplerate) -EXTERN_CVAR (Bool, snd_waterreverb) -EXTERN_CVAR (Bool, snd_pitched) - - -#define MAKE_PTRID(x) ((void*)(uintptr_t)(x)) -#define GET_PTRID(x) ((uint32)(uintptr_t)(x)) - - -static ALenum checkALError(const char *fn, unsigned int ln) -{ - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); - } - return err; -} -#define getALError() checkALError(__FILE__, __LINE__) - -static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) -{ - ALCenum err = alcGetError(device); - if(err != ALC_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); - } - return err; -} -#define getALCError(d) checkALCError((d), __FILE__, __LINE__) - - -// Fallback methods for when AL_SOFT_deferred_updates isn't available. In most -// cases these don't actually do anything, except on some Creative drivers -// where they act as appropriate fallbacks. -static ALvoid AL_APIENTRY _wrap_DeferUpdatesSOFT(void) -{ - alcSuspendContext(alcGetCurrentContext()); -} - -static ALvoid AL_APIENTRY _wrap_ProcessUpdatesSOFT(void) -{ - alcProcessContext(alcGetCurrentContext()); -} - - -class OpenALSoundStream : public SoundStream -{ - OpenALSoundRenderer *Renderer; - - SoundStreamCallback Callback; - void *UserData; - - TArray Data; - - ALsizei SampleRate; - ALenum Format; - ALsizei FrameSize; - - static const int BufferCount = 4; - ALuint Buffers[BufferCount]; - ALuint Source; - - bool Playing; - bool Looping; - ALfloat Volume; - - - FileReader *Reader; - SoundDecoder *Decoder; - static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) - { - OpenALSoundStream *self = static_cast(_sstream); - if(length < 0) return false; - - size_t got = self->Decoder->read((char*)ptr, length); - if(got < (unsigned int)length) - { - if(!self->Looping || !self->Decoder->seek(0)) - return false; - got += self->Decoder->read((char*)ptr+got, length-got); - } - - return (got == (unsigned int)length); - } - - - bool SetupSource() - { - /* Get a source, killing the farthest, lowest-priority sound if needed */ - if(Renderer->FreeSfx.Size() == 0) - { - FSoundChan *lowest = Renderer->FindLowestChannel(); - if(lowest) Renderer->StopChannel(lowest); - - if(Renderer->FreeSfx.Size() == 0) - return false; - } - Renderer->FreeSfx.Pop(Source); - - /* Set the default properties for localized playback */ - alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); - alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); - alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); - alSourcef(Source, AL_MAX_GAIN, 1.f); - alSourcef(Source, AL_GAIN, 1.f); - alSourcef(Source, AL_PITCH, 1.f); - alSourcef(Source, AL_ROLLOFF_FACTOR, 0.f); - alSourcef(Source, AL_SEC_OFFSET, 0.f); - alSourcei(Source, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(Source, AL_LOOPING, AL_FALSE); - if(Renderer->EnvSlot) - { - alSourcef(Source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - alSourcef(Source, AL_AIR_ABSORPTION_FACTOR, 0.f); - alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(Source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - - alGenBuffers(BufferCount, Buffers); - return (getALError() == AL_NO_ERROR); - } - -public: - OpenALSoundStream(OpenALSoundRenderer *renderer) - : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) - { - Renderer->Streams.Push(this); - memset(Buffers, 0, sizeof(Buffers)); - } - - virtual ~OpenALSoundStream() - { - if(Source) - { - alSourceRewind(Source); - alSourcei(Source, AL_BUFFER, 0); - - Renderer->FreeSfx.Push(Source); - Source = 0; - } - - if(Buffers[0]) - { - alDeleteBuffers(BufferCount, &Buffers[0]); - memset(Buffers, 0, sizeof(Buffers)); - } - getALError(); - - Renderer->Streams.Delete(Renderer->Streams.Find(this)); - Renderer = NULL; - - delete Decoder; - delete Reader; - } - - - virtual bool Play(bool loop, float vol) - { - SetVolume(vol); - - if(Playing) - return true; - - /* Clear the buffer queue, then fill and queue each buffer */ - alSourcei(Source, AL_BUFFER, 0); - for(int i = 0;i < BufferCount;i++) - { - if(!Callback(this, &Data[0], Data.Size(), UserData)) - { - if(i == 0) - return false; - break; - } - - alBufferData(Buffers[i], Format, &Data[0], Data.Size(), SampleRate); - alSourceQueueBuffers(Source, 1, &Buffers[i]); - } - if(getALError() != AL_NO_ERROR) - return false; - - alSourcePlay(Source); - Playing = (getALError()==AL_NO_ERROR); - - return Playing; - } - - virtual void Stop() - { - if(!Playing) - return; - - alSourceStop(Source); - alSourcei(Source, AL_BUFFER, 0); - getALError(); - - Playing = false; - } - - virtual void SetVolume(float vol) - { - Volume = vol; - UpdateVolume(); - } - - void UpdateVolume() - { - alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); - getALError(); - } - - virtual bool SetPaused(bool pause) - { - if(pause) - alSourcePause(Source); - else - alSourcePlay(Source); - return (getALError()==AL_NO_ERROR); - } - - virtual bool SetPosition(unsigned int ms_pos) - { - if(!Decoder->seek(ms_pos)) - return false; - - if(!Playing) - return true; - // Stop the source so that all buffers become processed, then call - // IsEnded() to refill and restart the source queue with the new - // position. - alSourceStop(Source); - getALError(); - return !IsEnded(); - } - - virtual unsigned int GetPosition() - { - ALint offset, queued, state; - alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - alGetSourcei(Source, AL_SOURCE_STATE, &state); - if(getALError() != AL_NO_ERROR) - return 0; - - size_t pos = Decoder->getSampleOffset(); - if(state != AL_STOPPED) - { - size_t rem = queued*(Data.Size()/FrameSize) - offset; - if(pos > rem) pos -= rem; - else pos = 0; - } - return (unsigned int)(pos * 1000.0 / SampleRate); - } - - virtual bool IsEnded() - { - if(!Playing) - return true; - - ALint state, processed; - alGetSourcei(Source, AL_SOURCE_STATE, &state); - alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - - Playing = (getALError()==AL_NO_ERROR); - if(!Playing) - return true; - - // For each processed buffer in the queue... - while(processed > 0) - { - ALuint bufid; - - // Unqueue the oldest buffer, fill it with more data, and queue it - // on the end - alSourceUnqueueBuffers(Source, 1, &bufid); - processed--; - - if(Callback(this, &Data[0], Data.Size(), UserData)) - { - alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); - alSourceQueueBuffers(Source, 1, &bufid); - } - } - - // If the source is not playing or paused, and there are buffers queued, - // then there was an underrun. Restart the source. - Playing = (getALError()==AL_NO_ERROR); - if(Playing && state != AL_PLAYING && state != AL_PAUSED) - { - ALint queued = 0; - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - - Playing = (getALError() == AL_NO_ERROR) && (queued > 0); - if(Playing) - { - alSourcePlay(Source); - Playing = (getALError()==AL_NO_ERROR); - } - } - - return !Playing; - } - - FString GetStats() - { - FString stats; - size_t pos, len; - ALfloat volume; - ALint offset; - ALint processed; - ALint queued; - ALint state; - ALenum err; - - alGetSourcef(Source, AL_GAIN, &volume); - alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); - alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - alGetSourcei(Source, AL_SOURCE_STATE, &state); - if((err=alGetError()) != AL_NO_ERROR) - { - stats = "Error getting stats: "; - stats += alGetString(err); - return stats; - } - - stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : - (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; - - pos = Decoder->getSampleOffset(); - len = Decoder->getSampleLength(); - if(state == AL_STOPPED) - offset = BufferCount * (Data.Size()/FrameSize); - else - { - size_t rem = queued*(Data.Size()/FrameSize) - offset; - if(pos > rem) pos -= rem; - else if(len > 0) pos += len - rem; - else pos = 0; - } - pos = (size_t)(pos * 1000.0 / SampleRate); - len = (size_t)(len * 1000.0 / SampleRate); - stats.AppendFormat(",%3u%% buffered", 100 - 100*offset/(BufferCount*(Data.Size()/FrameSize))); - stats.AppendFormat(", %zu.%03zu", pos/1000, pos%1000); - if(len > 0) - stats.AppendFormat(" / %zu.%03zu", len/1000, len%1000); - if(state == AL_PAUSED) - stats += ", paused"; - if(state == AL_PLAYING) - stats += ", playing"; - stats.AppendFormat(", %uHz", SampleRate); - if(!Playing) - stats += " XX"; - return stats; - } - - bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) - { - if(!SetupSource()) - return false; - - Callback = callback; - UserData = userdata; - SampleRate = samplerate; - - Format = AL_NONE; - if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */ - { - if((flags&Mono)) Format = AL_FORMAT_MONO8; - else Format = AL_FORMAT_STEREO8; - } - else if((flags&Float)) - { - if(alIsExtensionPresent("AL_EXT_FLOAT32")) - { - if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32; - else Format = AL_FORMAT_STEREO_FLOAT32; - } - } - else if((flags&Bits32)) - { - } - else - { - if((flags&Mono)) Format = AL_FORMAT_MONO16; - else Format = AL_FORMAT_STEREO16; - } - - if(Format == AL_NONE) - { - Printf("Unsupported format: 0x%x\n", flags); - return false; - } - - FrameSize = 1; - if((flags&Bits8)) - FrameSize *= 1; - else if((flags&(Bits32|Float))) - FrameSize *= 4; - else - FrameSize *= 2; - - if((flags&Mono)) - FrameSize *= 1; - else - FrameSize *= 2; - - buffbytes += FrameSize-1; - buffbytes -= buffbytes%FrameSize; - Data.Resize(buffbytes); - - return true; - } - - bool Init(FileReader *reader, bool loop) - { - if(!SetupSource()) - { - delete reader; - return false; - } - - if(Decoder) delete Decoder; - if(Reader) delete Reader; - Reader = reader; - Decoder = Renderer->CreateDecoder(Reader); - if(!Decoder) return false; - - Callback = DecoderCallback; - UserData = NULL; - Format = AL_NONE; - FrameSize = 1; - - ChannelConfig chans; - SampleType type; - int srate; - - Decoder->getInfo(&srate, &chans, &type); - if(chans == ChannelConfig_Mono) - { - if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; - if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; - FrameSize *= 1; - } - if(chans == ChannelConfig_Stereo) - { - if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; - if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; - FrameSize *= 2; - } - if(type == SampleType_UInt8) FrameSize *= 1; - if(type == SampleType_Int16) FrameSize *= 2; - - if(Format == AL_NONE) - { - Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), - GetSampleTypeName(type)); - return false; - } - SampleRate = srate; - Looping = loop; - - Data.Resize((size_t)(0.2 * SampleRate) * FrameSize); - - return true; - } -}; - - -extern ReverbContainer *ForcedEnvironment; - -#define AREA_SOUND_RADIUS (128.f) - -#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */ - -#define PITCH(pitch) (snd_pitched ? (pitch)/128.f : 1.f) - - -static float GetRolloff(const FRolloffInfo *rolloff, float distance) -{ - if(distance <= rolloff->MinDistance) - return 1.f; - // Logarithmic rolloff has no max distance where it goes silent. - if(rolloff->RolloffType == ROLLOFF_Log) - return rolloff->MinDistance / - (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); - if(distance >= rolloff->MaxDistance) - return 0.f; - - float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); - if(rolloff->RolloffType == ROLLOFF_Linear) - return volume; - - if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) - return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; - return (powf(10.f, volume) - 1.f) / 9.f; -} - -ALCdevice *OpenALSoundRenderer::InitDevice() -{ - ALCdevice *device = NULL; - if (IsOpenALPresent()) - { - if(strcmp(snd_aldevice, "Default") != 0) - { - device = alcOpenDevice(*snd_aldevice); - if(!device) - Printf(TEXTCOLOR_BLUE" Failed to open device " TEXTCOLOR_BOLD"%s" TEXTCOLOR_BLUE". Trying default.\n", *snd_aldevice); - } - - if(!device) - { - device = alcOpenDevice(NULL); - if(!device) - { - Printf(TEXTCOLOR_RED" Could not open audio device\n"); - } - } - } - else - { - Printf(TEXTCOLOR_ORANGE"Failed to load openal32.dll\n"); - } - return device; -} - - -template -static void LoadALFunc(const char *name, T *x) -{ *x = reinterpret_cast(alGetProcAddress(name)); } - -#define LOAD_FUNC(x) (LoadALFunc(#x, &x)) -OpenALSoundRenderer::OpenALSoundRenderer() - : Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) -{ - EnvFilters[0] = EnvFilters[1] = 0; - - Printf("I_InitSound: Initializing OpenAL\n"); - - Device = InitDevice(); - if (Device == NULL) return; - - const ALCchar *current = NULL; - if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) - current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); - if(alcGetError(Device) != ALC_NO_ERROR || !current) - current = alcGetString(Device, ALC_DEVICE_SPECIFIER); - Printf(" Opened device " TEXTCOLOR_ORANGE"%s\n", current); - - ALCint major=0, minor=0; - alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); - DPrintf(" ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - DPrintf(" ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); - - TArray attribs; - if(*snd_samplerate > 0) - { - attribs.Push(ALC_FREQUENCY); - attribs.Push(*snd_samplerate); - } - // Make sure one source is capable of stereo output with the rest doing - // mono, without running out of voices - attribs.Push(ALC_MONO_SOURCES); - attribs.Push(MAX(*snd_channels, 2) - 1); - attribs.Push(ALC_STEREO_SOURCES); - attribs.Push(1); - // Other attribs..? - attribs.Push(0); - - Context = alcCreateContext(Device, &attribs[0]); - if(!Context || alcMakeContextCurrent(Context) == ALC_FALSE) - { - Printf(TEXTCOLOR_RED" Failed to setup context: %s\n", alcGetString(Device, alcGetError(Device))); - if(Context) - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - attribs.Clear(); - - DPrintf(" Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); - DPrintf(" Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); - DPrintf(" Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); - DPrintf(" Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); - - ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); - ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect");; - AL.EXT_source_distance_model = !!alIsExtensionPresent("AL_EXT_source_distance_model"); - AL.SOFT_deferred_updates = !!alIsExtensionPresent("AL_SOFT_deferred_updates"); - AL.SOFT_loop_points = !!alIsExtensionPresent("AL_SOFT_loop_points"); - - alDopplerFactor(0.5f); - alSpeedOfSound(343.3f * 96.0f); - alDistanceModel(AL_INVERSE_DISTANCE); - if(AL.EXT_source_distance_model) - alEnable(AL_SOURCE_DISTANCE_MODEL); - - if(AL.SOFT_deferred_updates) - { - LOAD_FUNC(alDeferUpdatesSOFT); - LOAD_FUNC(alProcessUpdatesSOFT); - } - else - { - alDeferUpdatesSOFT = _wrap_DeferUpdatesSOFT; - alProcessUpdatesSOFT = _wrap_ProcessUpdatesSOFT; - } - - ALenum err = getALError(); - if(err != AL_NO_ERROR) - { - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - - ALCint numMono=0, numStereo=0; - alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); - alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); - - Sources.Resize(MIN(MAX(*snd_channels, 2), numMono+numStereo)); - for(size_t i = 0;i < Sources.Size();i++) - { - alGenSources(1, &Sources[i]); - if(getALError() != AL_NO_ERROR) - { - Sources.Resize(i); - Sources.ShrinkToFit(); - break; - } - } - if(Sources.Size() == 0) - { - Printf(TEXTCOLOR_RED" Error: could not generate any sound sources!\n"); - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - FreeSfx = Sources; - DPrintf(" Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); - - WasInWater = false; - if(*snd_efx && ALC.EXT_EFX) - { - // EFX function pointers - LOAD_FUNC(alGenEffects); - LOAD_FUNC(alDeleteEffects); - LOAD_FUNC(alIsEffect); - LOAD_FUNC(alEffecti); - LOAD_FUNC(alEffectiv); - LOAD_FUNC(alEffectf); - LOAD_FUNC(alEffectfv); - LOAD_FUNC(alGetEffecti); - LOAD_FUNC(alGetEffectiv); - LOAD_FUNC(alGetEffectf); - LOAD_FUNC(alGetEffectfv); - - LOAD_FUNC(alGenFilters); - LOAD_FUNC(alDeleteFilters); - LOAD_FUNC(alIsFilter); - LOAD_FUNC(alFilteri); - LOAD_FUNC(alFilteriv); - LOAD_FUNC(alFilterf); - LOAD_FUNC(alFilterfv); - LOAD_FUNC(alGetFilteri); - LOAD_FUNC(alGetFilteriv); - LOAD_FUNC(alGetFilterf); - LOAD_FUNC(alGetFilterfv); - - LOAD_FUNC(alGenAuxiliaryEffectSlots); - LOAD_FUNC(alDeleteAuxiliaryEffectSlots); - LOAD_FUNC(alIsAuxiliaryEffectSlot); - LOAD_FUNC(alAuxiliaryEffectSloti); - LOAD_FUNC(alAuxiliaryEffectSlotiv); - LOAD_FUNC(alAuxiliaryEffectSlotf); - LOAD_FUNC(alAuxiliaryEffectSlotfv); - LOAD_FUNC(alGetAuxiliaryEffectSloti); - LOAD_FUNC(alGetAuxiliaryEffectSlotiv); - LOAD_FUNC(alGetAuxiliaryEffectSlotf); - LOAD_FUNC(alGetAuxiliaryEffectSlotfv); - if(getALError() == AL_NO_ERROR) - { - ALuint envReverb; - alGenEffects(1, &envReverb); - if(getALError() == AL_NO_ERROR) - { - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - if(alGetError() == AL_NO_ERROR) - DPrintf(" EAX Reverb found\n"); - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); - if(alGetError() == AL_NO_ERROR) - DPrintf(" Standard Reverb found\n"); - - alDeleteEffects(1, &envReverb); - getALError(); - } - - alGenAuxiliaryEffectSlots(1, &EnvSlot); - alGenFilters(2, EnvFilters); - if(getALError() == AL_NO_ERROR) - { - alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); - alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); - if(getALError() == AL_NO_ERROR) - DPrintf(" Lowpass found\n"); - else - { - alDeleteFilters(2, EnvFilters); - EnvFilters[0] = EnvFilters[1] = 0; - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - EnvSlot = 0; - getALError(); - } - } - else - { - alDeleteFilters(2, EnvFilters); - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - EnvFilters[0] = EnvFilters[1] = 0; - EnvSlot = 0; - getALError(); - } - } - } - - if(EnvSlot) - Printf(" EFX enabled\n"); -} -#undef LOAD_FUNC - -OpenALSoundRenderer::~OpenALSoundRenderer() -{ - if(!Device) - return; - - while(Streams.Size() > 0) - delete Streams[0]; - - alDeleteSources(Sources.Size(), &Sources[0]); - Sources.Clear(); - FreeSfx.Clear(); - SfxGroup.Clear(); - PausableSfx.Clear(); - ReverbSfx.Clear(); - - if(EnvEffects.CountUsed() > 0) - { - EffectMapIter iter(EnvEffects); - EffectMap::Pair *pair; - while(iter.NextPair(pair)) - alDeleteEffects(1, &(pair->Value)); - } - EnvEffects.Clear(); - - if(EnvSlot) - { - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - alDeleteFilters(2, EnvFilters); - } - EnvSlot = 0; - EnvFilters[0] = EnvFilters[1] = 0; - - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; -} - -void OpenALSoundRenderer::SetSfxVolume(float volume) -{ - SfxVolume = volume; - - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel != NULL) - { - ALuint source = GET_PTRID(schan->SysChannel); - volume = SfxVolume; - - alDeferUpdatesSOFT(); - alSourcef(source, AL_MAX_GAIN, volume); - alSourcef(source, AL_GAIN, volume * schan->Volume); - } - schan = schan->NextChan; - } - - alProcessUpdatesSOFT(); - - getALError(); -} - -void OpenALSoundRenderer::SetMusicVolume(float volume) -{ - MusicVolume = volume; - for(uint32 i = 0;i < Streams.Size();++i) - Streams[i]->UpdateVolume(); -} - -unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) -{ - if(sfx.data) - { - ALuint buffer = GET_PTRID(sfx.data); - if(alIsBuffer(buffer)) - { - ALint bits, channels, freq, size; - alGetBufferi(buffer, AL_BITS, &bits); - alGetBufferi(buffer, AL_CHANNELS, &channels); - alGetBufferi(buffer, AL_FREQUENCY, &freq); - alGetBufferi(buffer, AL_SIZE, &size); - if(getALError() == AL_NO_ERROR) - return (unsigned int)(size / (channels*bits/8) * 1000. / freq); - } - } - return 0; -} - -unsigned int OpenALSoundRenderer::GetSampleLength(SoundHandle sfx) -{ - if(sfx.data) - { - ALuint buffer = GET_PTRID(sfx.data); - ALint bits, channels, size; - alGetBufferi(buffer, AL_BITS, &bits); - alGetBufferi(buffer, AL_CHANNELS, &channels); - alGetBufferi(buffer, AL_SIZE, &size); - if(getALError() == AL_NO_ERROR) - return (ALsizei)(size / (channels * bits / 8)); - } - return 0; -} - -float OpenALSoundRenderer::GetOutputRate() -{ - ALCint rate = 44100; // Default, just in case - alcGetIntegerv(Device, ALC_FREQUENCY, 1, &rate); - return (float)rate; -} - - -SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) -{ - SoundHandle retval = { NULL }; - - if(length == 0) return retval; - - if(bits == -8) - { - // Simple signed->unsigned conversion - for(int i = 0;i < length;i++) - sfxdata[i] ^= 0x80; - bits = -bits; - } - - ALenum format = AL_NONE; - if(bits == 16) - { - if(channels == 1) format = AL_FORMAT_MONO16; - if(channels == 2) format = AL_FORMAT_STEREO16; - } - else if(bits == 8) - { - if(channels == 1) format = AL_FORMAT_MONO8; - if(channels == 2) format = AL_FORMAT_STEREO8; - } - - if(format == AL_NONE || frequency <= 0) - { - Printf("Unhandled format: %d bit, %d channel, %d hz\n", bits, channels, frequency); - return retval; - } - length -= length%(channels*bits/8); - - ALenum err; - ALuint buffer = 0; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, sfxdata, length, frequency); - if((err=getALError()) != AL_NO_ERROR) - { - Printf("Failed to buffer data: %s\n", alGetString(err)); - alDeleteBuffers(1, &buffer); - getALError(); - return retval; - } - - if((loopstart > 0 || loopend > 0) && AL.SOFT_loop_points) - { - if(loopstart < 0) - loopstart = 0; - if(loopend < loopstart) - loopend = length / (channels*bits/8); - - ALint loops[2] = { loopstart, loopend }; - DPrintf("Setting loop points %d -> %d\n", loops[0], loops[1]); - alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); - getALError(); - } - else if(loopstart > 0 || loopend > 0) - { - static bool warned = false; - if(!warned) - Printf("Loop points not supported!\n"); - warned = true; - } - - retval.data = MAKE_PTRID(buffer); - return retval; -} - -SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) -{ - SoundHandle retval = { NULL }; - MemoryReader reader((const char*)sfxdata, length); - ALenum format = AL_NONE; - ChannelConfig chans; - SampleType type; - int srate; - - SoundDecoder *decoder = CreateDecoder(&reader); - if(!decoder) return retval; - - decoder->getInfo(&srate, &chans, &type); - if(chans == ChannelConfig_Mono) - { - if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; - if(type == SampleType_Int16) format = AL_FORMAT_MONO16; - } - if(chans == ChannelConfig_Stereo) - { - if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; - if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; - } - - if(format == AL_NONE) - { - Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), - GetSampleTypeName(type)); - delete decoder; - return retval; - } - - TArray data = decoder->readAll(); - - ALuint buffer = 0; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, &data[0], data.Size(), srate); - - ALenum err; - if((err=getALError()) != AL_NO_ERROR) - { - Printf("Failed to buffer data: %s\n", alGetString(err)); - alDeleteBuffers(1, &buffer); - getALError(); - delete decoder; - return retval; - } - - retval.data = MAKE_PTRID(buffer); - delete decoder; - return retval; -} - -void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) -{ - if(!sfx.data) - return; - - ALuint buffer = GET_PTRID(sfx.data); - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel) - { - ALint bufID = 0; - alGetSourcei(GET_PTRID(schan->SysChannel), AL_BUFFER, &bufID); - if((ALuint)bufID == buffer) - { - FSoundChan *next = schan->NextChan; - StopChannel(schan); - schan = next; - continue; - } - } - schan = schan->NextChan; - } - - alDeleteBuffers(1, &buffer); - getALError(); -} - - -SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) -{ - OpenALSoundStream *stream = new OpenALSoundStream(this); - if (!stream->Init(callback, buffbytes, flags, samplerate, userdata)) - { - delete stream; - return NULL; - } - return stream; -} - -SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) -{ - OpenALSoundStream *stream = new OpenALSoundStream(this); - if (!stream->Init(reader, !!(flags&SoundStream::Loop))) - { - delete stream; - return NULL; - } - return stream; -} - -FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) -{ - if(FreeSfx.Size() == 0) - { - FSoundChan *lowest = FindLowestChannel(); - if(lowest) StopChannel(lowest); - - if(FreeSfx.Size() == 0) - return NULL; - } - - ALuint buffer = GET_PTRID(sfx.data); - ALuint source = FreeSfx.Last(); - alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); - alSource3f(source, AL_VELOCITY, 0.f, 0.f, 0.f); - alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); - alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); - - alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); - - alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); - alSourcef(source, AL_MAX_DISTANCE, 1000.f); - alSourcef(source, AL_ROLLOFF_FACTOR, 0.f); - alSourcef(source, AL_MAX_GAIN, SfxVolume); - alSourcef(source, AL_GAIN, SfxVolume*vol); - - if(EnvSlot) - { - if(!(chanflags&SNDF_NOREVERB)) - { - alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - } - if(WasInWater && !(chanflags&SNDF_NOREVERB)) - alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); - else - alSourcef(source, AL_PITCH, PITCH(pitch)); - - if(!reuse_chan) - alSourcef(source, AL_SEC_OFFSET, 0.f); - else - { - if((chanflags&SNDF_ABSTIME)) - alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); - else - { - // FIXME: set offset based on the current time and the StartTime - alSourcef(source, AL_SEC_OFFSET, 0.f); - } - } - if(getALError() != AL_NO_ERROR) - return NULL; - - alSourcei(source, AL_BUFFER, buffer); - if((chanflags&SNDF_NOPAUSE) || !SFXPaused) - alSourcePlay(source); - if(getALError() != AL_NO_ERROR) - { - alSourcei(source, AL_BUFFER, 0); - getALError(); - return NULL; - } - - if(!(chanflags&SNDF_NOREVERB)) - ReverbSfx.Push(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.Push(source); - SfxGroup.Push(source); - FreeSfx.Pop(); - - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); - else chan->SysChannel = MAKE_PTRID(source); - - chan->Rolloff.RolloffType = ROLLOFF_Log; - chan->Rolloff.RolloffFactor = 0.f; - chan->Rolloff.MinDistance = 1.f; - chan->DistanceScale = 1.f; - chan->DistanceSqr = 0.f; - chan->ManualRolloff = false; - - return chan; -} - -FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, - FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, - int channum, int chanflags, FISoundChannel *reuse_chan) -{ - float dist_sqr = (float)(pos - listener->position).LengthSquared(); - - if(FreeSfx.Size() == 0) - { - FSoundChan *lowest = FindLowestChannel(); - if(lowest) - { - if(lowest->Priority < priority || (lowest->Priority == priority && - lowest->DistanceSqr > dist_sqr)) - StopChannel(lowest); - } - if(FreeSfx.Size() == 0) - return NULL; - } - - bool manualRolloff = true; - ALuint buffer = GET_PTRID(sfx.data); - ALuint source = FreeSfx.Last(); - if(rolloff->RolloffType == ROLLOFF_Log) - { - if(AL.EXT_source_distance_model) - alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); - alSourcef(source, AL_ROLLOFF_FACTOR, rolloff->RolloffFactor); - manualRolloff = false; - } - else if(rolloff->RolloffType == ROLLOFF_Linear && AL.EXT_source_distance_model) - { - alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); - alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); - manualRolloff = false; - } - if(manualRolloff) - { - // How manual rolloff works: - // - // If a sound is using Custom or Doom style rolloff, or Linear style - // when AL_EXT_source_distance_model is not supported, we have to play - // around a bit to get appropriate distance attenation. What we do is - // calculate the attenuation that should be applied, then given an - // Inverse Distance rolloff model with OpenAL, reverse the calculation - // to get the distance needed for that much attenuation. The Inverse - // Distance calculation is: - // - // Gain = MinDist / (MinDist + RolloffFactor*(Distance - MinDist)) - // - // Thus, the reverse is: - // - // Distance = (MinDist/Gain - MinDist)/RolloffFactor + MinDist - // - // This can be simplified by using a MinDist and RolloffFactor of 1, - // which makes it: - // - // Distance = 1.0f/Gain; - // - // The source position is then set that many units away from the - // listener position, and OpenAL takes care of the rest. - if(AL.EXT_source_distance_model) - alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); - alSourcef(source, AL_MAX_DISTANCE, 100000.f); - alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); - - FVector3 dir = pos - listener->position; - if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) - { - float gain = GetRolloff(rolloff, sqrt(dist_sqr) * distscale); - dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); - } - if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); - float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - dir += listener->position; - - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - } - else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 dir = pos - listener->position; - - float mindist = rolloff->MinDistance/distscale; - FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); - float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - - dir += listener->position; - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - } - else - alSource3f(source, AL_POSITION, pos[0], pos[1], -pos[2]); - alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); - alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); - - alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); - - alSourcef(source, AL_MAX_GAIN, SfxVolume); - alSourcef(source, AL_GAIN, SfxVolume); - - if(EnvSlot) - { - if(!(chanflags&SNDF_NOREVERB)) - { - alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - } - if(WasInWater && !(chanflags&SNDF_NOREVERB)) - alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); - else - alSourcef(source, AL_PITCH, PITCH(pitch)); - - if(!reuse_chan) - alSourcef(source, AL_SEC_OFFSET, 0.f); - else - { - if((chanflags&SNDF_ABSTIME)) - alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); - else - { - // FIXME: set offset based on the current time and the StartTime - alSourcef(source, AL_SAMPLE_OFFSET, 0.f); - } - } - if(getALError() != AL_NO_ERROR) - return NULL; - - alSourcei(source, AL_BUFFER, buffer); - if((chanflags&SNDF_NOPAUSE) || !SFXPaused) - alSourcePlay(source); - if(getALError() != AL_NO_ERROR) - { - alSourcei(source, AL_BUFFER, 0); - getALError(); - return NULL; - } - - if(!(chanflags&SNDF_NOREVERB)) - ReverbSfx.Push(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.Push(source); - SfxGroup.Push(source); - FreeSfx.Pop(); - - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); - else chan->SysChannel = MAKE_PTRID(source); - - chan->Rolloff = *rolloff; - chan->DistanceScale = distscale; - chan->DistanceSqr = dist_sqr; - chan->ManualRolloff = manualRolloff; - - return chan; -} - -void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - alDeferUpdatesSOFT(); - - ALuint source = GET_PTRID(chan->SysChannel); - alSourcef(source, AL_GAIN, SfxVolume * volume); -} - -void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - ALuint source = GET_PTRID(chan->SysChannel); - // Release first, so it can be properly marked as evicted if it's being - // forcefully killed - S_ChannelEnded(chan); - - alSourceRewind(source); - alSourcei(source, AL_BUFFER, 0); - getALError(); - - uint32 i; - if((i=PausableSfx.Find(source)) < PausableSfx.Size()) - PausableSfx.Delete(i); - if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) - ReverbSfx.Delete(i); - - SfxGroup.Delete(SfxGroup.Find(source)); - FreeSfx.Push(source); -} - -unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return 0; - - ALint pos; - alGetSourcei(GET_PTRID(chan->SysChannel), AL_SAMPLE_OFFSET, &pos); - if(getALError() == AL_NO_ERROR) - return pos; - return 0; -} - - -void OpenALSoundRenderer::SetSfxPaused(bool paused, int slot) -{ - int oldslots = SFXPaused; - - if(paused) - { - SFXPaused |= 1 << slot; - if(oldslots == 0 && PausableSfx.Size() > 0) - { - alSourcePausev(PausableSfx.Size(), &PausableSfx[0]); - getALError(); - PurgeStoppedSources(); - } - } - else - { - SFXPaused &= ~(1 << slot); - if(SFXPaused == 0 && oldslots != 0 && PausableSfx.Size() > 0) - { - alSourcePlayv(PausableSfx.Size(), &PausableSfx[0]); - getALError(); - } - } -} - -void OpenALSoundRenderer::SetInactive(SoundRenderer::EInactiveState state) -{ - switch(state) - { - case SoundRenderer::INACTIVE_Active: - alListenerf(AL_GAIN, 1.0f); - break; - - /* FIXME: This doesn't stop anything. */ - case SoundRenderer::INACTIVE_Complete: - case SoundRenderer::INACTIVE_Mute: - alListenerf(AL_GAIN, 0.0f); - break; - } -} - -void OpenALSoundRenderer::Sync(bool sync) -{ - if(sync) - { - if(SfxGroup.Size() > 0) - { - alSourcePausev(SfxGroup.Size(), &SfxGroup[0]); - getALError(); - PurgeStoppedSources(); - } - } - else - { - // Might already be something to handle this; basically, get a vector - // of all values in SfxGroup that are not also in PausableSfx (when - // SFXPaused is non-0). - TArray toplay = SfxGroup; - if(SFXPaused) - { - uint32 i = 0; - while(i < toplay.Size()) - { - uint32 p = PausableSfx.Find(toplay[i]); - if(p < PausableSfx.Size()) - toplay.Delete(i); - else - i++; - } - } - if(toplay.Size() > 0) - { - alSourcePlayv(toplay.Size(), &toplay[0]); - getALError(); - } - } -} - -void OpenALSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - alDeferUpdatesSOFT(); - - FVector3 dir = pos - listener->position; - chan->DistanceSqr = (float)dir.LengthSquared(); - - if(chan->ManualRolloff) - { - if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) - { - float gain = GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); - dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); - } - if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); - float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - } - else if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - float mindist = chan->Rolloff.MinDistance / chan->DistanceScale; - FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); - float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - dir += listener->position; - - ALuint source = GET_PTRID(chan->SysChannel); - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); - getALError(); -} - -void OpenALSoundRenderer::UpdateListener(SoundListener *listener) -{ - if(!listener->valid) - return; - - alDeferUpdatesSOFT(); - - float angle = listener->angle; - ALfloat orient[6]; - // forward - orient[0] = cos(angle); - orient[1] = 0.f; - orient[2] = -sin(angle); - // up - orient[3] = 0.f; - orient[4] = 1.f; - orient[5] = 0.f; - - alListenerfv(AL_ORIENTATION, orient); - alListener3f(AL_POSITION, listener->position.X, - listener->position.Y, - -listener->position.Z); - alListener3f(AL_VELOCITY, listener->velocity.X, - listener->velocity.Y, - -listener->velocity.Z); - getALError(); - - const ReverbContainer *env = ForcedEnvironment; - if(!env) - { - env = listener->Environment; - if(!env) - env = DefaultEnvironments[0]; - } - if(env != PrevEnvironment || env->Modified) - { - PrevEnvironment = env; - DPrintf("Reverb Environment %s\n", env->Name); - - if(EnvSlot != 0) - LoadReverb(env); - - const_cast(env)->Modified = false; - } - - // NOTE: Moving into and out of water will undo pitch variations on sounds. - if(listener->underwater || env->SoftwareWater) - { - if(!WasInWater) - { - WasInWater = true; - - if(EnvSlot != 0 && *snd_waterreverb) - { - // Find the "Underwater" reverb environment - env = S_FindEnvironment(0x1600); - LoadReverb(env ? env : DefaultEnvironments[0]); - - alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.125f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - - // Apply the updated filters on the sources - for(uint32 i = 0;i < ReverbSfx.Size();++i) - { - alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } - - for(uint32 i = 0;i < ReverbSfx.Size();++i) - alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); - getALError(); - } - } - else if(WasInWater) - { - WasInWater = false; - - if(EnvSlot != 0) - { - LoadReverb(env); - - alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - for(uint32 i = 0;i < ReverbSfx.Size();++i) - { - alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } - - for(uint32 i = 0;i < ReverbSfx.Size();++i) - alSourcef(ReverbSfx[i], AL_PITCH, 1.f); - getALError(); - } -} - -void OpenALSoundRenderer::UpdateSounds() -{ - alProcessUpdatesSOFT(); - - if(ALC.EXT_disconnect) - { - ALCint connected = ALC_TRUE; - alcGetIntegerv(Device, ALC_CONNECTED, 1, &connected); - if(connected == ALC_FALSE) - { - Printf("Sound device disconnected; restarting...\n"); - static char snd_reset[] = "snd_reset"; - AddCommandString(snd_reset); - return; - } - } - - PurgeStoppedSources(); -} - -void OpenALSoundRenderer::UpdateMusic() -{ - // For some reason this isn't being called? - for(uint32 i = 0;i < Streams.Size();++i) - Streams[i]->IsEnded(); -} - -bool OpenALSoundRenderer::IsValid() -{ - return Device != NULL; -} - -void OpenALSoundRenderer::MarkStartTime(FISoundChannel *chan) -{ - // FIXME: Get current time (preferably from the audio clock, but the system - // time will have to do) - chan->StartTime.AsOne = 0; -} - -float OpenALSoundRenderer::GetAudibility(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return 0.f; - - ALuint source = GET_PTRID(chan->SysChannel); - ALfloat volume = 0.f; - - alGetSourcef(source, AL_GAIN, &volume); - getALError(); - - volume *= GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); - return volume; -} - - -void OpenALSoundRenderer::PrintStatus() -{ - Printf("Output device: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); - getALCError(Device); - - ALCint frequency, major, minor, mono, stereo; - alcGetIntegerv(Device, ALC_FREQUENCY, 1, &frequency); - alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); - alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &mono); - alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &stereo); - if(getALCError(Device) == AL_NO_ERROR) - { - Printf("Device sample rate: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL"hz\n", frequency); - Printf("ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - Printf("ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); - Printf("Available sources: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" (" TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" mono, " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" stereo)\n", mono+stereo, mono, stereo); - } - if(!alcIsExtensionPresent(Device, "ALC_EXT_EFX")) - Printf("EFX not found\n"); - else - { - ALCint sends; - alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &minor); - alcGetIntegerv(Device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); - if(getALCError(Device) == AL_NO_ERROR) - { - Printf("EFX Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - Printf("Auxiliary sends: " TEXTCOLOR_BLUE"%d\n", sends); - } - } - Printf("Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); - Printf("Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); - Printf("Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); - Printf("Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); - getALError(); -} - -FString OpenALSoundRenderer::GatherStats() -{ - ALCint updates = 1; - alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); - getALCError(Device); - - uint32 total = Sources.Size(); - uint32 used = SfxGroup.Size()+Streams.Size(); - uint32 unused = FreeSfx.Size(); - - FString out; - out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", - total, used, unused, 1000/updates); - return out; -} - -void OpenALSoundRenderer::PrintDriversList() -{ - const ALCchar *drivers = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? - alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : - alcGetString(NULL, ALC_DEVICE_SPECIFIER)); - if(drivers == NULL) - { - Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); - return; - } - - const ALCchar *current = NULL; - if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) - current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); - if(alcGetError(Device) != ALC_NO_ERROR || !current) - current = alcGetString(Device, ALC_DEVICE_SPECIFIER); - if(current == NULL) - { - Printf(TEXTCOLOR_YELLOW"Failed to retrieve device name: %s\n", alcGetString(Device, alcGetError(Device))); - return; - } - - Printf("%c%s%2d. %s\n", ' ', ((strcmp(snd_aldevice, "Default") == 0) ? TEXTCOLOR_BOLD : ""), 0, - "Default"); - for(int i = 1;*drivers;i++) - { - Printf("%c%s%2d. %s\n", ((strcmp(current, drivers)==0) ? '*' : ' '), - ((strcmp(*snd_aldevice, drivers)==0) ? TEXTCOLOR_BOLD : ""), i, - drivers); - drivers += strlen(drivers)+1; - } -} - -void OpenALSoundRenderer::PurgeStoppedSources() -{ - // Release channels that are stopped - for(uint32 i = 0;i < SfxGroup.Size();++i) - { - ALuint src = SfxGroup[i]; - ALint state = AL_INITIAL; - alGetSourcei(src, AL_SOURCE_STATE, &state); - if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) - continue; - - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) - { - StopChannel(schan); - break; - } - schan = schan->NextChan; - } - } - getALError(); -} - -void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) -{ - ALuint *envReverb = EnvEffects.CheckKey(env->ID); - bool doLoad = (env->Modified || !envReverb); - - if(!envReverb) - { - bool ok = false; - - envReverb = &EnvEffects.Insert(env->ID, 0); - alGenEffects(1, envReverb); - if(getALError() == AL_NO_ERROR) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - ok = (alGetError() == AL_NO_ERROR); - if(!ok) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); - ok = (alGetError() == AL_NO_ERROR); - } - if(!ok) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_NULL); - ok = (alGetError() == AL_NO_ERROR); - } - if(!ok) - { - alDeleteEffects(1, envReverb); - getALError(); - } - } - if(!ok) - { - *envReverb = 0; - doLoad = false; - } - } - - if(doLoad) - { - const REVERB_PROPERTIES &props = env->Properties; - ALint type = AL_EFFECT_NULL; - - alGetEffecti(*envReverb, AL_EFFECT_TYPE, &type); -#define mB2Gain(x) ((float)pow(10., (x)/2000.)) - if(type == AL_EFFECT_EAXREVERB) - { - ALfloat reflectpan[3] = { props.ReflectionsPan0, - props.ReflectionsPan1, - props.ReflectionsPan2 }; - ALfloat latepan[3] = { props.ReverbPan0, props.ReverbPan1, - props.ReverbPan2 }; -#undef SETPARAM -#define SETPARAM(e,t,v) alEffectf((e), AL_EAXREVERB_##t, clamp((v), AL_EAXREVERB_MIN_##t, AL_EAXREVERB_MAX_##t)) - SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); - SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); - SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); - SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); - SETPARAM(*envReverb, GAINLF, mB2Gain(props.RoomLF)); - SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); - SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); - SETPARAM(*envReverb, DECAY_LFRATIO, props.DecayLFRatio); - SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); - SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); - alEffectfv(*envReverb, AL_EAXREVERB_REFLECTIONS_PAN, reflectpan); - SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); - SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); - alEffectfv(*envReverb, AL_EAXREVERB_LATE_REVERB_PAN, latepan); - SETPARAM(*envReverb, ECHO_TIME, props.EchoTime); - SETPARAM(*envReverb, ECHO_DEPTH, props.EchoDepth); - SETPARAM(*envReverb, MODULATION_TIME, props.ModulationTime); - SETPARAM(*envReverb, MODULATION_DEPTH, props.ModulationDepth); - SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); - SETPARAM(*envReverb, HFREFERENCE, props.HFReference); - SETPARAM(*envReverb, LFREFERENCE, props.LFReference); - SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); - alEffecti(*envReverb, AL_EAXREVERB_DECAY_HFLIMIT, - (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); -#undef SETPARAM - } - else if(type == AL_EFFECT_REVERB) - { -#define SETPARAM(e,t,v) alEffectf((e), AL_REVERB_##t, clamp((v), AL_REVERB_MIN_##t, AL_REVERB_MAX_##t)) - SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); - SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); - SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); - SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); - SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); - SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); - SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); - SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); - SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); - SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); - SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); - SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); - alEffecti(*envReverb, AL_REVERB_DECAY_HFLIMIT, - (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); -#undef SETPARAM - } -#undef mB2Gain - } - - alAuxiliaryEffectSloti(EnvSlot, AL_EFFECTSLOT_EFFECT, *envReverb); - getALError(); -} - -FSoundChan *OpenALSoundRenderer::FindLowestChannel() -{ - FSoundChan *schan = Channels; - FSoundChan *lowest = NULL; - while(schan) - { - if(schan->SysChannel != NULL) - { - if(!lowest || schan->Priority < lowest->Priority || - (schan->Priority == lowest->Priority && - schan->DistanceSqr > lowest->DistanceSqr)) - lowest = schan; - } - schan = schan->NextChan; - } - return lowest; -} - -#endif // NO_OPENAL +} + +void I_BuildALDeviceList(FOptionValues *opt) +{ + opt->mValues.Resize(1); + opt->mValues[0].TextValue = "Default"; + opt->mValues[0].Text = "Default"; + +#ifndef NO_OPENAL + if (IsOpenALPresent()) + { + const ALCchar *names = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if (!names) + Printf("Failed to get device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + else while (*names) + { + unsigned int i = opt->mValues.Reserve(1); + opt->mValues[i].TextValue = names; + opt->mValues[i].Text = names; + + names += strlen(names) + 1; + } + } +#endif +} + +#ifndef NO_OPENAL + + +EXTERN_CVAR (Int, snd_channels) +EXTERN_CVAR (Int, snd_samplerate) +EXTERN_CVAR (Bool, snd_waterreverb) +EXTERN_CVAR (Bool, snd_pitched) + + +#define MAKE_PTRID(x) ((void*)(uintptr_t)(x)) +#define GET_PTRID(x) ((uint32)(uintptr_t)(x)) + + +static ALenum checkALError(const char *fn, unsigned int ln) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); + } + return err; +} +#define getALError() checkALError(__FILE__, __LINE__) + +static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) +{ + ALCenum err = alcGetError(device); + if(err != ALC_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); + } + return err; +} +#define getALCError(d) checkALCError((d), __FILE__, __LINE__) + + +// Fallback methods for when AL_SOFT_deferred_updates isn't available. In most +// cases these don't actually do anything, except on some Creative drivers +// where they act as appropriate fallbacks. +static ALvoid AL_APIENTRY _wrap_DeferUpdatesSOFT(void) +{ + alcSuspendContext(alcGetCurrentContext()); +} + +static ALvoid AL_APIENTRY _wrap_ProcessUpdatesSOFT(void) +{ + alcProcessContext(alcGetCurrentContext()); +} + + +class OpenALSoundStream : public SoundStream +{ + OpenALSoundRenderer *Renderer; + + SoundStreamCallback Callback; + void *UserData; + + TArray Data; + + ALsizei SampleRate; + ALenum Format; + ALsizei FrameSize; + + static const int BufferCount = 4; + ALuint Buffers[BufferCount]; + ALuint Source; + + bool Playing; + bool Looping; + ALfloat Volume; + + + FileReader *Reader; + SoundDecoder *Decoder; + static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) + { + OpenALSoundStream *self = static_cast(_sstream); + if(length < 0) return false; + + size_t got = self->Decoder->read((char*)ptr, length); + if(got < (unsigned int)length) + { + if(!self->Looping || !self->Decoder->seek(0)) + return false; + got += self->Decoder->read((char*)ptr+got, length-got); + } + + return (got == (unsigned int)length); + } + + + bool SetupSource() + { + /* Get a source, killing the farthest, lowest-priority sound if needed */ + if(Renderer->FreeSfx.Size() == 0) + { + FSoundChan *lowest = Renderer->FindLowestChannel(); + if(lowest) Renderer->StopChannel(lowest); + + if(Renderer->FreeSfx.Size() == 0) + return false; + } + Renderer->FreeSfx.Pop(Source); + + /* Set the default properties for localized playback */ + alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); + alSourcef(Source, AL_MAX_GAIN, 1.f); + alSourcef(Source, AL_GAIN, 1.f); + alSourcef(Source, AL_PITCH, 1.f); + alSourcef(Source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_SEC_OFFSET, 0.f); + alSourcei(Source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(Source, AL_LOOPING, AL_FALSE); + if(Renderer->EnvSlot) + { + alSourcef(Source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_AIR_ABSORPTION_FACTOR, 0.f); + alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(Source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + + alGenBuffers(BufferCount, Buffers); + return (getALError() == AL_NO_ERROR); + } + +public: + OpenALSoundStream(OpenALSoundRenderer *renderer) + : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) + { + Renderer->Streams.Push(this); + memset(Buffers, 0, sizeof(Buffers)); + } + + virtual ~OpenALSoundStream() + { + if(Source) + { + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); + + Renderer->FreeSfx.Push(Source); + Source = 0; + } + + if(Buffers[0]) + { + alDeleteBuffers(BufferCount, &Buffers[0]); + memset(Buffers, 0, sizeof(Buffers)); + } + getALError(); + + Renderer->Streams.Delete(Renderer->Streams.Find(this)); + Renderer = NULL; + + delete Decoder; + delete Reader; + } + + + virtual bool Play(bool loop, float vol) + { + SetVolume(vol); + + if(Playing) + return true; + + /* Clear the buffer queue, then fill and queue each buffer */ + alSourcei(Source, AL_BUFFER, 0); + for(int i = 0;i < BufferCount;i++) + { + if(!Callback(this, &Data[0], Data.Size(), UserData)) + { + if(i == 0) + return false; + break; + } + + alBufferData(Buffers[i], Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &Buffers[i]); + } + if(getALError() != AL_NO_ERROR) + return false; + + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); + + return Playing; + } + + virtual void Stop() + { + if(!Playing) + return; + + alSourceStop(Source); + alSourcei(Source, AL_BUFFER, 0); + getALError(); + + Playing = false; + } + + virtual void SetVolume(float vol) + { + Volume = vol; + UpdateVolume(); + } + + void UpdateVolume() + { + alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); + getALError(); + } + + virtual bool SetPaused(bool pause) + { + if(pause) + alSourcePause(Source); + else + alSourcePlay(Source); + return (getALError()==AL_NO_ERROR); + } + + virtual bool SetPosition(unsigned int ms_pos) + { + if(!Decoder->seek(ms_pos)) + return false; + + if(!Playing) + return true; + // Stop the source so that all buffers become processed, then call + // IsEnded() to refill and restart the source queue with the new + // position. + alSourceStop(Source); + getALError(); + return !IsEnded(); + } + + virtual unsigned int GetPosition() + { + ALint offset, queued, state; + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if(getALError() != AL_NO_ERROR) + return 0; + + size_t pos = Decoder->getSampleOffset(); + if(state != AL_STOPPED) + { + size_t rem = queued*(Data.Size()/FrameSize) - offset; + if(pos > rem) pos -= rem; + else pos = 0; + } + return (unsigned int)(pos * 1000.0 / SampleRate); + } + + virtual bool IsEnded() + { + if(!Playing) + return true; + + ALint state, processed; + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + + Playing = (getALError()==AL_NO_ERROR); + if(!Playing) + return true; + + // For each processed buffer in the queue... + while(processed > 0) + { + ALuint bufid; + + // Unqueue the oldest buffer, fill it with more data, and queue it + // on the end + alSourceUnqueueBuffers(Source, 1, &bufid); + processed--; + + if(Callback(this, &Data[0], Data.Size(), UserData)) + { + alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &bufid); + } + } + + // If the source is not playing or paused, and there are buffers queued, + // then there was an underrun. Restart the source. + Playing = (getALError()==AL_NO_ERROR); + if(Playing && state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued = 0; + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + + Playing = (getALError() == AL_NO_ERROR) && (queued > 0); + if(Playing) + { + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); + } + } + + return !Playing; + } + + FString GetStats() + { + FString stats; + size_t pos, len; + ALfloat volume; + ALint offset; + ALint processed; + ALint queued; + ALint state; + ALenum err; + + alGetSourcef(Source, AL_GAIN, &volume); + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if((err=alGetError()) != AL_NO_ERROR) + { + stats = "Error getting stats: "; + stats += alGetString(err); + return stats; + } + + stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : + (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; + + pos = Decoder->getSampleOffset(); + len = Decoder->getSampleLength(); + if(state == AL_STOPPED) + offset = BufferCount * (Data.Size()/FrameSize); + else + { + size_t rem = queued*(Data.Size()/FrameSize) - offset; + if(pos > rem) pos -= rem; + else if(len > 0) pos += len - rem; + else pos = 0; + } + pos = (size_t)(pos * 1000.0 / SampleRate); + len = (size_t)(len * 1000.0 / SampleRate); + stats.AppendFormat(",%3u%% buffered", 100 - 100*offset/(BufferCount*(Data.Size()/FrameSize))); + stats.AppendFormat(", %zu.%03zu", pos/1000, pos%1000); + if(len > 0) + stats.AppendFormat(" / %zu.%03zu", len/1000, len%1000); + if(state == AL_PAUSED) + stats += ", paused"; + if(state == AL_PLAYING) + stats += ", playing"; + stats.AppendFormat(", %uHz", SampleRate); + if(!Playing) + stats += " XX"; + return stats; + } + + bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) + { + if(!SetupSource()) + return false; + + Callback = callback; + UserData = userdata; + SampleRate = samplerate; + + Format = AL_NONE; + if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */ + { + if((flags&Mono)) Format = AL_FORMAT_MONO8; + else Format = AL_FORMAT_STEREO8; + } + else if((flags&Float)) + { + if(alIsExtensionPresent("AL_EXT_FLOAT32")) + { + if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32; + else Format = AL_FORMAT_STEREO_FLOAT32; + } + } + else if((flags&Bits32)) + { + } + else + { + if((flags&Mono)) Format = AL_FORMAT_MONO16; + else Format = AL_FORMAT_STEREO16; + } + + if(Format == AL_NONE) + { + Printf("Unsupported format: 0x%x\n", flags); + return false; + } + + FrameSize = 1; + if((flags&Bits8)) + FrameSize *= 1; + else if((flags&(Bits32|Float))) + FrameSize *= 4; + else + FrameSize *= 2; + + if((flags&Mono)) + FrameSize *= 1; + else + FrameSize *= 2; + + buffbytes += FrameSize-1; + buffbytes -= buffbytes%FrameSize; + Data.Resize(buffbytes); + + return true; + } + + bool Init(FileReader *reader, bool loop) + { + if(!SetupSource()) + { + delete reader; + return false; + } + + if(Decoder) delete Decoder; + if(Reader) delete Reader; + Reader = reader; + Decoder = Renderer->CreateDecoder(Reader); + if(!Decoder) return false; + + Callback = DecoderCallback; + UserData = NULL; + Format = AL_NONE; + FrameSize = 1; + + ChannelConfig chans; + SampleType type; + int srate; + + Decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; + FrameSize *= 1; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; + FrameSize *= 2; + } + if(type == SampleType_UInt8) FrameSize *= 1; + if(type == SampleType_Int16) FrameSize *= 2; + + if(Format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + return false; + } + SampleRate = srate; + Looping = loop; + + Data.Resize((size_t)(0.2 * SampleRate) * FrameSize); + + return true; + } +}; + + +extern ReverbContainer *ForcedEnvironment; + +#define AREA_SOUND_RADIUS (128.f) + +#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */ + +#define PITCH(pitch) (snd_pitched ? (pitch)/128.f : 1.f) + + +static float GetRolloff(const FRolloffInfo *rolloff, float distance) +{ + if(distance <= rolloff->MinDistance) + return 1.f; + // Logarithmic rolloff has no max distance where it goes silent. + if(rolloff->RolloffType == ROLLOFF_Log) + return rolloff->MinDistance / + (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); + if(distance >= rolloff->MaxDistance) + return 0.f; + + float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); + if(rolloff->RolloffType == ROLLOFF_Linear) + return volume; + + if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) + return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; + return (powf(10.f, volume) - 1.f) / 9.f; +} + +ALCdevice *OpenALSoundRenderer::InitDevice() +{ + ALCdevice *device = NULL; + if (IsOpenALPresent()) + { + if(strcmp(snd_aldevice, "Default") != 0) + { + device = alcOpenDevice(*snd_aldevice); + if(!device) + Printf(TEXTCOLOR_BLUE" Failed to open device " TEXTCOLOR_BOLD"%s" TEXTCOLOR_BLUE". Trying default.\n", *snd_aldevice); + } + + if(!device) + { + device = alcOpenDevice(NULL); + if(!device) + { + Printf(TEXTCOLOR_RED" Could not open audio device\n"); + } + } + } + else + { + Printf(TEXTCOLOR_ORANGE"Failed to load openal32.dll\n"); + } + return device; +} + + +template +static void LoadALFunc(const char *name, T *x) +{ *x = reinterpret_cast(alGetProcAddress(name)); } + +#define LOAD_FUNC(x) (LoadALFunc(#x, &x)) +OpenALSoundRenderer::OpenALSoundRenderer() + : Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) +{ + EnvFilters[0] = EnvFilters[1] = 0; + + Printf("I_InitSound: Initializing OpenAL\n"); + + Device = InitDevice(); + if (Device == NULL) return; + + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + Printf(" Opened device " TEXTCOLOR_ORANGE"%s\n", current); + + ALCint major=0, minor=0; + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + DPrintf(" ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + DPrintf(" ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + + TArray attribs; + if(*snd_samplerate > 0) + { + attribs.Push(ALC_FREQUENCY); + attribs.Push(*snd_samplerate); + } + // Make sure one source is capable of stereo output with the rest doing + // mono, without running out of voices + attribs.Push(ALC_MONO_SOURCES); + attribs.Push(MAX(*snd_channels, 2) - 1); + attribs.Push(ALC_STEREO_SOURCES); + attribs.Push(1); + // Other attribs..? + attribs.Push(0); + + Context = alcCreateContext(Device, &attribs[0]); + if(!Context || alcMakeContextCurrent(Context) == ALC_FALSE) + { + Printf(TEXTCOLOR_RED" Failed to setup context: %s\n", alcGetString(Device, alcGetError(Device))); + if(Context) + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + attribs.Clear(); + + DPrintf(" Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + DPrintf(" Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + DPrintf(" Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + DPrintf(" Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + + ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); + ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect");; + AL.EXT_source_distance_model = !!alIsExtensionPresent("AL_EXT_source_distance_model"); + AL.SOFT_deferred_updates = !!alIsExtensionPresent("AL_SOFT_deferred_updates"); + AL.SOFT_loop_points = !!alIsExtensionPresent("AL_SOFT_loop_points"); + + alDopplerFactor(0.5f); + alSpeedOfSound(343.3f * 96.0f); + alDistanceModel(AL_INVERSE_DISTANCE); + if(AL.EXT_source_distance_model) + alEnable(AL_SOURCE_DISTANCE_MODEL); + + if(AL.SOFT_deferred_updates) + { + LOAD_FUNC(alDeferUpdatesSOFT); + LOAD_FUNC(alProcessUpdatesSOFT); + } + else + { + alDeferUpdatesSOFT = _wrap_DeferUpdatesSOFT; + alProcessUpdatesSOFT = _wrap_ProcessUpdatesSOFT; + } + + ALenum err = getALError(); + if(err != AL_NO_ERROR) + { + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + + ALCint numMono=0, numStereo=0; + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); + + Sources.Resize(MIN(MAX(*snd_channels, 2), numMono+numStereo)); + for(size_t i = 0;i < Sources.Size();i++) + { + alGenSources(1, &Sources[i]); + if(getALError() != AL_NO_ERROR) + { + Sources.Resize(i); + Sources.ShrinkToFit(); + break; + } + } + if(Sources.Size() == 0) + { + Printf(TEXTCOLOR_RED" Error: could not generate any sound sources!\n"); + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + FreeSfx = Sources; + DPrintf(" Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); + + WasInWater = false; + if(*snd_efx && ALC.EXT_EFX) + { + // EFX function pointers + LOAD_FUNC(alGenEffects); + LOAD_FUNC(alDeleteEffects); + LOAD_FUNC(alIsEffect); + LOAD_FUNC(alEffecti); + LOAD_FUNC(alEffectiv); + LOAD_FUNC(alEffectf); + LOAD_FUNC(alEffectfv); + LOAD_FUNC(alGetEffecti); + LOAD_FUNC(alGetEffectiv); + LOAD_FUNC(alGetEffectf); + LOAD_FUNC(alGetEffectfv); + + LOAD_FUNC(alGenFilters); + LOAD_FUNC(alDeleteFilters); + LOAD_FUNC(alIsFilter); + LOAD_FUNC(alFilteri); + LOAD_FUNC(alFilteriv); + LOAD_FUNC(alFilterf); + LOAD_FUNC(alFilterfv); + LOAD_FUNC(alGetFilteri); + LOAD_FUNC(alGetFilteriv); + LOAD_FUNC(alGetFilterf); + LOAD_FUNC(alGetFilterfv); + + LOAD_FUNC(alGenAuxiliaryEffectSlots); + LOAD_FUNC(alDeleteAuxiliaryEffectSlots); + LOAD_FUNC(alIsAuxiliaryEffectSlot); + LOAD_FUNC(alAuxiliaryEffectSloti); + LOAD_FUNC(alAuxiliaryEffectSlotiv); + LOAD_FUNC(alAuxiliaryEffectSlotf); + LOAD_FUNC(alAuxiliaryEffectSlotfv); + LOAD_FUNC(alGetAuxiliaryEffectSloti); + LOAD_FUNC(alGetAuxiliaryEffectSlotiv); + LOAD_FUNC(alGetAuxiliaryEffectSlotf); + LOAD_FUNC(alGetAuxiliaryEffectSlotfv); + if(getALError() == AL_NO_ERROR) + { + ALuint envReverb; + alGenEffects(1, &envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(" EAX Reverb found\n"); + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(" Standard Reverb found\n"); + + alDeleteEffects(1, &envReverb); + getALError(); + } + + alGenAuxiliaryEffectSlots(1, &EnvSlot); + alGenFilters(2, EnvFilters); + if(getALError() == AL_NO_ERROR) + { + alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + if(getALError() == AL_NO_ERROR) + DPrintf(" Lowpass found\n"); + else + { + alDeleteFilters(2, EnvFilters); + EnvFilters[0] = EnvFilters[1] = 0; + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvSlot = 0; + getALError(); + } + } + else + { + alDeleteFilters(2, EnvFilters); + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvFilters[0] = EnvFilters[1] = 0; + EnvSlot = 0; + getALError(); + } + } + } + + if(EnvSlot) + Printf(" EFX enabled\n"); +} +#undef LOAD_FUNC + +OpenALSoundRenderer::~OpenALSoundRenderer() +{ + if(!Device) + return; + + while(Streams.Size() > 0) + delete Streams[0]; + + alDeleteSources(Sources.Size(), &Sources[0]); + Sources.Clear(); + FreeSfx.Clear(); + SfxGroup.Clear(); + PausableSfx.Clear(); + ReverbSfx.Clear(); + + if(EnvEffects.CountUsed() > 0) + { + EffectMapIter iter(EnvEffects); + EffectMap::Pair *pair; + while(iter.NextPair(pair)) + alDeleteEffects(1, &(pair->Value)); + } + EnvEffects.Clear(); + + if(EnvSlot) + { + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + alDeleteFilters(2, EnvFilters); + } + EnvSlot = 0; + EnvFilters[0] = EnvFilters[1] = 0; + + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; +} + +void OpenALSoundRenderer::SetSfxVolume(float volume) +{ + SfxVolume = volume; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL) + { + ALuint source = GET_PTRID(schan->SysChannel); + volume = SfxVolume; + + alDeferUpdatesSOFT(); + alSourcef(source, AL_MAX_GAIN, volume); + alSourcef(source, AL_GAIN, volume * schan->Volume); + } + schan = schan->NextChan; + } + + alProcessUpdatesSOFT(); + + getALError(); +} + +void OpenALSoundRenderer::SetMusicVolume(float volume) +{ + MusicVolume = volume; + for(uint32 i = 0;i < Streams.Size();++i) + Streams[i]->UpdateVolume(); +} + +unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) +{ + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + if(alIsBuffer(buffer)) + { + ALint bits, channels, freq, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_FREQUENCY, &freq); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (unsigned int)(size / (channels*bits/8) * 1000. / freq); + } + } + return 0; +} + +unsigned int OpenALSoundRenderer::GetSampleLength(SoundHandle sfx) +{ + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + ALint bits, channels, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (ALsizei)(size / (channels * bits / 8)); + } + return 0; +} + +float OpenALSoundRenderer::GetOutputRate() +{ + ALCint rate = 44100; // Default, just in case + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &rate); + return (float)rate; +} + + +SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) +{ + SoundHandle retval = { NULL }; + + if(length == 0) return retval; + + if(bits == -8) + { + // Simple signed->unsigned conversion + for(int i = 0;i < length;i++) + sfxdata[i] ^= 0x80; + bits = -bits; + } + + ALenum format = AL_NONE; + if(bits == 16) + { + if(channels == 1) format = AL_FORMAT_MONO16; + if(channels == 2) format = AL_FORMAT_STEREO16; + } + else if(bits == 8) + { + if(channels == 1) format = AL_FORMAT_MONO8; + if(channels == 2) format = AL_FORMAT_STEREO8; + } + + if(format == AL_NONE || frequency <= 0) + { + Printf("Unhandled format: %d bit, %d channel, %d hz\n", bits, channels, frequency); + return retval; + } + length -= length%(channels*bits/8); + + ALenum err; + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, sfxdata, length, frequency); + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + return retval; + } + + if((loopstart > 0 || loopend > 0) && AL.SOFT_loop_points) + { + if(loopstart < 0) + loopstart = 0; + if(loopend < loopstart) + loopend = length / (channels*bits/8); + + ALint loops[2] = { loopstart, loopend }; + DPrintf("Setting loop points %d -> %d\n", loops[0], loops[1]); + alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); + getALError(); + } + else if(loopstart > 0 || loopend > 0) + { + static bool warned = false; + if(!warned) + Printf("Loop points not supported!\n"); + warned = true; + } + + retval.data = MAKE_PTRID(buffer); + return retval; +} + +SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) +{ + SoundHandle retval = { NULL }; + MemoryReader reader((const char*)sfxdata, length); + ALenum format = AL_NONE; + ChannelConfig chans; + SampleType type; + int srate; + + SoundDecoder *decoder = CreateDecoder(&reader); + if(!decoder) return retval; + + decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) format = AL_FORMAT_MONO16; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; + } + + if(format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + delete decoder; + return retval; + } + + TArray data = decoder->readAll(); + + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, &data[0], data.Size(), srate); + + ALenum err; + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + delete decoder; + return retval; + } + + retval.data = MAKE_PTRID(buffer); + delete decoder; + return retval; +} + +void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) +{ + if(!sfx.data) + return; + + ALuint buffer = GET_PTRID(sfx.data); + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel) + { + ALint bufID = 0; + alGetSourcei(GET_PTRID(schan->SysChannel), AL_BUFFER, &bufID); + if((ALuint)bufID == buffer) + { + FSoundChan *next = schan->NextChan; + StopChannel(schan); + schan = next; + continue; + } + } + schan = schan->NextChan; + } + + alDeleteBuffers(1, &buffer); + getALError(); +} + + +SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) +{ + OpenALSoundStream *stream = new OpenALSoundStream(this); + if (!stream->Init(callback, buffbytes, flags, samplerate, userdata)) + { + delete stream; + return NULL; + } + return stream; +} + +SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) +{ + OpenALSoundStream *stream = new OpenALSoundStream(this); + if (!stream->Init(reader, !!(flags&SoundStream::Loop))) + { + delete stream; + return NULL; + } + return stream; +} + +FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) +{ + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) StopChannel(lowest); + + if(FreeSfx.Size() == 0) + return NULL; + } + + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); + alSource3f(source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 1000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume*vol); + + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); + + if(!reuse_chan) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + // FIXME: set offset based on the current time and the StartTime + alSourcef(source, AL_SEC_OFFSET, 0.f); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; + + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } + + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); + + chan->Rolloff.RolloffType = ROLLOFF_Log; + chan->Rolloff.RolloffFactor = 0.f; + chan->Rolloff.MinDistance = 1.f; + chan->DistanceScale = 1.f; + chan->DistanceSqr = 0.f; + chan->ManualRolloff = false; + + return chan; +} + +FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, + FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, + int channum, int chanflags, FISoundChannel *reuse_chan) +{ + float dist_sqr = (float)(pos - listener->position).LengthSquared(); + + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) + { + if(lowest->Priority < priority || (lowest->Priority == priority && + lowest->DistanceSqr > dist_sqr)) + StopChannel(lowest); + } + if(FreeSfx.Size() == 0) + return NULL; + } + + bool manualRolloff = true; + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + if(rolloff->RolloffType == ROLLOFF_Log) + { + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, rolloff->RolloffFactor); + manualRolloff = false; + } + else if(rolloff->RolloffType == ROLLOFF_Linear && AL.EXT_source_distance_model) + { + alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + manualRolloff = false; + } + if(manualRolloff) + { + // How manual rolloff works: + // + // If a sound is using Custom or Doom style rolloff, or Linear style + // when AL_EXT_source_distance_model is not supported, we have to play + // around a bit to get appropriate distance attenation. What we do is + // calculate the attenuation that should be applied, then given an + // Inverse Distance rolloff model with OpenAL, reverse the calculation + // to get the distance needed for that much attenuation. The Inverse + // Distance calculation is: + // + // Gain = MinDist / (MinDist + RolloffFactor*(Distance - MinDist)) + // + // Thus, the reverse is: + // + // Distance = (MinDist/Gain - MinDist)/RolloffFactor + MinDist + // + // This can be simplified by using a MinDist and RolloffFactor of 1, + // which makes it: + // + // Distance = 1.0f/Gain; + // + // The source position is then set that many units away from the + // listener position, and OpenAL takes care of the rest. + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 100000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + + FVector3 dir = pos - listener->position; + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(rolloff, sqrt(dist_sqr) * distscale); + dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; + + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 dir = pos - listener->position; + + float mindist = rolloff->MinDistance/distscale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + + dir += listener->position; + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + else + alSource3f(source, AL_POSITION, pos[0], pos[1], -pos[2]); + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume*vol); + + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); + + if(!reuse_chan) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + // FIXME: set offset based on the current time and the StartTime + alSourcef(source, AL_SAMPLE_OFFSET, 0.f); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; + + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } + + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); + + chan->Rolloff = *rolloff; + chan->DistanceScale = distscale; + chan->DistanceSqr = dist_sqr; + chan->ManualRolloff = manualRolloff; + + return chan; +} + +void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + alDeferUpdatesSOFT(); + + ALuint source = GET_PTRID(chan->SysChannel); + alSourcef(source, AL_GAIN, SfxVolume * volume); +} + +void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + ALuint source = GET_PTRID(chan->SysChannel); + // Release first, so it can be properly marked as evicted if it's being + // forcefully killed + S_ChannelEnded(chan); + + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + getALError(); + + uint32 i; + if((i=PausableSfx.Find(source)) < PausableSfx.Size()) + PausableSfx.Delete(i); + if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) + ReverbSfx.Delete(i); + + SfxGroup.Delete(SfxGroup.Find(source)); + FreeSfx.Push(source); +} + +unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return 0; + + ALint pos; + alGetSourcei(GET_PTRID(chan->SysChannel), AL_SAMPLE_OFFSET, &pos); + if(getALError() == AL_NO_ERROR) + return pos; + return 0; +} + + +void OpenALSoundRenderer::SetSfxPaused(bool paused, int slot) +{ + int oldslots = SFXPaused; + + if(paused) + { + SFXPaused |= 1 << slot; + if(oldslots == 0 && PausableSfx.Size() > 0) + { + alSourcePausev(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + SFXPaused &= ~(1 << slot); + if(SFXPaused == 0 && oldslots != 0 && PausableSfx.Size() > 0) + { + alSourcePlayv(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + } + } +} + +void OpenALSoundRenderer::SetInactive(SoundRenderer::EInactiveState state) +{ + switch(state) + { + case SoundRenderer::INACTIVE_Active: + alListenerf(AL_GAIN, 1.0f); + break; + + /* FIXME: This doesn't stop anything. */ + case SoundRenderer::INACTIVE_Complete: + case SoundRenderer::INACTIVE_Mute: + alListenerf(AL_GAIN, 0.0f); + break; + } +} + +void OpenALSoundRenderer::Sync(bool sync) +{ + if(sync) + { + if(SfxGroup.Size() > 0) + { + alSourcePausev(SfxGroup.Size(), &SfxGroup[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + // Might already be something to handle this; basically, get a vector + // of all values in SfxGroup that are not also in PausableSfx (when + // SFXPaused is non-0). + TArray toplay = SfxGroup; + if(SFXPaused) + { + uint32 i = 0; + while(i < toplay.Size()) + { + uint32 p = PausableSfx.Find(toplay[i]); + if(p < PausableSfx.Size()) + toplay.Delete(i); + else + i++; + } + } + if(toplay.Size() > 0) + { + alSourcePlayv(toplay.Size(), &toplay[0]); + getALError(); + } + } +} + +void OpenALSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + alDeferUpdatesSOFT(); + + FVector3 dir = pos - listener->position; + chan->DistanceSqr = (float)dir.LengthSquared(); + + if(chan->ManualRolloff) + { + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); + dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + } + else if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + float mindist = chan->Rolloff.MinDistance / chan->DistanceScale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; + + ALuint source = GET_PTRID(chan->SysChannel); + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + getALError(); +} + +void OpenALSoundRenderer::UpdateListener(SoundListener *listener) +{ + if(!listener->valid) + return; + + alDeferUpdatesSOFT(); + + float angle = listener->angle; + ALfloat orient[6]; + // forward + orient[0] = cos(angle); + orient[1] = 0.f; + orient[2] = -sin(angle); + // up + orient[3] = 0.f; + orient[4] = 1.f; + orient[5] = 0.f; + + alListenerfv(AL_ORIENTATION, orient); + alListener3f(AL_POSITION, listener->position.X, + listener->position.Y, + -listener->position.Z); + alListener3f(AL_VELOCITY, listener->velocity.X, + listener->velocity.Y, + -listener->velocity.Z); + getALError(); + + const ReverbContainer *env = ForcedEnvironment; + if(!env) + { + env = listener->Environment; + if(!env) + env = DefaultEnvironments[0]; + } + if(env != PrevEnvironment || env->Modified) + { + PrevEnvironment = env; + DPrintf("Reverb Environment %s\n", env->Name); + + if(EnvSlot != 0) + LoadReverb(env); + + const_cast(env)->Modified = false; + } + + // NOTE: Moving into and out of water will undo pitch variations on sounds. + if(listener->underwater || env->SoftwareWater) + { + if(!WasInWater) + { + WasInWater = true; + + if(EnvSlot != 0 && *snd_waterreverb) + { + // Find the "Underwater" reverb environment + env = S_FindEnvironment(0x1600); + LoadReverb(env ? env : DefaultEnvironments[0]); + + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.125f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + + // Apply the updated filters on the sources + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); + getALError(); + } + } + else if(WasInWater) + { + WasInWater = false; + + if(EnvSlot != 0) + { + LoadReverb(env); + + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, 1.f); + getALError(); + } +} + +void OpenALSoundRenderer::UpdateSounds() +{ + alProcessUpdatesSOFT(); + + if(ALC.EXT_disconnect) + { + ALCint connected = ALC_TRUE; + alcGetIntegerv(Device, ALC_CONNECTED, 1, &connected); + if(connected == ALC_FALSE) + { + Printf("Sound device disconnected; restarting...\n"); + static char snd_reset[] = "snd_reset"; + AddCommandString(snd_reset); + return; + } + } + + PurgeStoppedSources(); +} + +void OpenALSoundRenderer::UpdateMusic() +{ + // For some reason this isn't being called? + for(uint32 i = 0;i < Streams.Size();++i) + Streams[i]->IsEnded(); +} + +bool OpenALSoundRenderer::IsValid() +{ + return Device != NULL; +} + +void OpenALSoundRenderer::MarkStartTime(FISoundChannel *chan) +{ + // FIXME: Get current time (preferably from the audio clock, but the system + // time will have to do) + chan->StartTime.AsOne = 0; +} + +float OpenALSoundRenderer::GetAudibility(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return 0.f; + + ALuint source = GET_PTRID(chan->SysChannel); + ALfloat volume = 0.f; + + alGetSourcef(source, AL_GAIN, &volume); + getALError(); + + volume *= GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); + return volume; +} + + +void OpenALSoundRenderer::PrintStatus() +{ + Printf("Output device: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); + getALCError(Device); + + ALCint frequency, major, minor, mono, stereo; + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &frequency); + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &mono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &stereo); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("Device sample rate: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL"hz\n", frequency); + Printf("ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + Printf("Available sources: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" (" TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" mono, " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" stereo)\n", mono+stereo, mono, stereo); + } + if(!alcIsExtensionPresent(Device, "ALC_EXT_EFX")) + Printf("EFX not found\n"); + else + { + ALCint sends; + alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("EFX Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("Auxiliary sends: " TEXTCOLOR_BLUE"%d\n", sends); + } + } + Printf("Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + Printf("Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + Printf("Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + Printf("Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + getALError(); +} + +FString OpenALSoundRenderer::GatherStats() +{ + ALCint updates = 1; + alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); + getALCError(Device); + + uint32 total = Sources.Size(); + uint32 used = SfxGroup.Size()+Streams.Size(); + uint32 unused = FreeSfx.Size(); + + FString out; + out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", + total, used, unused, 1000/updates); + return out; +} + +void OpenALSoundRenderer::PrintDriversList() +{ + const ALCchar *drivers = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if(drivers == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + return; + } + + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + if(current == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device name: %s\n", alcGetString(Device, alcGetError(Device))); + return; + } + + Printf("%c%s%2d. %s\n", ' ', ((strcmp(snd_aldevice, "Default") == 0) ? TEXTCOLOR_BOLD : ""), 0, + "Default"); + for(int i = 1;*drivers;i++) + { + Printf("%c%s%2d. %s\n", ((strcmp(current, drivers)==0) ? '*' : ' '), + ((strcmp(*snd_aldevice, drivers)==0) ? TEXTCOLOR_BOLD : ""), i, + drivers); + drivers += strlen(drivers)+1; + } +} + +void OpenALSoundRenderer::PurgeStoppedSources() +{ + // Release channels that are stopped + for(uint32 i = 0;i < SfxGroup.Size();++i) + { + ALuint src = SfxGroup[i]; + ALint state = AL_INITIAL; + alGetSourcei(src, AL_SOURCE_STATE, &state); + if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) + continue; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) + { + StopChannel(schan); + break; + } + schan = schan->NextChan; + } + } + getALError(); +} + +void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) +{ + ALuint *envReverb = EnvEffects.CheckKey(env->ID); + bool doLoad = (env->Modified || !envReverb); + + if(!envReverb) + { + bool ok = false; + + envReverb = &EnvEffects.Insert(env->ID, 0); + alGenEffects(1, envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + ok = (alGetError() == AL_NO_ERROR); + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_NULL); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alDeleteEffects(1, envReverb); + getALError(); + } + } + if(!ok) + { + *envReverb = 0; + doLoad = false; + } + } + + if(doLoad) + { + const REVERB_PROPERTIES &props = env->Properties; + ALint type = AL_EFFECT_NULL; + + alGetEffecti(*envReverb, AL_EFFECT_TYPE, &type); +#define mB2Gain(x) ((float)pow(10., (x)/2000.)) + if(type == AL_EFFECT_EAXREVERB) + { + ALfloat reflectpan[3] = { props.ReflectionsPan0, + props.ReflectionsPan1, + props.ReflectionsPan2 }; + ALfloat latepan[3] = { props.ReverbPan0, props.ReverbPan1, + props.ReverbPan2 }; +#undef SETPARAM +#define SETPARAM(e,t,v) alEffectf((e), AL_EAXREVERB_##t, clamp((v), AL_EAXREVERB_MIN_##t, AL_EAXREVERB_MAX_##t)) + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, GAINLF, mB2Gain(props.RoomLF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, DECAY_LFRATIO, props.DecayLFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + alEffectfv(*envReverb, AL_EAXREVERB_REFLECTIONS_PAN, reflectpan); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + alEffectfv(*envReverb, AL_EAXREVERB_LATE_REVERB_PAN, latepan); + SETPARAM(*envReverb, ECHO_TIME, props.EchoTime); + SETPARAM(*envReverb, ECHO_DEPTH, props.EchoDepth); + SETPARAM(*envReverb, MODULATION_TIME, props.ModulationTime); + SETPARAM(*envReverb, MODULATION_DEPTH, props.ModulationDepth); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, HFREFERENCE, props.HFReference); + SETPARAM(*envReverb, LFREFERENCE, props.LFReference); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_EAXREVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); +#undef SETPARAM + } + else if(type == AL_EFFECT_REVERB) + { +#define SETPARAM(e,t,v) alEffectf((e), AL_REVERB_##t, clamp((v), AL_REVERB_MIN_##t, AL_REVERB_MAX_##t)) + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_REVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); +#undef SETPARAM + } +#undef mB2Gain + } + + alAuxiliaryEffectSloti(EnvSlot, AL_EFFECTSLOT_EFFECT, *envReverb); + getALError(); +} + +FSoundChan *OpenALSoundRenderer::FindLowestChannel() +{ + FSoundChan *schan = Channels; + FSoundChan *lowest = NULL; + while(schan) + { + if(schan->SysChannel != NULL) + { + if(!lowest || schan->Priority < lowest->Priority || + (schan->Priority == lowest->Priority && + schan->DistanceSqr > lowest->DistanceSqr)) + lowest = schan; + } + schan = schan->NextChan; + } + return lowest; +} + +#endif // NO_OPENAL diff --git a/src/tflags.h b/src/tflags.h index 6c549a860..48866c828 100644 --- a/src/tflags.h +++ b/src/tflags.h @@ -94,6 +94,10 @@ public: static Self FromInt (TT value) { return Self (static_cast (value)); } private: + template Self operator| (X value) const { return Self::FromInt (Value | value); } + template Self operator& (X value) const { return Self::FromInt (Value & value); } + template Self operator^ (X value) const { return Self::FromInt (Value ^ value); } + TT Value; }; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index d200e083e..1bab73f35 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -789,15 +789,17 @@ static int DoJumpIfCloser(AActor *target, VM_ARGS) PARAM_ACTION_PROLOGUE; PARAM_FIXED (dist); PARAM_STATE (jump); + PARAM_BOOL_OPT(noz) { noz = false; } ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! // No target - no jump - if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && - ( (self->z > target->z && self->z - (target->z + target->height) < dist) || - (self->z <=target->z && target->z - (self->z + self->height) < dist) - ) - ) + if (!target) + return numret; + if (P_AproxDistance(self->x-target->x, self->y-target->y) < dist && + (noz || + ((self->z > target->z && self->z - (target->z + target->height) < dist) || + (self->z <= target->z && target->z - (self->z + self->height) < dist)))) { ACTION_JUMP(jump); } @@ -1156,7 +1158,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) targ = owner; missile->target = owner; // automatic handling of seeker missiles - if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) + if (self->flags2 & missile->flags2 & MF2_SEEKERMISSILE) { missile->tracer = self->tracer; } @@ -1577,6 +1579,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) PARAM_FIXED_OPT (lifesteal) { lifesteal = 0; } PARAM_INT_OPT (lifestealmax) { lifestealmax = 0; } PARAM_CLASS_OPT (armorbonustype, ABasicArmorBonus) { armorbonustype = NULL; } + PARAM_SOUND_OPT (MeleeSound) { MeleeSound = ""; } + PARAM_SOUND_OPT (MissSound) { MissSound = ""; } if (!self->player) return 0; @@ -1611,7 +1615,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) P_LineAttack (self, angle, range, pitch, damage, NAME_Melee, pufftype, puffFlags, &linetarget, &actualdamage); - if (linetarget) + if (!linetarget) + { + if (MissSound) S_Sound(self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); + } + else { if (lifesteal && !(linetarget->flags5 & MF5_DONTDRAIN)) { @@ -1643,7 +1651,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) } if (weapon != NULL) { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + if (MeleeSound) S_Sound(self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + else S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); } if (!(flags & CPF_NOTURN)) @@ -3718,7 +3727,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) lof_data.Flags = flags; lof_data.BadActor = false; - Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, + Trace(x1, y1, z1, sec, vx, vy, vz, range, ActorFlags::FromInt(0xFFFFFFFF), ML_BLOCKEVERYTHING, self, trace, 0, CheckLOFTraceFunc, &lof_data); if (trace.HitType == TRACE_HitActor || @@ -5040,6 +5049,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) PARAM_INT_OPT(flags) { flags = 0; } PARAM_STATE_OPT(success_state) { success_state = NULL; } PARAM_FIXED_OPT(heightoffset) { heightoffset = 0; } + PARAM_FIXED_OPT(radiusoffset) { radiusoffset = 0; } + PARAM_ANGLE_OPT(pitch) { pitch = 0; } AActor *reference; @@ -5059,7 +5070,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) return numret; } - if (P_Thing_Warp(self, reference, xofs, yofs, zofs, angle, flags, heightoffset)) + if (P_Thing_Warp(self, reference, xofs, yofs, zofs, angle, flags, heightoffset, radiusoffset, pitch)) { if (success_state) { @@ -5238,11 +5249,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) PARAM_INT_OPT (amount) { amount = 0; } PARAM_CLASS_OPT (filter, AActor) { filter = NULL; } PARAM_NAME_OPT (species) { species = NAME_None; } + PARAM_FIXED_OPT (mindist) { mindist = 0; } // We need a valid item, valid targets, and a valid range - if (item == NULL || (flags & RGF_MASK) == 0 || !flags || distance <= 0) + if (item == NULL || (flags & RGF_MASK) == 0 || !flags || distance <= 0 || mindist >= distance) { - return 0; + ACTION_SET_RESULT(false); + return numret; } if (amount == 0) @@ -5250,9 +5263,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) amount = 1; } FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); - double distsquared = double(distance) * double(distance); AActor *thing; + bool given = false; while ((thing = it.Next())) { //[MC] Check for a filter, species, and the related exfilter/expecies/either flag(s). @@ -5297,7 +5310,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) bool corpsePass = !!((flags & RGF_CORPSES) && thing->flags & MF_CORPSE); bool killedPass = !!((flags & RGF_KILLED) && thing->flags6 & MF6_KILLED); bool monsterPass = !!((flags & RGF_MONSTERS) && thing->flags3 & MF3_ISMONSTER); - bool objectPass = !!((flags & RGF_OBJECTS) && ((thing->flags & MF_SHOOTABLE) || (thing->flags6 & MF6_VULNERABLE))); + bool objectPass = !!((flags & RGF_OBJECTS) && (thing->player == NULL) && (!(thing->flags3 & MF3_ISMONSTER)) + && ((thing->flags & MF_SHOOTABLE) || (thing->flags6 & MF6_VULNERABLE))); bool playerPass = !!((flags & RGF_PLAYERS) && (thing->player != NULL) && (thing->player->mo == thing)); bool voodooPass = !!((flags & RGF_VOODOO) && (thing->player != NULL) && (thing->player->mo != thing)); //Self calls priority over the rest of this. @@ -5320,20 +5334,26 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) if (selfPass || monsterPass || corpsePass || killedPass || itemPass || objectPass || missilePass || playerPass || voodooPass) { + if (flags & RGF_CUBE) { // check if inside a cube - if (fabs((double)thing->x - self->x) > (double)distance || - fabs((double)thing->y - self->y) > (double)distance || - fabs((double)(thing->z + thing->height / 2) - (self->z + self->height / 2)) > (double)distance) + double dx = fabs((double)(thing->x - self->x)); + double dy = fabs((double)(thing->y - self->y)); + double dz = fabs((double)(thing->z + thing->height / 2) - (self->z + self->height / 2)); + double dist = (double)distance; + double min = (double)mindist; + if ((dx > dist || dy > dist || dz > dist) || (min && (dx < min && dy < min && dz < min))) { continue; } } else { // check if inside a sphere + double distsquared = double(distance) * double(distance); + double minsquared = double(mindist) * double(mindist); TVector3 tpos(thing->x, thing->y, thing->z + thing->height / 2); TVector3 spos(self->x, self->y, self->z + self->height / 2); - if ((tpos - spos).LengthSquared() > distsquared) + if ((tpos - spos).LengthSquared() > distsquared || (minsquared && ((tpos - spos).LengthSquared() < minsquared))) { continue; } @@ -5356,10 +5376,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) { gift->Destroy(); } + else + { + given = true; + } } } } - return 0; + + ACTION_SET_RESULT(given); + return numret; } @@ -5465,6 +5491,29 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatSpeed) return 0; } +//========================================================================== +// +// A_SetPainThreshold +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPainThreshold) +{ + PARAM_ACTION_PROLOGUE; + PARAM_INT(threshold); + PARAM_INT_OPT(ptr) { ptr = AAPTR_DEFAULT; } + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return numret; + } + + ref->PainThreshold = threshold; + return numret; +} + //=========================================================================== // // Common A_Damage handler @@ -6214,3 +6263,63 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) return 0; } +/*=========================================================================== +A_CheckBlock +(state block, int flags, int ptr) + +Checks if something is blocking the actor('s pointer) 'ptr'. + +The SET pointer flags only affect the caller, not the pointer. +===========================================================================*/ +enum CBF +{ + CBF_NOLINES = 1 << 0, //Don't check actors. + CBF_SETTARGET = 1 << 1, //Sets the caller/pointer's target to the actor blocking it. Actors only. + CBF_SETMASTER = 1 << 2, //^ but with master. + CBF_SETTRACER = 1 << 3, //^ but with tracer. + CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self. +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) +{ + PARAM_ACTION_PROLOGUE; + PARAM_STATE(block) + PARAM_INT_OPT(flags) { flags = 0; } + PARAM_INT_OPT(ptr) { ptr = AAPTR_DEFAULT; } + + AActor *mobj = COPY_AAPTR(self, ptr); + + ACTION_SET_RESULT(false); + //Needs at least one state jump to work. + if (!mobj) + { + return numret; + } + + //Nothing to block it so skip the rest. + if (P_TestMobjLocation(mobj)) return numret; + + if (mobj->BlockingMobj) + { + AActor *setter = (flags & CBF_SETONPTR) ? mobj : self; + if (setter) + { + if (flags & CBF_SETTARGET) setter->target = mobj->BlockingMobj; + if (flags & CBF_SETMASTER) setter->master = mobj->BlockingMobj; + if (flags & CBF_SETTRACER) setter->tracer = mobj->BlockingMobj; + } + } + + //[MC] If modders don't want jumping, but just getting the pointer, only abort at + //this point. I.e. A_CheckBlock("",CBF_SETTRACER) is like having CBF_NOLINES. + //It gets the mobj blocking, if any, and doesn't jump at all. + if (!block) + return numret; + + //[MC] Easiest way to tell if an actor is blocking it, use the pointers. + if (mobj->BlockingMobj || (!(flags & CBF_NOLINES) && mobj->BlockingLine != NULL)) + { + ACTION_JUMP(block); + } + return numret; +} \ No newline at end of file diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 183105591..f44e9b65f 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -252,12 +252,10 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF7, HITTARGET, AActor, flags7), DEFINE_FLAG(MF7, HITMASTER, AActor, flags7), DEFINE_FLAG(MF7, HITTRACER, AActor, flags7), - - // [ZK] Decal flags - DEFINE_FLAG(MF7, NODECAL, AActor, flags7), + DEFINE_FLAG(MF7, NODECAL, AActor, flags7), // [ZK] Decal flags DEFINE_FLAG(MF7, FORCEDECAL, AActor, flags7), - DEFINE_FLAG(MF7, LAXTELEFRAGDMG, AActor, flags7), + DEFINE_FLAG(MF7, ICESHATTER, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 8119d3802..653f6b907 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -1808,18 +1808,16 @@ void WI_updateStats () if (acceleratestage && sp_state != 10) { - if (acceleratestage) - { - acceleratestage = 0; - sp_state = 10; - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - } + acceleratestage = 0; + sp_state = 10; + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + cnt_kills[0] = plrs[me].skills; cnt_items[0] = plrs[me].sitems; cnt_secret[0] = plrs[me].ssecret; cnt_time = Tics2Seconds(plrs[me].stime); cnt_par = wbs->partime / TICRATE; - cnt_total_time = Tics2Seconds(wbs->totaltime); + cnt_total_time = Tics2Seconds(wbs->totaltime); } if (sp_state == 2) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 8bf4e5d3a..42d7196dd 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -180,9 +180,9 @@ ACTOR Actor native //: Thinker action native A_CustomBulletAttack(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET); action native A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, int aim = 0, float maxdiff = 0, class pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270); action native A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT); - action native A_JumpIfCloser(float distance, state label); - action native A_JumpIfTracerCloser(float distance, state label); - action native A_JumpIfMasterCloser(float distance, state label); + action native A_JumpIfCloser(float distance, state label, bool noz = false); + action native A_JumpIfTracerCloser(float distance, state label, bool noz = false); + action native A_JumpIfMasterCloser(float distance, state label, bool noz = false); action native A_JumpIfTargetOutsideMeleeRange(state label); action native A_JumpIfTargetInsideMeleeRange(state label); action native A_JumpIfInventory(class itemtype, int itemamount, state label, int owner = AAPTR_DEFAULT); @@ -217,7 +217,7 @@ ACTOR Actor native //: Thinker action native A_PlayerSkinCheck(state label); action native A_BasicAttack(int meleedamage, sound meleesound, class missiletype, float missileheight); action native A_Teleport(state teleportstate = "", class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 0, int ptr = AAPTR_DEFAULT); - action native A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = "", float heightoffset = 0); + action native A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = "", float heightoffset = 0, float radiusoffset = 0, float pitch = 0); action native A_ThrowGrenade(class itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true); action native A_Weave(int xspeed, int yspeed, float xdist, float ydist); @@ -225,7 +225,7 @@ ACTOR Actor native //: Thinker action native A_JumpIfInTargetInventory(class itemtype, int amount, state label, int forward_ptr = AAPTR_DEFAULT); action native A_GiveToTarget(class itemtype, int amount = 0, int forward_ptr = AAPTR_DEFAULT); action native A_TakeFromTarget(class itemtype, int amount = 0, int flags = 0, int forward_ptr = AAPTR_DEFAULT); - action native A_RadiusGive(class itemtype, int distance, int flags, int amount = 0, class filter = "None", name species = "None"); + action native A_RadiusGive(class itemtype, int distance, int flags, int amount = 0, class filter = "None", name species = "None", int mindist = 0); action native A_CountdownArg(int argnum, state targstate = ""); action native A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true); action native A_CustomComboAttack(class missiletype, float spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true); @@ -273,6 +273,7 @@ ACTOR Actor native //: Thinker action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed, int ptr = AAPTR_DEFAULT); action native A_SetFloatSpeed(float speed, int ptr = AAPTR_DEFAULT); + action native A_SetPainThreshold(int threshold, int ptr = AAPTR_DEFAULT); action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); @@ -304,7 +305,7 @@ ACTOR Actor native //: Thinker action native A_SetRipperLevel(int level); action native A_SetRipMin(int min); action native A_SetRipMax(int max); - + action native A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_CheckSightOrRange(float distance, state label, bool two_dimension = false); action native A_CheckRange(float distance, state label, bool two_dimension = false); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 0724c1109..7f8fbc09e 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -362,6 +362,8 @@ Const Int WARPF_ABSOLUTEPOSITION = 0x400; Const Int WARPF_BOB = 0x800; Const Int WARPF_MOVEPTR = 0x1000; Const Int WARPF_USETID = 0x2000; +Const Int WARPF_COPYVELOCITY = 0x4000; +Const Int WARPF_COPYPITCH = 0x8000; // flags for A_SetPitch/SetAngle/SetRoll const int SPF_FORCECLAMP = 1; @@ -482,3 +484,17 @@ enum QF_WAVE = 1 << 5, }; +// Flags for A_CheckBlock +// These flags only affect the calling actor('s pointer), not the ones being searched. +enum +{ + CBF_NOLINES = 1 << 0, //Don't check actors. + CBF_SETTARGET = 1 << 1, //Sets the caller/pointer's target to the actor blocking it. Actors only. + CBF_SETMASTER = 1 << 2, //^ but with master. + CBF_SETTRACER = 1 << 3, //^ but with tracer. + CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self. +}; + +// This is only here to provide one global variable for testing. +native int testglobalvar; + diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index e0901d712..ee9a35446 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -8,7 +8,7 @@ ACTOR Inventory native Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG" action native A_JumpIfNoAmmo(state label); - action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus"); + action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus", sound MeleeSound = "", sound MissSound = ""); action native A_FireBullets(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", int flags = 1, float range = 0); action native A_FireCustomMissile(class missiletype, float angle = 0, bool useammo = true, int spawnofs_xy = 0, float spawnheight = 0, int flags = 0, float pitch = 0); action native A_RailAttack(int damage, int spawnofs_xy = 0, bool useammo = true, color color1 = "", color color2 = "", int flags = 0, float maxdiff = 0, class pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270); diff --git a/wadsrc/static/actors/strife/macil.txt b/wadsrc/static/actors/strife/macil.txt index 24af35ada..7b001e778 100644 --- a/wadsrc/static/actors/strife/macil.txt +++ b/wadsrc/static/actors/strife/macil.txt @@ -44,7 +44,7 @@ ACTOR Macil1 Death: LEAD E 2 A_FaceTarget LEAD F 2 BRIGHT A_ShootGun - LEAD E 2 A_SentinelRefire + LEAD E 1 A_SentinelRefire Loop Pain: LEAD Y 3 @@ -80,7 +80,7 @@ ACTOR Macil2 : Macil1 LEAD K 3 LEAD L 3 A_NoBlocking LEAD MNOPQRSTUV 3 - LEAD W 4 Bright A_SpawnItemEx("AlienSpectre4", 0, 0, 0, 0, 0, random[spectrespawn](0,255)*0.0078125, 0, SXF_NOCHECKPOSITION) + LEAD W 3 A_SpawnItemEx("AlienSpectre4", 0, 0, 0, 0, 0, random[spectrespawn](0,255)*0.0078125, 0, SXF_NOCHECKPOSITION) LEAD X -1 Stop } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index bdefa1b86..005a3f57d 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1550,6 +1550,10 @@ TXT_RANDOMGOODBYE_3 = "See you later!"; TXT_HAVEENOUGH = "You seem to have enough!"; TXT_GOAWAY = "Go away!"; +TXT_COMM0 = "Incoming Message"; +TXT_COMM1 = "Incoming Message from BlackBird"; + + // Skills: SKILL_BABY = "I'm too young to die"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index b1ca44de7..eef2629d3 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -456,6 +456,7 @@ OptionMenu "CustomizeControls" Control "Look down", "+lookdown" Control "Center view", "centerview" Control "Run", "+speed" + Control "Toggle Run", "toggle cl_run" Control "Strafe", "+strafe" Control "Show Scoreboard", "+showscores" Control "Toggle Scoreboard", "togglescoreboard" @@ -1589,10 +1590,24 @@ OptionMenu AdvSoundOptions Option "OPL Emulator Core", "opl_core", "OplCores" StaticText " " StaticText "GUS Emulation", 1 + TextField "GUS config file", "midi_config" Slider "MIDI voices", "midi_voices", 16, 256, 4, 0 Option "Emulate TiMidity", "midi_timiditylike", "OnOff" Option "Read DMXGUS lumps", "midi_dmxgus", "OnOff" Option "GUS memory size", "gus_memsize", "GusMemory" + StaticText " " + StaticText "FluidSynth", 1 + TextField "Patch set", "fluid_patchset" + Slider "Gain", "fluid_gain", 0, 10, 0.5, 1 + Option "Reverb", "fluid_reverb", "OnOff" + Slider "MIDI voices", "fluid_voices", 16, 4096, 16, 1 + // Leaving out the more advanced stuff for now. + StaticText " " + StaticText "Timidity++", 1 + TextField "Path for executable", "timidity_exe" + Option "Reverb", "timidity_reverb", "OnOff" + Option "Chorus", "timidity_chorus", "OnOff" + Slider "Relative volume", "timidity_mastervolume", 0, 4, 0.2, 1 } /*=======================================