From a90ea55e2c2edd328411a99326ef7c5937ae709f Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 12 Sep 2015 23:01:56 +0100 Subject: [PATCH 01/48] - Removed duplicate 'acceleratestage' check. --- src/wi_stuff.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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) From 67305258553c2c3b79de90bc7ea4ce4ebeb572c4 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 17 Sep 2015 09:07:13 -0500 Subject: [PATCH 02/48] - Added A_SetPainThreshold for the calling actor('s pointer). --- src/thingdef/thingdef_codeptr.cpp | 22 ++++++++++++++++++++++ wadsrc/static/actors/actor.txt | 1 + 2 files changed, 23 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index dab68867b..6dcadf0ea 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5111,6 +5111,28 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatSpeed) ref->FloatSpeed = speed; } +//========================================================================== +// +// A_SetPainThreshold +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPainThreshold) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(threshold, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->PainThreshold = threshold; +} + //=========================================================================== // // Common A_Damage handler diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 5877192ce..723e5bafd 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -305,6 +305,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"); From 49519db257d1c717e6d0cf36bb8dfc5086e79064 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 19 Sep 2015 18:29:07 +0300 Subject: [PATCH 03/48] Fixed crash in RandomSpawner with -nomonsters See http://forum.zdoom.org/viewtopic.php?f=2&t=49520 --- src/g_shared/a_randomspawner.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index adfc09b6e..e8650c07f 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -19,6 +19,18 @@ #define MAX_RANDOMSPAWNERS_RECURSION 32 // Should be largely more than enough, honestly. static FRandom pr_randomspawn("RandomSpawn"); +static bool IsMonster(const FDropItem *di) +{ + const PClass *pclass = PClass::FindClass(di->Name); + + if (NULL == pclass) + { + return false; + } + + return GetDefaultByType(pclass)->flags3 & MF3_ISMONSTER; +} + class ARandomSpawner : public AActor { DECLARE_CLASS (ARandomSpawner, AActor) @@ -41,7 +53,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 +74,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)) From 96ff7160463b93fb9d9401274b16f81dfaf097eb Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 19 Sep 2015 18:29:59 +0300 Subject: [PATCH 04/48] Added error message about missing class to drop from RandomSpawner --- src/g_shared/a_randomspawner.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index e8650c07f..160766fa7 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -15,6 +15,7 @@ #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"); @@ -118,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; } } From 28622cecaf16cc4a95f90e89d4cd1b55757ace42 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 29 Sep 2015 11:40:44 -0500 Subject: [PATCH 05/48] - Added mindist parameter to A_RadiusGive. - Actors must be this far away to receive items. Mindist must be less than distance. - Fixed RGF_OBJECTS not discriminating players and monsters from shootable or vulnerable actors. --- src/thingdef/thingdef_codeptr.cpp | 30 ++++++++++++++++++++++-------- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index dab68867b..9333a09d4 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4880,17 +4880,19 @@ enum RadiusGiveFlags DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) { - ACTION_PARAM_START(6); + ACTION_PARAM_START(7); ACTION_PARAM_CLASS(item, 0); ACTION_PARAM_FIXED(distance, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_INT(amount, 3); ACTION_PARAM_CLASS(filter, 4); ACTION_PARAM_NAME(species, 5); + ACTION_PARAM_FIXED(mindist, 6); // 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) { + ACTION_SET_RESULT(false); return; } @@ -4899,9 +4901,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). @@ -4946,7 +4948,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. @@ -4969,20 +4972,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; } @@ -5005,9 +5014,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) { gift->Destroy(); } + else + { + given = true; + } } } } + ACTION_SET_RESULT(given); } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 5877192ce..4832865c7 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -257,7 +257,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); From af9478f818e940164ec8f00d396d8c4e700ff673 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 3 Oct 2015 17:28:54 -0500 Subject: [PATCH 06/48] - Added Warp properties RadiusOffset and Pitch. - RadiusOffset is a multiplier of the target actor's radius added onto the offsets x and y. - Pitch is added to the warping actor's current pitch, provided WARPF_USEPITCH is supplied. - Fixed WARPF_TOFLOOR not working as intended. --- src/p_acs.cpp | 4 +++- src/p_local.h | 4 +++- src/p_things.cpp | 38 ++++++++++++++++++++---------- src/thingdef/thingdef_codeptr.cpp | 8 ++++--- wadsrc/static/actors/actor.txt | 2 +- wadsrc/static/actors/constants.txt | 2 ++ 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 4e5875836..ca0f62576 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5864,6 +5864,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()->ActorInfo->FindStateByString(statename, exact) : 0; @@ -5881,7 +5883,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_local.h b/src/p_local.h index 95d7541a8..0ac70ce16 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -176,7 +176,7 @@ bool P_Thing_Raise(AActor *thing, AActor *raiser); bool P_Thing_CanRaise(AActor *thing); const PClass *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 { @@ -198,6 +198,8 @@ enum WARPF WARPF_MOVEPTR = 0x1000, WARPF_USEPTR = 0x2000, WARPF_USETID = 0x2000, + WARPF_COPYVELOCITY = 0x4000, + WARPF_COPYPITCH = 0x8000, }; diff --git a/src/p_things.cpp b/src/p_things.cpp index b7e3f058b..edd7777b4 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,9 +692,10 @@ 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); - + + const fixed_t rad = FixedMul(radiusoffset, reference->radius); + const angle_t fineangle = angle >> ANGLETOFINESHIFT; if (!(flags & WARPF_ABSOLUTEANGLE)) { @@ -704,13 +705,12 @@ int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, { 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 +722,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 +739,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 +758,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/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index dab68867b..bf4536360 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4673,7 +4673,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) { - ACTION_PARAM_START(8); + ACTION_PARAM_START(10); ACTION_PARAM_INT(destination_selector, 0); ACTION_PARAM_FIXED(xofs, 1); @@ -4682,7 +4682,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) ACTION_PARAM_ANGLE(angle, 4); ACTION_PARAM_INT(flags, 5); ACTION_PARAM_STATE(success_state, 6); - ACTION_PARAM_FIXED(heightoffset,7) + ACTION_PARAM_FIXED(heightoffset, 7); + ACTION_PARAM_FIXED(radiusoffset, 8); + ACTION_PARAM_ANGLE(pitch, 9); AActor *reference; @@ -4702,7 +4704,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) return; } - 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) { diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 5877192ce..2f8167eb8 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -249,7 +249,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); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index b5a8b8cd4..f125a37e7 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; From e2d874e343da34df6edfad0bb47370cbe10f4bae Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 4 Oct 2015 12:37:49 -0500 Subject: [PATCH 07/48] Applied _mental_'s suggestion --- src/p_things.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/p_things.cpp b/src/p_things.cpp index edd7777b4..7e38a3c68 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -694,13 +694,15 @@ int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t oldz = caller->z; zofs += FixedMul(reference->height, heightoffset); - const fixed_t rad = FixedMul(radiusoffset, reference->radius); - const angle_t fineangle = angle >> ANGLETOFINESHIFT; 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)) From 154e02380081fb0badbce7e8172c1e72325b7ab8 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 4 Oct 2015 16:00:40 -0500 Subject: [PATCH 08/48] - Added A_CheckBlock(state block, int flags, int ptr). - Performs a jump if an actor or a line is in the way. - Can be used without a jump state if the desire is only to have a pointer change. - CBF_NOLINES disables jumping if a line is involved. - CBF_SET* flags set the target, master or tracer to whoever is blocking, for the actor calling the function. - CBF_SETONPTR causes the pointer changing flags to apply to the pointed actor instead of itself. --- src/thingdef/thingdef_codeptr.cpp | 61 ++++++++++++++++++++++++++++++ wadsrc/static/actors/actor.txt | 2 +- wadsrc/static/actors/constants.txt | 12 ++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index dab68867b..7dceb2923 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5846,3 +5846,64 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) self->RipLevelMax = max; } +/*=========================================================================== +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) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STATE(block, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *mobj = COPY_AAPTR(self, ptr); + + ACTION_SET_RESULT(false); + //Needs at least one state jump to work. + if (!mobj) + { + return; + } + + bool blockTest = P_TestMobjLocation(mobj); + + //Nothing to block it so skip the rest. + if (blockTest) return; + + 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; + + //[MC] Easiest way to tell if an actor is blocking it, use the pointers. + if (!blockTest && (mobj->BlockingMobj || (!(flags & CBF_NOLINES) && mobj->BlockingLine != NULL))) + { + ACTION_JUMP(block); + } +} \ No newline at end of file diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 5877192ce..8969486be 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -336,7 +336,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 b5a8b8cd4..31014eb1e 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -482,5 +482,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; + From 2f3b69e77022a74731f536bcf63aa73daa48ba2d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 4 Oct 2015 16:12:35 -0500 Subject: [PATCH 09/48] Optimize a little more... --- src/thingdef/thingdef_codeptr.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 7dceb2923..7ba70c2ea 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5879,10 +5879,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) return; } - bool blockTest = P_TestMobjLocation(mobj); - //Nothing to block it so skip the rest. - if (blockTest) return; + if (P_TestMobjLocation(mobj)) return; if (mobj->BlockingMobj) { @@ -5902,7 +5900,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) return; //[MC] Easiest way to tell if an actor is blocking it, use the pointers. - if (!blockTest && (mobj->BlockingMobj || (!(flags & CBF_NOLINES) && mobj->BlockingLine != NULL))) + if (mobj->BlockingMobj || (!(flags & CBF_NOLINES) && mobj->BlockingLine != NULL)) { ACTION_JUMP(block); } From 005e468fa98578277c0c14923c7aae7193e9685f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 7 Oct 2015 12:11:27 +0300 Subject: [PATCH 10/48] Fixed crash when menu item uses non-existent texture See http://forum.zdoom.org/viewtopic.php?t=49696 --- src/menu/listmenu.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 402c1d5e9..8c63eb35b 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) From 99dd664029153eaf09ef8b2fa34e5a8b41c8a8f8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 7 Oct 2015 12:13:11 +0300 Subject: [PATCH 11/48] Print warning to console if unknown texture found in menu definition --- src/menu/menudef.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) 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 From bad99613760763e588f685fb168b2dbfb10a2aa2 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 10 Oct 2015 15:03:14 +0300 Subject: [PATCH 12/48] Fixed crash when clicking on back button in main menu If menu item selection overlaps back button in main menu, clicking on back button with mouse caused a crash See http://forum.zdoom.org/viewtopic.php?t=49711 --- src/menu/listmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 8c63eb35b..0f50d1e00 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -491,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; } From 455b70630dac9187469df823c2725de136959f5a Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 10 Oct 2015 10:11:59 -0500 Subject: [PATCH 13/48] - Added A_JumpIfCloser NoZ boolean. Disables Z distance checking if true. --- src/thingdef/thingdef_codeptr.cpp | 12 +++++++----- wadsrc/static/actors/actor.txt | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 7ba70c2ea..ae9223319 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -663,15 +663,17 @@ void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) ACTION_PARAM_START(2); ACTION_PARAM_FIXED(dist, 0); ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(noz, 2); 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; + 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); } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 8969486be..18911be33 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -212,9 +212,9 @@ ACTOR Actor native //: Thinker action native A_CustomBulletAttack(float spread_xy, float 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, bool aim = false, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float 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); From 21dc45bdecd811983c4ee94549b698fc290fc20b Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 10 Oct 2015 15:30:18 -0500 Subject: [PATCH 14/48] This did not save out properly... --- src/thingdef/thingdef_codeptr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index ae9223319..9c86e43e6 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -660,7 +660,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) //========================================================================== void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_FIXED(dist, 0); ACTION_PARAM_STATE(jump, 1); ACTION_PARAM_BOOL(noz, 2); From fe61b8064adf5f42575d4c57056466d49878b9d1 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 11 Oct 2015 09:28:38 +0300 Subject: [PATCH 15/48] Fixed MSVC warning in a_randomspawner.cpp ...\src\g_shared\a_randomspawner.cpp(32): warning C4800: 'DWORD' : forcing value to bool 'true' or 'false' (performance warning) See http://forum.zdoom.org/viewtopic.php?t=49737 --- src/g_shared/a_randomspawner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index 160766fa7..7d000f3d4 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -29,7 +29,7 @@ static bool IsMonster(const FDropItem *di) return false; } - return GetDefaultByType(pclass)->flags3 & MF3_ISMONSTER; + return 0 != (GetDefaultByType(pclass)->flags3 & MF3_ISMONSTER); } class ARandomSpawner : public AActor From 380b5c1eb79e747a3f404e8ec746aa9a0d819f34 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 11 Oct 2015 09:10:33 -0500 Subject: [PATCH 16/48] - Added ICESHATTER flag. - Any inflictor with this flag can break ice corpses --- src/actor.h | 1 + src/p_interaction.cpp | 2 +- src/thingdef/thingdef_data.cpp | 6 ++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/actor.h b/src/actor.h index 52c88e901..468dcc0c6 100644 --- a/src/actor.h +++ b/src/actor.h @@ -373,6 +373,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/p_interaction.cpp b/src/p_interaction.cpp index 603535b7c..25724172c 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -970,7 +970,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; } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 817afd0fa..f9a282c76 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -253,12 +253,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), From 6678c3550ea3ce88e4541afbce3fca1f1e6d33a4 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 13 Oct 2015 00:30:06 +0200 Subject: [PATCH 17/48] - Improve the TFlags code and fix the new errors. The previous version didn't detect some real mistakes in code which used operator& with the wrong flagset (for now 'converted' to the correcly equivalent counterpart, waiting for the proper fix). --- src/d_dehacked.cpp | 20 ++++++++++---------- src/fragglescript/t_func.cpp | 2 +- src/g_hexen/a_clericstaff.cpp | 2 +- src/p_acs.cpp | 4 ++-- src/p_local.h | 2 +- src/p_map.cpp | 2 +- src/p_mobj.cpp | 14 +++++++------- src/p_trace.cpp | 5 +++-- src/p_trace.h | 3 ++- src/tflags.h | 4 ++++ src/thingdef/thingdef_codeptr.cpp | 4 ++-- 11 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index a994f7603..bc0acc666 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -784,7 +784,7 @@ static int PatchThing (int thingy) bool hadStyle = false; FStateDefinitions statedef; bool patchedStates = false; - int oldflags; + ActorFlags oldflags; const PClass *type; SWORD *ednum, dummyed; @@ -1139,28 +1139,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/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 6fcd50341..7e05be8da 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 45551f55f..b397fc12b 100644 --- a/src/g_hexen/a_clericstaff.cpp +++ b/src/g_hexen/a_clericstaff.cpp @@ -75,7 +75,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/p_acs.cpp b/src/p_acs.cpp index 4e5875836..e61f962ca 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5761,9 +5761,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; diff --git a/src/p_local.h b/src/p_local.h index 95d7541a8..a3df41f21 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -500,7 +500,7 @@ enum // P_LineAttack flags AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, const PClass *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 c9b2f14cf..e8e5d6c84 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3912,7 +3912,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 901372eef..d5c5eb5d9 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -6467,25 +6467,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_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/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 f56fa6633..bf05ed298 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1014,7 +1014,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->flags & MF_STEALTH) && (missile->flags2 & MF2_SEEKERMISSILE)) { missile->tracer=self->tracer; } @@ -3414,7 +3414,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 || From 4cd793ca2f57231d578b9f0631fc606023fd57e9 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 13 Oct 2015 09:13:37 +0200 Subject: [PATCH 18/48] - Fixed broken projectile firing projectiles code. This went unnoticed since ZDoom 2.0.90, which introduced the bug. Discovered with the recent TFlags improvements. --- src/thingdef/thingdef_codeptr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index bf05ed298..9b451d40b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1014,7 +1014,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) targ=owner; missile->target=owner; // automatic handling of seeker missiles - if ((self->flags & MF_STEALTH) && (missile->flags2 & MF2_SEEKERMISSILE)) + if (self->flags2 & missile->flags2 & MF2_SEEKERMISSILE) { missile->tracer=self->tracer; } From 916b5f796de3991f8f0b52027bacfd085f7d7149 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 14 Oct 2015 13:17:21 +0300 Subject: [PATCH 19/48] Updated list of video resolutions for Cocoa backend Added resolutions for iMac Retina 4K 21.5" Added comments about a few resolutions specific to Macs --- src/posix/cocoa/i_video.mm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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" }; From c93bc2561e4bfcc2140c312b36e649d27560d805 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 15 Oct 2015 10:33:28 +0300 Subject: [PATCH 20/48] Added Toggle Run action to Customize Controls menu --- wadsrc/static/menudef.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index b1ca44de7..1b9a2c606 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" From 869e16849537a0a1af05a677489975a95abe8183 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 18 Oct 2015 15:52:45 +0300 Subject: [PATCH 21/48] 'no monsters' option is no longer ignored in ACS Spawn functions See http://forum.zdoom.org/viewtopic.php?t=46459 --- src/p_acs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e61f962ca..bca6ac62b 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -3365,6 +3365,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) { From 27135d5c5da22a2027f617ba363b056af38abea4 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 18 Oct 2015 16:28:42 +0300 Subject: [PATCH 22/48] Fixed crash on loading map with corrupt linedefs and/or sectors See http://forum.zdoom.org/viewtopic.php?t=49276 --- src/p_setup.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 14b19348e..311bf8fc4 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -2353,7 +2353,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 { From 2612fad326be24ab1493087840f8302221ed7454 Mon Sep 17 00:00:00 2001 From: John Palomo Jr Date: Sat, 21 Nov 2015 06:05:16 -0500 Subject: [PATCH 23/48] Backport RETURNTID and FORCETID flags for PickActor from GLOOME. Renamed prefix to avoid confusion with Pain Attack flags. Fixed broken enum. --- src/p_acs.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 83146b743..4f8a0c6c3 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) @@ -5779,11 +5789,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); @@ -5791,15 +5800,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; From 20bf4d6c8ebe8cf4bc3fedf24e9a609843a8b5b3 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 21 Nov 2015 17:43:49 +0200 Subject: [PATCH 24/48] Fixed crash on finishgame CCMD in non-game modes http://forum.zdoom.org/viewtopic.php?t=49943 --- src/g_level.cpp | 7 +++++++ 1 file changed, 7 insertions(+) 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; From 7b017f472aef982a0e4049809f4aefdeb57e6687 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 11:49:34 +0100 Subject: [PATCH 25/48] - fixed: Due to the default skybox being moved to the global level data, there was insufficient information to distinguish between using the default skybox or the regular sky. Fixed by adding two new sector flags which get set by a 0-tid SkyPicker. --- src/g_shared/a_skies.cpp | 2 ++ src/p_sectors.cpp | 15 +++++++++++++++ src/r_bsp.cpp | 5 +++-- src/r_defs.h | 4 ++++ 4 files changed, 24 insertions(+), 2 deletions(-) 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/p_sectors.cpp b/src/p_sectors.cpp index 904c76c1b..bc7abd380 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,20 @@ int sector_t::GetCeilingLight () const } } + +ASkyViewpoint *sector_t::GetSkyBox(int which) +{ + if (which == floor) + { + return FloorSkyBox != NULL ? FloorSkyBox : (MoreFlags & SECF_NOFLOORSKYBOX)? (ASkyViewpoint*)NULL : level.DefaultSkybox; + } + else + { + return CeilingSkyBox != NULL ? CeilingSkyBox : (MoreFlags & SECF_NOCEILINGSKYBOX)? (ASkyViewpoint*)NULL : level.DefaultSkybox; + } +} + + sector_t *sector_t::GetHeightSec() const { if (heightsec == NULL) 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, From 4a18a28448a6c99d9b63fa45dd54787330becac3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 12:20:35 +0100 Subject: [PATCH 26/48] - fixed: am_showtriggerlines will now ignore any non-activatable linedef types. --- src/am_map.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 From 43dff3021b4e6491ae48bd3e5054954be291b646 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 13:00:56 +0100 Subject: [PATCH 27/48] - fixed: The OpenAL backend ignored PlaySound's 'volume' parameter. --- src/sound/oalsound.cpp | 3898 ++++++++++++++++++++-------------------- 1 file changed, 1949 insertions(+), 1949 deletions(-) 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 From 01917d9f15fdfbc8fc557c8e33348d11b5b83f39 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 13:06:39 +0100 Subject: [PATCH 28/48] - fixed incorrect state settings in Macil. --- wadsrc/static/actors/strife/macil.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 } From 1e0366f98f7eae81ade2d860e6f9c9c6596bde19 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 13:27:35 +0100 Subject: [PATCH 29/48] - fixed: $musicalias must be resolved before comparing for equality with the currently playing music. --- src/s_sound.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/s_sound.cpp b/src/s_sound.cpp index bacd5aa94..7b38e816c 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. From f4ab6074faf10624e76bad222cfe749d5c7bcbaf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 13:30:12 +0100 Subject: [PATCH 30/48] - today's 'GCC sucks' fix. (Yet again some overtly anal type issues with the '? :' operator...) --- src/p_sectors.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index bc7abd380..361b220a7 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -806,12 +806,15 @@ ASkyViewpoint *sector_t::GetSkyBox(int which) { if (which == floor) { - return FloorSkyBox != NULL ? FloorSkyBox : (MoreFlags & SECF_NOFLOORSKYBOX)? (ASkyViewpoint*)NULL : level.DefaultSkybox; + if (FloorSkyBox != NULL) return FloorSkyBox; + if (MoreFlags & SECF_NOFLOORSKYBOX) return NULL; } else { - return CeilingSkyBox != NULL ? CeilingSkyBox : (MoreFlags & SECF_NOCEILINGSKYBOX)? (ASkyViewpoint*)NULL : level.DefaultSkybox; + if (CeilingSkyBox != NULL) return CeilingSkyBox; + if (MoreFlags & SECF_NOCEILINGSKYBOX) return NULL; } + return level.DefaultSkybox; } From 355f70986a9f106f857f38b261b0cd306659c114 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 13:36:42 +0100 Subject: [PATCH 31/48] - fixed: 'give' inventory functions ignored an items MaxAmount setting. --- src/g_shared/a_pickups.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 5d494c935..e87929677 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -680,6 +680,7 @@ AInventory *AInventory::CreateCopy (AActor *other) { AInventory *copy; + Amount = MIN(Amount, MaxAmount); if (GoAway ()) { copy = static_cast(Spawn (GetClass(), 0, 0, 0, NO_REPLACE)); From 78deb70a000b72be1bfa67f2ab84298ce4e941f4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 13:40:18 +0100 Subject: [PATCH 32/48] - fixed: ConfigSections were not properly deallocated --- src/configfile.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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; From 6efc2a0ec747d90c536c2b3cb525caae1a3559d8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 25 Nov 2015 13:47:04 +0100 Subject: [PATCH 33/48] - fixed positioning of map name on Hexen automap. --- src/g_shared/shared_sbar.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index c0442fc74..b74350633 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; } } } From fca469b053a86d17088a357e019485a25e69f5cf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 26 Nov 2015 10:25:05 +0100 Subject: [PATCH 34/48] - moved the notification messages for SendToCommunicator into LANGUAGE. As a side effect this will now allow using custom messages with this function as well by using the arg2 parameter as part of the message's name (arg2=0 will use TXT_COMM0, arg1 TXT_COMM1 and so on.) --- src/p_lnspec.cpp | 14 ++++++++------ wadsrc/static/language.enu | 4 ++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 37d6d6058..35b0300ef 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, \ @@ -2985,13 +2986,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 (msg != 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/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"; From 7e8721687819fb83dd413982f21d91190ec02e40 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Nov 2015 10:44:37 +0100 Subject: [PATCH 35/48] - fixed: r_visibility values >= 205 appear to cause fixed point overflows at least on 5:4 aspect ratio so they should be blocked. --- src/r_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From a55d85c51c9a928218bcbde480a21c46f5714380 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Nov 2015 12:12:08 +0100 Subject: [PATCH 36/48] - do not wait infinitely for termination of the Timidity++ process. This can lock up the engine indefinitely if the child process fails to exit. --- src/sound/music_midi_timidity.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 ) From 3fadfec77d58d41b8aab7cbcabbe17182f1b4ccb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Nov 2015 00:43:39 +0100 Subject: [PATCH 37/48] - fixed typo in SendToCommunicator enhancement. --- src/p_lnspec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 35b0300ef..26e736cb5 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2991,7 +2991,7 @@ FUNC(LS_SendToCommunicator) FString msg; msg.Format("TXT_COMM%d", arg2); const char *str = GStrings[msg]; - if (msg != NULL) + if (str != NULL) { Printf (PRINT_CHAT, "%s\n", str); } From 888f356e58c31f3a88544c7dbb458f9225ae4091 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Nov 2015 12:43:01 +0100 Subject: [PATCH 38/48] - use the recently added text input menu item to add the config file for GUS, the patch set for Fluidsynth and the timidity.exe path for Timidity++ to the menu. Even though there is no proper file select box, this is still better than nothing. - changed the text input item so that it realigns itself to show the entire text when in text input mode. --- src/menu/optionmenuitems.h | 13 +++++++++++++ wadsrc/static/menudef.txt | 14 ++++++++++++++ 2 files changed, 27 insertions(+) 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/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 1b9a2c606..eef2629d3 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1590,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 } /*======================================= From 9bfd67678330da477c3bf996536b6eaa2e33ff66 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 11:28:26 +0100 Subject: [PATCH 39/48] - allow setting the FloatbobPhase through UDMF. --- specs/udmf_zdoom.txt | 1 + src/doomdata.h | 1 + src/namedef.h | 1 + src/p_buildmap.cpp | 1 + src/p_mobj.cpp | 1 + src/p_setup.cpp | 2 ++ src/p_udmf.cpp | 6 ++++++ 7 files changed, 13 insertions(+) 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/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/namedef.h b/src/namedef.h index 0d49bba76..22dbe1b51 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -396,6 +396,7 @@ xx(Roll) xx(Scale) xx(ScaleX) xx(ScaleY) +xx(Floatbobphase) xx(Blocking) xx(Blockmonsters) diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index 46c2b9f4c..670f53751 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_mobj.cpp b/src/p_mobj.cpp index d5c5eb5d9..ab89ae04c 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4917,6 +4917,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; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 311bf8fc4..50e7f41d6 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1758,6 +1758,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) @@ -1842,6 +1843,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; } 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); From 5515cb02a6059155ce4c075d02aa69d785879b2d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 11:35:12 +0100 Subject: [PATCH 40/48] - fixed incorrect error method call in decal parser. --- src/decallib.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decallib.cpp b/src/decallib.cpp index 0ea3423c4..08b4dfda2 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; } From 1a0faf47619c15575109fd315e5770cd3862e89f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 11:41:14 +0100 Subject: [PATCH 41/48] - allow optional decal generator definitions. --- src/decallib.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/decallib.cpp b/src/decallib.cpp index 08b4dfda2..14b824a7b 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -603,16 +603,17 @@ void FDecalLib::ParseGenerator (FScanner &sc) { const PClass *type; FDecalBase *decal; - AActor *actor; + bool optional = false; // Get name of generator (actor) sc.MustGetString (); + optional = sc.Compare("optional"); + type = PClass::FindClass (sc.String); if (type == NULL || type->ActorInfo == 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 (); @@ -628,11 +629,14 @@ void FDecalLib::ParseGenerator (FScanner &sc) 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); + } } } From 1ad02a6ce8503341e57be1d4601f51cb45b17207 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 12:10:12 +0100 Subject: [PATCH 42/48] - allow specifying infighting through skills. --- src/g_level.h | 2 ++ src/g_skill.cpp | 17 +++++++++++++++++ src/p_interaction.cpp | 6 ++---- src/p_map.cpp | 5 +---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/g_level.h b/src/g_level.h index 496c5c0f4..60b371ba0 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -565,6 +565,7 @@ enum ESkillProperty SKILLP_ArmorFactor, SKILLP_EasyKey, SKILLP_SlowMonsters, + SKILLP_Infight, }; int G_SkillProperty(ESkillProperty prop); const char * G_SkillName(); @@ -602,6 +603,7 @@ struct FSkillInfo fixed_t MonsterHealth; fixed_t FriendlyHealth; bool NoPain; + int Infighting; fixed_t ArmorFactor; FSkillInfo() {} diff --git a/src/g_skill.cpp b/src/g_skill.cpp index ddfdbb4cc..718f1cd00 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -83,6 +83,7 @@ void FMapInfoParser::ParseSkill () skill.FriendlyHealth = FRACUNIT; skill.NoPain = false; skill.ArmorFactor = FRACUNIT; + skill.Infighting = 0; sc.MustGetString(); skill.Name = sc.String; @@ -266,6 +267,14 @@ void FMapInfoParser::ParseSkill () sc.MustGetFloat(); skill.ArmorFactor = 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 +393,14 @@ int G_SkillProperty(ESkillProperty prop) case SKILLP_ArmorFactor: return AllSkills[gameskill].ArmorFactor; + + 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; diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 25724172c..203d1688d 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1639,10 +1639,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_map.cpp b/src/p_map.cpp index e8e5d6c84..5dd0967b9 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) { From 106886a9bb995e6975163c60df20e4eb1429a0d5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 12:30:50 +0100 Subject: [PATCH 43/48] - allow setting the ice translation with Thing_SetTranslation. This requires passing a magic value because this translation is defined differently than all the rest which can be used in ACS. --- src/p_lnspec.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 26e736cb5..9c93ca15c 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1600,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) { @@ -1616,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; From f7cdb28eaccc4ece9869f400b0e710420852bc37 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 12:58:17 +0100 Subject: [PATCH 44/48] - added a HealthFactor skill property. --- src/g_level.h | 2 ++ src/g_shared/a_pickups.cpp | 4 +++- src/g_skill.cpp | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/g_level.h b/src/g_level.h index 60b371ba0..018737f09 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -563,6 +563,7 @@ enum ESkillProperty SKILLP_FriendlyHealth, SKILLP_NoPain, SKILLP_ArmorFactor, + SKILLP_HealthFactor, SKILLP_EasyKey, SKILLP_SlowMonsters, SKILLP_Infight, @@ -605,6 +606,7 @@ struct FSkillInfo 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 e87929677..eb2831024 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -236,10 +236,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) { diff --git a/src/g_skill.cpp b/src/g_skill.cpp index 718f1cd00..bbee4ea11 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -84,6 +84,7 @@ void FMapInfoParser::ParseSkill () skill.NoPain = false; skill.ArmorFactor = FRACUNIT; skill.Infighting = 0; + skill.HealthFactor = FRACUNIT; sc.MustGetString(); skill.Name = sc.String; @@ -267,6 +268,12 @@ 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; From c9e4f120e721604939db490e1d3a91e6ef4657dc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 15:27:20 +0100 Subject: [PATCH 45/48] - forgot to save this before committing. --- src/g_skill.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/g_skill.cpp b/src/g_skill.cpp index bbee4ea11..8f2cdc9f0 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -401,6 +401,9 @@ 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; From f4a60f29f354e18d73f61dd53ca9eeee86a13d8e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 30 Nov 2015 09:21:45 +0100 Subject: [PATCH 46/48] - added missing sc.MustGetString() to 'optional' case of decal parser. --- src/decallib.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/decallib.cpp b/src/decallib.cpp index 14b824a7b..784998977 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -608,6 +608,7 @@ void FDecalLib::ParseGenerator (FScanner &sc) // Get name of generator (actor) sc.MustGetString (); optional = sc.Compare("optional"); + if (optional) sc.MustGetString(); type = PClass::FindClass (sc.String); if (type == NULL || type->ActorInfo == NULL) @@ -626,7 +627,7 @@ 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); } } if (type != NULL) From 8594bfaa8ba5e6a5834ce41576a7394e8990fc82 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 30 Nov 2015 11:42:08 -0600 Subject: [PATCH 47/48] A_CustomPunch Extension - Added Melee/Miss parameters just like A_CustomMeleeAttack. --- src/thingdef/thingdef_codeptr.cpp | 11 +++++++++-- wadsrc/static/actors/shared/inventory.txt | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 14440d884..9fd631d16 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1414,6 +1414,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) ACTION_PARAM_FIXED(LifeSteal, 5); ACTION_PARAM_INT(lifestealmax, 6); ACTION_PARAM_CLASS(armorbonustype, 7); + ACTION_PARAM_SOUND(MeleeSound, 8); + ACTION_PARAM_SOUND(MissSound, 9); if (!self->player) return; @@ -1443,7 +1445,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)) { @@ -1474,7 +1480,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)) diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index 13ae00936..a44515efb 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 spread_xy, float 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, int useammo = true, color color1 = "", color color2 = "", int flags = 0, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float 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); From 4adf421513ba7ebbeb81a2cb9ee9f149bf4446b8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 1 Dec 2015 14:30:57 +0200 Subject: [PATCH 48/48] Fix incomplete assignment operator of FSkillInfo See http://forum.zdoom.org/viewtopic.php?t=50026 --- src/g_skill.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/g_skill.cpp b/src/g_skill.cpp index 8f2cdc9f0..f4cb63ea5 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -490,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; }